You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by GitBox <gi...@apache.org> on 2022/07/22 00:13:49 UTC

[GitHub] [fineract] josehernandezfintecheandomx opened a new pull request, #2442: Delinquency buckets management for Loans

josehernandezfintecheandomx opened a new pull request, #2442:
URL: https://github.com/apache/fineract/pull/2442

   ## Description
   
   As a user would like to be set Delinquency overdue ranges in order to classify disbursed loans for accounting
   
   ## Checklist
   
   Please make sure these boxes are checked before submitting your pull request - thanks!
   
   - [ ] Write the commit message as per https://github.com/apache/fineract/#pull-requests
   
   - [ ] Acknowledge that we will not review PRs that are not passing the build _("green")_ - it is your responsibility to get a proposed PR to pass the build, not primarily the project's maintainers.
   
   - [ ] Create/update unit or integration tests for verifying the changes made.
   
   - [ ] Follow coding conventions at https://cwiki.apache.org/confluence/display/FINERACT/Coding+Conventions.
   
   - [ ] Add required Swagger annotation and update API documentation at fineract-provider/src/main/resources/static/legacy-docs/apiLive.htm with details of any API changes
   
   - [ ] Submission is not a "code dump".  (Large changes can be made "in repository" via a branch.  Ask on the developer mailing list for guidance, if required.)
   
   FYI our guidelines for code reviews are at https://cwiki.apache.org/confluence/display/FINERACT/Code+Review+Guide.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938098274


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");
+    }
+
+    @Test
+    public void testUpdateDelinquencyRanges() {
+        // given
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        jsonRange = DelinquencyRangesHelper.getAsJSON(1, 7);
+
+        // when
+        final PutDelinquencyRangeResponse delinquencyRangeResponse02 = DelinquencyRangesHelper.updateDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId(), jsonRange);
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse01.getResourceId());
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(delinquencyRangeResponse02);
+        assertNotNull(range);
+        assertNotEquals(3, range.getMaximumAgeDays());
+        assertEquals(7, range.getMaximumAgeDays());
+    }
+
+    @Test
+    public void testDelinquencyBuckets() {
+        // given
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 30);
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+
+        // when
+        final ArrayList<GetDelinquencyBucketsResponse> bucketList = DelinquencyBucketsHelper.getDelinquencyBuckets(requestSpec,
+                responseSpec);
+        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketResponse.getResourceId());
+
+        // then
+        assertNotNull(bucketList);
+        assertEquals(1, bucketList.size(), "Array size with one element");
+        assertNotNull(delinquencyBucket);
+    }
+}

Review Comment:
   @josehernandezfintecheandomx 
   Missing test cases:
   - Delinquency range is overlapping
   - Delinquency range is deleted
   - Delinquency range cannot be deleted due it is added to Loan
   - Delinquency bucket is already existing
   - Delinquency bucket was configured on Loan product
   - Delinquency bucket updated
   - Delinquency bucket deleted
   - Delinquency bucket cannot be deleted due it is configured on Loan product
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938351151


##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <createTable tableName="m_delinquency_bucket">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="name" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938061972


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java:
##########
@@ -239,6 +240,7 @@ public final class LoanAccountData {
     private LocalDate expectedDisbursementDate;
 
     private final CollectionData delinquent;

Review Comment:
   @josehernandezfintecheandomx What is the deal with this "other delinquent" logic?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938799421


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/api/DelinquencyApiResource.java:
##########
@@ -0,0 +1,217 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.Collection;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
+import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Path("delinquency")
+@Component
+@Tag(name = "Delinquency Range and Buckets Management", description = "Delinquency Range and Buckets management enables you to set up, fetch and adjust Delinquency overdue ranges")
+public class DelinquencyApiResource {
+
+    private final ApiRequestParameterHelper parameterHelper;
+    private final PlatformSecurityContext securityContext;
+    private final DefaultToApiJsonSerializer<DelinquencyBucketData> jsonSerializerBucket;
+    private final DefaultToApiJsonSerializer<DelinquencyRangeData> jsonSerializerRange;
+    private final DelinquencyReadPlatformService readPlatformService;
+    private final PortfolioCommandSourceWritePlatformService commandWritePlatformService;
+
+    @GET
+    @Path("ranges")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "List all Delinquency Ranges", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class)))) })
+    public String getDelinquencyRanges(@Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final Collection<DelinquencyRangeData> delinquencyRangeData = this.readPlatformService.retrieveAllDelinquencyRanges();
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @GET
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Retrieve a specific Delinquency Range based on the Id", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class))) })
+    public String getDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            @Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final DelinquencyRangeData delinquencyRangeData = this.readPlatformService.retrieveDelinquencyRange(delinquencyRangeId);
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @POST
+    @Path("ranges")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Create Delinquency Range", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeResponse.class))) })
+    public String createDelinquencyRange(final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().createDelinquencyRange().withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @PUT
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Range based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyRangeResponse.class))) })
+    public String updateDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().updateDelinquencyRange(delinquencyRangeId)
+                .withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @DELETE
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Range based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyRangeResponse.class))) })
+    public String deleteDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteDelinquencyRange(delinquencyRangeId).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @GET
+    @Path("buckets")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "List all Delinquency Buckets", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyBucketsResponse.class)))) })
+    public String getDelinquencyBuckets(@Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final Collection<DelinquencyBucketData> delinquencyBucketData = this.readPlatformService.retrieveAllDelinquencyBuckets();
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerBucket.serialize(settings, delinquencyBucketData);
+    }
+
+    @GET
+    @Path("buckets/{delinquencyBucketId}")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Retrieve a specific Delinquency Bucket based on the Id", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyBucketsResponse.class))) })
+    public String getDelinquencyBucket(
+            @PathParam("delinquencyBucketId") @Parameter(description = "delinquencyBucketId") final Long delinquencyBucketId,
+            @Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final DelinquencyBucketData delinquencyBucketData = this.readPlatformService.retrieveDelinquencyBucket(delinquencyBucketId);
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerBucket.serialize(settings, delinquencyBucketData);
+    }
+
+    @POST
+    @Path("buckets")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Create Delinquency Bucket", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyBucketRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyBucketResponse.class))) })
+    public String createDelinquencyBucket(final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().createDelinquencyBucket().withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @PUT
+    @Path("buckets/{delinquencyBucketId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Bucket based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyBucketRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyBucketResponse.class))) })
+    public String updateDelinquencyBucket(
+            @PathParam("delinquencyBucketId") @Parameter(description = "delinquencyBucketId") final Long delinquencyBucketId,
+            final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932808849


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/DelinquencyRange.java:
##########
@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
+import javax.validation.constraints.NotNull;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_delinquency_range", uniqueConstraints = {
+        @UniqueConstraint(name = "uq_delinquency_range_classification", columnNames = { "classification" }) })
+public class DelinquencyRange extends AbstractAuditableCustom {
+
+    @Column(name = "classification", nullable = false)
+    private String classification;
+
+    @Column(name = "min_age", nullable = false)

Review Comment:
   Done! Changed



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938082686


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyBucketParseAndValidator.java:
##########
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.validator;
+
+import com.google.gson.JsonObject;
+import java.util.ArrayList;
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.infrastructure.core.validator.ParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Component
+public class DelinquencyBucketParseAndValidator extends ParseAndValidator {
+
+    private final FromJsonHelper jsonHelper;
+
+    public DelinquencyBucketData validateAndParseUpdate(@NotNull final JsonCommand command) {

Review Comment:
   @josehernandezfintecheandomx Misleading method name. This validator is not used only for parsing and validating deliquency bucket creation request.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938497492


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java:
##########
@@ -287,6 +288,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
     @Column(name = "loan_product_counter")
     private Integer loanProductCounter;
 
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "delinquency_range_id", nullable = true)
+    private DelinquencyRange delinquencyRange;

Review Comment:
   @josehernandezfintecheandomx If LoanDeliquencyTags are for history:
   -  please rename it to "LoanDeliquencyHistory"
   - In the LoanDeliquencyTags, the loan OneToOne must be changed to ManyToOne as well!
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938351350


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java:
##########
@@ -0,0 +1,105 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
+import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyBucketMapper;
+import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyRangeMapper;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+@RequiredArgsConstructor
+@Service
+public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatformService {
+
+    private final JdbcTemplate jdbcTemplate;
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyBucketRepository repositoryBucket;
+    private final DelinquencyRangeMapper mapperRange;
+    private final DelinquencyBucketMapper mapperBucket;
+
+    @Override
+    public Collection<DelinquencyRangeData> retrieveAllDelinquencyRanges() {
+        final List<DelinquencyRange> delinquencyRangeList = repositoryRange.findAll();
+        return mapperRange.map(delinquencyRangeList);
+    }
+
+    @Override
+    public DelinquencyRangeData retrieveDelinquencyRange(Long delinquencyRangeId) {
+        DelinquencyRange delinquencyRangeList = repositoryRange.getReferenceById(delinquencyRangeId);
+        return mapperRange.map(delinquencyRangeList);
+    }
+
+    @Override
+    public Collection<DelinquencyBucketData> retrieveAllDelinquencyBuckets() {
+        final List<DelinquencyBucket> delinquencyRangeList = repositoryBucket.findAll();
+        return mapperBucket.map(delinquencyRangeList);
+    }
+
+    @Override
+    public DelinquencyBucketData retrieveDelinquencyBucket(Long delinquencyBucketId) {
+        try {
+            final DelinquencyBucket delinquencyBucket = repositoryBucket.getReferenceById(delinquencyBucketId);
+            final DelinquencyBucketData delinquencyBucketData = mapperBucket.map(delinquencyBucket);
+
+            final DelinquencyBucketMappingMapper rmMappings = new DelinquencyBucketMappingMapper();
+            final String sql = "select " + rmMappings.schema() + " where buc.id = ?";
+            List<DelinquencyRangeData> ranges = this.jdbcTemplate.query(sql, rmMappings, new Object[] { delinquencyBucketId }); // NOSONAR

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938072739


##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <createTable tableName="m_delinquency_bucket">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="name" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="3">
+        <createTable tableName="m_delinquency_bucket_mappings">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="delinquency_range_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="delinquency_bucket_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>

Review Comment:
   @josehernandezfintecheandomx last_modifiied_by and last_modified_on_utc cannot be null! Please change them!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938098274


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");
+    }
+
+    @Test
+    public void testUpdateDelinquencyRanges() {
+        // given
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        jsonRange = DelinquencyRangesHelper.getAsJSON(1, 7);
+
+        // when
+        final PutDelinquencyRangeResponse delinquencyRangeResponse02 = DelinquencyRangesHelper.updateDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId(), jsonRange);
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse01.getResourceId());
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(delinquencyRangeResponse02);
+        assertNotNull(range);
+        assertNotEquals(3, range.getMaximumAgeDays());
+        assertEquals(7, range.getMaximumAgeDays());
+    }
+
+    @Test
+    public void testDelinquencyBuckets() {
+        // given
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 30);
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+
+        // when
+        final ArrayList<GetDelinquencyBucketsResponse> bucketList = DelinquencyBucketsHelper.getDelinquencyBuckets(requestSpec,
+                responseSpec);
+        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketResponse.getResourceId());
+
+        // then
+        assertNotNull(bucketList);
+        assertEquals(1, bucketList.size(), "Array size with one element");
+        assertNotNull(delinquencyBucket);
+    }
+}

Review Comment:
   @josehernandezfintecheandomx 
   Missing test cases:
   - Delinquency range is deleted
   - Delinquency bucket is already existing
   - Delinquency bucket was configured on Loan product
   - Delinquency bucket updated
   - Delinquency bucket deleted
   - Delinquency bucket cannot be deleted due it is configured on Loan product
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] IOhacker commented on pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
IOhacker commented on PR #2442:
URL: https://github.com/apache/fineract/pull/2442#issuecomment-1208509639

   LGTM
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938097943


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/api/DelinquencyApiResource.java:
##########
@@ -0,0 +1,217 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.Collection;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
+import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Path("delinquency")
+@Component
+@Tag(name = "Delinquency Range and Buckets Management", description = "Delinquency Range and Buckets management enables you to set up, fetch and adjust Delinquency overdue ranges")
+public class DelinquencyApiResource {
+
+    private final ApiRequestParameterHelper parameterHelper;
+    private final PlatformSecurityContext securityContext;
+    private final DefaultToApiJsonSerializer<DelinquencyBucketData> jsonSerializerBucket;
+    private final DefaultToApiJsonSerializer<DelinquencyRangeData> jsonSerializerRange;
+    private final DelinquencyReadPlatformService readPlatformService;
+    private final PortfolioCommandSourceWritePlatformService commandWritePlatformService;
+
+    @GET
+    @Path("ranges")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "List all Delinquency Ranges", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class)))) })
+    public String getDelinquencyRanges(@Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final Collection<DelinquencyRangeData> delinquencyRangeData = this.readPlatformService.retrieveAllDelinquencyRanges();
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @GET
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Retrieve a specific Delinquency Range based on the Id", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class))) })
+    public String getDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            @Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final DelinquencyRangeData delinquencyRangeData = this.readPlatformService.retrieveDelinquencyRange(delinquencyRangeId);
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @POST
+    @Path("ranges")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Create Delinquency Range", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeResponse.class))) })
+    public String createDelinquencyRange(final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().createDelinquencyRange().withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @PUT
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Range based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyRangeResponse.class))) })
+    public String updateDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().updateDelinquencyRange(delinquencyRangeId)
+                .withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @DELETE
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Range based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyRangeResponse.class))) })
+    public String deleteDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteDelinquencyRange(delinquencyRangeId).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @GET
+    @Path("buckets")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "List all Delinquency Buckets", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyBucketsResponse.class)))) })
+    public String getDelinquencyBuckets(@Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final Collection<DelinquencyBucketData> delinquencyBucketData = this.readPlatformService.retrieveAllDelinquencyBuckets();
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerBucket.serialize(settings, delinquencyBucketData);
+    }
+
+    @GET
+    @Path("buckets/{delinquencyBucketId}")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Retrieve a specific Delinquency Bucket based on the Id", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyBucketsResponse.class))) })
+    public String getDelinquencyBucket(
+            @PathParam("delinquencyBucketId") @Parameter(description = "delinquencyBucketId") final Long delinquencyBucketId,
+            @Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final DelinquencyBucketData delinquencyBucketData = this.readPlatformService.retrieveDelinquencyBucket(delinquencyBucketId);
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerBucket.serialize(settings, delinquencyBucketData);
+    }
+
+    @POST
+    @Path("buckets")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Create Delinquency Bucket", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyBucketRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyBucketResponse.class))) })
+    public String createDelinquencyBucket(final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().createDelinquencyBucket().withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @PUT
+    @Path("buckets/{delinquencyBucketId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Bucket based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyBucketRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyBucketResponse.class))) })
+    public String updateDelinquencyBucket(
+            @PathParam("delinquencyBucketId") @Parameter(description = "delinquencyBucketId") final Long delinquencyBucketId,
+            final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().updateDelinquencyBucket(delinquencyBucketId)
+                .withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+

Review Comment:
   @josehernandezfintecheandomx Missing feature:
   Delete delinquency bucket



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r940043777


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");
+    }
+
+    @Test
+    public void testUpdateDelinquencyRanges() {
+        // given
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        jsonRange = DelinquencyRangesHelper.getAsJSON(1, 7);
+
+        // when
+        final PutDelinquencyRangeResponse delinquencyRangeResponse02 = DelinquencyRangesHelper.updateDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId(), jsonRange);
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse01.getResourceId());
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(delinquencyRangeResponse02);
+        assertNotNull(range);
+        assertNotEquals(3, range.getMaximumAgeDays());
+        assertEquals(7, range.getMaximumAgeDays());
+    }
+
+    @Test
+    public void testDelinquencyBuckets() {
+        // given
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 30);
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+
+        // when
+        final ArrayList<GetDelinquencyBucketsResponse> bucketList = DelinquencyBucketsHelper.getDelinquencyBuckets(requestSpec,
+                responseSpec);
+        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketResponse.getResourceId());
+
+        // then
+        assertNotNull(bucketList);
+        assertEquals(1, bucketList.size(), "Array size with one element");
+        assertNotNull(delinquencyBucket);
+    }
+}

Review Comment:
   @josehernandezfintecheandomx I am not sure what you mean here.... 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r937982005


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java:
##########
@@ -0,0 +1,183 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappings;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeNotFoundException;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlatformService {
+
+    private final DelinquencyBucketParseAndValidator dataValidatorBucket;
+    private final DelinquencyRangeParseAndValidator dataValidatorRange;
+
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyBucketRepository repositoryBucket;
+    private final DelinquencyBucketMappingsRepository repositoryBucketMappings;
+
+    @Override
+    public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyRange delinquencyRange = updateOrCreateDelinquencyRange(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        DelinquencyRange delinquencyRange = this.repositoryRange.getReferenceById(delinquencyRangeId);
+        if (delinquencyRange == null) {
+            throw DelinquencyRangeNotFoundException.notFound(delinquencyRangeId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyRange = updateDelinquencyRange(delinquencyRange, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        final DelinquencyRange delinquencyRange = repositoryRange.getReferenceById(delinquencyRangeId);
+        repositoryRange.delete(delinquencyRange);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).build();
+    }
+
+    @Override
+    public CommandProcessingResult createDelinquencyBucket(JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyBucket delinquencyBucket = updateOrCreateDelinquencyBucket(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        DelinquencyBucket delinquencyBucket = this.repositoryBucket.getReferenceById(delinquencyBucketId);

Review Comment:
   @josehernandezfintecheandomx EclipseLink will throw immediately "EntityNotFoundException" if the reference does not exist.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938071122


##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <createTable tableName="m_delinquency_bucket">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="name" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>

Review Comment:
   @josehernandezfintecheandomx last_modifiied_by and last_modified_on_utc cannot be null! Please change them!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938072499


##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <createTable tableName="m_delinquency_bucket">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="name" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">

Review Comment:
   @josehernandezfintecheandomx last_modifiied_by and last_modified_on_utc cannot be null! Please change them!



##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <createTable tableName="m_delinquency_bucket">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="name" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="3">
+        <createTable tableName="m_delinquency_bucket_mappings">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="delinquency_range_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="delinquency_bucket_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">

Review Comment:
   @josehernandezfintecheandomx last_modifiied_by and last_modified_on_utc cannot be null! Please change them!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] galovics commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
galovics commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r929795946


##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java:
##########
@@ -49,7 +49,8 @@ public enum JobName {
                                                                                                                                                                                                                     "Update Trial Balance Details"), EXECUTE_DIRTY_JOBS(
                                                                                                                                                                                                                             "Execute All Dirty Jobs"), INCREASE_BUSINESS_DATE_BY_1_DAY(
                                                                                                                                                                                                                                     "Increase Business Date by 1 day"), INCREASE_COB_DATE_BY_1_DAY(
-                                                                                                                                                                                                                                            "Increase COB Date by 1 day");
+                                                                                                                                                                                                                                            "Increase COB Date by 1 day"), LOAN_DELINQUENCY_CLASSIFICATION(
+                                                                                                                                                                                                                                                    "Loan Delinquency Classification");

Review Comment:
   Let's not add a new job. This PR should be only about the ability to configure delinquency buckets. Job will follow later.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/DelinquencyBucketMappings.java:
##########
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_delinquency_bucket_mappings", uniqueConstraints = {
+        @UniqueConstraint(name = "uq_delinquency_bucket_mapping", columnNames = { "delinquencyBucket", "delinquencyRange" }) })
+public class DelinquencyBucketMappings extends AbstractAuditableCustom {

Review Comment:
   Why do we need this mapping table in between the Bucket and the Ranges? Couldn't it be simplified to a Bucket -> OneToMany Range relationship?



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/DelinquencyBucket.java:
##########
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_delinquency_bucket", uniqueConstraints = {
+        @UniqueConstraint(name = "uq_delinquency_bucket_name", columnNames = { "name" }) })
+public class DelinquencyBucket extends AbstractAuditableCustom {
+
+    @Column(name = "name", nullable = false)
+    private String name;
+

Review Comment:
   Why doesn't the DelinquencyBucket entity has a relationship to all the related DelinquencyRange entities? It'd be definitely useful to have that.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/DelinquencyBucket.java:
##########
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_delinquency_bucket", uniqueConstraints = {
+        @UniqueConstraint(name = "uq_delinquency_bucket_name", columnNames = { "name" }) })
+public class DelinquencyBucket extends AbstractAuditableCustom {
+
+    @Column(name = "name", nullable = false)
+    private String name;
+
+    @Version
+    private Long version;
+
+    protected DelinquencyBucket(String name) {
+        this.name = name;
+    }
+
+    public static DelinquencyBucket instance(String name) {

Review Comment:
   Not sure why the static factory here as well.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyTags.java:
##########
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_delinquency_tags")
+public class LoanDelinquencyTags extends AbstractAuditableCustom {
+
+    @OneToOne(fetch = FetchType.EAGER)
+    @JoinColumn(name = "delinquency_range_id", nullable = false)
+    private DelinquencyRange delinquencyRange;
+
+    @OneToOne(fetch = FetchType.EAGER)
+    @JoinColumn(name = "loan_id", nullable = false)
+    private Loan loan;
+
+    @Version
+    private Long version;
+
+    public LoanDelinquencyTags(DelinquencyRange delinquencyRange, Loan loan) {
+        this.delinquencyRange = delinquencyRange;
+        this.loan = loan;
+    }
+
+    public static LoanDelinquencyTags instance(DelinquencyRange delinquencyRange, Loan loan) {

Review Comment:
   Not sure about the factory here either.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/DelinquencyRange.java:
##########
@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
+import javax.validation.constraints.NotNull;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_delinquency_range", uniqueConstraints = {
+        @UniqueConstraint(name = "uq_delinquency_range_classification", columnNames = { "classification" }) })
+public class DelinquencyRange extends AbstractAuditableCustom {
+
+    @Column(name = "classification", nullable = false)
+    private String classification;
+
+    @Column(name = "min_age", nullable = false)

Review Comment:
   Let's call out the unit as well. `min_age_days` or something like that.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java:
##########
@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
+import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
+import org.apache.fineract.infrastructure.jobs.service.JobName;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappings;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeNotFoundException;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlatformService {
+
+    private final DelinquencyBucketParseAndValidator dataValidatorBucket;
+    private final DelinquencyRangeParseAndValidator dataValidatorRange;
+
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyBucketRepository repositoryBucket;
+    private final DelinquencyBucketMappingsRepository repositoryBucketMappings;
+
+    @Override
+    public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyRange delinquencyRange = updateOrCreateDelinquencyRange(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        DelinquencyRange delinquencyRange = this.repositoryRange.getReferenceById(delinquencyRangeId);
+        if (delinquencyRange == null) {
+            throw DelinquencyRangeNotFoundException.notFound(delinquencyRangeId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyRange = updateDelinquencyRange(delinquencyRange, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        final DelinquencyRange delinquencyRange = repositoryRange.getReferenceById(delinquencyRangeId);
+        repositoryRange.delete(delinquencyRange);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).build();
+    }
+
+    @Override
+    public CommandProcessingResult createDelinquencyBucket(JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyBucket delinquencyBucket = updateOrCreateDelinquencyBucket(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        DelinquencyBucket delinquencyBucket = this.repositoryBucket.getReferenceById(delinquencyBucketId);
+        if (delinquencyBucket == null) {
+            throw DelinquencyBucketNotFoundException.notFound(delinquencyBucketId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyBucket = updateDelinquencyBucket(delinquencyBucket, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        final DelinquencyBucket delinquencyBucket = repositoryBucket.getReferenceById(delinquencyBucketId);

Review Comment:
   Shouldn't we handle the case when the method is called for a non-existing delinquencyBucketId; i.e. not found?



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java:
##########
@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
+import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
+import org.apache.fineract.infrastructure.jobs.service.JobName;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappings;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeNotFoundException;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlatformService {
+
+    private final DelinquencyBucketParseAndValidator dataValidatorBucket;
+    private final DelinquencyRangeParseAndValidator dataValidatorRange;
+
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyBucketRepository repositoryBucket;
+    private final DelinquencyBucketMappingsRepository repositoryBucketMappings;
+
+    @Override
+    public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyRange delinquencyRange = updateOrCreateDelinquencyRange(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        DelinquencyRange delinquencyRange = this.repositoryRange.getReferenceById(delinquencyRangeId);
+        if (delinquencyRange == null) {
+            throw DelinquencyRangeNotFoundException.notFound(delinquencyRangeId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyRange = updateDelinquencyRange(delinquencyRange, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        final DelinquencyRange delinquencyRange = repositoryRange.getReferenceById(delinquencyRangeId);
+        repositoryRange.delete(delinquencyRange);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).build();
+    }
+
+    @Override
+    public CommandProcessingResult createDelinquencyBucket(JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyBucket delinquencyBucket = updateOrCreateDelinquencyBucket(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        DelinquencyBucket delinquencyBucket = this.repositoryBucket.getReferenceById(delinquencyBucketId);
+        if (delinquencyBucket == null) {
+            throw DelinquencyBucketNotFoundException.notFound(delinquencyBucketId);

Review Comment:
   This code is never going to run because the delinquencyBucket variable is never null (as IntelliJ suggests it too).
   the getReferenceById call will return the reference if found and "throw an javax.persistence.EntityNotFoundException on first access"



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java:
##########
@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
+import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
+import org.apache.fineract.infrastructure.jobs.service.JobName;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappings;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeNotFoundException;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlatformService {
+
+    private final DelinquencyBucketParseAndValidator dataValidatorBucket;
+    private final DelinquencyRangeParseAndValidator dataValidatorRange;
+
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyBucketRepository repositoryBucket;
+    private final DelinquencyBucketMappingsRepository repositoryBucketMappings;
+
+    @Override
+    public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyRange delinquencyRange = updateOrCreateDelinquencyRange(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        DelinquencyRange delinquencyRange = this.repositoryRange.getReferenceById(delinquencyRangeId);
+        if (delinquencyRange == null) {
+            throw DelinquencyRangeNotFoundException.notFound(delinquencyRangeId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyRange = updateDelinquencyRange(delinquencyRange, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        final DelinquencyRange delinquencyRange = repositoryRange.getReferenceById(delinquencyRangeId);
+        repositoryRange.delete(delinquencyRange);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).build();
+    }
+
+    @Override
+    public CommandProcessingResult createDelinquencyBucket(JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyBucket delinquencyBucket = updateOrCreateDelinquencyBucket(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        DelinquencyBucket delinquencyBucket = this.repositoryBucket.getReferenceById(delinquencyBucketId);
+        if (delinquencyBucket == null) {
+            throw DelinquencyBucketNotFoundException.notFound(delinquencyBucketId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyBucket = updateDelinquencyBucket(delinquencyBucket, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        final DelinquencyBucket delinquencyBucket = repositoryBucket.getReferenceById(delinquencyBucketId);
+        repositoryBucket.delete(delinquencyBucket);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).build();
+    }
+
+    @Override
+    @CronTarget(jobName = JobName.LOAN_DELINQUENCY_CLASSIFICATION)

Review Comment:
   Not needed in the context of this PR.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java:
##########
@@ -292,6 +293,10 @@ public class Loan extends AbstractPersistableCustom {
     @Column(name = "loan_product_counter")
     private Integer loanProductCounter;
 
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "delinquency_range_id", nullable = true)
+    private DelinquencyRange delinquencyRange;

Review Comment:
   Maybe I misunderstood but isn't the LoanDelinquencyTags storing which delinquency range a loan is related to? What's the point of this? Thanks.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/DelinquencyRange.java:
##########
@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
+import javax.validation.constraints.NotNull;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_delinquency_range", uniqueConstraints = {
+        @UniqueConstraint(name = "uq_delinquency_range_classification", columnNames = { "classification" }) })
+public class DelinquencyRange extends AbstractAuditableCustom {

Review Comment:
   AbstractAuditableWithUTCDateTimeCustom



##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0020_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="createdby_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_date" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="lastmodifiedby_id" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="lastmodified_date" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <createTable tableName="m_delinquency_bucket">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="name" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="createdby_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_date" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="lastmodifiedby_id" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="lastmodified_date" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="3">
+        <createTable tableName="m_delinquency_bucket_mappings">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="delinquency_range_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="delinquency_bucket_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="createdby_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_date" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="lastmodifiedby_id" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="lastmodified_date" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="4">
+        <addForeignKeyConstraint baseColumnNames="delinquency_range_id" baseTableName="m_delinquency_bucket_mappings"
+                                 constraintName="FK_m_delinquency_range_mapping" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_range" validate="true"/>
+        <addForeignKeyConstraint baseColumnNames="delinquency_bucket_id" baseTableName="m_delinquency_bucket_mappings"
+                                 constraintName="FK_m_delinquency_bucket_mapping" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_bucket" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="5">
+        <addColumn tableName="m_product_loan">
+            <column name="delinquency_bucket_id" type="BIGINT" defaultValueComputed="NULL"/>
+        </addColumn>
+        <addForeignKeyConstraint baseColumnNames="delinquency_bucket_id" baseTableName="m_product_loan"
+                                 constraintName="FK_m_product_loan_m_delinquency_bucket" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_bucket" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="6">
+        <addColumn tableName="m_loan">
+            <column name="delinquency_range_id" type="BIGINT" defaultValueComputed="NULL"/>
+        </addColumn>
+        <addForeignKeyConstraint baseColumnNames="delinquency_range_id" baseTableName="m_loan"
+                                 constraintName="FK_m_delinquency_range_loan" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_range" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="7">
+        <createTable tableName="m_loan_delinquency_tags">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="delinquency_range_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="loan_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="createdby_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_date" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column defaultValueComputed="NULL" name="lastmodifiedby_id" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="lastmodified_date" type="DATETIME"/>
+        </createTable>
+        <addForeignKeyConstraint baseColumnNames="delinquency_range_id" baseTableName="m_loan_delinquency_tags"
+                                 constraintName="FK_m_delinquency_tags_range" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_range" validate="true"/>
+        <addForeignKeyConstraint baseColumnNames="loan_id" baseTableName="m_loan_delinquency_tags"
+                                 constraintName="FK_m_delinquency_tags_loan" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_loan" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="8">
+        <insert tableName="m_permission">
+            <column name="grouping" value="organisation"/>
+            <column name="code" value="READ_DELINQUENCY_BUCKET"/>
+            <column name="entity_name" value="DELINQUENCY_BUCKET"/>
+            <column name="action_name" value="READ"/>
+            <column name="can_maker_checker" valueBoolean="false"/>
+        </insert>
+        <insert tableName="m_permission">
+            <column name="grouping" value="organisation"/>
+            <column name="code" value="CREATE_DELINQUENCY_BUCKET"/>
+            <column name="entity_name" value="DELINQUENCY_BUCKET"/>
+            <column name="action_name" value="CREATE"/>
+            <column name="can_maker_checker" valueBoolean="false"/>
+        </insert>
+        <insert tableName="m_permission">
+            <column name="grouping" value="organisation"/>
+            <column name="code" value="UPDATE_DELINQUENCY_BUCKET"/>
+            <column name="entity_name" value="DELINQUENCY_BUCKET"/>
+            <column name="action_name" value="UPDATE"/>
+            <column name="can_maker_checker" valueBoolean="false"/>
+        </insert>
+        <insert tableName="m_permission">
+            <column name="grouping" value="organisation"/>
+            <column name="code" value="DELETE_DELINQUENCY_BUCKET"/>
+            <column name="entity_name" value="DELINQUENCY_BUCKET"/>
+            <column name="action_name" value="DELETE"/>
+            <column name="can_maker_checker" valueBoolean="false"/>
+        </insert>
+        <insert tableName="m_permission">
+            <column name="grouping" value="organisation"/>
+            <column name="code" value="CREATE_DELINQUENCY_RANGE"/>
+            <column name="entity_name" value="DELINQUENCY_RANGE"/>
+            <column name="action_name" value="CREATE"/>
+            <column name="can_maker_checker" valueBoolean="false"/>
+        </insert>
+        <insert tableName="m_permission">
+            <column name="grouping" value="organisation"/>
+            <column name="code" value="UPDATE_DELINQUENCY_RANGE"/>
+            <column name="entity_name" value="DELINQUENCY_RANGE"/>
+            <column name="action_name" value="UPDATE"/>
+            <column name="can_maker_checker" valueBoolean="false"/>
+        </insert>
+        <insert tableName="m_permission">
+            <column name="grouping" value="organisation"/>
+            <column name="code" value="DELETE_DELINQUENCY_RANGE"/>
+            <column name="entity_name" value="DELINQUENCY_RANGE"/>
+            <column name="action_name" value="DELETE"/>
+            <column name="can_maker_checker" valueBoolean="false"/>
+        </insert>
+    </changeSet>
+    <changeSet author="fineract" id="9">

Review Comment:
   No need for this in this ticket.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/data/DelinquencyBucketData.java:
##########
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.data;
+
+import java.io.Serializable;
+import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+@ToString
+@Getter
+@Setter
+public class DelinquencyBucketData implements Serializable {
+
+    private Long id;
+    private String name;
+    private List<DelinquencyRangeData> ranges;
+
+    public DelinquencyBucketData(String name, List<DelinquencyRangeData> ranges) {
+        this.name = name;
+        this.ranges = ranges;
+    }
+
+    public DelinquencyBucketData(Long id, String name, List<DelinquencyRangeData> ranges) {
+        this.id = id;
+        this.name = name;
+        this.ranges = ranges;
+    }
+
+    public static DelinquencyBucketData instance(Long id, String name, List<DelinquencyRangeData> ranges) {

Review Comment:
   Do you mind explaining why these static factories are needed? Thanks.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyTags.java:
##########
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_delinquency_tags")
+public class LoanDelinquencyTags extends AbstractAuditableCustom {
+
+    @OneToOne(fetch = FetchType.EAGER)

Review Comment:
   This is a big no-no. We don't want EAGER relationships for 99% of the cases, let's not do it.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/DelinquencyRange.java:
##########
@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
+import javax.validation.constraints.NotNull;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_delinquency_range", uniqueConstraints = {
+        @UniqueConstraint(name = "uq_delinquency_range_classification", columnNames = { "classification" }) })
+public class DelinquencyRange extends AbstractAuditableCustom {
+
+    @Column(name = "classification", nullable = false)

Review Comment:
   I thought we agreed that the classification name is going to come from the m_code table as a reference data. This looks to me like any user-entered String is accepted.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyTags.java:
##########
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_delinquency_tags")
+public class LoanDelinquencyTags extends AbstractAuditableCustom {

Review Comment:
   For the Tags, we're missing the historical information when a Loan gets moved in and out of a range.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java:
##########
@@ -0,0 +1,122 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
+import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyRangeMapper;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+@RequiredArgsConstructor
+@Service
+public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatformService {
+
+    private final JdbcTemplate jdbcTemplate;
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyRangeMapper mapperRange;
+
+    @Override
+    public Collection<DelinquencyRangeData> retrieveAllDelinquencyRanges() {
+        List<DelinquencyRange> delinquencyRangeList = repositoryRange.findAll();
+        return mapperRange.map(delinquencyRangeList);
+    }
+
+    @Override
+    public DelinquencyRangeData retrieveDelinquencyRange(Long delinquencyRangeId) {
+        DelinquencyRange delinquencyRangeList = repositoryRange.getReferenceById(delinquencyRangeId);
+        return mapperRange.map(delinquencyRangeList);
+    }
+
+    @Override
+    public Collection<DelinquencyBucketData> retrieveAllDelinquencyBuckets() {
+        final DelinquencyBucketMapper rm = new DelinquencyBucketMapper();

Review Comment:
   Big no-no. Let's not introduce native queries for places where we don't need to. The only place we should use native queries is where things couldn't be solved by using JPQL/CriteriaAPI/etc.
   
   This whole thing could be replaced by a simple Repository call with a Spring Data/JPQL Projection.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyTags.java:
##########
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_delinquency_tags")
+public class LoanDelinquencyTags extends AbstractAuditableCustom {
+
+    @OneToOne(fetch = FetchType.EAGER)
+    @JoinColumn(name = "delinquency_range_id", nullable = false)
+    private DelinquencyRange delinquencyRange;
+
+    @OneToOne(fetch = FetchType.EAGER)

Review Comment:
   Same here.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java:
##########
@@ -0,0 +1,122 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
+import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyRangeMapper;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+@RequiredArgsConstructor
+@Service
+public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatformService {
+
+    private final JdbcTemplate jdbcTemplate;
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyRangeMapper mapperRange;
+
+    @Override
+    public Collection<DelinquencyRangeData> retrieveAllDelinquencyRanges() {
+        List<DelinquencyRange> delinquencyRangeList = repositoryRange.findAll();
+        return mapperRange.map(delinquencyRangeList);
+    }
+
+    @Override
+    public DelinquencyRangeData retrieveDelinquencyRange(Long delinquencyRangeId) {
+        DelinquencyRange delinquencyRangeList = repositoryRange.getReferenceById(delinquencyRangeId);
+        return mapperRange.map(delinquencyRangeList);
+    }
+
+    @Override
+    public Collection<DelinquencyBucketData> retrieveAllDelinquencyBuckets() {
+        final DelinquencyBucketMapper rm = new DelinquencyBucketMapper();
+        final String sql = "select " + rm.schema() + " order by db.name";
+
+        return this.jdbcTemplate.query(sql, rm, new Object[] {}); // NOSONAR
+    }
+
+    @Override
+    public DelinquencyBucketData retrieveDelinquencyBucket(Long delinquencyBucketId) {
+        try {
+            final DelinquencyBucketMapper rm = new DelinquencyBucketMapper();

Review Comment:
   Same here.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java:
##########
@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
+import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
+import org.apache.fineract.infrastructure.jobs.service.JobName;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappings;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeNotFoundException;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlatformService {
+
+    private final DelinquencyBucketParseAndValidator dataValidatorBucket;
+    private final DelinquencyRangeParseAndValidator dataValidatorRange;
+
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyBucketRepository repositoryBucket;
+    private final DelinquencyBucketMappingsRepository repositoryBucketMappings;
+
+    @Override
+    public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyRange delinquencyRange = updateOrCreateDelinquencyRange(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        DelinquencyRange delinquencyRange = this.repositoryRange.getReferenceById(delinquencyRangeId);
+        if (delinquencyRange == null) {
+            throw DelinquencyRangeNotFoundException.notFound(delinquencyRangeId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyRange = updateDelinquencyRange(delinquencyRange, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        final DelinquencyRange delinquencyRange = repositoryRange.getReferenceById(delinquencyRangeId);
+        repositoryRange.delete(delinquencyRange);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).build();
+    }
+
+    @Override
+    public CommandProcessingResult createDelinquencyBucket(JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyBucket delinquencyBucket = updateOrCreateDelinquencyBucket(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        DelinquencyBucket delinquencyBucket = this.repositoryBucket.getReferenceById(delinquencyBucketId);
+        if (delinquencyBucket == null) {
+            throw DelinquencyBucketNotFoundException.notFound(delinquencyBucketId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyBucket = updateDelinquencyBucket(delinquencyBucket, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        final DelinquencyBucket delinquencyBucket = repositoryBucket.getReferenceById(delinquencyBucketId);
+        repositoryBucket.delete(delinquencyBucket);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).build();
+    }
+
+    @Override
+    @CronTarget(jobName = JobName.LOAN_DELINQUENCY_CLASSIFICATION)
+    public void setLoanDelinquencyClassification() throws JobExecutionException {
+
+    }
+
+    private DelinquencyRange updateOrCreateDelinquencyRange(DelinquencyRangeData data, Map<String, Object> changes) {

Review Comment:
   I'm not really sure why these 2 cases have been implemented as one method.
   
   Let's separate the responsibilities and have one method for creation and one for update.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/DelinquencyBucket.java:
##########
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_delinquency_bucket", uniqueConstraints = {
+        @UniqueConstraint(name = "uq_delinquency_bucket_name", columnNames = { "name" }) })
+public class DelinquencyBucket extends AbstractAuditableCustom {

Review Comment:
   Please use the new AbstractAuditableWithUTCDateTimeCustom



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java:
##########
@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
+import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
+import org.apache.fineract.infrastructure.jobs.service.JobName;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappings;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeNotFoundException;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlatformService {
+
+    private final DelinquencyBucketParseAndValidator dataValidatorBucket;
+    private final DelinquencyRangeParseAndValidator dataValidatorRange;
+
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyBucketRepository repositoryBucket;
+    private final DelinquencyBucketMappingsRepository repositoryBucketMappings;
+
+    @Override
+    public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyRange delinquencyRange = updateOrCreateDelinquencyRange(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        DelinquencyRange delinquencyRange = this.repositoryRange.getReferenceById(delinquencyRangeId);
+        if (delinquencyRange == null) {
+            throw DelinquencyRangeNotFoundException.notFound(delinquencyRangeId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyRange = updateDelinquencyRange(delinquencyRange, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        final DelinquencyRange delinquencyRange = repositoryRange.getReferenceById(delinquencyRangeId);
+        repositoryRange.delete(delinquencyRange);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).build();
+    }
+
+    @Override
+    public CommandProcessingResult createDelinquencyBucket(JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyBucket delinquencyBucket = updateOrCreateDelinquencyBucket(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        DelinquencyBucket delinquencyBucket = this.repositoryBucket.getReferenceById(delinquencyBucketId);
+        if (delinquencyBucket == null) {
+            throw DelinquencyBucketNotFoundException.notFound(delinquencyBucketId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyBucket = updateDelinquencyBucket(delinquencyBucket, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        final DelinquencyBucket delinquencyBucket = repositoryBucket.getReferenceById(delinquencyBucketId);
+        repositoryBucket.delete(delinquencyBucket);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).build();
+    }
+
+    @Override
+    @CronTarget(jobName = JobName.LOAN_DELINQUENCY_CLASSIFICATION)
+    public void setLoanDelinquencyClassification() throws JobExecutionException {
+
+    }
+
+    private DelinquencyRange updateOrCreateDelinquencyRange(DelinquencyRangeData data, Map<String, Object> changes) {
+        Optional<DelinquencyRange> delinquencyRange = repositoryRange.findByClassification(data.getClassification());
+
+        if (delinquencyRange.isEmpty()) {
+            DelinquencyRange newDelinquencyRange = DelinquencyRange.instance(data.getClassification(), data.getMinimumAge(),
+                    data.getMaximumAge());
+            return repositoryRange.saveAndFlush(newDelinquencyRange);
+        } else {
+            return updateDelinquencyRange(delinquencyRange.get(), data, changes);
+        }
+    }
+
+    private DelinquencyRange updateDelinquencyRange(DelinquencyRange delinquencyRange, DelinquencyRangeData data,
+            Map<String, Object> changes) {
+        if (!data.getClassification().equalsIgnoreCase(delinquencyRange.getClassification())) {
+            delinquencyRange.setClassification(data.getClassification());
+            changes.put(DelinquencyApiConstants.CLASSIFICATION_PARAM_NAME, data.getClassification());
+        }
+        if (!data.getMinimumAge().equals(delinquencyRange.getMinimumAge())) {
+            delinquencyRange.setMinimumAge(data.getMinimumAge());
+            changes.put(DelinquencyApiConstants.MINIMUMAGE_PARAM_NAME, data.getMinimumAge());
+        }
+        if (!data.getMaximumAge().equals(delinquencyRange.getMaximumAge())) {
+            delinquencyRange.setMaximumAge(data.getMaximumAge());
+            changes.put(DelinquencyApiConstants.MAXIMUMAGE_PARAM_NAME, data.getMaximumAge());
+        }
+        if (!changes.isEmpty()) {
+            delinquencyRange = repositoryRange.saveAndFlush(delinquencyRange);
+        }
+        return delinquencyRange;
+    }
+
+    private DelinquencyBucket updateOrCreateDelinquencyBucket(DelinquencyBucketData data, Map<String, Object> changes) {

Review Comment:
   Same here.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/DelinquencyBucketMappings.java:
##########
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_delinquency_bucket_mappings", uniqueConstraints = {
+        @UniqueConstraint(name = "uq_delinquency_bucket_mapping", columnNames = { "delinquencyBucket", "delinquencyRange" }) })
+public class DelinquencyBucketMappings extends AbstractAuditableCustom {

Review Comment:
   AbstractAuditableWithUTCDateTimeCustom



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyTags.java:
##########
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_delinquency_tags")
+public class LoanDelinquencyTags extends AbstractAuditableCustom {

Review Comment:
   AbstractAuditableWithUTCDateTimeCustom



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932894677


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java:
##########
@@ -0,0 +1,122 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
+import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyRangeMapper;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+@RequiredArgsConstructor
+@Service
+public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatformService {
+
+    private final JdbcTemplate jdbcTemplate;
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyRangeMapper mapperRange;
+
+    @Override
+    public Collection<DelinquencyRangeData> retrieveAllDelinquencyRanges() {
+        List<DelinquencyRange> delinquencyRangeList = repositoryRange.findAll();
+        return mapperRange.map(delinquencyRangeList);
+    }
+
+    @Override
+    public DelinquencyRangeData retrieveDelinquencyRange(Long delinquencyRangeId) {
+        DelinquencyRange delinquencyRangeList = repositoryRange.getReferenceById(delinquencyRangeId);
+        return mapperRange.map(delinquencyRangeList);
+    }
+
+    @Override
+    public Collection<DelinquencyBucketData> retrieveAllDelinquencyBuckets() {
+        final DelinquencyBucketMapper rm = new DelinquencyBucketMapper();
+        final String sql = "select " + rm.schema() + " order by db.name";
+
+        return this.jdbcTemplate.query(sql, rm, new Object[] {}); // NOSONAR
+    }
+
+    @Override
+    public DelinquencyBucketData retrieveDelinquencyBucket(Long delinquencyBucketId) {
+        try {
+            final DelinquencyBucketMapper rm = new DelinquencyBucketMapper();

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932772338


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/data/DelinquencyBucketData.java:
##########
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.data;
+
+import java.io.Serializable;
+import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+@ToString
+@Getter
+@Setter
+public class DelinquencyBucketData implements Serializable {
+
+    private Long id;
+    private String name;
+    private List<DelinquencyRangeData> ranges;
+
+    public DelinquencyBucketData(String name, List<DelinquencyRangeData> ranges) {
+        this.name = name;
+        this.ranges = ranges;
+    }
+
+    public DelinquencyBucketData(Long id, String name, List<DelinquencyRangeData> ranges) {
+        this.id = id;
+        this.name = name;
+        this.ranges = ranges;
+    }
+
+    public static DelinquencyBucketData instance(Long id, String name, List<DelinquencyRangeData> ranges) {

Review Comment:
   Done! Removed



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938098274


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");
+    }
+
+    @Test
+    public void testUpdateDelinquencyRanges() {
+        // given
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        jsonRange = DelinquencyRangesHelper.getAsJSON(1, 7);
+
+        // when
+        final PutDelinquencyRangeResponse delinquencyRangeResponse02 = DelinquencyRangesHelper.updateDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId(), jsonRange);
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse01.getResourceId());
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(delinquencyRangeResponse02);
+        assertNotNull(range);
+        assertNotEquals(3, range.getMaximumAgeDays());
+        assertEquals(7, range.getMaximumAgeDays());
+    }
+
+    @Test
+    public void testDelinquencyBuckets() {
+        // given
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 30);
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+
+        // when
+        final ArrayList<GetDelinquencyBucketsResponse> bucketList = DelinquencyBucketsHelper.getDelinquencyBuckets(requestSpec,
+                responseSpec);
+        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketResponse.getResourceId());
+
+        // then
+        assertNotNull(bucketList);
+        assertEquals(1, bucketList.size(), "Array size with one element");
+        assertNotNull(delinquencyBucket);
+    }
+}

Review Comment:
   @josehernandezfintecheandomx 
   Missing test cases:
   - Delinquency range is overlapping
   - Delinquency range is deleted
   - Delinquency bucket is already existing
   - Delinquency bucket was configured on Loan product
   - Delinquency bucket updated
   - Delinquency bucket deleted
   - Delinquency bucket cannot be deleted due it is configured on Loan product
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r940010175


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyRangeParseAndValidator.java:
##########
@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.validator;
+
+import com.google.gson.JsonObject;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import javax.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.infrastructure.core.validator.ParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Component
+public class DelinquencyRangeParseAndValidator extends ParseAndValidator {
+
+    private final FromJsonHelper jsonHelper;
+
+    public DelinquencyRangeData validateAndParseUpdate(@NotNull final JsonCommand command) {

Review Comment:
   @josehernandezfintecheandomx I see no changes here... still has the same method name which refer to "Update" but this validation is used to validate and parse the "create" request as well. Wouldnt it be better to use "validateAndParseRequest"?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932778894


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyTags.java:
##########
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_delinquency_tags")
+public class LoanDelinquencyTags extends AbstractAuditableCustom {

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932809571


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyTags.java:
##########
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_delinquency_tags")
+public class LoanDelinquencyTags extends AbstractAuditableCustom {
+
+    @OneToOne(fetch = FetchType.EAGER)

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932825863


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java:
##########
@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
+import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
+import org.apache.fineract.infrastructure.jobs.service.JobName;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappings;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeNotFoundException;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlatformService {
+
+    private final DelinquencyBucketParseAndValidator dataValidatorBucket;
+    private final DelinquencyRangeParseAndValidator dataValidatorRange;
+
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyBucketRepository repositoryBucket;
+    private final DelinquencyBucketMappingsRepository repositoryBucketMappings;
+
+    @Override
+    public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyRange delinquencyRange = updateOrCreateDelinquencyRange(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        DelinquencyRange delinquencyRange = this.repositoryRange.getReferenceById(delinquencyRangeId);
+        if (delinquencyRange == null) {
+            throw DelinquencyRangeNotFoundException.notFound(delinquencyRangeId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyRange = updateDelinquencyRange(delinquencyRange, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        final DelinquencyRange delinquencyRange = repositoryRange.getReferenceById(delinquencyRangeId);
+        repositoryRange.delete(delinquencyRange);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).build();
+    }
+
+    @Override
+    public CommandProcessingResult createDelinquencyBucket(JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyBucket delinquencyBucket = updateOrCreateDelinquencyBucket(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        DelinquencyBucket delinquencyBucket = this.repositoryBucket.getReferenceById(delinquencyBucketId);
+        if (delinquencyBucket == null) {
+            throw DelinquencyBucketNotFoundException.notFound(delinquencyBucketId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyBucket = updateDelinquencyBucket(delinquencyBucket, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        final DelinquencyBucket delinquencyBucket = repositoryBucket.getReferenceById(delinquencyBucketId);
+        repositoryBucket.delete(delinquencyBucket);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).build();
+    }
+
+    @Override
+    @CronTarget(jobName = JobName.LOAN_DELINQUENCY_CLASSIFICATION)
+    public void setLoanDelinquencyClassification() throws JobExecutionException {
+
+    }
+
+    private DelinquencyRange updateOrCreateDelinquencyRange(DelinquencyRangeData data, Map<String, Object> changes) {
+        Optional<DelinquencyRange> delinquencyRange = repositoryRange.findByClassification(data.getClassification());
+
+        if (delinquencyRange.isEmpty()) {
+            DelinquencyRange newDelinquencyRange = DelinquencyRange.instance(data.getClassification(), data.getMinimumAge(),
+                    data.getMaximumAge());
+            return repositoryRange.saveAndFlush(newDelinquencyRange);
+        } else {
+            return updateDelinquencyRange(delinquencyRange.get(), data, changes);
+        }
+    }
+
+    private DelinquencyRange updateDelinquencyRange(DelinquencyRange delinquencyRange, DelinquencyRangeData data,
+            Map<String, Object> changes) {
+        if (!data.getClassification().equalsIgnoreCase(delinquencyRange.getClassification())) {
+            delinquencyRange.setClassification(data.getClassification());
+            changes.put(DelinquencyApiConstants.CLASSIFICATION_PARAM_NAME, data.getClassification());
+        }
+        if (!data.getMinimumAge().equals(delinquencyRange.getMinimumAge())) {
+            delinquencyRange.setMinimumAge(data.getMinimumAge());
+            changes.put(DelinquencyApiConstants.MINIMUMAGE_PARAM_NAME, data.getMinimumAge());
+        }
+        if (!data.getMaximumAge().equals(delinquencyRange.getMaximumAge())) {
+            delinquencyRange.setMaximumAge(data.getMaximumAge());
+            changes.put(DelinquencyApiConstants.MAXIMUMAGE_PARAM_NAME, data.getMaximumAge());
+        }
+        if (!changes.isEmpty()) {
+            delinquencyRange = repositoryRange.saveAndFlush(delinquencyRange);
+        }
+        return delinquencyRange;
+    }
+
+    private DelinquencyBucket updateOrCreateDelinquencyBucket(DelinquencyBucketData data, Map<String, Object> changes) {

Review Comment:
   The same as above, same recent implementation as BusinessDate



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938775278


##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <createTable tableName="m_delinquency_bucket">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="name" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="3">
+        <createTable tableName="m_delinquency_bucket_mappings">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="delinquency_range_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="delinquency_bucket_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938815717


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyTags.java:
##########
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_delinquency_tags")
+public class LoanDelinquencyTags extends AbstractAuditableWithUTCDateTimeCustom {
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "delinquency_range_id", nullable = false)
+    private DelinquencyRange delinquencyRange;
+
+    @OneToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "loan_id", nullable = false)
+    private Loan loan;

Review Comment:
   Done! moved to ManyToOne



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r937925579


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java:
##########
@@ -0,0 +1,105 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
+import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyBucketMapper;
+import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyRangeMapper;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+@RequiredArgsConstructor
+@Service
+public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatformService {
+
+    private final JdbcTemplate jdbcTemplate;
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyBucketRepository repositoryBucket;
+    private final DelinquencyRangeMapper mapperRange;
+    private final DelinquencyBucketMapper mapperBucket;
+
+    @Override
+    public Collection<DelinquencyRangeData> retrieveAllDelinquencyRanges() {
+        final List<DelinquencyRange> delinquencyRangeList = repositoryRange.findAll();
+        return mapperRange.map(delinquencyRangeList);
+    }
+
+    @Override
+    public DelinquencyRangeData retrieveDelinquencyRange(Long delinquencyRangeId) {
+        DelinquencyRange delinquencyRangeList = repositoryRange.getReferenceById(delinquencyRangeId);
+        return mapperRange.map(delinquencyRangeList);
+    }
+
+    @Override
+    public Collection<DelinquencyBucketData> retrieveAllDelinquencyBuckets() {
+        final List<DelinquencyBucket> delinquencyRangeList = repositoryBucket.findAll();
+        return mapperBucket.map(delinquencyRangeList);
+    }
+
+    @Override
+    public DelinquencyBucketData retrieveDelinquencyBucket(Long delinquencyBucketId) {
+        try {
+            final DelinquencyBucket delinquencyBucket = repositoryBucket.getReferenceById(delinquencyBucketId);
+            final DelinquencyBucketData delinquencyBucketData = mapperBucket.map(delinquencyBucket);
+
+            final DelinquencyBucketMappingMapper rmMappings = new DelinquencyBucketMappingMapper();
+            final String sql = "select " + rmMappings.schema() + " where buc.id = ?";
+            List<DelinquencyRangeData> ranges = this.jdbcTemplate.query(sql, rmMappings, new Object[] { delinquencyBucketId }); // NOSONAR

Review Comment:
   Minor: the query params can be unwrapped....



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938064690


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java:
##########
@@ -287,6 +288,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
     @Column(name = "loan_product_counter")
     private Integer loanProductCounter;
 
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "delinquency_range_id", nullable = true)
+    private DelinquencyRange delinquencyRange;

Review Comment:
   @josehernandezfintecheandomx  We are storing the actual DeliquencyRange at 2 places:
   - Loan.deliquencyRange
   - LoanDeliquencyTags 
   
   Please pick just one place to store the same data!
   If you want this detail on the Loan, please rather add the LoanDeliquenctTag as association



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938190883


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/handler/UpdateDelinquencyBucketCommandHandler.java:
##########
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.handler;
+
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@RequiredArgsConstructor
+@CommandType(entity = "DELINQUENCY_BUCKET", action = "CREATE")

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932895022


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java:
##########
@@ -0,0 +1,122 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
+import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyRangeMapper;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+@RequiredArgsConstructor
+@Service
+public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatformService {
+
+    private final JdbcTemplate jdbcTemplate;
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyRangeMapper mapperRange;
+
+    @Override
+    public Collection<DelinquencyRangeData> retrieveAllDelinquencyRanges() {
+        List<DelinquencyRange> delinquencyRangeList = repositoryRange.findAll();
+        return mapperRange.map(delinquencyRangeList);
+    }
+
+    @Override
+    public DelinquencyRangeData retrieveDelinquencyRange(Long delinquencyRangeId) {
+        DelinquencyRange delinquencyRangeList = repositoryRange.getReferenceById(delinquencyRangeId);
+        return mapperRange.map(delinquencyRangeList);
+    }
+
+    @Override
+    public Collection<DelinquencyBucketData> retrieveAllDelinquencyBuckets() {
+        final DelinquencyBucketMapper rm = new DelinquencyBucketMapper();

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938820396


##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <createTable tableName="m_delinquency_bucket">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="name" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938823800


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java:
##########
@@ -239,6 +240,7 @@ public final class LoanAccountData {
     private LocalDate expectedDisbursementDate;
 
     private final CollectionData delinquent;

Review Comment:
   It was to store some attributes related to delinquency too, please review the next ticket https://mifosforge.jira.com/browse/PS-600



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r939600839


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyTags.java:
##########
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_delinquency_tags")
+public class LoanDelinquencyTags extends AbstractAuditableWithUTCDateTimeCustom {

Review Comment:
   Refactoring done! In the next PR for Job Loan Tags application will be extended that



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938215895


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java:
##########
@@ -287,6 +288,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
     @Column(name = "loan_product_counter")
     private Integer loanProductCounter;
 
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "delinquency_range_id", nullable = true)
+    private DelinquencyRange delinquencyRange;

Review Comment:
   We need both:
   `LoanDeliquencyTags` will be for the history (It is part of the requirement), and It could be used to know the latest tags, but How you will know when the Loan account has had one o more tags in the past but now It has not due It's not in arrears? So, that it the reason to use `Loan.deliquencyRange` 



##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938070880


##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>

Review Comment:
   @josehernandezfintecheandomx last_modifiied_by and last_modified_on_utc cannot be null! Please change them!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r937994402


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java:
##########
@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
+import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
+import org.apache.fineract.infrastructure.jobs.service.JobName;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappings;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeNotFoundException;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlatformService {
+
+    private final DelinquencyBucketParseAndValidator dataValidatorBucket;
+    private final DelinquencyRangeParseAndValidator dataValidatorRange;
+
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyBucketRepository repositoryBucket;
+    private final DelinquencyBucketMappingsRepository repositoryBucketMappings;
+
+    @Override
+    public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyRange delinquencyRange = updateOrCreateDelinquencyRange(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        DelinquencyRange delinquencyRange = this.repositoryRange.getReferenceById(delinquencyRangeId);
+        if (delinquencyRange == null) {
+            throw DelinquencyRangeNotFoundException.notFound(delinquencyRangeId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyRange = updateDelinquencyRange(delinquencyRange, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        final DelinquencyRange delinquencyRange = repositoryRange.getReferenceById(delinquencyRangeId);
+        repositoryRange.delete(delinquencyRange);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).build();
+    }
+
+    @Override
+    public CommandProcessingResult createDelinquencyBucket(JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyBucket delinquencyBucket = updateOrCreateDelinquencyBucket(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        DelinquencyBucket delinquencyBucket = this.repositoryBucket.getReferenceById(delinquencyBucketId);
+        if (delinquencyBucket == null) {
+            throw DelinquencyBucketNotFoundException.notFound(delinquencyBucketId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyBucket = updateDelinquencyBucket(delinquencyBucket, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        final DelinquencyBucket delinquencyBucket = repositoryBucket.getReferenceById(delinquencyBucketId);
+        repositoryBucket.delete(delinquencyBucket);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).build();
+    }
+
+    @Override
+    @CronTarget(jobName = JobName.LOAN_DELINQUENCY_CLASSIFICATION)
+    public void setLoanDelinquencyClassification() throws JobExecutionException {
+
+    }
+
+    private DelinquencyRange updateOrCreateDelinquencyRange(DelinquencyRangeData data, Map<String, Object> changes) {

Review Comment:
   Guilty... :/



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r940000771


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/api/DelinquencyApiResourceSwagger.java:
##########
@@ -0,0 +1,116 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+public final class DelinquencyApiResourceSwagger {
+
+    private DelinquencyApiResourceSwagger() {}
+
+    @Schema(description = "GetDelinquencyRangesResponse")
+    public static final class GetDelinquencyRangesResponse {
+
+        private GetDelinquencyRangesResponse() {}
+
+        @Schema(example = "1")
+        public Integer id;
+        @Schema(example = "Delinquent 1")
+        public String classification;
+        @Schema(example = "1")
+        public Long minimumAgeDays;
+        @Schema(example = "3")
+        public Long maximumAgeDays;
+    }
+
+    @Schema(description = "GetDelinquencyBucketsResponse")
+    public static final class GetDelinquencyBucketsResponse {
+
+        private GetDelinquencyBucketsResponse() {}
+
+        @Schema(example = "1")
+        public Integer id;
+        @Schema(example = "Delinquent Bucket Set 1")
+        public String name;
+    }
+
+    @Schema(description = "PostDelinquencyRangeRequest")
+    public static final class PostDelinquencyRangeRequest {
+
+        private PostDelinquencyRangeRequest() {}
+
+        @Schema(example = "Delinquent 1")
+        public String classification;
+        @Schema(example = "1")
+        public Integer minimumAgeDays;
+        @Schema(example = "3")
+        public Integer maximumAgeDays;
+        @Schema(example = "en")
+        public String locale;
+    }
+
+    @Schema(description = "PostDelinquencyRangeResponse")
+    public static final class PostDelinquencyRangeResponse {
+
+        private PostDelinquencyRangeResponse() {}
+
+        @Schema(example = "1")
+        public Integer resourceId;
+    }
+
+    @Schema(description = "PutDelinquencyRangeResponse")
+    public static final class PutDelinquencyRangeResponse {
+
+        private PutDelinquencyRangeResponse() {}
+
+        @Schema(example = "1")
+        public Integer resourceId;
+        public PostDelinquencyRangeRequest changes;
+    }
+
+    @Schema(description = "PostDelinquencyBucketRequest")
+    public static final class PostDelinquencyBucketRequest {
+
+        private PostDelinquencyBucketRequest() {}
+
+        @Schema(example = "Delinquent 1")
+        public String name;
+        @Schema(example = "[1,2,3]")
+        public Long[] ranges;
+    }
+
+    @Schema(description = "PostDelinquencyBucketResponse")
+    public static final class PostDelinquencyBucketResponse {
+
+        private PostDelinquencyBucketResponse() {}
+
+        @Schema(example = "1")
+        public Integer resourceId;
+    }
+
+    @Schema(description = "PutDelinquencyBucketResponse")
+    public static final class PutDelinquencyBucketResponse {
+
+        private PutDelinquencyBucketResponse() {}
+
+        @Schema(example = "1")
+        public Integer resourceId;
+    }
+

Review Comment:
   @josehernandezfintecheandomx Delete bucket request&response or delete range request&response is missing



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938499431


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyTags.java:
##########
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_delinquency_tags")
+public class LoanDelinquencyTags extends AbstractAuditableWithUTCDateTimeCustom {

Review Comment:
   @josehernandezfintecheandomx If this is history, please rename it to LoanDelinquencyHistory and the table name as well.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938061972


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java:
##########
@@ -239,6 +240,7 @@ public final class LoanAccountData {
     private LocalDate expectedDisbursementDate;
 
     private final CollectionData delinquent;

Review Comment:
   @josehernandezfintecheandomx What is the deal with this "other delinquency" logic?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938808286


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");
+    }
+
+    @Test
+    public void testUpdateDelinquencyRanges() {
+        // given
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        jsonRange = DelinquencyRangesHelper.getAsJSON(1, 7);
+
+        // when
+        final PutDelinquencyRangeResponse delinquencyRangeResponse02 = DelinquencyRangesHelper.updateDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId(), jsonRange);
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse01.getResourceId());
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(delinquencyRangeResponse02);
+        assertNotNull(range);
+        assertNotEquals(3, range.getMaximumAgeDays());
+        assertEquals(7, range.getMaximumAgeDays());
+    }
+
+    @Test
+    public void testDelinquencyBuckets() {
+        // given
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 30);
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+
+        // when
+        final ArrayList<GetDelinquencyBucketsResponse> bucketList = DelinquencyBucketsHelper.getDelinquencyBuckets(requestSpec,
+                responseSpec);
+        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketResponse.getResourceId());
+
+        // then
+        assertNotNull(bucketList);
+        assertEquals(1, bucketList.size(), "Array size with one element");
+        assertNotNull(delinquencyBucket);

Review Comment:
   If we add that, the random way to run the tests will fail sometimes due some ranges were added in other tests could be considered here too



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938820551


##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938775474


##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <createTable tableName="m_delinquency_bucket">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="name" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="3">
+        <createTable tableName="m_delinquency_bucket_mappings">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="delinquency_range_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="delinquency_bucket_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="4">
+        <addForeignKeyConstraint baseColumnNames="delinquency_range_id" baseTableName="m_delinquency_bucket_mappings"
+                                 constraintName="FK_m_delinquency_range_mapping" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_range" validate="true"/>
+        <addForeignKeyConstraint baseColumnNames="delinquency_bucket_id" baseTableName="m_delinquency_bucket_mappings"
+                                 constraintName="FK_m_delinquency_bucket_mapping" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_bucket" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="5">
+        <addColumn tableName="m_product_loan">
+            <column name="delinquency_bucket_id" type="BIGINT" defaultValueComputed="NULL"/>
+        </addColumn>
+        <addForeignKeyConstraint baseColumnNames="delinquency_bucket_id" baseTableName="m_product_loan"
+                                 constraintName="FK_m_product_loan_m_delinquency_bucket" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_bucket" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="6">
+        <addColumn tableName="m_loan">
+            <column name="delinquency_range_id" type="BIGINT" defaultValueComputed="NULL"/>
+        </addColumn>
+        <addForeignKeyConstraint baseColumnNames="delinquency_range_id" baseTableName="m_loan"
+                                 constraintName="FK_m_delinquency_range_loan" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_range" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="7">
+        <createTable tableName="m_loan_delinquency_tags">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="delinquency_range_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="loan_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938091093


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");
+    }
+
+    @Test
+    public void testUpdateDelinquencyRanges() {
+        // given
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        jsonRange = DelinquencyRangesHelper.getAsJSON(1, 7);
+
+        // when
+        final PutDelinquencyRangeResponse delinquencyRangeResponse02 = DelinquencyRangesHelper.updateDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId(), jsonRange);
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse01.getResourceId());
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(delinquencyRangeResponse02);
+        assertNotNull(range);
+        assertNotEquals(3, range.getMaximumAgeDays());
+        assertEquals(7, range.getMaximumAgeDays());
+    }
+
+    @Test
+    public void testDelinquencyBuckets() {
+        // given
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 30);
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+
+        // when
+        final ArrayList<GetDelinquencyBucketsResponse> bucketList = DelinquencyBucketsHelper.getDelinquencyBuckets(requestSpec,
+                responseSpec);
+        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketResponse.getResourceId());
+
+        // then
+        assertNotNull(bucketList);
+        assertEquals(1, bucketList.size(), "Array size with one element");
+        assertNotNull(delinquencyBucket);

Review Comment:
   @josehernandezfintecheandomx Missing assertion of checking whether the bucket has 2 ranges



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r940000771


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/api/DelinquencyApiResourceSwagger.java:
##########
@@ -0,0 +1,116 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+public final class DelinquencyApiResourceSwagger {
+
+    private DelinquencyApiResourceSwagger() {}
+
+    @Schema(description = "GetDelinquencyRangesResponse")
+    public static final class GetDelinquencyRangesResponse {
+
+        private GetDelinquencyRangesResponse() {}
+
+        @Schema(example = "1")
+        public Integer id;
+        @Schema(example = "Delinquent 1")
+        public String classification;
+        @Schema(example = "1")
+        public Long minimumAgeDays;
+        @Schema(example = "3")
+        public Long maximumAgeDays;
+    }
+
+    @Schema(description = "GetDelinquencyBucketsResponse")
+    public static final class GetDelinquencyBucketsResponse {
+
+        private GetDelinquencyBucketsResponse() {}
+
+        @Schema(example = "1")
+        public Integer id;
+        @Schema(example = "Delinquent Bucket Set 1")
+        public String name;
+    }
+
+    @Schema(description = "PostDelinquencyRangeRequest")
+    public static final class PostDelinquencyRangeRequest {
+
+        private PostDelinquencyRangeRequest() {}
+
+        @Schema(example = "Delinquent 1")
+        public String classification;
+        @Schema(example = "1")
+        public Integer minimumAgeDays;
+        @Schema(example = "3")
+        public Integer maximumAgeDays;
+        @Schema(example = "en")
+        public String locale;
+    }
+
+    @Schema(description = "PostDelinquencyRangeResponse")
+    public static final class PostDelinquencyRangeResponse {
+
+        private PostDelinquencyRangeResponse() {}
+
+        @Schema(example = "1")
+        public Integer resourceId;
+    }
+
+    @Schema(description = "PutDelinquencyRangeResponse")
+    public static final class PutDelinquencyRangeResponse {
+
+        private PutDelinquencyRangeResponse() {}
+
+        @Schema(example = "1")
+        public Integer resourceId;
+        public PostDelinquencyRangeRequest changes;
+    }
+
+    @Schema(description = "PostDelinquencyBucketRequest")
+    public static final class PostDelinquencyBucketRequest {
+
+        private PostDelinquencyBucketRequest() {}
+
+        @Schema(example = "Delinquent 1")
+        public String name;
+        @Schema(example = "[1,2,3]")
+        public Long[] ranges;
+    }
+
+    @Schema(description = "PostDelinquencyBucketResponse")
+    public static final class PostDelinquencyBucketResponse {
+
+        private PostDelinquencyBucketResponse() {}
+
+        @Schema(example = "1")
+        public Integer resourceId;
+    }
+
+    @Schema(description = "PutDelinquencyBucketResponse")
+    public static final class PutDelinquencyBucketResponse {
+
+        private PutDelinquencyBucketResponse() {}
+
+        @Schema(example = "1")
+        public Integer resourceId;
+    }
+

Review Comment:
   @josehernandezfintecheandomx Delete bucket request&response or delete range request&response is missing



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932830922


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java:
##########
@@ -292,6 +293,10 @@ public class Loan extends AbstractPersistableCustom {
     @Column(name = "loan_product_counter")
     private Integer loanProductCounter;
 
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "delinquency_range_id", nullable = true)
+    private DelinquencyRange delinquencyRange;

Review Comment:
   It is to represent the current tag, considering LoanDelinquencyTags could work for the next scenarios:
   1. Loan without delinquency tag because It is active (no delinquency yet)
   2. Loan with one or more delinquency tags (history) and you can take the latest one to know the current delinquency tag,
   But In the scenario, after go out of delinquency, when the loan goes back to active, how you can reflect that? No delinquency
   
   Plus It can help to know quickly If the loan has or not a tag, If It has a tag, exactly which is it.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932895269


##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java:
##########
@@ -49,7 +49,8 @@ public enum JobName {
                                                                                                                                                                                                                     "Update Trial Balance Details"), EXECUTE_DIRTY_JOBS(
                                                                                                                                                                                                                             "Execute All Dirty Jobs"), INCREASE_BUSINESS_DATE_BY_1_DAY(
                                                                                                                                                                                                                                     "Increase Business Date by 1 day"), INCREASE_COB_DATE_BY_1_DAY(
-                                                                                                                                                                                                                                            "Increase COB Date by 1 day");
+                                                                                                                                                                                                                                            "Increase COB Date by 1 day"), LOAN_DELINQUENCY_CLASSIFICATION(
+                                                                                                                                                                                                                                                    "Loan Delinquency Classification");

Review Comment:
   Done! removed



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932810492


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyTags.java:
##########
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_delinquency_tags")
+public class LoanDelinquencyTags extends AbstractAuditableCustom {
+
+    @OneToOne(fetch = FetchType.EAGER)
+    @JoinColumn(name = "delinquency_range_id", nullable = false)
+    private DelinquencyRange delinquencyRange;
+
+    @OneToOne(fetch = FetchType.EAGER)
+    @JoinColumn(name = "loan_id", nullable = false)
+    private Loan loan;
+
+    @Version
+    private Long version;
+
+    public LoanDelinquencyTags(DelinquencyRange delinquencyRange, Loan loan) {
+        this.delinquencyRange = delinquencyRange;
+        this.loan = loan;
+    }
+
+    public static LoanDelinquencyTags instance(DelinquencyRange delinquencyRange, Loan loan) {

Review Comment:
   Done! Removed



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938071762


##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">

Review Comment:
   For postgres the columnType should be "TIMESTAMP WITH TIME ZONE"



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938800697


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/api/DelinquencyApiResource.java:
##########
@@ -0,0 +1,217 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.Collection;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
+import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Path("delinquency")
+@Component
+@Tag(name = "Delinquency Range and Buckets Management", description = "Delinquency Range and Buckets management enables you to set up, fetch and adjust Delinquency overdue ranges")
+public class DelinquencyApiResource {
+
+    private final ApiRequestParameterHelper parameterHelper;
+    private final PlatformSecurityContext securityContext;
+    private final DefaultToApiJsonSerializer<DelinquencyBucketData> jsonSerializerBucket;
+    private final DefaultToApiJsonSerializer<DelinquencyRangeData> jsonSerializerRange;
+    private final DelinquencyReadPlatformService readPlatformService;
+    private final PortfolioCommandSourceWritePlatformService commandWritePlatformService;
+
+    @GET
+    @Path("ranges")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "List all Delinquency Ranges", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class)))) })
+    public String getDelinquencyRanges(@Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final Collection<DelinquencyRangeData> delinquencyRangeData = this.readPlatformService.retrieveAllDelinquencyRanges();
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @GET
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Retrieve a specific Delinquency Range based on the Id", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class))) })
+    public String getDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            @Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final DelinquencyRangeData delinquencyRangeData = this.readPlatformService.retrieveDelinquencyRange(delinquencyRangeId);
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @POST
+    @Path("ranges")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Create Delinquency Range", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeResponse.class))) })
+    public String createDelinquencyRange(final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().createDelinquencyRange().withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @PUT
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Range based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyRangeResponse.class))) })
+    public String updateDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().updateDelinquencyRange(delinquencyRangeId)
+                .withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @DELETE
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Range based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyRangeResponse.class))) })
+    public String deleteDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");

Review Comment:
   Done! validateHasDeletePermission implemented



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r937904748


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyTags.java:
##########
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_delinquency_tags")
+public class LoanDelinquencyTags extends AbstractAuditableWithUTCDateTimeCustom {
+
+    @OneToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "delinquency_range_id", nullable = false)
+    private DelinquencyRange delinquencyRange;

Review Comment:
   @josehernandezfintecheandomx This seems wrong to me!
   The same DeliquencyRange can be assigned to multiple loans...



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938500360


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyTags.java:
##########
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_delinquency_tags")
+public class LoanDelinquencyTags extends AbstractAuditableWithUTCDateTimeCustom {

Review Comment:
   @josehernandezfintecheandomx Also, if this is history, we dont have anything to represent the "Not delinquent" status aka: there is no delinquency range applied, or it was fully repaid and the prior tag was removed.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932776040


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/DelinquencyBucket.java:
##########
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_delinquency_bucket", uniqueConstraints = {
+        @UniqueConstraint(name = "uq_delinquency_bucket_name", columnNames = { "name" }) })
+public class DelinquencyBucket extends AbstractAuditableCustom {
+
+    @Column(name = "name", nullable = false)
+    private String name;
+

Review Comment:
   Done!
   
   <img width="1429" alt="Screen Shot 2022-07-28 at 19 34 35" src="https://user-images.githubusercontent.com/44206706/181659208-c25496e8-f275-4c13-9f08-2113b6d820a8.png">
   
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932778673


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/DelinquencyRange.java:
##########
@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
+import javax.validation.constraints.NotNull;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_delinquency_range", uniqueConstraints = {
+        @UniqueConstraint(name = "uq_delinquency_range_classification", columnNames = { "classification" }) })
+public class DelinquencyRange extends AbstractAuditableCustom {

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r939436635


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/api/DelinquencyApiResource.java:
##########
@@ -0,0 +1,217 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.Collection;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
+import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Path("delinquency")
+@Component
+@Tag(name = "Delinquency Range and Buckets Management", description = "Delinquency Range and Buckets management enables you to set up, fetch and adjust Delinquency overdue ranges")
+public class DelinquencyApiResource {
+
+    private final ApiRequestParameterHelper parameterHelper;
+    private final PlatformSecurityContext securityContext;
+    private final DefaultToApiJsonSerializer<DelinquencyBucketData> jsonSerializerBucket;
+    private final DefaultToApiJsonSerializer<DelinquencyRangeData> jsonSerializerRange;
+    private final DelinquencyReadPlatformService readPlatformService;
+    private final PortfolioCommandSourceWritePlatformService commandWritePlatformService;
+
+    @GET
+    @Path("ranges")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "List all Delinquency Ranges", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class)))) })
+    public String getDelinquencyRanges(@Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final Collection<DelinquencyRangeData> delinquencyRangeData = this.readPlatformService.retrieveAllDelinquencyRanges();
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @GET
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Retrieve a specific Delinquency Range based on the Id", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class))) })
+    public String getDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            @Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final DelinquencyRangeData delinquencyRangeData = this.readPlatformService.retrieveDelinquencyRange(delinquencyRangeId);
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @POST
+    @Path("ranges")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Create Delinquency Range", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeResponse.class))) })
+    public String createDelinquencyRange(final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().createDelinquencyRange().withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @PUT
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Range based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyRangeResponse.class))) })
+    public String updateDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r939600808


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");
+    }
+
+    @Test
+    public void testUpdateDelinquencyRanges() {
+        // given
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        jsonRange = DelinquencyRangesHelper.getAsJSON(1, 7);
+
+        // when
+        final PutDelinquencyRangeResponse delinquencyRangeResponse02 = DelinquencyRangesHelper.updateDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId(), jsonRange);
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse01.getResourceId());
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(delinquencyRangeResponse02);
+        assertNotNull(range);
+        assertNotEquals(3, range.getMaximumAgeDays());
+        assertEquals(7, range.getMaximumAgeDays());
+    }
+
+    @Test
+    public void testDelinquencyBuckets() {
+        // given
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 30);
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+
+        // when
+        final ArrayList<GetDelinquencyBucketsResponse> bucketList = DelinquencyBucketsHelper.getDelinquencyBuckets(requestSpec,
+                responseSpec);
+        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketResponse.getResourceId());
+
+        // then
+        assertNotNull(bucketList);
+        assertEquals(1, bucketList.size(), "Array size with one element");
+        assertNotNull(delinquencyBucket);
+    }
+}

Review Comment:
   Done! Just pending, the evaluations are there in the code, just more complex
   
   - Delinquency bucket cannot be deleted due it is configured on Loan product
   - Delinquency range cannot be deleted due it is added to Loan



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r939998239


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/api/DelinquencyApiResource.java:
##########
@@ -0,0 +1,217 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.Collection;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
+import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Path("delinquency")
+@Component
+@Tag(name = "Delinquency Range and Buckets Management", description = "Delinquency Range and Buckets management enables you to set up, fetch and adjust Delinquency overdue ranges")
+public class DelinquencyApiResource {
+
+    private final ApiRequestParameterHelper parameterHelper;
+    private final PlatformSecurityContext securityContext;
+    private final DefaultToApiJsonSerializer<DelinquencyBucketData> jsonSerializerBucket;
+    private final DefaultToApiJsonSerializer<DelinquencyRangeData> jsonSerializerRange;
+    private final DelinquencyReadPlatformService readPlatformService;
+    private final PortfolioCommandSourceWritePlatformService commandWritePlatformService;
+
+    @GET
+    @Path("ranges")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "List all Delinquency Ranges", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class)))) })
+    public String getDelinquencyRanges(@Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final Collection<DelinquencyRangeData> delinquencyRangeData = this.readPlatformService.retrieveAllDelinquencyRanges();
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @GET
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Retrieve a specific Delinquency Range based on the Id", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class))) })
+    public String getDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            @Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final DelinquencyRangeData delinquencyRangeData = this.readPlatformService.retrieveDelinquencyRange(delinquencyRangeId);
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @POST
+    @Path("ranges")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Create Delinquency Range", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeResponse.class))) })
+    public String createDelinquencyRange(final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().createDelinquencyRange().withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @PUT
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Range based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyRangeResponse.class))) })
+    public String updateDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");

Review Comment:
   @josehernandezfintecheandomx it looks the same to me :/
   `validateHasCreatePermission`



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932777628


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/DelinquencyBucketMappings.java:
##########
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_delinquency_bucket_mappings", uniqueConstraints = {
+        @UniqueConstraint(name = "uq_delinquency_bucket_mapping", columnNames = { "delinquencyBucket", "delinquencyRange" }) })
+public class DelinquencyBucketMappings extends AbstractAuditableCustom {

Review Comment:
   The Idea is to have a pool of Ranges, and those can be linked to one or more Buckets through the mappings table



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932810023


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyTags.java:
##########
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_delinquency_tags")
+public class LoanDelinquencyTags extends AbstractAuditableCustom {

Review Comment:
   This entity `LoanDelinquencyTags` will be in charge to get this history



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r937993365


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java:
##########
@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
+import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
+import org.apache.fineract.infrastructure.jobs.service.JobName;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappings;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeNotFoundException;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlatformService {
+
+    private final DelinquencyBucketParseAndValidator dataValidatorBucket;
+    private final DelinquencyRangeParseAndValidator dataValidatorRange;
+
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyBucketRepository repositoryBucket;
+    private final DelinquencyBucketMappingsRepository repositoryBucketMappings;
+
+    @Override
+    public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyRange delinquencyRange = updateOrCreateDelinquencyRange(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        DelinquencyRange delinquencyRange = this.repositoryRange.getReferenceById(delinquencyRangeId);
+        if (delinquencyRange == null) {
+            throw DelinquencyRangeNotFoundException.notFound(delinquencyRangeId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyRange = updateDelinquencyRange(delinquencyRange, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        final DelinquencyRange delinquencyRange = repositoryRange.getReferenceById(delinquencyRangeId);
+        repositoryRange.delete(delinquencyRange);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).build();
+    }
+
+    @Override
+    public CommandProcessingResult createDelinquencyBucket(JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyBucket delinquencyBucket = updateOrCreateDelinquencyBucket(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        DelinquencyBucket delinquencyBucket = this.repositoryBucket.getReferenceById(delinquencyBucketId);
+        if (delinquencyBucket == null) {
+            throw DelinquencyBucketNotFoundException.notFound(delinquencyBucketId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyBucket = updateDelinquencyBucket(delinquencyBucket, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        final DelinquencyBucket delinquencyBucket = repositoryBucket.getReferenceById(delinquencyBucketId);

Review Comment:
   @josehernandezfintecheandomx EclipseLink will throw immediately "EntityNotFoundException" if the reference does not exist.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938079955


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/api/DelinquencyApiResource.java:
##########
@@ -0,0 +1,217 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.Collection;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
+import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Path("delinquency")
+@Component
+@Tag(name = "Delinquency Range and Buckets Management", description = "Delinquency Range and Buckets management enables you to set up, fetch and adjust Delinquency overdue ranges")
+public class DelinquencyApiResource {
+
+    private final ApiRequestParameterHelper parameterHelper;
+    private final PlatformSecurityContext securityContext;
+    private final DefaultToApiJsonSerializer<DelinquencyBucketData> jsonSerializerBucket;
+    private final DefaultToApiJsonSerializer<DelinquencyRangeData> jsonSerializerRange;
+    private final DelinquencyReadPlatformService readPlatformService;
+    private final PortfolioCommandSourceWritePlatformService commandWritePlatformService;
+
+    @GET
+    @Path("ranges")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "List all Delinquency Ranges", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class)))) })
+    public String getDelinquencyRanges(@Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final Collection<DelinquencyRangeData> delinquencyRangeData = this.readPlatformService.retrieveAllDelinquencyRanges();
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @GET
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Retrieve a specific Delinquency Range based on the Id", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class))) })
+    public String getDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            @Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final DelinquencyRangeData delinquencyRangeData = this.readPlatformService.retrieveDelinquencyRange(delinquencyRangeId);
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @POST
+    @Path("ranges")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Create Delinquency Range", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeResponse.class))) })
+    public String createDelinquencyRange(final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().createDelinquencyRange().withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @PUT
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Range based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyRangeResponse.class))) })
+    public String updateDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().updateDelinquencyRange(delinquencyRangeId)
+                .withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @DELETE
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Range based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyRangeResponse.class))) })
+    public String deleteDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");

Review Comment:
   @josehernandezfintecheandomx Wrong permission got checked. It should use the "validateHasDeletePermission" (which is not existing, needs to be created)



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938083186


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyRangeParseAndValidator.java:
##########
@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.validator;
+
+import com.google.gson.JsonObject;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import javax.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.infrastructure.core.validator.ParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Component
+public class DelinquencyRangeParseAndValidator extends ParseAndValidator {
+
+    private final FromJsonHelper jsonHelper;
+
+    public DelinquencyRangeData validateAndParseUpdate(@NotNull final JsonCommand command) {

Review Comment:
   @josehernandezfintecheandomx Misleading method name. This validator is not used only for parsing and validating deliquency range update request.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938496567


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyTags.java:
##########
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_delinquency_tags")
+public class LoanDelinquencyTags extends AbstractAuditableWithUTCDateTimeCustom {
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "delinquency_range_id", nullable = false)
+    private DelinquencyRange delinquencyRange;
+
+    @OneToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "loan_id", nullable = false)
+    private Loan loan;

Review Comment:
   @josehernandezfintecheandomx If this is for history, the @OneToOne association must be changed here as well!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r937909219


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/handler/UpdateDelinquencyBucketCommandHandler.java:
##########
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.handler;
+
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@RequiredArgsConstructor
+@CommandType(entity = "DELINQUENCY_BUCKET", action = "CREATE")

Review Comment:
   @josehernandezfintecheandomx This seems wrong. The action should be "UPDATE"



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938082686


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyBucketParseAndValidator.java:
##########
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.validator;
+
+import com.google.gson.JsonObject;
+import java.util.ArrayList;
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.infrastructure.core.validator.ParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Component
+public class DelinquencyBucketParseAndValidator extends ParseAndValidator {
+
+    private final FromJsonHelper jsonHelper;
+
+    public DelinquencyBucketData validateAndParseUpdate(@NotNull final JsonCommand command) {

Review Comment:
   @josehernandezfintecheandomx Misleading method name. This validator is not used only for parsing and validating deliquency bucket update request.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938806155


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");
+    }
+
+    @Test
+    public void testUpdateDelinquencyRanges() {
+        // given
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        jsonRange = DelinquencyRangesHelper.getAsJSON(1, 7);
+
+        // when
+        final PutDelinquencyRangeResponse delinquencyRangeResponse02 = DelinquencyRangesHelper.updateDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId(), jsonRange);
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse01.getResourceId());
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938840659


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/api/DelinquencyApiResource.java:
##########
@@ -0,0 +1,217 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.Collection;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
+import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Path("delinquency")
+@Component
+@Tag(name = "Delinquency Range and Buckets Management", description = "Delinquency Range and Buckets management enables you to set up, fetch and adjust Delinquency overdue ranges")
+public class DelinquencyApiResource {
+
+    private final ApiRequestParameterHelper parameterHelper;
+    private final PlatformSecurityContext securityContext;
+    private final DefaultToApiJsonSerializer<DelinquencyBucketData> jsonSerializerBucket;
+    private final DefaultToApiJsonSerializer<DelinquencyRangeData> jsonSerializerRange;
+    private final DelinquencyReadPlatformService readPlatformService;
+    private final PortfolioCommandSourceWritePlatformService commandWritePlatformService;
+
+    @GET
+    @Path("ranges")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "List all Delinquency Ranges", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class)))) })
+    public String getDelinquencyRanges(@Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final Collection<DelinquencyRangeData> delinquencyRangeData = this.readPlatformService.retrieveAllDelinquencyRanges();
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @GET
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Retrieve a specific Delinquency Range based on the Id", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class))) })
+    public String getDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            @Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final DelinquencyRangeData delinquencyRangeData = this.readPlatformService.retrieveDelinquencyRange(delinquencyRangeId);
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @POST
+    @Path("ranges")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Create Delinquency Range", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeResponse.class))) })
+    public String createDelinquencyRange(final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().createDelinquencyRange().withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @PUT
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Range based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyRangeResponse.class))) })
+    public String updateDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().updateDelinquencyRange(delinquencyRangeId)
+                .withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @DELETE
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Range based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyRangeResponse.class))) })
+    public String deleteDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteDelinquencyRange(delinquencyRangeId).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @GET
+    @Path("buckets")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "List all Delinquency Buckets", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyBucketsResponse.class)))) })
+    public String getDelinquencyBuckets(@Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final Collection<DelinquencyBucketData> delinquencyBucketData = this.readPlatformService.retrieveAllDelinquencyBuckets();
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerBucket.serialize(settings, delinquencyBucketData);
+    }
+
+    @GET
+    @Path("buckets/{delinquencyBucketId}")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Retrieve a specific Delinquency Bucket based on the Id", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyBucketsResponse.class))) })
+    public String getDelinquencyBucket(
+            @PathParam("delinquencyBucketId") @Parameter(description = "delinquencyBucketId") final Long delinquencyBucketId,
+            @Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final DelinquencyBucketData delinquencyBucketData = this.readPlatformService.retrieveDelinquencyBucket(delinquencyBucketId);
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerBucket.serialize(settings, delinquencyBucketData);
+    }
+
+    @POST
+    @Path("buckets")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Create Delinquency Bucket", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyBucketRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyBucketResponse.class))) })
+    public String createDelinquencyBucket(final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().createDelinquencyBucket().withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @PUT
+    @Path("buckets/{delinquencyBucketId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Bucket based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyBucketRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyBucketResponse.class))) })
+    public String updateDelinquencyBucket(
+            @PathParam("delinquencyBucketId") @Parameter(description = "delinquencyBucketId") final Long delinquencyBucketId,
+            final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().updateDelinquencyBucket(delinquencyBucketId)
+                .withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938098274


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");
+    }
+
+    @Test
+    public void testUpdateDelinquencyRanges() {
+        // given
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        jsonRange = DelinquencyRangesHelper.getAsJSON(1, 7);
+
+        // when
+        final PutDelinquencyRangeResponse delinquencyRangeResponse02 = DelinquencyRangesHelper.updateDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId(), jsonRange);
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse01.getResourceId());
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(delinquencyRangeResponse02);
+        assertNotNull(range);
+        assertNotEquals(3, range.getMaximumAgeDays());
+        assertEquals(7, range.getMaximumAgeDays());
+    }
+
+    @Test
+    public void testDelinquencyBuckets() {
+        // given
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 30);
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+
+        // when
+        final ArrayList<GetDelinquencyBucketsResponse> bucketList = DelinquencyBucketsHelper.getDelinquencyBuckets(requestSpec,
+                responseSpec);
+        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketResponse.getResourceId());
+
+        // then
+        assertNotNull(bucketList);
+        assertEquals(1, bucketList.size(), "Array size with one element");
+        assertNotNull(delinquencyBucket);
+    }
+}

Review Comment:
   @josehernandezfintecheandomx 
   Missing test cases:
   - Delinquency bucket is already existing
   - Delinquency bucket was configured on Loan product
   - Delinquency bucket cannot be deleted due it is configured on Loan product
   



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");
+    }
+
+    @Test
+    public void testUpdateDelinquencyRanges() {
+        // given
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        jsonRange = DelinquencyRangesHelper.getAsJSON(1, 7);
+
+        // when
+        final PutDelinquencyRangeResponse delinquencyRangeResponse02 = DelinquencyRangesHelper.updateDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId(), jsonRange);
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse01.getResourceId());
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(delinquencyRangeResponse02);
+        assertNotNull(range);
+        assertNotEquals(3, range.getMaximumAgeDays());
+        assertEquals(7, range.getMaximumAgeDays());
+    }
+
+    @Test
+    public void testDelinquencyBuckets() {
+        // given
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 30);
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+
+        // when
+        final ArrayList<GetDelinquencyBucketsResponse> bucketList = DelinquencyBucketsHelper.getDelinquencyBuckets(requestSpec,
+                responseSpec);
+        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketResponse.getResourceId());
+
+        // then
+        assertNotNull(bucketList);
+        assertEquals(1, bucketList.size(), "Array size with one element");
+        assertNotNull(delinquencyBucket);
+    }
+}

Review Comment:
   @josehernandezfintecheandomx I am not sure what you mean here.... 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938098274


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");
+    }
+
+    @Test
+    public void testUpdateDelinquencyRanges() {
+        // given
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        jsonRange = DelinquencyRangesHelper.getAsJSON(1, 7);
+
+        // when
+        final PutDelinquencyRangeResponse delinquencyRangeResponse02 = DelinquencyRangesHelper.updateDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId(), jsonRange);
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse01.getResourceId());
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(delinquencyRangeResponse02);
+        assertNotNull(range);
+        assertNotEquals(3, range.getMaximumAgeDays());
+        assertEquals(7, range.getMaximumAgeDays());
+    }
+
+    @Test
+    public void testDelinquencyBuckets() {
+        // given
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 30);
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+
+        // when
+        final ArrayList<GetDelinquencyBucketsResponse> bucketList = DelinquencyBucketsHelper.getDelinquencyBuckets(requestSpec,
+                responseSpec);
+        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketResponse.getResourceId());
+
+        // then
+        assertNotNull(bucketList);
+        assertEquals(1, bucketList.size(), "Array size with one element");
+        assertNotNull(delinquencyBucket);
+    }
+}

Review Comment:
   @josehernandezfintecheandomx 
   Missing test cases:
   - Delinquency bucket is already existing
   - Delinquency bucket was configured on Loan product
   - Delinquency bucket updated
   - Delinquency bucket deleted
   - Delinquency bucket cannot be deleted due it is configured on Loan product
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932825430


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java:
##########
@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
+import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
+import org.apache.fineract.infrastructure.jobs.service.JobName;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappings;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeNotFoundException;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlatformService {
+
+    private final DelinquencyBucketParseAndValidator dataValidatorBucket;
+    private final DelinquencyRangeParseAndValidator dataValidatorRange;
+
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyBucketRepository repositoryBucket;
+    private final DelinquencyBucketMappingsRepository repositoryBucketMappings;
+
+    @Override
+    public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyRange delinquencyRange = updateOrCreateDelinquencyRange(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        DelinquencyRange delinquencyRange = this.repositoryRange.getReferenceById(delinquencyRangeId);
+        if (delinquencyRange == null) {
+            throw DelinquencyRangeNotFoundException.notFound(delinquencyRangeId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyRange = updateDelinquencyRange(delinquencyRange, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        final DelinquencyRange delinquencyRange = repositoryRange.getReferenceById(delinquencyRangeId);
+        repositoryRange.delete(delinquencyRange);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).build();
+    }
+
+    @Override
+    public CommandProcessingResult createDelinquencyBucket(JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyBucket delinquencyBucket = updateOrCreateDelinquencyBucket(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        DelinquencyBucket delinquencyBucket = this.repositoryBucket.getReferenceById(delinquencyBucketId);
+        if (delinquencyBucket == null) {
+            throw DelinquencyBucketNotFoundException.notFound(delinquencyBucketId);

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932778194


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/DelinquencyBucket.java:
##########
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_delinquency_bucket", uniqueConstraints = {
+        @UniqueConstraint(name = "uq_delinquency_bucket_name", columnNames = { "name" }) })
+public class DelinquencyBucket extends AbstractAuditableCustom {

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932780453


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/DelinquencyRange.java:
##########
@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
+import javax.validation.constraints.NotNull;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_delinquency_range", uniqueConstraints = {
+        @UniqueConstraint(name = "uq_delinquency_range_classification", columnNames = { "classification" }) })
+public class DelinquencyRange extends AbstractAuditableCustom {
+
+    @Column(name = "classification", nullable = false)

Review Comment:
   No, the agreement was not to use DataCodes, and the Classification value is defined in the Delinquency Range management with the Min and Max age values. But those Delinquency Ranges will works like a pool of them to be grouped in the Delinquency Bucket entity



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932894849


##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0020_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="createdby_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_date" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="lastmodifiedby_id" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="lastmodified_date" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <createTable tableName="m_delinquency_bucket">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="name" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="createdby_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_date" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="lastmodifiedby_id" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="lastmodified_date" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="3">
+        <createTable tableName="m_delinquency_bucket_mappings">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="delinquency_range_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="delinquency_bucket_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="createdby_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_date" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="lastmodifiedby_id" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="lastmodified_date" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="4">
+        <addForeignKeyConstraint baseColumnNames="delinquency_range_id" baseTableName="m_delinquency_bucket_mappings"
+                                 constraintName="FK_m_delinquency_range_mapping" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_range" validate="true"/>
+        <addForeignKeyConstraint baseColumnNames="delinquency_bucket_id" baseTableName="m_delinquency_bucket_mappings"
+                                 constraintName="FK_m_delinquency_bucket_mapping" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_bucket" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="5">
+        <addColumn tableName="m_product_loan">
+            <column name="delinquency_bucket_id" type="BIGINT" defaultValueComputed="NULL"/>
+        </addColumn>
+        <addForeignKeyConstraint baseColumnNames="delinquency_bucket_id" baseTableName="m_product_loan"
+                                 constraintName="FK_m_product_loan_m_delinquency_bucket" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_bucket" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="6">
+        <addColumn tableName="m_loan">
+            <column name="delinquency_range_id" type="BIGINT" defaultValueComputed="NULL"/>
+        </addColumn>
+        <addForeignKeyConstraint baseColumnNames="delinquency_range_id" baseTableName="m_loan"
+                                 constraintName="FK_m_delinquency_range_loan" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_range" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="7">
+        <createTable tableName="m_loan_delinquency_tags">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="delinquency_range_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="loan_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="createdby_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_date" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column defaultValueComputed="NULL" name="lastmodifiedby_id" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="lastmodified_date" type="DATETIME"/>
+        </createTable>
+        <addForeignKeyConstraint baseColumnNames="delinquency_range_id" baseTableName="m_loan_delinquency_tags"
+                                 constraintName="FK_m_delinquency_tags_range" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_range" validate="true"/>
+        <addForeignKeyConstraint baseColumnNames="loan_id" baseTableName="m_loan_delinquency_tags"
+                                 constraintName="FK_m_delinquency_tags_loan" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_loan" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="8">
+        <insert tableName="m_permission">
+            <column name="grouping" value="organisation"/>
+            <column name="code" value="READ_DELINQUENCY_BUCKET"/>
+            <column name="entity_name" value="DELINQUENCY_BUCKET"/>
+            <column name="action_name" value="READ"/>
+            <column name="can_maker_checker" valueBoolean="false"/>
+        </insert>
+        <insert tableName="m_permission">
+            <column name="grouping" value="organisation"/>
+            <column name="code" value="CREATE_DELINQUENCY_BUCKET"/>
+            <column name="entity_name" value="DELINQUENCY_BUCKET"/>
+            <column name="action_name" value="CREATE"/>
+            <column name="can_maker_checker" valueBoolean="false"/>
+        </insert>
+        <insert tableName="m_permission">
+            <column name="grouping" value="organisation"/>
+            <column name="code" value="UPDATE_DELINQUENCY_BUCKET"/>
+            <column name="entity_name" value="DELINQUENCY_BUCKET"/>
+            <column name="action_name" value="UPDATE"/>
+            <column name="can_maker_checker" valueBoolean="false"/>
+        </insert>
+        <insert tableName="m_permission">
+            <column name="grouping" value="organisation"/>
+            <column name="code" value="DELETE_DELINQUENCY_BUCKET"/>
+            <column name="entity_name" value="DELINQUENCY_BUCKET"/>
+            <column name="action_name" value="DELETE"/>
+            <column name="can_maker_checker" valueBoolean="false"/>
+        </insert>
+        <insert tableName="m_permission">
+            <column name="grouping" value="organisation"/>
+            <column name="code" value="CREATE_DELINQUENCY_RANGE"/>
+            <column name="entity_name" value="DELINQUENCY_RANGE"/>
+            <column name="action_name" value="CREATE"/>
+            <column name="can_maker_checker" valueBoolean="false"/>
+        </insert>
+        <insert tableName="m_permission">
+            <column name="grouping" value="organisation"/>
+            <column name="code" value="UPDATE_DELINQUENCY_RANGE"/>
+            <column name="entity_name" value="DELINQUENCY_RANGE"/>
+            <column name="action_name" value="UPDATE"/>
+            <column name="can_maker_checker" valueBoolean="false"/>
+        </insert>
+        <insert tableName="m_permission">
+            <column name="grouping" value="organisation"/>
+            <column name="code" value="DELETE_DELINQUENCY_RANGE"/>
+            <column name="entity_name" value="DELINQUENCY_RANGE"/>
+            <column name="action_name" value="DELETE"/>
+            <column name="can_maker_checker" valueBoolean="false"/>
+        </insert>
+    </changeSet>
+    <changeSet author="fineract" id="9">

Review Comment:
   Done! Removed



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938071762


##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">

Review Comment:
   @josehernandezfintecheandomx For postgres the columnType should be "TIMESTAMP WITH TIME ZONE"



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938086461


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");

Review Comment:
   @josehernandezfintecheandomx Missing test case to test the "minimumAgeDays"



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r937931405


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java:
##########
@@ -0,0 +1,183 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappings;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeNotFoundException;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlatformService {
+
+    private final DelinquencyBucketParseAndValidator dataValidatorBucket;
+    private final DelinquencyRangeParseAndValidator dataValidatorRange;
+
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyBucketRepository repositoryBucket;
+    private final DelinquencyBucketMappingsRepository repositoryBucketMappings;
+
+    @Override
+    public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyRange delinquencyRange = updateOrCreateDelinquencyRange(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        DelinquencyRange delinquencyRange = this.repositoryRange.getReferenceById(delinquencyRangeId);
+        if (delinquencyRange == null) {
+            throw DelinquencyRangeNotFoundException.notFound(delinquencyRangeId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyRange = updateDelinquencyRange(delinquencyRange, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        final DelinquencyRange delinquencyRange = repositoryRange.getReferenceById(delinquencyRangeId);

Review Comment:
   @josehernandezfintecheandomx EclipseLink will throw immediately "EntityNotFoundException" if the reference does not exist.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938192775


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyTags.java:
##########
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_delinquency_tags")
+public class LoanDelinquencyTags extends AbstractAuditableWithUTCDateTimeCustom {
+
+    @OneToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "delinquency_range_id", nullable = false)
+    private DelinquencyRange delinquencyRange;

Review Comment:
   Done! Moved to OneToMany



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on PR #2442:
URL: https://github.com/apache/fineract/pull/2442#issuecomment-1205360750

   @josehernandezfintecheandomx Please resolve the conflicts!


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938077578


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/api/DelinquencyApiResource.java:
##########
@@ -0,0 +1,217 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.Collection;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
+import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Path("delinquency")
+@Component
+@Tag(name = "Delinquency Range and Buckets Management", description = "Delinquency Range and Buckets management enables you to set up, fetch and adjust Delinquency overdue ranges")
+public class DelinquencyApiResource {
+
+    private final ApiRequestParameterHelper parameterHelper;
+    private final PlatformSecurityContext securityContext;
+    private final DefaultToApiJsonSerializer<DelinquencyBucketData> jsonSerializerBucket;
+    private final DefaultToApiJsonSerializer<DelinquencyRangeData> jsonSerializerRange;
+    private final DelinquencyReadPlatformService readPlatformService;
+    private final PortfolioCommandSourceWritePlatformService commandWritePlatformService;
+
+    @GET
+    @Path("ranges")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "List all Delinquency Ranges", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class)))) })
+    public String getDelinquencyRanges(@Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final Collection<DelinquencyRangeData> delinquencyRangeData = this.readPlatformService.retrieveAllDelinquencyRanges();
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @GET
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Retrieve a specific Delinquency Range based on the Id", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class))) })
+    public String getDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            @Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final DelinquencyRangeData delinquencyRangeData = this.readPlatformService.retrieveDelinquencyRange(delinquencyRangeId);
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @POST
+    @Path("ranges")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Create Delinquency Range", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeResponse.class))) })
+    public String createDelinquencyRange(final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().createDelinquencyRange().withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @PUT
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Range based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyRangeResponse.class))) })
+    public String updateDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");

Review Comment:
   @josehernandezfintecheandomx Wrong permission got checked. It should use the "validateHasUpdatePermission"



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938073236


##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <createTable tableName="m_delinquency_bucket">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="name" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="3">
+        <createTable tableName="m_delinquency_bucket_mappings">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="delinquency_range_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="delinquency_bucket_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="4">
+        <addForeignKeyConstraint baseColumnNames="delinquency_range_id" baseTableName="m_delinquency_bucket_mappings"
+                                 constraintName="FK_m_delinquency_range_mapping" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_range" validate="true"/>
+        <addForeignKeyConstraint baseColumnNames="delinquency_bucket_id" baseTableName="m_delinquency_bucket_mappings"
+                                 constraintName="FK_m_delinquency_bucket_mapping" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_bucket" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="5">
+        <addColumn tableName="m_product_loan">
+            <column name="delinquency_bucket_id" type="BIGINT" defaultValueComputed="NULL"/>
+        </addColumn>
+        <addForeignKeyConstraint baseColumnNames="delinquency_bucket_id" baseTableName="m_product_loan"
+                                 constraintName="FK_m_product_loan_m_delinquency_bucket" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_bucket" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="6">
+        <addColumn tableName="m_loan">
+            <column name="delinquency_range_id" type="BIGINT" defaultValueComputed="NULL"/>
+        </addColumn>
+        <addForeignKeyConstraint baseColumnNames="delinquency_range_id" baseTableName="m_loan"
+                                 constraintName="FK_m_delinquency_range_loan" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_range" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="7">
+        <createTable tableName="m_loan_delinquency_tags">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="delinquency_range_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="loan_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>

Review Comment:
   @josehernandezfintecheandomx last_modifiied_by and last_modified_on_utc cannot be null! Please change them!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938072632


##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <createTable tableName="m_delinquency_bucket">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="name" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="3">
+        <createTable tableName="m_delinquency_bucket_mappings">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="delinquency_range_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="delinquency_bucket_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">

Review Comment:
   @josehernandezfintecheandomx For postgres the columnType should be "TIMESTAMP WITH TIME ZONE"



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r940039142


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,161 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(1, ranges.get(0).getMinimumAgeDays(), "Expected Min Age Days to 1");
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");
+    }
+
+    @Test
+    public void testUpdateDelinquencyRanges() {
+        // given
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        jsonRange = DelinquencyRangesHelper.getAsJSON(1, 7);
+        assertNotNull(delinquencyRangeResponse01);
+
+        // when
+        final PutDelinquencyRangeResponse delinquencyRangeResponse02 = DelinquencyRangesHelper.updateDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId(), jsonRange);
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse01.getResourceId());
+        final PutDelinquencyRangeResponse deleteDelinquencyRangeResponse = DelinquencyRangesHelper.deleteDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId());
+
+        // then
+        assertNotNull(delinquencyRangeResponse02);
+        assertNotNull(deleteDelinquencyRangeResponse);
+        assertNotNull(range);
+        assertNotEquals(3, range.getMaximumAgeDays());
+        assertEquals(1, range.getMinimumAgeDays());
+        assertEquals(7, range.getMaximumAgeDays());
+    }
+
+    @Test
+    public void testDelinquencyBuckets() {
+        // given
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 30);
+        // Create
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+        // Update
+        jsonRange = DelinquencyRangesHelper.getAsJSON(31, 60);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PutDelinquencyBucketResponse updateDelinquencyBucketResponse = DelinquencyBucketsHelper.updateDelinquencyBucket(requestSpec,
+                responseSpec, delinquencyBucketResponse.getResourceId(), jsonBucket);
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        // Delete
+        PutDelinquencyBucketResponse deleteDelinquencyBucketResponse = DelinquencyBucketsHelper.deleteDelinquencyBucket(requestSpec,
+                responseSpec, delinquencyRangeResponse.getResourceId());

Review Comment:
   @josehernandezfintecheandomx I think this is wrong! You cannot use the "delinquencyRangeResponse.getResourceId" to delete DelinquencyBucket... 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r940040214


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");
+    }
+
+    @Test
+    public void testUpdateDelinquencyRanges() {
+        // given
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        jsonRange = DelinquencyRangesHelper.getAsJSON(1, 7);
+
+        // when
+        final PutDelinquencyRangeResponse delinquencyRangeResponse02 = DelinquencyRangesHelper.updateDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId(), jsonRange);
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse01.getResourceId());
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(delinquencyRangeResponse02);
+        assertNotNull(range);
+        assertNotEquals(3, range.getMaximumAgeDays());
+        assertEquals(7, range.getMaximumAgeDays());
+    }
+
+    @Test
+    public void testDelinquencyBuckets() {
+        // given
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 30);
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+
+        // when
+        final ArrayList<GetDelinquencyBucketsResponse> bucketList = DelinquencyBucketsHelper.getDelinquencyBuckets(requestSpec,
+                responseSpec);
+        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketResponse.getResourceId());
+
+        // then
+        assertNotNull(bucketList);
+        assertEquals(1, bucketList.size(), "Array size with one element");
+        assertNotNull(delinquencyBucket);

Review Comment:
   How could it be considered here? You are created a new bucket at line 113, you have added 2 ranges, so it will be only 2 at the end. 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r940009508


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyBucketParseAndValidator.java:
##########
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.validator;
+
+import com.google.gson.JsonObject;
+import java.util.ArrayList;
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.infrastructure.core.validator.ParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Component
+public class DelinquencyBucketParseAndValidator extends ParseAndValidator {
+
+    private final FromJsonHelper jsonHelper;
+
+    public DelinquencyBucketData validateAndParseUpdate(@NotNull final JsonCommand command) {

Review Comment:
   @josehernandezfintecheandomx I see no changes here... still has the same method name which refer to "Update" but this validation is used to validate and parse the "create" request as well. Wouldnt it be better to use "validateAndParseRequest"? 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938803422


##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <createTable tableName="m_delinquency_bucket">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="name" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="3">
+        <createTable tableName="m_delinquency_bucket_mappings">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="delinquency_range_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="delinquency_bucket_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">

Review Comment:
   Done!



##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <createTable tableName="m_delinquency_bucket">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="name" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="3">
+        <createTable tableName="m_delinquency_bucket_mappings">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="delinquency_range_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="delinquency_bucket_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="4">
+        <addForeignKeyConstraint baseColumnNames="delinquency_range_id" baseTableName="m_delinquency_bucket_mappings"
+                                 constraintName="FK_m_delinquency_range_mapping" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_range" validate="true"/>
+        <addForeignKeyConstraint baseColumnNames="delinquency_bucket_id" baseTableName="m_delinquency_bucket_mappings"
+                                 constraintName="FK_m_delinquency_bucket_mapping" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_bucket" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="5">
+        <addColumn tableName="m_product_loan">
+            <column name="delinquency_bucket_id" type="BIGINT" defaultValueComputed="NULL"/>
+        </addColumn>
+        <addForeignKeyConstraint baseColumnNames="delinquency_bucket_id" baseTableName="m_product_loan"
+                                 constraintName="FK_m_product_loan_m_delinquency_bucket" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_bucket" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="6">
+        <addColumn tableName="m_loan">
+            <column name="delinquency_range_id" type="BIGINT" defaultValueComputed="NULL"/>
+        </addColumn>
+        <addForeignKeyConstraint baseColumnNames="delinquency_range_id" baseTableName="m_loan"
+                                 constraintName="FK_m_delinquency_range_loan" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_range" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="7">
+        <createTable tableName="m_loan_delinquency_tags">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="delinquency_range_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="loan_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938804591


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938077869


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/api/DelinquencyApiResource.java:
##########
@@ -0,0 +1,217 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.Collection;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
+import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Path("delinquency")
+@Component
+@Tag(name = "Delinquency Range and Buckets Management", description = "Delinquency Range and Buckets management enables you to set up, fetch and adjust Delinquency overdue ranges")
+public class DelinquencyApiResource {
+
+    private final ApiRequestParameterHelper parameterHelper;
+    private final PlatformSecurityContext securityContext;
+    private final DefaultToApiJsonSerializer<DelinquencyBucketData> jsonSerializerBucket;
+    private final DefaultToApiJsonSerializer<DelinquencyRangeData> jsonSerializerRange;
+    private final DelinquencyReadPlatformService readPlatformService;
+    private final PortfolioCommandSourceWritePlatformService commandWritePlatformService;
+
+    @GET
+    @Path("ranges")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "List all Delinquency Ranges", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class)))) })
+    public String getDelinquencyRanges(@Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final Collection<DelinquencyRangeData> delinquencyRangeData = this.readPlatformService.retrieveAllDelinquencyRanges();
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @GET
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Retrieve a specific Delinquency Range based on the Id", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyRangesResponse.class))) })
+    public String getDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            @Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final DelinquencyRangeData delinquencyRangeData = this.readPlatformService.retrieveDelinquencyRange(delinquencyRangeId);
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerRange.serialize(settings, delinquencyRangeData);
+    }
+
+    @POST
+    @Path("ranges")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Create Delinquency Range", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeResponse.class))) })
+    public String createDelinquencyRange(final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().createDelinquencyRange().withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @PUT
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Range based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyRangeResponse.class))) })
+    public String updateDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().updateDelinquencyRange(delinquencyRangeId)
+                .withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @DELETE
+    @Path("ranges/{delinquencyRangeId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Range based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyRangeRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyRangeResponse.class))) })
+    public String deleteDelinquencyRange(
+            @PathParam("delinquencyRangeId") @Parameter(description = "delinquencyRangeId") final Long delinquencyRangeId,
+            @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteDelinquencyRange(delinquencyRangeId).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @GET
+    @Path("buckets")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "List all Delinquency Buckets", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyBucketsResponse.class)))) })
+    public String getDelinquencyBuckets(@Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final Collection<DelinquencyBucketData> delinquencyBucketData = this.readPlatformService.retrieveAllDelinquencyBuckets();
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerBucket.serialize(settings, delinquencyBucketData);
+    }
+
+    @GET
+    @Path("buckets/{delinquencyBucketId}")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Retrieve a specific Delinquency Bucket based on the Id", description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyBucketsResponse.class))) })
+    public String getDelinquencyBucket(
+            @PathParam("delinquencyBucketId") @Parameter(description = "delinquencyBucketId") final Long delinquencyBucketId,
+            @Context final UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasReadPermission("DELINQUENCY_BUCKET");
+        final DelinquencyBucketData delinquencyBucketData = this.readPlatformService.retrieveDelinquencyBucket(delinquencyBucketId);
+        ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters());
+        return this.jsonSerializerBucket.serialize(settings, delinquencyBucketData);
+    }
+
+    @POST
+    @Path("buckets")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Create Delinquency Bucket", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyBucketRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyBucketResponse.class))) })
+    public String createDelinquencyBucket(final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");
+        final CommandWrapper commandRequest = new CommandWrapperBuilder().createDelinquencyBucket().withJson(jsonRequestBody).build();
+
+        CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest);
+        return jsonSerializerRange.serialize(result);
+    }
+
+    @PUT
+    @Path("buckets/{delinquencyBucketId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update Delinquency Bucket based on the Id", description = "")
+    @RequestBody(required = true, content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PostDelinquencyBucketRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.PutDelinquencyBucketResponse.class))) })
+    public String updateDelinquencyBucket(
+            @PathParam("delinquencyBucketId") @Parameter(description = "delinquencyBucketId") final Long delinquencyBucketId,
+            final String jsonRequestBody, @Context UriInfo uriInfo) {
+        securityContext.authenticatedUser().validateHasCreatePermission("DELINQUENCY_BUCKET");

Review Comment:
   @josehernandezfintecheandomx Wrong permission got checked. It should use the "validateHasUpdatePermission"



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938088758


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");
+    }
+
+    @Test
+    public void testUpdateDelinquencyRanges() {
+        // given
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        jsonRange = DelinquencyRangesHelper.getAsJSON(1, 7);
+
+        // when
+        final PutDelinquencyRangeResponse delinquencyRangeResponse02 = DelinquencyRangesHelper.updateDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId(), jsonRange);
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse01.getResourceId());
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);

Review Comment:
   @josehernandezfintecheandomx Useless check. If the delinquencyRangeResponse01 is null, there will be a NPE at line 82



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r937928371


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java:
##########
@@ -0,0 +1,183 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappings;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeNotFoundException;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlatformService {
+
+    private final DelinquencyBucketParseAndValidator dataValidatorBucket;
+    private final DelinquencyRangeParseAndValidator dataValidatorRange;
+
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyBucketRepository repositoryBucket;
+    private final DelinquencyBucketMappingsRepository repositoryBucketMappings;
+
+    @Override
+    public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyRange delinquencyRange = updateOrCreateDelinquencyRange(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        DelinquencyRange delinquencyRange = this.repositoryRange.getReferenceById(delinquencyRangeId);

Review Comment:
   @josehernandezfintecheandomx EclipseLink will throw immediately "EntityNotFoundException" if the reference does not exist.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938076513


##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <createTable tableName="m_delinquency_bucket">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="name" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="3">
+        <createTable tableName="m_delinquency_bucket_mappings">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="delinquency_range_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="delinquency_bucket_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="4">
+        <addForeignKeyConstraint baseColumnNames="delinquency_range_id" baseTableName="m_delinquency_bucket_mappings"
+                                 constraintName="FK_m_delinquency_range_mapping" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_range" validate="true"/>
+        <addForeignKeyConstraint baseColumnNames="delinquency_bucket_id" baseTableName="m_delinquency_bucket_mappings"
+                                 constraintName="FK_m_delinquency_bucket_mapping" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_bucket" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="5">
+        <addColumn tableName="m_product_loan">
+            <column name="delinquency_bucket_id" type="BIGINT" defaultValueComputed="NULL"/>
+        </addColumn>
+        <addForeignKeyConstraint baseColumnNames="delinquency_bucket_id" baseTableName="m_product_loan"
+                                 constraintName="FK_m_product_loan_m_delinquency_bucket" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_bucket" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="6">
+        <addColumn tableName="m_loan">
+            <column name="delinquency_range_id" type="BIGINT" defaultValueComputed="NULL"/>
+        </addColumn>
+        <addForeignKeyConstraint baseColumnNames="delinquency_range_id" baseTableName="m_loan"
+                                 constraintName="FK_m_delinquency_range_loan" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_delinquency_range" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="7">
+        <createTable tableName="m_loan_delinquency_tags">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="delinquency_range_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="loan_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">

Review Comment:
   @josehernandezfintecheandomx For postgres the columnType should be "TIMESTAMP WITH TIME ZONE"



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r938072499


##########
fineract-provider/src/main/resources/db/changelog/tenant/parts/0025_add_delinquency_buckets.xml:
##########
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet author="fineract" id="1">
+        <createTable tableName="m_delinquency_range">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="classification" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="min_age_days" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="max_age_days" type="BIGINT">
+                <constraints nullable="true" />
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column defaultValueComputed="NULL" name="last_modified_by" type="BIGINT"/>
+            <column defaultValueComputed="NULL" name="last_modified_on_utc" type="DATETIME"/>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <createTable tableName="m_delinquency_bucket">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="name" type="VARCHAR(100)">
+                <constraints unique="true" nullable="false"/>
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">

Review Comment:
   @josehernandezfintecheandomx For postgres the columnType should be "TIMESTAMP WITH TIME ZONE"



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r940413110


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,161 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(1, ranges.get(0).getMinimumAgeDays(), "Expected Min Age Days to 1");
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");
+    }
+
+    @Test
+    public void testUpdateDelinquencyRanges() {
+        // given
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        jsonRange = DelinquencyRangesHelper.getAsJSON(1, 7);
+        assertNotNull(delinquencyRangeResponse01);
+
+        // when
+        final PutDelinquencyRangeResponse delinquencyRangeResponse02 = DelinquencyRangesHelper.updateDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId(), jsonRange);
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse01.getResourceId());
+        final PutDelinquencyRangeResponse deleteDelinquencyRangeResponse = DelinquencyRangesHelper.deleteDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId());
+
+        // then
+        assertNotNull(delinquencyRangeResponse02);
+        assertNotNull(deleteDelinquencyRangeResponse);
+        assertNotNull(range);
+        assertNotEquals(3, range.getMaximumAgeDays());
+        assertEquals(1, range.getMinimumAgeDays());
+        assertEquals(7, range.getMaximumAgeDays());
+    }
+
+    @Test
+    public void testDelinquencyBuckets() {
+        // given
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 30);
+        // Create
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+        // Update
+        jsonRange = DelinquencyRangesHelper.getAsJSON(31, 60);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PutDelinquencyBucketResponse updateDelinquencyBucketResponse = DelinquencyBucketsHelper.updateDelinquencyBucket(requestSpec,
+                responseSpec, delinquencyBucketResponse.getResourceId(), jsonBucket);
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        // Delete
+        PutDelinquencyBucketResponse deleteDelinquencyBucketResponse = DelinquencyBucketsHelper.deleteDelinquencyBucket(requestSpec,
+                responseSpec, delinquencyRangeResponse.getResourceId());

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] adamsaghy commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r940040214


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java:
##########
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
+import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DelinquencyBucketsIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testCreateDelinquencyRanges() {
+        // given
+        final String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+
+        // when
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        final ArrayList<GetDelinquencyRangesResponse> ranges = DelinquencyRangesHelper.getDelinquencyRanges(requestSpec, responseSpec);
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(ranges);
+        assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");
+    }
+
+    @Test
+    public void testUpdateDelinquencyRanges() {
+        // given
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        final PostDelinquencyRangeResponse delinquencyRangeResponse01 = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                responseSpec, jsonRange);
+        jsonRange = DelinquencyRangesHelper.getAsJSON(1, 7);
+
+        // when
+        final PutDelinquencyRangeResponse delinquencyRangeResponse02 = DelinquencyRangesHelper.updateDelinquencyRange(requestSpec,
+                responseSpec, delinquencyRangeResponse01.getResourceId(), jsonRange);
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse01.getResourceId());
+
+        // then
+        assertNotNull(delinquencyRangeResponse01);
+        assertNotNull(delinquencyRangeResponse02);
+        assertNotNull(range);
+        assertNotEquals(3, range.getMaximumAgeDays());
+        assertEquals(7, range.getMaximumAgeDays());
+    }
+
+    @Test
+    public void testDelinquencyBuckets() {
+        // given
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 30);
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+
+        // when
+        final ArrayList<GetDelinquencyBucketsResponse> bucketList = DelinquencyBucketsHelper.getDelinquencyBuckets(requestSpec,
+                responseSpec);
+        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketResponse.getResourceId());
+
+        // then
+        assertNotNull(bucketList);
+        assertEquals(1, bucketList.size(), "Array size with one element");
+        assertNotNull(delinquencyBucket);

Review Comment:
   How could it be considered here? You are created a new bucket: line 113, you have added 2 ranges, so it will be only 2 at the end. 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] IOhacker merged pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
IOhacker merged PR #2442:
URL: https://github.com/apache/fineract/pull/2442


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932776846


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/DelinquencyBucket.java:
##########
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_delinquency_bucket", uniqueConstraints = {
+        @UniqueConstraint(name = "uq_delinquency_bucket_name", columnNames = { "name" }) })
+public class DelinquencyBucket extends AbstractAuditableCustom {
+
+    @Column(name = "name", nullable = false)
+    private String name;
+
+    @Version
+    private Long version;
+
+    protected DelinquencyBucket(String name) {
+        this.name = name;
+    }
+
+    public static DelinquencyBucket instance(String name) {

Review Comment:
   Done! Removed



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932778339


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/DelinquencyBucketMappings.java:
##########
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_delinquency_bucket_mappings", uniqueConstraints = {
+        @UniqueConstraint(name = "uq_delinquency_bucket_mapping", columnNames = { "delinquencyBucket", "delinquencyRange" }) })
+public class DelinquencyBucketMappings extends AbstractAuditableCustom {

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932810145


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyTags.java:
##########
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_delinquency_tags")
+public class LoanDelinquencyTags extends AbstractAuditableCustom {
+
+    @OneToOne(fetch = FetchType.EAGER)
+    @JoinColumn(name = "delinquency_range_id", nullable = false)
+    private DelinquencyRange delinquencyRange;
+
+    @OneToOne(fetch = FetchType.EAGER)

Review Comment:
   Done!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932824428


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java:
##########
@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
+import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
+import org.apache.fineract.infrastructure.jobs.service.JobName;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappings;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeNotFoundException;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlatformService {
+
+    private final DelinquencyBucketParseAndValidator dataValidatorBucket;
+    private final DelinquencyRangeParseAndValidator dataValidatorRange;
+
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyBucketRepository repositoryBucket;
+    private final DelinquencyBucketMappingsRepository repositoryBucketMappings;
+
+    @Override
+    public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyRange delinquencyRange = updateOrCreateDelinquencyRange(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        DelinquencyRange delinquencyRange = this.repositoryRange.getReferenceById(delinquencyRangeId);
+        if (delinquencyRange == null) {
+            throw DelinquencyRangeNotFoundException.notFound(delinquencyRangeId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyRange = updateDelinquencyRange(delinquencyRange, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        final DelinquencyRange delinquencyRange = repositoryRange.getReferenceById(delinquencyRangeId);
+        repositoryRange.delete(delinquencyRange);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).build();
+    }
+
+    @Override
+    public CommandProcessingResult createDelinquencyBucket(JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyBucket delinquencyBucket = updateOrCreateDelinquencyBucket(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        DelinquencyBucket delinquencyBucket = this.repositoryBucket.getReferenceById(delinquencyBucketId);
+        if (delinquencyBucket == null) {
+            throw DelinquencyBucketNotFoundException.notFound(delinquencyBucketId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyBucket = updateDelinquencyBucket(delinquencyBucket, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        final DelinquencyBucket delinquencyBucket = repositoryBucket.getReferenceById(delinquencyBucketId);
+        repositoryBucket.delete(delinquencyBucket);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).build();
+    }
+
+    @Override
+    @CronTarget(jobName = JobName.LOAN_DELINQUENCY_CLASSIFICATION)
+    public void setLoanDelinquencyClassification() throws JobExecutionException {
+
+    }
+
+    private DelinquencyRange updateOrCreateDelinquencyRange(DelinquencyRangeData data, Map<String, Object> changes) {

Review Comment:
   This is a similar of a recent implementation, BusinessDate to be more precise 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2442: Delinquency buckets management for Loans

Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2442:
URL: https://github.com/apache/fineract/pull/2442#discussion_r932824502


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java:
##########
@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
+import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
+import org.apache.fineract.infrastructure.jobs.service.JobName;
+import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
+import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappings;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
+import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeNotFoundException;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlatformService {
+
+    private final DelinquencyBucketParseAndValidator dataValidatorBucket;
+    private final DelinquencyRangeParseAndValidator dataValidatorRange;
+
+    private final DelinquencyRangeRepository repositoryRange;
+    private final DelinquencyBucketRepository repositoryBucket;
+    private final DelinquencyBucketMappingsRepository repositoryBucketMappings;
+
+    @Override
+    public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyRange delinquencyRange = updateOrCreateDelinquencyRange(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        DelinquencyRangeData data = dataValidatorRange.validateAndParseUpdate(command);
+        DelinquencyRange delinquencyRange = this.repositoryRange.getReferenceById(delinquencyRangeId);
+        if (delinquencyRange == null) {
+            throw DelinquencyRangeNotFoundException.notFound(delinquencyRangeId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyRange = updateDelinquencyRange(delinquencyRange, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyRange(Long delinquencyRangeId, JsonCommand command) {
+        final DelinquencyRange delinquencyRange = repositoryRange.getReferenceById(delinquencyRangeId);
+        repositoryRange.delete(delinquencyRange);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).build();
+    }
+
+    @Override
+    public CommandProcessingResult createDelinquencyBucket(JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        Map<String, Object> changes = new HashMap<>();
+        DelinquencyBucket delinquencyBucket = updateOrCreateDelinquencyBucket(data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult updateDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        DelinquencyBucketData data = dataValidatorBucket.validateAndParseUpdate(command);
+        DelinquencyBucket delinquencyBucket = this.repositoryBucket.getReferenceById(delinquencyBucketId);
+        if (delinquencyBucket == null) {
+            throw DelinquencyBucketNotFoundException.notFound(delinquencyBucketId);
+        }
+        Map<String, Object> changes = new HashMap<>();
+        delinquencyBucket = updateDelinquencyBucket(delinquencyBucket, data, changes);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).with(changes)
+                .build();
+    }
+
+    @Override
+    public CommandProcessingResult deleteDelinquencyBucket(Long delinquencyBucketId, JsonCommand command) {
+        final DelinquencyBucket delinquencyBucket = repositoryBucket.getReferenceById(delinquencyBucketId);
+        repositoryBucket.delete(delinquencyBucket);
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).build();
+    }
+
+    @Override
+    @CronTarget(jobName = JobName.LOAN_DELINQUENCY_CLASSIFICATION)

Review Comment:
   Done! Removed



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org