This is an automated email from the ASF dual-hosted git repository.
arnold pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new 25c0b6a08 FINERACT-1760: Enhanced external id support for loan charges
25c0b6a08 is described below
commit 25c0b6a08d879db9291907138f929753492b6d46
Author: Adam Saghy <[email protected]>
AuthorDate: Mon Nov 28 01:21:48 2022 +0100
FINERACT-1760: Enhanced external id support for loan charges
---
.../fineract/portfolio/charge/data/ChargeData.java | 3 +-
.../loanaccount/api/LoanChargesApiResource.java | 464 +++++++++++++++++----
.../api/LoanChargesApiResourceSwagger.java | 8 +
.../portfolio/loanaccount/data/LoanChargeData.java | 24 +-
.../portfolio/loanaccount/domain/Loan.java | 9 +-
.../portfolio/loanaccount/domain/LoanCharge.java | 100 +----
.../loanaccount/domain/LoanChargeRepository.java | 9 +-
.../LoanAccrualWritePlatformServiceImpl.java | 4 +-
.../loanaccount/service/LoanChargeAssembler.java | 126 +++++-
.../service/LoanChargeReadPlatformService.java | 5 +-
.../service/LoanChargeReadPlatformServiceImpl.java | 79 ++--
.../LoanChargeWritePlatformServiceImpl.java | 38 +-
.../LoanWritePlatformServiceJpaRepositoryImpl.java | 6 -
.../portfolio/loanaccount/domain/LoanTest.java | 3 +-
.../ExternalIdSupportIntegrationTest.java | 149 ++++++-
.../common/loans/LoanTransactionHelper.java | 136 +++++-
16 files changed, 873 insertions(+), 290 deletions(-)
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/data/ChargeData.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/data/ChargeData.java
index 2b36e0c9c..a018cc604 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/data/ChargeData.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/data/ChargeData.java
@@ -28,6 +28,7 @@ import java.util.Map;
import lombok.Getter;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
@@ -308,7 +309,7 @@ public final class ChargeData implements
Comparable<ChargeData>, Serializable {
}
return LoanChargeData.newLoanChargeDetails(this.id, this.name,
this.currency, this.amount, percentage, this.chargeTimeType,
- this.chargeCalculationType, this.penalty,
this.chargePaymentMode, this.minCap, this.maxCap);
+ this.chargeCalculationType, this.penalty,
this.chargePaymentMode, this.minCap, this.maxCap, ExternalId.empty());
}
public SavingsAccountChargeData toSavingsAccountChargeData() {
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResource.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResource.java
index 2559c8a38..cc5bdcd04 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResource.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResource.java
@@ -43,15 +43,18 @@ import javax.ws.rs.QueryParam;
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.domain.ExternalId;
import
org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
import
org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import
org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.service.CommandParameterUtil;
+import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
import
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
@@ -59,49 +62,37 @@ import
org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
import
org.apache.fineract.portfolio.loanaccount.data.LoanInstallmentChargeData;
import
org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformService;
-import org.springframework.beans.factory.annotation.Autowired;
+import
org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
-@Path("/loans/{loanId}/charges")
+@Path("/loans")
@Component
@Scope("singleton")
@Tag(name = "Loan Charges", description = "Its typical for MFIs to add extra
costs for their loan products. They can be either Fees or Penalties.\n"
+ "\n"
+ "Loan Charges are instances of Charges and represent either fees and
penalties for loan products. Refer Charges for documentation of the various
properties of a charge, Only additional properties ( specific to the context of
a Charge being associated with a Loan) are described here")
+@RequiredArgsConstructor
public class LoanChargesApiResource {
+ public static final String COMMAND_PAY = "pay";
+ public static final String COMMAND_WAIVE = "waive";
+ public static final String COMMAND_ADJUSTMENT = "adjustment";
private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(
Arrays.asList("id", "chargeId", "name", "penalty",
"chargeTimeType", "dueAsOfDate", "chargeCalculationType", "percentage",
"amountPercentageAppliedTo", "currency", "amountWaived",
"amountWrittenOff", "amountOutstanding", "amountOrPercentage",
- "amount", "amountPaid", "chargeOptions",
"installmentChargeData"));
-
+ "amount", "amountPaid", "chargeOptions",
"installmentChargeData", "externalId"));
private static final String RESOURCE_NAME_FOR_PERMISSIONS = "LOAN";
- public static final String COMMAND_PAY = "pay";
- public static final String COMMAND_WAIVE = "waive";
- public static final String COMMAND_ADJUSTMENT = "adjustment";
-
private final PlatformSecurityContext context;
private final ChargeReadPlatformService chargeReadPlatformService;
private final LoanChargeReadPlatformService loanChargeReadPlatformService;
private final DefaultToApiJsonSerializer<LoanChargeData>
toApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PortfolioCommandSourceWritePlatformService
commandsSourceWritePlatformService;
-
- @Autowired
- public LoanChargesApiResource(final PlatformSecurityContext context, final
ChargeReadPlatformService chargeReadPlatformService,
- final LoanChargeReadPlatformService loanChargeReadPlatformService,
- final DefaultToApiJsonSerializer<LoanChargeData>
toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper,
- final PortfolioCommandSourceWritePlatformService
commandsSourceWritePlatformService) {
- this.context = context;
- this.chargeReadPlatformService = chargeReadPlatformService;
- this.loanChargeReadPlatformService = loanChargeReadPlatformService;
- this.toApiJsonSerializer = toApiJsonSerializer;
- this.apiRequestParameterHelper = apiRequestParameterHelper;
- this.commandsSourceWritePlatformService =
commandsSourceWritePlatformService;
- }
+ private final LoanReadPlatformService loanReadPlatformService;
@GET
+ @Path("{loanId}/charges")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "List Loan Charges", description = "It lists all the
Loan Charges specific to a Loan \n\n" + "Example Requests:\n"
@@ -111,16 +102,26 @@ public class LoanChargesApiResource {
public String retrieveAllLoanCharges(@PathParam("loanId")
@Parameter(description = "loanId") final Long loanId,
@Context final UriInfo uriInfo) {
-
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
+ return retrieveAllLoanCharges(loanId, null, uriInfo);
+ }
- final Collection<LoanChargeData> loanCharges =
this.loanChargeReadPlatformService.retrieveLoanCharges(loanId);
+ @GET
+ @Path("external-id/{loanExternalId}/charges")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "List Loan Charges", description = "It lists all the
Loan Charges specific to a Loan \n\n" + "Example Requests:\n"
+ + "\n" + "loans/1/charges\n" + "\n" + "\n" +
"loans/1/charges?fields=name,amountOrPercentage")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(array = @ArraySchema(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.GetLoansLoanIdChargesChargeIdResponse.class)))) })
+ public String retrieveAllLoanCharges(
+ @PathParam("loanExternalId") @Parameter(description =
"loanExternalId") final String loanExternalId,
+ @Context final UriInfo uriInfo) {
- final ApiRequestJsonSerializationSettings settings =
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
- return this.toApiJsonSerializer.serialize(settings, loanCharges,
RESPONSE_DATA_PARAMETERS);
+ return retrieveAllLoanCharges(null, loanExternalId, uriInfo);
}
@GET
- @Path("template")
+ @Path("{loanId}/charges/template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve Loan Charges Template", description = "This
is a convenience resource. It can be useful when building maintenance user
interface screens for client applications. The template data returned consists
of any or all of:\n"
@@ -130,44 +131,87 @@ public class LoanChargesApiResource {
public String retrieveTemplate(@PathParam("loanId") @Parameter(description
= "loanId") final Long loanId,
@Context final UriInfo uriInfo) {
-
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
+ return retrieveTemplate(loanId, null, uriInfo);
+ }
- final Collection<ChargeData> chargeOptions =
this.chargeReadPlatformService.retrieveLoanAccountApplicableCharges(loanId,
- new ChargeTimeType[] { ChargeTimeType.OVERDUE_INSTALLMENT });
- final LoanChargeData loanChargeTemplate =
LoanChargeData.template(chargeOptions);
+ @GET
+ @Path("external-id/{loanExternalId}/charges/template")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve Loan Charges Template", description = "This
is a convenience resource. It can be useful when building maintenance user
interface screens for client applications. The template data returned consists
of any or all of:\n"
+ + "\n" + "Field Defaults\n" + "Allowed description Lists\n" +
"Example Request:\n" + "\n" + "loans/1/charges/template\n" + "\n")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.GetLoansLoanIdChargesTemplateResponse.class))) })
+ public String retrieveTemplate(@PathParam("loanExternalId")
@Parameter(description = "loanExternalId") final String loanExternalId,
+ @Context final UriInfo uriInfo) {
- final ApiRequestJsonSerializationSettings settings =
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
- return this.toApiJsonSerializer.serialize(settings,
loanChargeTemplate, RESPONSE_DATA_PARAMETERS);
+ return retrieveTemplate(null, loanExternalId, uriInfo);
}
@GET
- @Path("{chargeId}")
+ @Path("{loanId}/charges/{loanChargeId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- @Operation(summary = "Retrieve a Loan Charge", description = "Retrieves
Loan Charge according to the Loan ID and Charge ID"
+ @Operation(summary = "Retrieve a Loan Charge", description = "Retrieves
Loan Charge according to the Loan ID and Loan Charge ID"
+ "Example Requests:\n" + "\n" + "/loans/1/charges/1\n" + "\n" +
"\n" + "/loans/1/charges/1?fields=name,amountOrPercentage")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.GetLoansLoanIdChargesChargeIdResponse.class))) })
public String retrieveLoanCharge(@PathParam("loanId")
@Parameter(description = "loanId") final Long loanId,
- @PathParam("chargeId") @Parameter(description = "chargeId") final
Long loanChargeId, @Context final UriInfo uriInfo) {
+ @PathParam("loanChargeId") @Parameter(description =
"loanChargeId") final Long loanChargeId, @Context final UriInfo uriInfo) {
-
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
+ return retrieveLoanCharge(loanId, null, loanChargeId, null, uriInfo);
+ }
- final LoanChargeData loanCharge =
this.loanChargeReadPlatformService.retrieveLoanChargeDetails(loanChargeId,
loanId);
+ @GET
+ @Path("{loanId}/charges/external-id/{loanChargeExternalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Loan Charge", description = "Retrieves
Loan Charge according to the Loan ID and Loan Charge External ID"
+ + "Example Requests:\n" + "\n" + "/loans/1/charges/1\n" + "\n" +
"\n"
+ + "/loans/1/charges/external-id/1?fields=name,amountOrPercentage")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.GetLoansLoanIdChargesChargeIdResponse.class))) })
+ public String retrieveLoanCharge(@PathParam("loanId")
@Parameter(description = "loanId") final Long loanId,
+ @PathParam("loanChargeExternalId") @Parameter(description =
"loanChargeExternalId") final String loanChargeExternalId,
+ @Context final UriInfo uriInfo) {
- final Collection<LoanInstallmentChargeData> installmentChargeData =
this.loanChargeReadPlatformService
- .retrieveInstallmentLoanCharges(loanChargeId, true);
+ return retrieveLoanCharge(loanId, null, null, loanChargeExternalId,
uriInfo);
+ }
- final LoanChargeData loanChargeData = new LoanChargeData(loanCharge,
installmentChargeData);
+ @GET
+ @Path("external-id/{loanExternalId}/charges/{loanChargeId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Loan Charge", description = "Retrieves
Loan Charge according to the Loan external ID and Loan Charge ID"
+ + "Example Requests:\n" + "\n" + "/loans/1/charges/1\n" + "\n" +
"\n" + "/loans/1/charges/1?fields=name,amountOrPercentage")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.GetLoansLoanIdChargesChargeIdResponse.class))) })
+ public String retrieveLoanCharge(@PathParam("loanExternalId")
@Parameter(description = "loanExternalId") final String loanExternalId,
+ @PathParam("loanChargeId") @Parameter(description =
"loanChargeId") final Long loanChargeId, @Context final UriInfo uriInfo) {
- final ApiRequestJsonSerializationSettings settings =
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
- return this.toApiJsonSerializer.serialize(settings, loanChargeData,
RESPONSE_DATA_PARAMETERS);
+ return retrieveLoanCharge(null, loanExternalId, loanChargeId, null,
uriInfo);
+ }
+
+ @GET
+
@Path("external-id/{loanExternalId}/charges/external-id/{loanChargeExternalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Loan Charge", description = "Retrieves
Loan Charge according to the Loan External ID and Loan Charge External ID"
+ + "Example Requests:\n" + "\n" + "/loans/1/charges/1\n" + "\n" +
"\n" + "/loans/1/charges/1?fields=name,amountOrPercentage")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.GetLoansLoanIdChargesChargeIdResponse.class))) })
+ public String retrieveLoanCharge(@PathParam("loanExternalId")
@Parameter(description = "loanExternalId") final String loanExternalId,
+ @PathParam("loanChargeExternalId") @Parameter(description =
"loanChargeExternalId") final String loanChargeExternalId,
+ @Context final UriInfo uriInfo) {
+
+ return retrieveLoanCharge(null, loanExternalId, null,
loanChargeExternalId, uriInfo);
}
@POST
+ @Path("{loanId}/charges")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- @Operation(summary = "Create a Loan Charge", description = "It Creates a
Loan Charge")
+ @Operation(summary = "Create a Loan Charge (no command provided) or Pay a
charge (command=pay)", description = "Creates a Loan Charge | Pay a Loan
Charge")
@RequestBody(required = true, content = @Content(schema =
@Schema(implementation =
LoanChargesApiResourceSwagger.PostLoansLoanIdChargesRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.PostLoansLoanIdChargesResponse.class))) })
@@ -175,22 +219,90 @@ public class LoanChargesApiResource {
@QueryParam("command") @Parameter(description = "command") final
String commandParam,
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
- CommandProcessingResult result;
- if (CommandParameterUtil.is(commandParam, COMMAND_PAY)) {
- final CommandWrapper commandRequest = new
CommandWrapperBuilder().payLoanCharge(loanId,
null).withJson(apiRequestBodyAsJson)
- .build();
- result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
- } else {
- final CommandWrapper commandRequest = new
CommandWrapperBuilder().createLoanCharge(loanId).withJson(apiRequestBodyAsJson)
- .build();
- result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
- }
+ return handleExecuteLoanCharge(loanId, null, commandParam,
apiRequestBodyAsJson);
+ }
- return this.toApiJsonSerializer.serialize(result);
+ @POST
+ @Path("external-id/{loanExternalId}/charges")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Create a Loan Charge (no command provided) or Pay a
charge (command=pay)", description = "Creates a Loan Charge | Pay a Loan
Charge")
+ @RequestBody(required = true, content = @Content(schema =
@Schema(implementation =
LoanChargesApiResourceSwagger.PostLoansLoanIdChargesRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.PostLoansLoanIdChargesResponse.class))) })
+ public String executeLoanCharge(@PathParam("loanExternalId")
@Parameter(description = "loanExternalId") final String loanExternalId,
+ @QueryParam("command") @Parameter(description = "command") final
String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+
+ return handleExecuteLoanCharge(null, loanExternalId, commandParam,
apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("{loanId}/charges/{loanChargeId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Pay / Waive / Adjustment for Loan Charge",
description = "Loan Charge will be paid if the loan is linked with a savings
account | Waive Loan Charge | Add Charge Adjustment")
+ @RequestBody(required = true, content = @Content(schema =
@Schema(implementation =
LoanChargesApiResourceSwagger.PostLoansLoanIdChargesChargeIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.PostLoansLoanIdChargesChargeIdResponse.class))) })
+ public String executeLoanCharge(@PathParam("loanId")
@Parameter(description = "loanId") final Long loanId,
+ @PathParam("loanChargeId") @Parameter(description =
"loanChargeId") final Long loanChargeId,
+ @QueryParam("command") @Parameter(description = "command") final
String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+
+ return handleExecuteLoanCharge(loanId, null, loanChargeId, null,
commandParam, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("{loanId}/charges/external-id/{loanChargeExternalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Pay / Waive / Adjustment for Loan Charge",
description = "Loan Charge will be paid if the loan is linked with a savings
account | Waive Loan Charge | Add Charge Adjustment")
+ @RequestBody(required = true, content = @Content(schema =
@Schema(implementation =
LoanChargesApiResourceSwagger.PostLoansLoanIdChargesChargeIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.PostLoansLoanIdChargesChargeIdResponse.class))) })
+ public String executeLoanCharge(@PathParam("loanId")
@Parameter(description = "loanId") final Long loanId,
+ @PathParam("loanChargeExternalId") @Parameter(description =
"loanChargeExternalId") final String loanChargeExternalId,
+ @QueryParam("command") @Parameter(description = "command") final
String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+
+ return handleExecuteLoanCharge(loanId, null, null,
loanChargeExternalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("external-id/{loanExternalId}/charges/{loanChargeId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Pay / Waive / Adjustment for Loan Charge",
description = "Loan Charge will be paid if the loan is linked with a savings
account | Waive Loan Charge | Add Charge Adjustment")
+ @RequestBody(required = true, content = @Content(schema =
@Schema(implementation =
LoanChargesApiResourceSwagger.PostLoansLoanIdChargesChargeIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.PostLoansLoanIdChargesChargeIdResponse.class))) })
+ public String executeLoanCharge(@PathParam("loanExternalId")
@Parameter(description = "loanExternalId") final String loanExternalId,
+ @PathParam("loanChargeId") @Parameter(description =
"loanChargeId") final Long loanChargeId,
+ @QueryParam("command") @Parameter(description = "command") final
String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+
+ return handleExecuteLoanCharge(null, loanExternalId, loanChargeId,
null, commandParam, apiRequestBodyAsJson);
+ }
+
+ @POST
+
@Path("external-id/{loanExternalId}/charges/external-id/{loanChargeExternalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Pay / Waive / Adjustment for Loan Charge",
description = "Loan Charge will be paid if the loan is linked with a savings
account | Waive Loan Charge | Add Charge Adjustment")
+ @RequestBody(required = true, content = @Content(schema =
@Schema(implementation =
LoanChargesApiResourceSwagger.PostLoansLoanIdChargesChargeIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.PostLoansLoanIdChargesChargeIdResponse.class))) })
+ public String executeLoanCharge(@PathParam("loanExternalId")
@Parameter(description = "loanExternalId") final String loanExternalId,
+ @PathParam("loanChargeExternalId") @Parameter(description =
"loanChargeExternalId") final String loanChargeExternalId,
+ @QueryParam("command") @Parameter(description = "command") final
String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+
+ return handleExecuteLoanCharge(null, loanExternalId, null,
loanChargeExternalId, commandParam, apiRequestBodyAsJson);
}
@PUT
- @Path("{chargeId}")
+ @Path("{loanId}/charges/{loanChargeId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Update a Loan Charge", description = "Currently Loan
Charges may be updated only if the Loan is not yet approved")
@@ -198,40 +310,186 @@ public class LoanChargesApiResource {
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.PutLoansLoanIdChargesChargeIdResponse.class))) })
public String updateLoanCharge(@PathParam("loanId") @Parameter(description
= "loanId") final Long loanId,
- @PathParam("chargeId") @Parameter(description = "chargeId") final
Long loanChargeId,
+ @PathParam("loanChargeId") @Parameter(description =
"loanChargeId") final Long loanChargeId,
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
- final CommandWrapper commandRequest = new
CommandWrapperBuilder().updateLoanCharge(loanId, loanChargeId)
- .withJson(apiRequestBodyAsJson).build();
+ return updateLoanCharge(loanId, null, loanChargeId, null,
apiRequestBodyAsJson);
+ }
- final CommandProcessingResult result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ @PUT
+ @Path("{loanId}/charges/external-id/{loanChargeExternalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Update a Loan Charge", description = "Currently Loan
Charges may be updated only if the Loan is not yet approved")
+ @RequestBody(required = true, content = @Content(schema =
@Schema(implementation =
LoanChargesApiResourceSwagger.PutLoansLoanIdChargesChargeIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.PutLoansLoanIdChargesChargeIdResponse.class))) })
+ public String updateLoanCharge(@PathParam("loanId") @Parameter(description
= "loanId") final Long loanId,
+ @PathParam("loanChargeExternalId") @Parameter(description =
"loanChargeExternalId") final String loanChargeExternalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
- return this.toApiJsonSerializer.serialize(result);
+ return updateLoanCharge(loanId, null, null, loanChargeExternalId,
apiRequestBodyAsJson);
}
- @POST
- @Path("{chargeId}")
+ @PUT
+ @Path("external-id/{loanExternalId}/charges/{loanChargeId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- @Operation(summary = "Pay Loan Charge", description = "Loan Charge will be
paid if the loan is linked with a savings account")
- @RequestBody(required = true, content = @Content(schema =
@Schema(implementation =
LoanChargesApiResourceSwagger.PostLoansLoanIdChargesChargeIdRequest.class)))
+ @Operation(summary = "Update a Loan Charge", description = "Currently Loan
Charges may be updated only if the Loan is not yet approved")
+ @RequestBody(required = true, content = @Content(schema =
@Schema(implementation =
LoanChargesApiResourceSwagger.PutLoansLoanIdChargesChargeIdRequest.class)))
@ApiResponses({
- @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.PostLoansLoanIdChargesChargeIdResponse.class))) })
- public String executeLoanCharge(@PathParam("loanId")
@Parameter(description = "loanId") final Long loanId,
- @PathParam("chargeId") @Parameter(description = "chargeId") final
Long loanChargeId,
- @QueryParam("command") @Parameter(description = "command") final
String commandParam,
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.PutLoansLoanIdChargesChargeIdResponse.class))) })
+ public String updateLoanCharge(@PathParam("loanExternalId")
@Parameter(description = "loanExternalId") final String loanExternalId,
+ @PathParam("loanChargeId") @Parameter(description =
"loanChargeId") final Long loanChargeId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+
+ return updateLoanCharge(null, loanExternalId, loanChargeId, null,
apiRequestBodyAsJson);
+ }
+
+ @PUT
+
@Path("external-id/{loanExternalId}/charges/external-id/{loanChargeExternalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Update a Loan Charge", description = "Currently Loan
Charges may be updated only if the Loan is not yet approved")
+ @RequestBody(required = true, content = @Content(schema =
@Schema(implementation =
LoanChargesApiResourceSwagger.PutLoansLoanIdChargesChargeIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.PutLoansLoanIdChargesChargeIdResponse.class))) })
+ public String updateLoanCharge(@PathParam("loanExternalId")
@Parameter(description = "loanExternalId") final String loanExternalId,
+ @PathParam("loanChargeExternalId") @Parameter(description =
"loanChargeExternalId") final String loanChargeExternalId,
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateLoanCharge(null, loanExternalId, null,
loanChargeExternalId, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("{loanId}/charges/{loanChargeId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Loan Charge", description = "Note:
Currently, A Loan Charge may only be removed from Loans that are not yet
approved.")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.DeleteLoansLoanIdChargesChargeIdResponse.class)))
})
+ public String deleteLoanCharge(@PathParam("loanId") @Parameter(description
= "loanId") final Long loanId,
+ @PathParam("loanChargeId") @Parameter(description =
"loanChargeId") final Long loanChargeId) {
+
+ return deleteLoanCharge(loanId, null, loanChargeId, null);
+ }
+
+ @DELETE
+ @Path("{loanId}/charges/external-id/{loanChargeExternalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Loan Charge", description = "Note:
Currently, A Loan Charge may only be removed from Loans that are not yet
approved.")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.DeleteLoansLoanIdChargesChargeIdResponse.class)))
})
+ public String deleteLoanCharge(@PathParam("loanId") @Parameter(description
= "loanId") final Long loanId,
+ @PathParam("loanChargeExternalId") @Parameter(description =
"loanChargeExternalId") final String loanChargeExternalId) {
+
+ return deleteLoanCharge(loanId, null, null, loanChargeExternalId);
+ }
+
+ @DELETE
+ @Path("external-id/{loanExternalId}/charges/{loanChargeId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Loan Charge", description = "Note:
Currently, A Loan Charge may only be removed from Loans that are not yet
approved.")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.DeleteLoansLoanIdChargesChargeIdResponse.class)))
})
+ public String deleteLoanCharge(@PathParam("loanExternalId")
@Parameter(description = "loanExternalId") final String loanExternalId,
+ @PathParam("loanChargeId") @Parameter(description =
"loanChargeId") final Long loanChargeId) {
+
+ return deleteLoanCharge(null, loanExternalId, loanChargeId, null);
+ }
+
+ @DELETE
+
@Path("external-id/{loanExternalId}/charges/external-id/{loanChargeExternalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Loan Charge", description = "Note:
Currently, A Loan Charge may only be removed from Loans that are not yet
approved.")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.DeleteLoansLoanIdChargesChargeIdResponse.class)))
})
+ public String deleteLoanCharge(@PathParam("loanExternalId")
@Parameter(description = "loanExternalId") final String loanExternalId,
+ @PathParam("loanChargeExternalId") @Parameter(description =
"loanChargeExternalId") final String loanChargeExternalId) {
+
+ return deleteLoanCharge(null, loanExternalId, null,
loanChargeExternalId);
+ }
+
+ private String deleteLoanCharge(final Long loanId, final String
loanExternalIdStr, final Long loanChargeId,
+ final String loanChargeExternalIdStr) {
+
+ ExternalId loanExternalId =
ExternalIdFactory.produce(loanExternalIdStr);
+ ExternalId loanChargeExternalId =
ExternalIdFactory.produce(loanChargeExternalIdStr);
+
+ Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+ Long resolvedLoanChargeId = getResolvedLoanChargeId(loanChargeId,
loanChargeExternalId);
+
+ final CommandWrapper commandRequest = new
CommandWrapperBuilder().deleteLoanCharge(resolvedLoanId,
resolvedLoanChargeId).build();
+
+ final CommandProcessingResult result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+
+ return this.toApiJsonSerializer.serialize(result);
+ }
+
+ private String retrieveLoanCharge(final Long loanId, final String
loanExternalIdStr, final Long loanChargeId,
+ final String loanChargeExternalIdStr, final UriInfo uriInfo) {
+
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
+
+ ExternalId loanExternalId =
ExternalIdFactory.produce(loanExternalIdStr);
+ ExternalId loanChargeExternalId =
ExternalIdFactory.produce(loanChargeExternalIdStr);
+
+ Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+ Long resolvedLoanChargeId = getResolvedLoanChargeId(loanChargeId,
loanChargeExternalId);
+
+ final LoanChargeData loanCharge =
this.loanChargeReadPlatformService.retrieveLoanChargeDetails(resolvedLoanChargeId,
+ resolvedLoanId);
+
+ final Collection<LoanInstallmentChargeData> installmentChargeData =
this.loanChargeReadPlatformService
+ .retrieveInstallmentLoanCharges(resolvedLoanChargeId, true);
+
+ final LoanChargeData loanChargeData = new LoanChargeData(loanCharge,
installmentChargeData);
+
+ final ApiRequestJsonSerializationSettings settings =
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return this.toApiJsonSerializer.serialize(settings, loanChargeData,
RESPONSE_DATA_PARAMETERS);
+ }
+
+ private String handleExecuteLoanCharge(final Long loanId, final String
loanExternalIdStr, final String commandParam,
+ final String apiRequestBodyAsJson) {
+
+ ExternalId loanExternalId =
ExternalIdFactory.produce(loanExternalIdStr);
+ Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+
+ CommandProcessingResult result;
+ if (CommandParameterUtil.is(commandParam, COMMAND_PAY)) {
+ final CommandWrapper commandRequest = new
CommandWrapperBuilder().payLoanCharge(resolvedLoanId, null)
+ .withJson(apiRequestBodyAsJson).build();
+ result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ } else {
+ final CommandWrapper commandRequest = new
CommandWrapperBuilder().createLoanCharge(resolvedLoanId)
+ .withJson(apiRequestBodyAsJson).build();
+ result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ }
+
+ return this.toApiJsonSerializer.serialize(result);
+ }
+
+ private String handleExecuteLoanCharge(final Long loanId, final String
loanExternalIdStr, final Long loanChargeId,
+ final String loanChargeExternalIdStr, final String commandParam,
final String apiRequestBodyAsJson) {
+
+ ExternalId loanExternalId =
ExternalIdFactory.produce(loanExternalIdStr);
+ ExternalId loanChargeExternalId =
ExternalIdFactory.produce(loanChargeExternalIdStr);
+
+ Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+ Long resolvedLoanChargeId = getResolvedLoanChargeId(loanChargeId,
loanChargeExternalId);
+
final CommandWrapperBuilder builder = new
CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
CommandProcessingResult result;
if (CommandParameterUtil.is(commandParam, COMMAND_WAIVE)) {
- final CommandWrapper commandRequest =
builder.waiveLoanCharge(loanId, loanChargeId).build();
+ final CommandWrapper commandRequest =
builder.waiveLoanCharge(resolvedLoanId, resolvedLoanChargeId).build();
result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (CommandParameterUtil.is(commandParam, COMMAND_PAY)) {
- final CommandWrapper commandRequest =
builder.payLoanCharge(loanId, loanChargeId).build();
+ final CommandWrapper commandRequest =
builder.payLoanCharge(resolvedLoanId, resolvedLoanChargeId).build();
result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (CommandParameterUtil.is(commandParam, COMMAND_ADJUSTMENT)) {
- final CommandWrapper commandRequest =
builder.adjustmentForLoanCharge(loanId, loanChargeId).build();
+ final CommandWrapper commandRequest =
builder.adjustmentForLoanCharge(resolvedLoanId, resolvedLoanChargeId).build();
result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else {
throw new UnrecognizedQueryParamException("command", commandParam);
@@ -243,20 +501,64 @@ public class LoanChargesApiResource {
return this.toApiJsonSerializer.serialize(result);
}
- @DELETE
- @Path("{chargeId}")
- @Consumes({ MediaType.APPLICATION_JSON })
- @Produces({ MediaType.APPLICATION_JSON })
- @Operation(summary = "Delete a Loan Charge", description = "Note:
Currently, A Loan Charge may only be removed from Loans that are not yet
approved.")
- @ApiResponses({
- @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanChargesApiResourceSwagger.DeleteLoansLoanIdChargesChargeIdResponse.class)))
})
- public String deleteLoanCharge(@PathParam("loanId") @Parameter(description
= "loanId") final Long loanId,
- @PathParam("chargeId") @Parameter(description = "chargeId") final
Long loanChargeId) {
+ private String updateLoanCharge(final Long loanId, final String
loanExternalIdStr, final Long loanChargeId,
+ final String loanChargeExternalIdStr, final String
apiRequestBodyAsJson) {
- final CommandWrapper commandRequest = new
CommandWrapperBuilder().deleteLoanCharge(loanId, loanChargeId).build();
+ ExternalId loanExternalId =
ExternalIdFactory.produce(loanExternalIdStr);
+ ExternalId loanChargeExternalId =
ExternalIdFactory.produce(loanChargeExternalIdStr);
+
+ Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+ Long resolvedLoanChargeId = getResolvedLoanChargeId(loanChargeId,
loanChargeExternalId);
+
+ final CommandWrapper commandRequest = new
CommandWrapperBuilder().updateLoanCharge(resolvedLoanId, resolvedLoanChargeId)
+ .withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
+
+ private String retrieveAllLoanCharges(final Long loanId, final String
loanExternalIdStr, final UriInfo uriInfo) {
+
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
+
+ ExternalId loanExternalId =
ExternalIdFactory.produce(loanExternalIdStr);
+ Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+
+ final Collection<LoanChargeData> loanCharges =
this.loanChargeReadPlatformService.retrieveLoanCharges(resolvedLoanId);
+
+ final ApiRequestJsonSerializationSettings settings =
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return this.toApiJsonSerializer.serialize(settings, loanCharges,
RESPONSE_DATA_PARAMETERS);
+ }
+
+ private String retrieveTemplate(final Long loanId, final String
loanExternalIdStr, final UriInfo uriInfo) {
+
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
+
+ ExternalId loanExternalId =
ExternalIdFactory.produce(loanExternalIdStr);
+ Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+
+ final Collection<ChargeData> chargeOptions =
this.chargeReadPlatformService.retrieveLoanAccountApplicableCharges(resolvedLoanId,
+ new ChargeTimeType[] { ChargeTimeType.OVERDUE_INSTALLMENT });
+ final LoanChargeData loanChargeTemplate =
LoanChargeData.template(chargeOptions);
+
+ final ApiRequestJsonSerializationSettings settings =
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return this.toApiJsonSerializer.serialize(settings,
loanChargeTemplate, RESPONSE_DATA_PARAMETERS);
+ }
+
+ private Long getResolvedLoanChargeId(final Long loanChargeId, final
ExternalId loanChargeExternalId) {
+ Long resolvedLoanChargeId = loanChargeId;
+ if (resolvedLoanChargeId == null) {
+ loanChargeExternalId.throwExceptionIfEmpty();
+ resolvedLoanChargeId =
this.loanChargeReadPlatformService.retrieveLoanChargeIdByExternalId(loanChargeExternalId);
+ }
+ return resolvedLoanChargeId;
+ }
+
+ private Long getResolvedLoanId(final Long loanId, final ExternalId
loanExternalId) {
+ Long resolvedLoanId = loanId;
+ if (resolvedLoanId == null) {
+ loanExternalId.throwExceptionIfEmpty();
+ resolvedLoanId =
this.loanReadPlatformService.retrieveLoanIdByExternalId(loanExternalId.getValue());
+ }
+ return resolvedLoanId;
+ }
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResourceSwagger.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResourceSwagger.java
index b01ffb3cb..f991d4894 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResourceSwagger.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResourceSwagger.java
@@ -105,6 +105,8 @@ final class LoanChargesApiResourceSwagger {
public Boolean penalty;
@Schema(example = "27 March 2013")
public LocalDate submittedOnDate;
+ @Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7")
+ public String externalId;
}
@Schema(description = "GetLoansLoanIdChargesTemplateResponse")
@@ -199,6 +201,8 @@ final class LoanChargesApiResourceSwagger {
public Long loanId;
@Schema(example = "31")
public Long resourceId;
+ @Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7")
+ public String resourceExternalId;
}
@Schema(description = " PutLoansLoanIdChargesChargeIdRequest")
@@ -229,6 +233,8 @@ final class LoanChargesApiResourceSwagger {
public Long loanId;
@Schema(example = "6")
public Long resourceId;
+ @Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7")
+ public String resourceExternalId;
public PutLoansLoanIdChargesChargeIdRequest changes;
}
@@ -325,5 +331,7 @@ final class LoanChargesApiResourceSwagger {
public Long loanId;
@Schema(example = "2")
public Long resourceId;
+ @Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7")
+ public String resourceExternalId;
}
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanChargeData.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanChargeData.java
index 9acde0759..c8c542e0a 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanChargeData.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanChargeData.java
@@ -25,6 +25,7 @@ import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
@@ -89,11 +90,11 @@ public class LoanChargeData {
private BigDecimal amountUnrecognized;
- private String externalId;
+ private final ExternalId externalId;
public static LoanChargeData template(final Collection<ChargeData>
chargeOptions) {
return new LoanChargeData(null, null, null, null, null, null, null,
null, chargeOptions, false, null, false, false, null, null,
- null, null, null);
+ null, null, null, ExternalId.empty());
}
/**
@@ -102,9 +103,9 @@ public class LoanChargeData {
public static LoanChargeData newLoanChargeDetails(final Long chargeId,
final String name, final CurrencyData currency,
final BigDecimal amount, final BigDecimal percentage, final
EnumOptionData chargeTimeType,
final EnumOptionData chargeCalculationType, final boolean penalty,
final EnumOptionData chargePaymentMode,
- final BigDecimal minCap, final BigDecimal maxCap) {
+ final BigDecimal minCap, final BigDecimal maxCap, final ExternalId
externalId) {
return new LoanChargeData(null, chargeId, name, currency, amount,
percentage, chargeTimeType, chargeCalculationType, null, penalty,
- chargePaymentMode, false, false, null, minCap, maxCap, null,
null);
+ chargePaymentMode, false, false, null, minCap, maxCap, null,
null, externalId);
}
public LoanChargeData(final Long id, final Long chargeId, final String
name, final CurrencyData currency, final BigDecimal amount,
@@ -113,7 +114,7 @@ public class LoanChargeData {
final LocalDate dueDate, final EnumOptionData
chargeCalculationType, final BigDecimal percentage,
final BigDecimal amountPercentageAppliedTo, final boolean penalty,
final EnumOptionData chargePaymentMode, final boolean paid,
final boolean waived, final Long loanId, final BigDecimal minCap,
final BigDecimal maxCap, final BigDecimal amountOrPercentage,
- Collection<LoanInstallmentChargeData> installmentChargeData, final
String externalId) {
+ Collection<LoanInstallmentChargeData> installmentChargeData, final
ExternalId externalId) {
this.id = id;
this.chargeId = chargeId;
this.name = name;
@@ -158,7 +159,7 @@ public class LoanChargeData {
final BigDecimal percentage, final EnumOptionData chargeTimeType,
final EnumOptionData chargeCalculationType,
final Collection<ChargeData> chargeOptions, final boolean penalty,
final EnumOptionData chargePaymentMode, final boolean paid,
final boolean waived, final Long loanId, final BigDecimal minCap,
final BigDecimal maxCap, final BigDecimal amountOrPercentage,
- Collection<LoanInstallmentChargeData> installmentChargeData) {
+ Collection<LoanInstallmentChargeData> installmentChargeData, final
ExternalId externalId) {
this.id = id;
this.chargeId = chargeId;
this.name = name;
@@ -197,10 +198,11 @@ public class LoanChargeData {
this.installmentChargeData = installmentChargeData;
this.amountAccrued = null;
this.amountUnrecognized = null;
+ this.externalId = externalId;
}
public LoanChargeData(final Long id, final LocalDate dueAsOfDate, final
BigDecimal amountOutstanding, EnumOptionData chargeTimeType,
- final Long loanId, Collection<LoanInstallmentChargeData>
installmentChargeData) {
+ final Long loanId, Collection<LoanInstallmentChargeData>
installmentChargeData, final ExternalId externalId) {
this.id = id;
this.chargeId = null;
this.name = null;
@@ -229,10 +231,12 @@ public class LoanChargeData {
this.installmentChargeData = installmentChargeData;
this.amountAccrued = null;
this.amountUnrecognized = null;
+ this.externalId = externalId;
}
public LoanChargeData(final Long id, final Long chargeId, final LocalDate
dueAsOfDate, EnumOptionData chargeTimeType,
- final BigDecimal amount, final BigDecimal amountAccrued, final
BigDecimal amountWaived, final boolean penalty) {
+ final BigDecimal amount, final BigDecimal amountAccrued, final
BigDecimal amountWaived, final boolean penalty,
+ final ExternalId externalId) {
this.id = id;
this.chargeId = chargeId;
this.name = null;
@@ -261,6 +265,7 @@ public class LoanChargeData {
this.installmentChargeData = null;
this.amountAccrued = amountAccrued;
this.amountUnrecognized = null;
+ this.externalId = externalId;
}
public LoanChargeData(final BigDecimal amountUnrecognized, final
LoanChargeData chargeData) {
@@ -292,6 +297,7 @@ public class LoanChargeData {
this.installmentChargeData = null;
this.amountAccrued = chargeData.amountAccrued;
this.amountUnrecognized = amountUnrecognized;
+ this.externalId = chargeData.externalId;
}
public LoanChargeData(LoanChargeData chargeData,
Collection<LoanInstallmentChargeData> installmentChargeData) {
@@ -356,6 +362,7 @@ public class LoanChargeData {
this.installmentChargeData = null;
this.amountAccrued = null;
this.amountUnrecognized = null;
+ this.externalId = ExternalId.empty();
}
public LoanChargeData(final Long id, final LocalDate dueAsOfDate, final
BigDecimal amountOrPercentage) {
@@ -387,6 +394,7 @@ public class LoanChargeData {
this.installmentChargeData = null;
this.amountAccrued = null;
this.amountUnrecognized = null;
+ this.externalId = ExternalId.empty();
}
public boolean isChargePayable() {
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index 4ca79c749..7df1cc288 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -1950,9 +1950,12 @@ public class Loan extends
AbstractAuditableWithUTCDateTimeCustom {
this.disbursementDetails.add(disbursementDetails);
for (LoanTrancheCharge trancheCharge : trancheCharges) {
Charge chargeDefinition = trancheCharge.getCharge();
- final LoanCharge loanCharge =
LoanCharge.createNewWithoutLoan(chargeDefinition, principal, null, null, null,
- expectedDisbursementDate, null, null);
- loanCharge.update(this);
+ ExternalId externalId = ExternalId.empty();
+ if
(TemporaryConfigurationServiceContainer.isExternalIdAutoGenerationEnabled()) {
+ externalId = ExternalId.generate();
+ }
+ final LoanCharge loanCharge = new LoanCharge(this,
chargeDefinition, principal, null, null, null, expectedDisbursementDate,
+ null, null, BigDecimal.ZERO, externalId);
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge =
new LoanTrancheDisbursementCharge(loanCharge,
disbursementDetails);
loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge);
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCharge.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCharge.java
index 545a74cc9..3c5ddf8ae 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCharge.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCharge.java
@@ -27,7 +27,6 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -44,6 +43,7 @@ import javax.persistence.UniqueConstraint;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import
org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
@@ -131,7 +131,7 @@ public class LoanCharge extends AbstractPersistableCustom {
private boolean active = true;
@Column(name = "external_id")
- private String externalId;
+ private ExternalId externalId;
@OneToOne(mappedBy = "loancharge", cascade = CascadeType.ALL, optional =
true, orphanRemoval = true, fetch = FetchType.EAGER)
private LoanOverdueInstallmentCharge overdueInstallmentCharge;
@@ -142,99 +142,14 @@ public class LoanCharge extends AbstractPersistableCustom
{
@OneToMany(mappedBy = "loanCharge", cascade = CascadeType.ALL,
orphanRemoval = true, fetch = FetchType.EAGER)
private Set<LoanChargePaidBy> loanChargePaidBySet;
- public static LoanCharge createNewFromJson(final Loan loan, final Charge
chargeDefinition, final JsonCommand command) {
- final LocalDate dueDate =
command.localDateValueOfParameterNamed("dueDate");
- if
(chargeDefinition.getChargeTimeType().equals(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
&& dueDate == null) {
- final String defaultUserMessage = "Loan charge is missing due
date.";
- throw new LoanChargeWithoutMandatoryFieldException("loanCharge",
"dueDate", defaultUserMessage, chargeDefinition.getId(),
- chargeDefinition.getName());
- }
- return createNewFromJson(loan, chargeDefinition, command, dueDate);
- }
-
- public static LoanCharge createNewFromJson(final Loan loan, final Charge
chargeDefinition, final JsonCommand command,
- final LocalDate dueDate) {
- final Locale locale = command.extractLocale();
- final BigDecimal amount =
command.bigDecimalValueOfParameterNamed("amount", locale);
-
- final ChargeTimeType chargeTime = null;
- final ChargeCalculationType chargeCalculation = null;
- final ChargePaymentMode chargePaymentMode = null;
- BigDecimal amountPercentageAppliedTo = BigDecimal.ZERO;
- switch
(ChargeCalculationType.fromInt(chargeDefinition.getChargeCalculation())) {
- case PERCENT_OF_AMOUNT:
- if (command.hasParameter("principal")) {
- amountPercentageAppliedTo =
command.bigDecimalValueOfParameterNamed("principal");
- } else {
- amountPercentageAppliedTo =
loan.getPrincipal().getAmount();
- }
- break;
- case PERCENT_OF_AMOUNT_AND_INTEREST:
- if (command.hasParameter("principal") &&
command.hasParameter("interest")) {
- amountPercentageAppliedTo =
command.bigDecimalValueOfParameterNamed("principal")
-
.add(command.bigDecimalValueOfParameterNamed("interest"));
- } else {
- amountPercentageAppliedTo =
loan.getPrincipal().getAmount().add(loan.getTotalInterest());
- }
- break;
- case PERCENT_OF_INTEREST:
- if (command.hasParameter("interest")) {
- amountPercentageAppliedTo =
command.bigDecimalValueOfParameterNamed("interest");
- } else {
- amountPercentageAppliedTo = loan.getTotalInterest();
- }
- break;
- default:
- break;
- }
-
- BigDecimal loanCharge = BigDecimal.ZERO;
- if
(ChargeTimeType.fromInt(chargeDefinition.getChargeTimeType()).equals(ChargeTimeType.INSTALMENT_FEE))
{
- BigDecimal percentage = amount;
- if (percentage == null) {
- percentage = chargeDefinition.getAmount();
- }
- loanCharge =
loan.calculatePerInstallmentChargeAmount(ChargeCalculationType.fromInt(chargeDefinition.getChargeCalculation()),
- percentage);
- }
-
- // If charge type is specified due date and loan is multi disburment
- // loan.
- // Then we need to get as of this loan charge due date how much amount
- // disbursed.
- if
(chargeDefinition.getChargeTimeType().equals(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
&& loan.isMultiDisburmentLoan()) {
- amountPercentageAppliedTo = BigDecimal.ZERO;
- for (final LoanDisbursementDetails loanDisbursementDetails :
loan.getDisbursementDetails()) {
- if
(!loanDisbursementDetails.expectedDisbursementDate().isAfter(dueDate)) {
- amountPercentageAppliedTo =
amountPercentageAppliedTo.add(loanDisbursementDetails.principal());
- }
- }
- }
-
- LoanCharge newLoanCharge = new LoanCharge(loan, chargeDefinition,
amountPercentageAppliedTo, amount, chargeTime, chargeCalculation,
- dueDate, chargePaymentMode, null, loanCharge);
- final String externalId =
command.stringValueOfParameterNamedAllowingNull("externalId");
- newLoanCharge.setExternalId(externalId);
- return newLoanCharge;
- }
-
- /*
- * loanPrincipal is required for charges that are percentage based
- */
- public static LoanCharge createNewWithoutLoan(final Charge
chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount,
- final ChargeTimeType chargeTime, final ChargeCalculationType
chargeCalculation, final LocalDate dueDate,
- final ChargePaymentMode chargePaymentMode, final Integer
numberOfRepayments) {
- return new LoanCharge(null, chargeDefinition, loanPrincipal, amount,
chargeTime, chargeCalculation, dueDate, chargePaymentMode,
- numberOfRepayments, BigDecimal.ZERO);
- }
-
protected LoanCharge() {
//
}
public LoanCharge(final Loan loan, final Charge chargeDefinition, final
BigDecimal loanPrincipal, final BigDecimal amount,
final ChargeTimeType chargeTime, final ChargeCalculationType
chargeCalculation, final LocalDate dueDate,
- final ChargePaymentMode chargePaymentMode, final Integer
numberOfRepayments, final BigDecimal loanCharge) {
+ final ChargePaymentMode chargePaymentMode, final Integer
numberOfRepayments, final BigDecimal loanCharge,
+ final ExternalId externalId) {
this.loan = loan;
this.charge = chargeDefinition;
this.submittedOnDate = DateUtils.getBusinessLocalDate();
@@ -278,6 +193,7 @@ public class LoanCharge extends AbstractPersistableCustom {
populateDerivedFields(loanPrincipal, chargeAmount, numberOfRepayments,
loanCharge);
this.paid = determineIfFullyPaid();
+ this.externalId = externalId;
}
private void populateDerivedFields(final BigDecimal
amountPercentageAppliedTo, final BigDecimal chargeAmount,
@@ -1093,14 +1009,10 @@ public class LoanCharge extends
AbstractPersistableCustom {
this.waived = false;
}
- public String getExternalId() {
+ public ExternalId getExternalId() {
return externalId;
}
- public void setExternalId(String externalId) {
- this.externalId = externalId;
- }
-
public ChargeTimeType getChargeTimeType() {
return ChargeTimeType.fromInt(this.chargeTime);
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanChargeRepository.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanChargeRepository.java
index c5ff8ef49..e3c5ac9ed 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanChargeRepository.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanChargeRepository.java
@@ -18,9 +18,16 @@
*/
package org.apache.fineract.portfolio.loanaccount.domain;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
public interface LoanChargeRepository extends JpaRepository<LoanCharge, Long>,
JpaSpecificationExecutor<LoanCharge> {
- // no added behaviour
+
+ String FIND_ID_BY_EXTERNAL_ID = "SELECT loanCharge.id FROM LoanCharge
loanCharge WHERE loanCharge.externalId = :externalId";
+
+ @Query(FIND_ID_BY_EXTERNAL_ID)
+ Long findIdByExternalId(@Param("externalId") ExternalId externalId);
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java
index 7042ce369..26090f0bb 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java
@@ -67,7 +67,7 @@ public class LoanAccrualWritePlatformServiceImpl implements
LoanAccrualWritePlat
@Override
@Transactional
public void addAccrualAccounting(final Long loanId, final
Collection<LoanScheduleAccrualData> loanScheduleAccrualData) {
- Collection<LoanChargeData> chargeData =
this.loanChargeReadPlatformService.retrieveLoanChargesForAccural(loanId);
+ Collection<LoanChargeData> chargeData =
this.loanChargeReadPlatformService.retrieveLoanChargesForAccrual(loanId);
Collection<LoanSchedulePeriodData> loanWaiverScheduleData = new
ArrayList<>(1);
Collection<LoanTransactionData> loanWaiverTransactionData = new
ArrayList<>(1);
@@ -87,7 +87,7 @@ public class LoanAccrualWritePlatformServiceImpl implements
LoanAccrualWritePlat
public void addPeriodicAccruals(final LocalDate tillDate, Long loanId,
Collection<LoanScheduleAccrualData> loanScheduleAccrualData) {
boolean firstTime = true;
LocalDate accruedTill = null;
- Collection<LoanChargeData> chargeData =
this.loanChargeReadPlatformService.retrieveLoanChargesForAccural(loanId);
+ Collection<LoanChargeData> chargeData =
this.loanChargeReadPlatformService.retrieveLoanChargesForAccrual(loanId);
Collection<LoanSchedulePeriodData> loanWaiverScheduleData = new
ArrayList<>(1);
Collection<LoanTransactionData> loanWaiverTransactionData = new
ArrayList<>(1);
for (final LoanScheduleAccrualData accrualData :
loanScheduleAccrualData) {
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeAssembler.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeAssembler.java
index 4165b7932..827c8d3ef 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeAssembler.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeAssembler.java
@@ -29,14 +29,20 @@ import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
import
org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeAddedException;
+import
org.apache.fineract.portfolio.charge.exception.LoanChargeWithoutMandatoryFieldException;
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
import
org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
@@ -44,25 +50,17 @@ import
org.apache.fineract.portfolio.loanaccount.domain.LoanTrancheDisbursementC
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
import
org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
+@RequiredArgsConstructor
public class LoanChargeAssembler {
private final FromJsonHelper fromApiJsonHelper;
private final ChargeRepositoryWrapper chargeRepository;
private final LoanChargeRepository loanChargeRepository;
private final LoanProductRepository loanProductRepository;
-
- @Autowired
- public LoanChargeAssembler(final FromJsonHelper fromApiJsonHelper, final
ChargeRepositoryWrapper chargeRepository,
- final LoanChargeRepository loanChargeRepository, final
LoanProductRepository loanProductRepository) {
- this.fromApiJsonHelper = fromApiJsonHelper;
- this.chargeRepository = chargeRepository;
- this.loanChargeRepository = loanChargeRepository;
- this.loanProductRepository = loanProductRepository;
- }
+ private final ExternalIdFactory externalIdFactory;
public Set<LoanCharge> fromParsedJson(final JsonElement element,
List<LoanDisbursementDetails> disbursementDetails) {
JsonArray jsonDisbursement =
this.fromApiJsonHelper.extractJsonArrayNamed("disbursementData", element);
@@ -117,6 +115,8 @@ public class LoanChargeAssembler {
locale);
final Integer chargePaymentMode =
this.fromApiJsonHelper.extractIntegerNamed("chargePaymentMode",
loanChargeElement,
locale);
+ final String externalIdStr =
this.fromApiJsonHelper.extractStringNamed("externalId", loanChargeElement);
+ final ExternalId externalId =
externalIdFactory.create(externalIdStr);
if (id == null) {
final Charge chargeDefinition =
this.chargeRepository.findOneWithNotFoundDetection(chargeId);
@@ -140,8 +140,8 @@ public class LoanChargeAssembler {
chargePaymentModeEnum =
ChargePaymentMode.fromInt(chargePaymentMode);
}
if (!isMultiDisbursal) {
- final LoanCharge loanCharge =
LoanCharge.createNewWithoutLoan(chargeDefinition, principal, amount, chargeTime,
- chargeCalculation, dueDate,
chargePaymentModeEnum, numberOfRepayments);
+ final LoanCharge loanCharge =
createNewWithoutLoan(chargeDefinition, principal, amount, chargeTime,
+ chargeCalculation, dueDate,
chargePaymentModeEnum, numberOfRepayments, externalId);
loanCharges.add(loanCharge);
} else {
if (topLevelJsonElement.has("disbursementData") &&
topLevelJsonElement.get("disbursementData").isJsonArray()) {
@@ -159,8 +159,8 @@ public class LoanChargeAssembler {
LoanTrancheDisbursementCharge
loanTrancheDisbursementCharge = null;
if
(chargeDefinition.isPercentageOfApprovedAmount()
&&
disbursementDetail.expectedDisbursementDateAsLocalDate().equals(expectedDisbursementDate))
{
- final LoanCharge loanCharge =
LoanCharge.createNewWithoutLoan(chargeDefinition, principal, amount,
- chargeTime, chargeCalculation,
dueDate, chargePaymentModeEnum, numberOfRepayments);
+ final LoanCharge loanCharge =
createNewWithoutLoan(chargeDefinition, principal, amount, chargeTime,
+ chargeCalculation, dueDate,
chargePaymentModeEnum, numberOfRepayments, externalId);
loanCharges.add(loanCharge);
if
(loanCharge.isTrancheDisbursementCharge()) {
loanTrancheDisbursementCharge =
new LoanTrancheDisbursementCharge(loanCharge,
@@ -169,10 +169,10 @@ public class LoanChargeAssembler {
}
} else {
if
(disbursementDetail.expectedDisbursementDateAsLocalDate().equals(expectedDisbursementDate))
{
- final LoanCharge loanCharge =
LoanCharge.createNewWithoutLoan(chargeDefinition,
+ final LoanCharge loanCharge =
createNewWithoutLoan(chargeDefinition,
disbursementDetail.principal(), amount, chargeTime, chargeCalculation,
disbursementDetail.expectedDisbursementDateAsLocalDate(), chargePaymentModeEnum,
- numberOfRepayments);
+ numberOfRepayments,
externalId);
loanCharges.add(loanCharge);
if
(loanCharge.isTrancheDisbursementCharge()) {
loanTrancheDisbursementCharge
= new LoanTrancheDisbursementCharge(loanCharge,
@@ -186,18 +186,18 @@ public class LoanChargeAssembler {
LoanTrancheDisbursementCharge
loanTrancheDisbursementCharge = null;
for (LoanDisbursementDetails
disbursementDetail : disbursementDetails) {
if
(ChargeTimeType.TRANCHE_DISBURSEMENT.getValue().equals(chargeDefinition.getChargeTimeType()))
{
- final LoanCharge loanCharge =
LoanCharge.createNewWithoutLoan(chargeDefinition,
-
disbursementDetail.principal(), amount, chargeTime, chargeCalculation,
+ final LoanCharge loanCharge =
createNewWithoutLoan(chargeDefinition, disbursementDetail.principal(),
+ amount, chargeTime,
chargeCalculation,
disbursementDetail.expectedDisbursementDateAsLocalDate(), chargePaymentModeEnum,
- numberOfRepayments);
+ numberOfRepayments,
externalId);
loanCharges.add(loanCharge);
loanTrancheDisbursementCharge = new
LoanTrancheDisbursementCharge(loanCharge, disbursementDetail);
loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge);
}
}
} else {
- final LoanCharge loanCharge =
LoanCharge.createNewWithoutLoan(chargeDefinition, principal, amount,
- chargeTime, chargeCalculation,
dueDate, chargePaymentModeEnum, numberOfRepayments);
+ final LoanCharge loanCharge =
createNewWithoutLoan(chargeDefinition, principal, amount, chargeTime,
+ chargeCalculation, dueDate,
chargePaymentModeEnum, numberOfRepayments, externalId);
loanCharges.add(loanCharge);
}
}
@@ -239,4 +239,88 @@ public class LoanChargeAssembler {
}
return associatedChargesForLoan;
}
+
+ public LoanCharge createNewFromJson(final Loan loan, final Charge
chargeDefinition, final JsonCommand command) {
+ final LocalDate dueDate =
command.localDateValueOfParameterNamed("dueDate");
+ if
(chargeDefinition.getChargeTimeType().equals(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
&& dueDate == null) {
+ final String defaultUserMessage = "Loan charge is missing due
date.";
+ throw new LoanChargeWithoutMandatoryFieldException("loanCharge",
"dueDate", defaultUserMessage, chargeDefinition.getId(),
+ chargeDefinition.getName());
+ }
+ return createNewFromJson(loan, chargeDefinition, command, dueDate);
+ }
+
+ public LoanCharge createNewFromJson(final Loan loan, final Charge
chargeDefinition, final JsonCommand command,
+ final LocalDate dueDate) {
+ final Locale locale = command.extractLocale();
+ final BigDecimal amount =
command.bigDecimalValueOfParameterNamed("amount", locale);
+
+ final ChargeTimeType chargeTime = null;
+ final ChargeCalculationType chargeCalculation = null;
+ final ChargePaymentMode chargePaymentMode = null;
+ BigDecimal amountPercentageAppliedTo = BigDecimal.ZERO;
+ switch
(ChargeCalculationType.fromInt(chargeDefinition.getChargeCalculation())) {
+ case PERCENT_OF_AMOUNT:
+ if (command.hasParameter("principal")) {
+ amountPercentageAppliedTo =
command.bigDecimalValueOfParameterNamed("principal");
+ } else {
+ amountPercentageAppliedTo =
loan.getPrincipal().getAmount();
+ }
+ break;
+ case PERCENT_OF_AMOUNT_AND_INTEREST:
+ if (command.hasParameter("principal") &&
command.hasParameter("interest")) {
+ amountPercentageAppliedTo =
command.bigDecimalValueOfParameterNamed("principal")
+
.add(command.bigDecimalValueOfParameterNamed("interest"));
+ } else {
+ amountPercentageAppliedTo =
loan.getPrincipal().getAmount().add(loan.getTotalInterest());
+ }
+ break;
+ case PERCENT_OF_INTEREST:
+ if (command.hasParameter("interest")) {
+ amountPercentageAppliedTo =
command.bigDecimalValueOfParameterNamed("interest");
+ } else {
+ amountPercentageAppliedTo = loan.getTotalInterest();
+ }
+ break;
+ default:
+ break;
+ }
+
+ BigDecimal loanCharge = BigDecimal.ZERO;
+ if
(ChargeTimeType.fromInt(chargeDefinition.getChargeTimeType()).equals(ChargeTimeType.INSTALMENT_FEE))
{
+ BigDecimal percentage = amount;
+ if (percentage == null) {
+ percentage = chargeDefinition.getAmount();
+ }
+ loanCharge =
loan.calculatePerInstallmentChargeAmount(ChargeCalculationType.fromInt(chargeDefinition.getChargeCalculation()),
+ percentage);
+ }
+
+ // If charge type is specified due date and loan is multi disburment
+ // loan.
+ // Then we need to get as of this loan charge due date how much amount
+ // disbursed.
+ if
(chargeDefinition.getChargeTimeType().equals(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
&& loan.isMultiDisburmentLoan()) {
+ amountPercentageAppliedTo = BigDecimal.ZERO;
+ for (final LoanDisbursementDetails loanDisbursementDetails :
loan.getDisbursementDetails()) {
+ if
(!loanDisbursementDetails.expectedDisbursementDate().isAfter(dueDate)) {
+ amountPercentageAppliedTo =
amountPercentageAppliedTo.add(loanDisbursementDetails.principal());
+ }
+ }
+ }
+
+ ExternalId externalId = externalIdFactory.createFromCommand(command,
"externalId");
+ return new LoanCharge(loan, chargeDefinition,
amountPercentageAppliedTo, amount, chargeTime, chargeCalculation, dueDate,
+ chargePaymentMode, null, loanCharge, externalId);
+ }
+
+ /*
+ * loanPrincipal is required for charges that are percentage based
+ */
+ public LoanCharge createNewWithoutLoan(final Charge chargeDefinition,
final BigDecimal loanPrincipal, final BigDecimal amount,
+ final ChargeTimeType chargeTime, final ChargeCalculationType
chargeCalculation, final LocalDate dueDate,
+ final ChargePaymentMode chargePaymentMode, final Integer
numberOfRepayments, final ExternalId externalId) {
+ return new LoanCharge(null, chargeDefinition, loanPrincipal, amount,
chargeTime, chargeCalculation, dueDate, chargePaymentMode,
+ numberOfRepayments, BigDecimal.ZERO, externalId);
+ }
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeReadPlatformService.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeReadPlatformService.java
index 7c66cd080..fb2588fb4 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeReadPlatformService.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeReadPlatformService.java
@@ -19,6 +19,7 @@
package org.apache.fineract.portfolio.loanaccount.service;
import java.util.Collection;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
@@ -41,8 +42,10 @@ public interface LoanChargeReadPlatformService {
Collection<Integer> retrieveOverdueInstallmentChargeFrequencyNumber(Loan
loan, Charge charge, Integer periodNumber);
- Collection<LoanChargeData> retrieveLoanChargesForAccural(Long loanId);
+ Collection<LoanChargeData> retrieveLoanChargesForAccrual(Long loanId);
Collection<LoanChargePaidByData> retrieveLoanChargesPaidBy(Long chargeId,
LoanTransactionType transactionType,
Integer installmentNumber);
+
+ Long retrieveLoanChargeIdByExternalId(ExternalId loanChargeExternalId);
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java
index 518e548ab..477735cc3 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java
@@ -30,7 +30,9 @@ import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
+import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.domain.Charge;
@@ -42,6 +44,7 @@ import
org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
import
org.apache.fineract.portfolio.loanaccount.data.LoanInstallmentChargeData;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -55,23 +58,24 @@ public class LoanChargeReadPlatformServiceImpl implements
LoanChargeReadPlatform
private final JdbcTemplate jdbcTemplate;
private final ChargeDropdownReadPlatformService
chargeDropdownReadPlatformService;
private final DropdownReadPlatformService dropdownReadPlatformService;
+ private final LoanChargeRepository loanChargeRepository;
private static final class LoanChargeMapper implements
RowMapper<LoanChargeData> {
public String schema() {
- return "lc.id as id, lc.external_id as externalId, c.id as
chargeId, c.name as name, lc.submitted_on_date as submittedOnDate, "
- + "lc.amount as amountDue, " + "lc.amount_paid_derived as
amountPaid, " + "lc.amount_waived_derived as amountWaived, "
- + "lc.amount_writtenoff_derived as amountWrittenOff, " +
"lc.amount_outstanding_derived as amountOutstanding, "
- + "lc.calculation_percentage as percentageOf,
lc.calculation_on_amount as amountPercentageAppliedTo, "
- + "lc.charge_time_enum as chargeTime, " + "lc.is_penalty
as penalty, "
- + "lc.due_for_collection_as_of_date as dueAsOfDate, " +
"lc.charge_calculation_enum as chargeCalculation, "
- + "lc.charge_payment_mode_enum as chargePaymentMode, " +
"lc.is_paid_derived as paid, " + "lc.waived as waived, "
- + "lc.min_cap as minCap, lc.max_cap as maxCap, " +
"lc.charge_amount_or_percentage as amountOrPercentage, "
- + "lc.loan_id as loanId, c.currency_code as currencyCode,
oc.name as currencyName, "
- +
"date(coalesce(dd.disbursedon_date,dd.expected_disburse_date)) as
disbursementDate, "
- + "oc.decimal_places as currencyDecimalPlaces,
oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as
currencyDisplaySymbol, "
- + "oc.internationalized_name_code as currencyNameCode from
m_charge c "
- + "join m_organisation_currency oc on c.currency_code =
oc.code " + "join m_loan_charge lc on lc.charge_id = c.id "
+ return "lc.id as id, lc.external_id as externalId, c.id as
chargeId, c.name as name, lc.submitted_on_date as submittedOnDate, " //
+ + "lc.amount as amountDue, lc.amount_paid_derived as
amountPaid, lc.amount_waived_derived as amountWaived, " //
+ + "lc.amount_writtenoff_derived as amountWrittenOff,
lc.amount_outstanding_derived as amountOutstanding, " //
+ + "lc.calculation_percentage as percentageOf,
lc.calculation_on_amount as amountPercentageAppliedTo, " //
+ + "lc.charge_time_enum as chargeTime, lc.is_penalty as
penalty, " //
+ + "lc.due_for_collection_as_of_date as dueAsOfDate,
lc.charge_calculation_enum as chargeCalculation, " //
+ + "lc.charge_payment_mode_enum as chargePaymentMode,
lc.is_paid_derived as paid, lc.waived as waived, " //
+ + "lc.min_cap as minCap, lc.max_cap as maxCap,
lc.charge_amount_or_percentage as amountOrPercentage, " //
+ + "lc.loan_id as loanId, c.currency_code as currencyCode,
oc.name as currencyName, " //
+ +
"date(coalesce(dd.disbursedon_date,dd.expected_disburse_date)) as
disbursementDate, " //
+ + "oc.decimal_places as currencyDecimalPlaces,
oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as
currencyDisplaySymbol, " //
+ + "oc.internationalized_name_code as currencyNameCode from
m_charge c " //
+ + "join m_organisation_currency oc on c.currency_code =
oc.code join m_loan_charge lc on lc.charge_id = c.id " //
+ "left join m_loan_tranche_disbursement_charge dc on
dc.loan_charge_id=lc.id left join m_loan_disbursement_detail dd on
dd.id=dc.disbursement_detail_id ";
}
@@ -122,7 +126,8 @@ public class LoanChargeReadPlatformServiceImpl implements
LoanChargeReadPlatform
if (disbursementDate != null) {
dueAsOfDate = disbursementDate;
}
- final String externalId = rs.getString("externalId");
+ final String externalIdStr = rs.getString("externalId");
+ final ExternalId externalId =
ExternalIdFactory.produce(externalIdStr);
return new LoanChargeData(id, chargeId, name, currency, amount,
amountPaid, amountWaived, amountWrittenOff, amountOutstanding,
chargeTimeType, submittedOnDate, dueAsOfDate,
chargeCalculationType, percentageOf, amountPercentageAppliedTo, penalty,
@@ -192,9 +197,9 @@ public class LoanChargeReadPlatformServiceImpl implements
LoanChargeReadPlatform
private static final class LoanChargeMapperWithLoanId implements
RowMapper<LoanChargeData> {
public String schema() {
- return "lc.id as id, lc.due_for_collection_as_of_date as
dueAsOfDate, " + "lc.amount_outstanding_derived as amountOutstanding, "
- + "lc.charge_time_enum as chargeTime, " + "loan.id as
loanId " + "from m_loan_charge lc "
- + "join m_loan loan on loan.id = lc.loan_id ";
+ return " lc.id as id, lc.due_for_collection_as_of_date as
dueAsOfDate, lc.amount_outstanding_derived as amountOutstanding, "
+ + " lc.charge_time_enum as chargeTime, loan.id as loanId,
lc.external_id as externalId from m_loan_charge lc "
+ + " join m_loan loan on loan.id = lc.loan_id ";
}
@Override
@@ -206,8 +211,10 @@ public class LoanChargeReadPlatformServiceImpl implements
LoanChargeReadPlatform
final BigDecimal amountOutstanding =
rs.getBigDecimal("amountOutstanding");
final int chargeTime = rs.getInt("chargeTime");
final EnumOptionData chargeTimeType =
ChargeEnumerations.chargeTimeType(chargeTime);
+ final String externalIdStr = rs.getString("externalId");
+ final ExternalId externalId =
ExternalIdFactory.produce(externalIdStr);
- return new LoanChargeData(id, dueAsOfDate, amountOutstanding,
chargeTimeType, loanId, null);
+ return new LoanChargeData(id, dueAsOfDate, amountOutstanding,
chargeTimeType, loanId, null, externalId);
}
}
@@ -226,8 +233,8 @@ public class LoanChargeReadPlatformServiceImpl implements
LoanChargeReadPlatform
public String schema() {
return " lsi.installment as installmentNumber, lsi.duedate as
dueAsOfDate, "
- + "lic.amount_outstanding_derived as amountOutstanding," +
"lic.amount as amount, " + "lic.is_paid_derived as paid, "
- + "lic.amount_waived_derived as amountWaived, " +
"lic.waived as waived " + "from m_loan_installment_charge lic "
+ + "lic.amount_outstanding_derived as amountOutstanding,
lic.amount as amount, lic.is_paid_derived as paid, "
+ + "lic.amount_waived_derived as amountWaived, lic.waived
as waived from m_loan_installment_charge lic "
+ "join m_loan_repayment_schedule lsi on lsi.id =
lic.loan_schedule_id ";
}
@@ -262,7 +269,7 @@ public class LoanChargeReadPlatformServiceImpl implements
LoanChargeReadPlatform
}
@Override
- public Collection<LoanChargeData> retrieveLoanChargesForAccural(final Long
loanId) {
+ public Collection<LoanChargeData> retrieveLoanChargesForAccrual(final Long
loanId) {
final LoanChargeAccrualMapper rm = new LoanChargeAccrualMapper();
@@ -298,20 +305,20 @@ public class LoanChargeReadPlatformServiceImpl implements
LoanChargeReadPlatform
LoanChargeAccrualMapper() {
StringBuilder sb = new StringBuilder(50);
- sb.append("lc.id as id, lc.charge_id as chargeId, ");
- sb.append("lc.amount as amountDue, ");
- sb.append("lc.amount_waived_derived as amountWaived, ");
- sb.append("lc.charge_time_enum as chargeTime, ");
+ sb.append(" lc.id as id, lc.charge_id as chargeId, lc.external_id
as externalId, ");
+ sb.append(" lc.amount as amountDue, ");
+ sb.append(" lc.amount_waived_derived as amountWaived, ");
+ sb.append(" lc.charge_time_enum as chargeTime, ");
sb.append(" sum(cp.amount) as amountAccrued, ");
- sb.append("lc.is_penalty as penalty, ");
- sb.append("lc.due_for_collection_as_of_date as dueAsOfDate ");
+ sb.append(" lc.is_penalty as penalty, ");
+ sb.append(" lc.due_for_collection_as_of_date as dueAsOfDate ");
sb.append(" from m_loan_charge lc ");
- sb.append("left join (");
- sb.append("select lcp.loan_charge_id, lcp.amount");
+ sb.append(" left join ( ");
+ sb.append(" select lcp.loan_charge_id, lcp.amount ");
sb.append(" from m_loan_charge_paid_by lcp ");
sb.append(
- "inner join m_loan_transaction lt on lt.id =
lcp.loan_transaction_id and lt.is_reversed = false and lt.transaction_type_enum
= ? and lt.loan_id = ?");
- sb.append(") cp on cp.loan_charge_id= lc.id ");
+ " inner join m_loan_transaction lt on lt.id =
lcp.loan_transaction_id and lt.is_reversed = false and lt.transaction_type_enum
= ? and lt.loan_id = ? ");
+ sb.append(" ) cp on cp.loan_charge_id= lc.id ");
schemaSql = sb.toString();
}
@@ -335,7 +342,10 @@ public class LoanChargeReadPlatformServiceImpl implements
LoanChargeReadPlatform
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs,
"dueAsOfDate");
final boolean penalty = rs.getBoolean("penalty");
- return new LoanChargeData(id, chargeId, dueAsOfDate,
chargeTimeType, amount, amountAccrued, amountWaived, penalty);
+ final String externalIdStr = rs.getString("externalId");
+ final ExternalId externalId =
ExternalIdFactory.produce(externalIdStr);
+
+ return new LoanChargeData(id, chargeId, dueAsOfDate,
chargeTimeType, amount, amountAccrued, amountWaived, penalty, externalId);
}
}
@@ -516,6 +526,11 @@ public class LoanChargeReadPlatformServiceImpl implements
LoanChargeReadPlatform
return this.jdbcTemplate.query(sb.toString(), rm, args.toArray());
}
+ @Override
+ public Long retrieveLoanChargeIdByExternalId(ExternalId externalId) {
+ return loanChargeRepository.findIdByExternalId(externalId);
+ }
+
private static final class LoanChargesPaidByMapper implements
RowMapper<LoanChargePaidByData> {
private final String schemaSql;
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
index da86b0967..064ce0948 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
@@ -152,8 +152,8 @@ public class LoanChargeWritePlatformServiceImpl implements
LoanChargeWritePlatfo
private final ConfigurationDomainService configurationDomainService;
private final LoanRepaymentScheduleTransactionProcessorFactory
loanRepaymentScheduleTransactionProcessorFactory;
private final ExternalIdFactory externalIdFactory;
-
private final AccountTransferDetailRepository
accountTransferDetailRepository;
+ private final LoanChargeAssembler loanChargeAssembler;
private static boolean isPartOfThisInstallment(LoanCharge loanCharge,
LoanRepaymentScheduleInstallment e) {
return e.getFromDate().isBefore(loanCharge.getDueDate()) &&
!loanCharge.getDueDate().isAfter(e.getDueDate());
@@ -185,10 +185,17 @@ public class LoanChargeWritePlatformServiceImpl
implements LoanChargeWritePlatfo
LocalDate recalculateFrom = loan.fetchInterestRecalculateFromDate();
if (chargeDefinition.isPercentageOfDisbursementAmount()) {
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge;
+ ExternalId externalId =
externalIdFactory.createFromCommand(command, "externalId");
+ boolean needToGenerateNewExternalId = false;
for (LoanDisbursementDetails disbursementDetail :
loanDisburseDetails) {
if (disbursementDetail.actualDisbursementDate() == null) {
- loanCharge =
LoanCharge.createNewWithoutLoan(chargeDefinition,
disbursementDetail.principal(), null, null, null,
-
disbursementDetail.expectedDisbursementDateAsLocalDate(), null, null);
+ // If multiple charges to be applied, only the first one
will get the provided externalId, for the
+ // rest we generate new ones (if needed)
+ if (needToGenerateNewExternalId) {
+ externalId = externalIdFactory.create();
+ }
+ loanCharge =
loanChargeAssembler.createNewWithoutLoan(chargeDefinition,
disbursementDetail.principal(), null, null,
+ null,
disbursementDetail.expectedDisbursementDateAsLocalDate(), null, null,
externalId);
loanTrancheDisbursementCharge = new
LoanTrancheDisbursementCharge(loanCharge, disbursementDetail);
loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge);
businessEventNotifierService.notifyPreBusinessEvent(new
LoanAddChargeBusinessEvent(loanCharge));
@@ -198,6 +205,7 @@ public class LoanChargeWritePlatformServiceImpl implements
LoanChargeWritePlatfo
if
(recalculateFrom.isAfter(disbursementDetail.expectedDisbursementDateAsLocalDate()))
{
recalculateFrom =
disbursementDetail.expectedDisbursementDateAsLocalDate();
}
+ needToGenerateNewExternalId = true;
}
}
if (loanCharge == null) {
@@ -207,7 +215,7 @@ public class LoanChargeWritePlatformServiceImpl implements
LoanChargeWritePlatfo
}
loan.addTrancheLoanCharge(chargeDefinition);
} else {
- loanCharge = LoanCharge.createNewFromJson(loan, chargeDefinition,
command);
+ loanCharge = loanChargeAssembler.createNewFromJson(loan,
chargeDefinition, command);
businessEventNotifierService.notifyPreBusinessEvent(new
LoanAddChargeBusinessEvent(loanCharge));
validateAddLoanCharge(loan, chargeDefinition, loanCharge);
@@ -252,6 +260,7 @@ public class LoanChargeWritePlatformServiceImpl implements
LoanChargeWritePlatfo
businessEventNotifierService.notifyPostBusinessEvent(new
LoanAddChargeBusinessEvent(loanCharge));
return new
CommandProcessingResultBuilder().withCommandId(command.commandId()) //
.withEntityId(loanCharge.getId()) //
+ .withEntityExternalId(loanCharge.getExternalId()) //
.withOfficeId(loan.getOfficeId()) //
.withClientId(loan.getClientId()) //
.withGroupId(loan.getGroupId()) //
@@ -346,7 +355,7 @@ public class LoanChargeWritePlatformServiceImpl implements
LoanChargeWritePlatfo
LoanChargeWaiveCannotBeReversedException.LoanChargeWaiveCannotUndoReason.LOAN_INACTIVE,
loanCharge.getId());
}
- final Map<String, Object> changes = new LinkedHashMap<>(3);
+ final Map<String, Object> changes = new LinkedHashMap<>();
businessEventNotifierService.notifyPreBusinessEvent(new
LoanWaiveChargeUndoBusinessEvent(loanCharge));
@@ -367,6 +376,7 @@ public class LoanChargeWritePlatformServiceImpl implements
LoanChargeWritePlatfo
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(loanCharge.getId()) //
+ .withEntityExternalId(loanCharge.getExternalId()) //
.withSubEntityId(loanTransaction.getId()) //
.withSubEntityExternalId(loanTransaction.getExternalId()) //
.withLoanId(loanId) //
@@ -399,6 +409,7 @@ public class LoanChargeWritePlatformServiceImpl implements
LoanChargeWritePlatfo
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(loanChargeId) //
+ .withEntityExternalId(loanCharge.getExternalId()) //
.withOfficeId(loan.getOfficeId()) //
.withClientId(loan.getClientId()) //
.withGroupId(loan.getGroupId()) //
@@ -458,7 +469,7 @@ public class LoanChargeWritePlatformServiceImpl implements
LoanChargeWritePlatfo
loanInstallmentNumber =
chargePerInstallment.getRepaymentInstallment().getInstallmentNumber();
}
- final Map<String, Object> changes = new LinkedHashMap<>(3);
+ final Map<String, Object> changes = new LinkedHashMap<>();
changes.put(LoanApiConstants.externalIdParameterName, externalId);
final List<Long> existingTransactionIds = new ArrayList<>();
@@ -490,6 +501,7 @@ public class LoanChargeWritePlatformServiceImpl implements
LoanChargeWritePlatfo
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(loanChargeId) //
+ .withEntityExternalId(loanCharge.getExternalId()) //
.withSubEntityId(waiveTransaction.getId()) //
.withSubEntityExternalId(waiveTransaction.getExternalId()) //
.withOfficeId(loan.getOfficeId()) //
@@ -523,6 +535,7 @@ public class LoanChargeWritePlatformServiceImpl implements
LoanChargeWritePlatfo
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(loanChargeId) //
+ .withEntityExternalId(loanCharge.getExternalId()) //
.withOfficeId(loan.getOfficeId()) //
.withClientId(loan.getClientId()) //
.withGroupId(loan.getGroupId()) //
@@ -613,6 +626,7 @@ public class LoanChargeWritePlatformServiceImpl implements
LoanChargeWritePlatfo
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(loanChargeId) //
+ .withEntityExternalId(loanCharge.getExternalId()) //
.withSubEntityId(loanTransaction.getId()).withSubEntityExternalId(loanTransaction.getExternalId())
.withOfficeId(loan.getOfficeId()) //
.withClientId(loan.getClientId()) //
@@ -629,7 +643,7 @@ public class LoanChargeWritePlatformServiceImpl implements
LoanChargeWritePlatfo
final LoanCharge loanCharge = retrieveLoanChargeBy(loanId,
loanChargeId);
final LocalDate transactionDate = DateUtils.getBusinessLocalDate();
final BigDecimal transactionAmount =
command.bigDecimalValueOfParameterNamed("amount");
- final ExternalId txnExternalId =
externalIdFactory.createFromCommand(command, "externalId");
+ final ExternalId externalId =
externalIdFactory.createFromCommand(command, "externalId");
final String locale = command.locale();
loanChargeAdjustmentEntranceValidation(loanCharge, transactionAmount);
@@ -637,7 +651,7 @@ public class LoanChargeWritePlatformServiceImpl implements
LoanChargeWritePlatfo
final CommandProcessingResultBuilder commandProcessingResultBuilder =
new CommandProcessingResultBuilder();
- LoanTransaction loanTransaction = applyChargeAdjustment(loan,
loanCharge, transactionAmount, transactionDate, txnExternalId);
+ LoanTransaction loanTransaction = applyChargeAdjustment(loan,
loanCharge, transactionAmount, transactionDate, externalId);
// Update loan transaction on repayment.
if (AccountType.fromInt(loan.getLoanType()).isIndividualAccount()) {
@@ -658,13 +672,15 @@ public class LoanChargeWritePlatformServiceImpl
implements LoanChargeWritePlatfo
businessEventNotifierService.notifyPostBusinessEvent(new
LoanBalanceChangedBusinessEvent(loan));
businessEventNotifierService.notifyPostBusinessEvent(new
LoanChargeAdjustmentPostBusinessEvent(loanTransaction));
Map<String, Object> changes = new HashMap<>();
- changes.put("externalId", txnExternalId);
+ changes.put("externalId", externalId);
changes.put("amount", transactionAmount);
changes.put("transactionDate", transactionDate);
changes.put("locale", locale);
return
commandProcessingResultBuilder.withCommandId(command.commandId()) //
.withLoanId(loanId) //
-
.withEntityId(loanChargeId).withSubEntityId(loanTransaction.getId()) //
+ .withEntityId(loanChargeId) //
+ .withEntityExternalId(loanCharge.getExternalId()) //
+ .withSubEntityId(loanTransaction.getId()) //
.withSubEntityExternalId(loanTransaction.getExternalId()) //
.with(changes) //
.build();
@@ -1008,7 +1024,7 @@ public class LoanChargeWritePlatformServiceImpl
implements LoanChargeWritePlatfo
businessEventNotifierService.notifyPreBusinessEvent(new
LoanApplyOverdueChargeBusinessEvent(loan));
for (Map.Entry<Integer, LocalDate> entry :
scheduleDates.entrySet()) {
- final LoanCharge loanCharge =
LoanCharge.createNewFromJson(loan, chargeDefinition, command, entry.getValue());
+ final LoanCharge loanCharge =
loanChargeAssembler.createNewFromJson(loan, chargeDefinition, command,
entry.getValue());
if (BigDecimal.ZERO.compareTo(loanCharge.amount()) == 0) {
continue;
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index 9c988127f..ae7acca09 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -119,7 +119,6 @@ import
org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
import org.apache.fineract.portfolio.calendar.domain.CalendarRepository;
import org.apache.fineract.portfolio.calendar.domain.CalendarType;
import
org.apache.fineract.portfolio.calendar.exception.CalendarParameterUpdateNotSupportedException;
-import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
import
org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement;
@@ -140,7 +139,6 @@ import
org.apache.fineract.portfolio.loanaccount.domain.GroupLoanIndividualMonit
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import
org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
import
org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagement;
import
org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
import
org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetailsRepository;
@@ -209,8 +207,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl
implements LoanWritePlatf
private final LoanTransactionRepository loanTransactionRepository;
private final LoanTransactionRelationRepository
loanTransactionRelationRepository;
private final LoanAssembler loanAssembler;
- private final ChargeRepositoryWrapper chargeRepository;
- private final LoanChargeRepository loanChargeRepository;
private final JournalEntryWritePlatformService
journalEntryWritePlatformService;
private final CalendarInstanceRepository calendarInstanceRepository;
private final PaymentDetailWritePlatformService
paymentDetailWritePlatformService;
@@ -220,7 +216,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl
implements LoanWritePlatf
private final AccountTransfersWritePlatformService
accountTransfersWritePlatformService;
private final AccountTransfersReadPlatformService
accountTransfersReadPlatformService;
private final AccountAssociationsReadPlatformService
accountAssociationsReadPlatformService;
- private final LoanChargeReadPlatformService loanChargeReadPlatformService;
private final LoanReadPlatformService loanReadPlatformService;
private final FromJsonHelper fromApiJsonHelper;
private final AccountTransferRepository accountTransferRepository;
@@ -245,7 +240,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl
implements LoanWritePlatf
private final LoanRepaymentScheduleInstallmentRepository
loanRepaymentScheduleInstallmentRepository;
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
private final LoanAccountLockService loanAccountLockService;
-
private final ExternalIdFactory externalIdFactory;
@Transactional
diff --git
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTest.java
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTest.java
index ab8e62d30..604620a91 100644
---
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTest.java
+++
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTest.java
@@ -25,6 +25,7 @@ import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Collection;
import java.util.Collections;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
@@ -83,6 +84,6 @@ public class LoanTest {
private LoanCharge buildLoanCharge() {
return new LoanCharge(mock(Loan.class), mock(Charge.class), new
BigDecimal(100), new BigDecimal(100),
ChargeTimeType.TRANCHE_DISBURSEMENT,
ChargeCalculationType.FLAT, LocalDate.of(2022, 6, 27),
ChargePaymentMode.REGULAR, 1,
- new BigDecimal(100));
+ new BigDecimal(100), ExternalId.generate());
}
}
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalIdSupportIntegrationTest.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalIdSupportIntegrationTest.java
index cf8b3ab81..f86628906 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalIdSupportIntegrationTest.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalIdSupportIntegrationTest.java
@@ -30,14 +30,22 @@ import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.HashMap;
+import java.util.List;
import java.util.UUID;
+import
org.apache.fineract.client.models.DeleteLoansLoanIdChargesChargeIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdChargesChargeIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdChargesTemplateResponse;
import
org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
import org.apache.fineract.client.models.PostClientsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest;
import
org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdChargesRequest;
+import org.apache.fineract.client.models.PostLoansLoanIdChargesResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import
org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
+import org.apache.fineract.client.models.PutLoansLoanIdChargesChargeIdRequest;
+import org.apache.fineract.client.models.PutLoansLoanIdChargesChargeIdResponse;
import org.apache.fineract.integrationtests.client.IntegrationTest;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
@@ -113,11 +121,38 @@ public class ExternalIdSupportIntegrationTest extends
IntegrationTest {
LocalDate targetDate = LocalDate.of(2022, 9, 7);
final String penaltyCharge1AddedDate =
dateFormatter.format(targetDate);
+ String penalty1LoanChargeExternalId = UUID.randomUUID().toString();
Integer penalty1LoanChargeId =
this.loanTransactionHelper.addChargesForLoan(loanId,
-
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty),
penaltyCharge1AddedDate, "10"));
-
- Integer penalty2LoanChargeId =
this.loanTransactionHelper.addChargesForLoan(loanId,
-
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty),
penaltyCharge1AddedDate, "10"));
+
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty),
penaltyCharge1AddedDate, "10",
+ penalty1LoanChargeExternalId));
+
+ // Get loan charges
+ List<GetLoansLoanIdChargesChargeIdResponse> loanChargesResult =
loanTransactionHelper.getLoanCharges((long) loanId);
+ assertEquals(penalty1LoanChargeExternalId,
loanChargesResult.get(0).getExternalId());
+ loanChargesResult =
loanTransactionHelper.getLoanCharges(loanExternalIdStr);
+ assertEquals(penalty1LoanChargeExternalId,
loanChargesResult.get(0).getExternalId());
+
+ // Get loan charge template
+ GetLoansLoanIdChargesTemplateResponse loanChargeTemplateResult =
loanTransactionHelper.getLoanChargeTemplate((long) loanId);
+ assertNotNull(loanChargeTemplateResult);
+ loanChargeTemplateResult =
loanTransactionHelper.getLoanChargeTemplate(loanExternalIdStr);
+ assertNotNull(loanChargeTemplateResult);
+
+ // Get loan charge
+ GetLoansLoanIdChargesChargeIdResponse loanChargeResult =
loanTransactionHelper.getLoanCharge((long) loanId,
+ (long) penalty1LoanChargeId);
+ assertEquals(penalty1LoanChargeExternalId,
loanChargeResult.getExternalId());
+ loanChargeResult =
loanTransactionHelper.getLoanCharge(loanExternalIdStr, (long)
penalty1LoanChargeId);
+ assertEquals(penalty1LoanChargeExternalId,
loanChargeResult.getExternalId());
+ loanChargeResult = loanTransactionHelper.getLoanCharge((long) loanId,
penalty1LoanChargeExternalId);
+ assertEquals(penalty1LoanChargeExternalId,
loanChargeResult.getExternalId());
+ loanChargeResult =
loanTransactionHelper.getLoanCharge(loanExternalIdStr,
penalty1LoanChargeExternalId);
+ assertEquals(penalty1LoanChargeExternalId,
loanChargeResult.getExternalId());
+
+ PostLoansLoanIdChargesResponse penalty2Result =
this.loanTransactionHelper.addLoanCharge(loanExternalIdStr,
+ new PostLoansLoanIdChargesRequest().chargeId((long)
penalty).amount(10.0).dueDate(penaltyCharge1AddedDate)
+ .dateFormat("dd MMMM yyyy").locale("en"));
+ assertNotNull(penalty2Result.getResourceExternalId());
// Check whether we can fetch transaction templates with proper result
http code (HTTP 200..300)
ok(fineract().loanTransactions.retrieveTransactionTemplate1(loanExternalIdStr,
"repayment", null, null, null));
@@ -135,6 +170,7 @@ public class ExternalIdSupportIntegrationTest extends
IntegrationTest {
PostLoansLoanIdChargesChargeIdResponse waiveLoanChargeResult =
loanTransactionHelper.waiveLoanCharge((long) loanId,
(long) penalty1LoanChargeId, new
PostLoansLoanIdChargesChargeIdRequest().externalId(waiveChargeExternalIdStr));
assertEquals(waiveChargeExternalIdStr,
waiveLoanChargeResult.getSubResourceExternalId());
+ assertEquals(penalty1LoanChargeExternalId,
waiveLoanChargeResult.getResourceExternalId());
GetLoansLoanIdTransactionsTransactionIdResponse response =
loanTransactionHelper.getLoanTransactionDetails((long) loanId,
waiveChargeExternalIdStr);
@@ -157,9 +193,10 @@ public class ExternalIdSupportIntegrationTest extends
IntegrationTest {
assertEquals(waiveChargeExternalIdStr, response.getExternalId());
// Check whether an external id was generated
- waiveLoanChargeResult = loanTransactionHelper.waiveLoanCharge((long)
loanId, (long) penalty1LoanChargeId,
+ waiveLoanChargeResult =
loanTransactionHelper.waiveLoanCharge(loanExternalIdStr, (long)
penalty1LoanChargeId,
new PostLoansLoanIdChargesChargeIdRequest());
assertNotNull(waiveLoanChargeResult.getSubResourceExternalId());
+ assertEquals(penalty1LoanChargeExternalId,
waiveLoanChargeResult.getResourceExternalId());
// Check whether an external id was generated
undoWaiveLoanChargeResult =
loanTransactionHelper.undoWaiveLoanCharge(loanExternalIdStr,
waiveLoanChargeResult.getSubResourceId());
@@ -167,9 +204,10 @@ public class ExternalIdSupportIntegrationTest extends
IntegrationTest {
// Check whether an external id was generated
waiveChargeExternalIdStr = UUID.randomUUID().toString();
- waiveLoanChargeResult = loanTransactionHelper.waiveLoanCharge((long)
loanId, (long) penalty1LoanChargeId,
+ waiveLoanChargeResult =
loanTransactionHelper.waiveLoanCharge(loanExternalIdStr,
penalty1LoanChargeExternalId,
new
PostLoansLoanIdChargesChargeIdRequest().externalId(waiveChargeExternalIdStr));
assertEquals(waiveChargeExternalIdStr,
waiveLoanChargeResult.getSubResourceExternalId());
+ assertEquals(penalty1LoanChargeExternalId,
waiveLoanChargeResult.getResourceExternalId());
// Check whether an external id was generated
undoWaiveLoanChargeResult =
loanTransactionHelper.undoWaiveLoanCharge(loanExternalIdStr,
@@ -389,7 +427,73 @@ public class ExternalIdSupportIntegrationTest extends
IntegrationTest {
final HashMap loanWithInterest =
this.loanTransactionHelper.getLoanId(loanApplicationJSON, "");
Integer loanWithInterestId = (Integer)
loanWithInterest.get("resourceId");
+ String chargeExternalId = UUID.randomUUID().toString();
+ PostLoansLoanIdChargesResponse loanChargeForApprovedLoanResult =
this.loanTransactionHelper.addLoanCharge(loanExternalIdStr,
+ new
PostLoansLoanIdChargesRequest().externalId(chargeExternalId).amount(1.0).chargeId((long)
penalty)
+ .dateFormat("dd MMMM
yyyy").locale("en").dueDate(formattedDate));
+
+ PutLoansLoanIdChargesChargeIdResponse
updatedLoanChargeForApprovedLoanResult =
this.loanTransactionHelper.updateLoanCharge(
+ (long) loanWithInterestId,
loanChargeForApprovedLoanResult.getResourceId(),
+ new PutLoansLoanIdChargesChargeIdRequest().amount(2.0));
+ assertEquals(loanChargeForApprovedLoanResult.getResourceId(),
updatedLoanChargeForApprovedLoanResult.getResourceId());
+ assertEquals(loanChargeForApprovedLoanResult.getResourceExternalId(),
+
updatedLoanChargeForApprovedLoanResult.getResourceExternalId());
+
+ DeleteLoansLoanIdChargesChargeIdResponse deleteLoanChargeResult =
this.loanTransactionHelper
+ .deleteLoanCharge((long) loanWithInterestId,
loanChargeForApprovedLoanResult.getResourceId());
+ assertEquals(loanChargeForApprovedLoanResult.getResourceId(),
deleteLoanChargeResult.getResourceId());
+ assertEquals(loanChargeForApprovedLoanResult.getResourceExternalId(),
deleteLoanChargeResult.getResourceExternalId());
+
+ chargeExternalId = UUID.randomUUID().toString();
+ loanChargeForApprovedLoanResult =
this.loanTransactionHelper.addLoanCharge(loanExternalIdStr,
+ new
PostLoansLoanIdChargesRequest().externalId(chargeExternalId).amount(1.0).chargeId((long)
penalty)
+ .dateFormat("dd MMMM
yyyy").locale("en").dueDate(formattedDate));
+
+ updatedLoanChargeForApprovedLoanResult =
this.loanTransactionHelper.updateLoanCharge((long) loanWithInterestId,
+ loanChargeForApprovedLoanResult.getResourceExternalId(), new
PutLoansLoanIdChargesChargeIdRequest().amount(1.0));
+ assertEquals(loanChargeForApprovedLoanResult.getResourceId(),
updatedLoanChargeForApprovedLoanResult.getResourceId());
+ assertEquals(loanChargeForApprovedLoanResult.getResourceExternalId(),
+
updatedLoanChargeForApprovedLoanResult.getResourceExternalId());
+
+ deleteLoanChargeResult =
this.loanTransactionHelper.deleteLoanCharge((long) loanWithInterestId,
+ loanChargeForApprovedLoanResult.getResourceExternalId());
+ assertEquals(loanChargeForApprovedLoanResult.getResourceId(),
deleteLoanChargeResult.getResourceId());
+ assertEquals(loanChargeForApprovedLoanResult.getResourceExternalId(),
deleteLoanChargeResult.getResourceExternalId());
+
+ chargeExternalId = UUID.randomUUID().toString();
+ loanChargeForApprovedLoanResult =
this.loanTransactionHelper.addLoanCharge(loanExternalIdStr,
+ new
PostLoansLoanIdChargesRequest().externalId(chargeExternalId).amount(1.0).chargeId((long)
penalty)
+ .dateFormat("dd MMMM
yyyy").locale("en").dueDate(formattedDate));
+
+ updatedLoanChargeForApprovedLoanResult =
this.loanTransactionHelper.updateLoanCharge(loanExternalIdStr,
+ loanChargeForApprovedLoanResult.getResourceId(), new
PutLoansLoanIdChargesChargeIdRequest().amount(1.0));
+ assertEquals(loanChargeForApprovedLoanResult.getResourceId(),
updatedLoanChargeForApprovedLoanResult.getResourceId());
+ assertEquals(loanChargeForApprovedLoanResult.getResourceExternalId(),
+
updatedLoanChargeForApprovedLoanResult.getResourceExternalId());
+
+ deleteLoanChargeResult =
this.loanTransactionHelper.deleteLoanCharge(loanExternalIdStr,
+ loanChargeForApprovedLoanResult.getResourceId());
+ assertEquals(loanChargeForApprovedLoanResult.getResourceId(),
deleteLoanChargeResult.getResourceId());
+ assertEquals(loanChargeForApprovedLoanResult.getResourceExternalId(),
deleteLoanChargeResult.getResourceExternalId());
+
+ chargeExternalId = UUID.randomUUID().toString();
+ loanChargeForApprovedLoanResult =
this.loanTransactionHelper.addLoanCharge(loanExternalIdStr,
+ new
PostLoansLoanIdChargesRequest().externalId(chargeExternalId).amount(1.0).chargeId((long)
penalty)
+ .dateFormat("dd MMMM
yyyy").locale("en").dueDate(formattedDate));
+
+ updatedLoanChargeForApprovedLoanResult =
this.loanTransactionHelper.updateLoanCharge(loanExternalIdStr,
+ loanChargeForApprovedLoanResult.getResourceExternalId(), new
PutLoansLoanIdChargesChargeIdRequest().amount(2.0));
+ assertEquals(loanChargeForApprovedLoanResult.getResourceId(),
updatedLoanChargeForApprovedLoanResult.getResourceId());
+ assertEquals(loanChargeForApprovedLoanResult.getResourceExternalId(),
+
updatedLoanChargeForApprovedLoanResult.getResourceExternalId());
+
+ deleteLoanChargeResult =
this.loanTransactionHelper.deleteLoanCharge(loanExternalIdStr,
+ loanChargeForApprovedLoanResult.getResourceExternalId());
+ assertEquals(loanChargeForApprovedLoanResult.getResourceId(),
deleteLoanChargeResult.getResourceId());
+ assertEquals(loanChargeForApprovedLoanResult.getResourceExternalId(),
deleteLoanChargeResult.getResourceExternalId());
+
this.loanTransactionHelper.approveLoan(formattedDate,
loanWithInterestId);
+
final HashMap disbursedLoanWithInterestResult =
this.loanTransactionHelper.disburseLoan(formattedDate, loanWithInterestId,
"1000",
null);
// Check whether an external id was generated
@@ -397,14 +501,16 @@ public class ExternalIdSupportIntegrationTest extends
IntegrationTest {
LocalDate aMonthBeforePlus3Days = aMonthBefore.plusDays(3);
formattedDate = dateFormatter.format(aMonthBeforePlus3Days);
- Integer penalty3LoanChargeId =
this.loanTransactionHelper.addChargesForLoan(loanWithInterestId,
-
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty),
formattedDate, "10"));
+ String penalty3LoanChargeExternalId = UUID.randomUUID().toString();
+ Integer penalty3LoanChargeId =
this.loanTransactionHelper.addChargesForLoan(loanWithInterestId,
LoanTransactionHelper
+
.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty),
formattedDate, "10", penalty3LoanChargeExternalId));
Integer penalty4LoanChargeId =
this.loanTransactionHelper.addChargesForLoan(loanWithInterestId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty2),
formattedDate, "1000"));
- Integer penalty5LoanChargeId =
this.loanTransactionHelper.addChargesForLoan(loanWithInterestId,
-
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty2),
formattedDate, "1000"));
+ String penalty5LoanChargeExternalId = UUID.randomUUID().toString();
+ Integer penalty5LoanChargeId =
this.loanTransactionHelper.addChargesForLoan(loanWithInterestId,
LoanTransactionHelper
+
.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty2),
formattedDate, "1000", penalty5LoanChargeExternalId));
// Check whether an external id was generated
final PostLoansLoanIdTransactionsResponse waiveInterestResult =
loanTransactionHelper.makeWaiveInterest(loanExternalIdStr,
@@ -520,6 +626,7 @@ public class ExternalIdSupportIntegrationTest extends
IntegrationTest {
(long) penalty3LoanChargeId,
new
PostLoansLoanIdChargesChargeIdRequest().externalId(chargeAdjustmentExternalIdStr).amount(1.0).locale("en"));
assertEquals(chargeAdjustmentExternalIdStr,
chargeAdjustmentResult.getSubResourceExternalId());
+ assertEquals(penalty3LoanChargeExternalId,
chargeAdjustmentResult.getResourceExternalId());
response = loanTransactionHelper.getLoanTransactionDetails((long)
loanWithInterestId, chargeAdjustmentExternalIdStr);
assertEquals(chargeAdjustmentExternalIdStr, response.getExternalId());
@@ -529,23 +636,27 @@ public class ExternalIdSupportIntegrationTest extends
IntegrationTest {
assertEquals(chargeAdjustmentExternalIdStr, response.getExternalId());
// Check whether an external id was generated
- chargeAdjustmentResult = loanTransactionHelper.chargeAdjustment((long)
loanWithInterestId, (long) penalty3LoanChargeId,
+ chargeAdjustmentResult =
loanTransactionHelper.chargeAdjustment(loanExternalIdStr,
penalty3LoanChargeExternalId,
new
PostLoansLoanIdChargesChargeIdRequest().amount(1.0).locale("en"));
assertNotNull(chargeAdjustmentResult.getSubResourceExternalId());
+ assertEquals(penalty3LoanChargeExternalId,
chargeAdjustmentResult.getResourceExternalId());
- HashMap payChargeResult =
this.loanTransactionHelper.payChargesForLoan(loanWithInterestId,
penalty4LoanChargeId,
- LoanTransactionHelper.getPayChargeJSON(formattedDate, null),
"");
- assertNotNull(payChargeResult.get("subResourceExternalId"));
+ PostLoansLoanIdChargesChargeIdResponse payChargeResult =
this.loanTransactionHelper.payLoanCharge(loanExternalIdStr,
+ (long) penalty4LoanChargeId,
+ new
PostLoansLoanIdChargesChargeIdRequest().locale("en").dateFormat("dd MMMM
yyyy").transactionDate(formattedDate));
+ assertNotNull(payChargeResult.getSubResourceExternalId());
+ assertNotNull(payChargeResult.getResourceExternalId());
String payChargeExternalIdStr = UUID.randomUUID().toString();
- payChargeResult =
this.loanTransactionHelper.payChargesForLoan(loanWithInterestId,
penalty5LoanChargeId,
- LoanTransactionHelper.getPayChargeJSON(formattedDate, null,
payChargeExternalIdStr), "");
- assertEquals(payChargeExternalIdStr,
payChargeResult.get("subResourceExternalId"));
+ payChargeResult =
this.loanTransactionHelper.payLoanCharge(loanExternalIdStr,
penalty5LoanChargeExternalId,
+ new
PostLoansLoanIdChargesChargeIdRequest().locale("en").dateFormat("dd MMMM
yyyy").transactionDate(formattedDate)
+ .externalId(payChargeExternalIdStr));
+ assertEquals(payChargeExternalIdStr,
payChargeResult.getSubResourceExternalId());
+ assertEquals(penalty5LoanChargeExternalId,
payChargeResult.getResourceExternalId());
response = loanTransactionHelper.getLoanTransactionDetails((long)
loanWithInterestId, payChargeExternalIdStr);
assertEquals(payChargeExternalIdStr, response.getExternalId());
- response =
loanTransactionHelper.getLoanTransactionDetails(loanExternalIdStr,
- Long.valueOf(payChargeResult.get("subResourceId").toString()));
+ response =
loanTransactionHelper.getLoanTransactionDetails(loanExternalIdStr,
payChargeResult.getSubResourceId());
assertEquals(payChargeExternalIdStr, response.getExternalId());
response =
loanTransactionHelper.getLoanTransactionDetails(loanExternalIdStr,
payChargeExternalIdStr);
assertEquals(payChargeExternalIdStr, response.getExternalId());
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
index 71e99cc91..07616bd0e 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
@@ -42,8 +42,11 @@ import java.util.Map;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import lombok.extern.slf4j.Slf4j;
+import
org.apache.fineract.client.models.DeleteLoansLoanIdChargesChargeIdResponse;
import org.apache.fineract.client.models.GetDelinquencyTagHistoryResponse;
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdChargesChargeIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdChargesTemplateResponse;
import org.apache.fineract.client.models.GetLoansLoanIdCollectionData;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
@@ -53,11 +56,14 @@ import
org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionId
import org.apache.fineract.client.models.GetPaymentTypesResponse;
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest;
import
org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdChargesRequest;
import org.apache.fineract.client.models.PostLoansLoanIdChargesResponse;
import org.apache.fineract.client.models.PostLoansLoanIdResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import
org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
+import org.apache.fineract.client.models.PutLoansLoanIdChargesChargeIdRequest;
+import org.apache.fineract.client.models.PutLoansLoanIdChargesChargeIdResponse;
import org.apache.fineract.client.models.PutLoansLoanIdResponse;
import org.apache.fineract.client.util.JSON;
import org.apache.fineract.integrationtests.client.IntegrationTest;
@@ -254,6 +260,22 @@ public class LoanTransactionHelper extends IntegrationTest
{
return Utils.performServerGet(requestSpec, responseSpec,
GET_LOAN_CHARGES_URL, "");
}
+ public List<GetLoansLoanIdChargesChargeIdResponse> getLoanCharges(final
Long loanId) {
+ return ok(fineract().loanCharges.retrieveAllLoanCharges(loanId));
+ }
+
+ public List<GetLoansLoanIdChargesChargeIdResponse> getLoanCharges(final
String loanExternalId) {
+ return
ok(fineract().loanCharges.retrieveAllLoanCharges1(loanExternalId));
+ }
+
+ public GetLoansLoanIdChargesTemplateResponse getLoanChargeTemplate(final
Long loanId) {
+ return ok(fineract().loanCharges.retrieveTemplate8(loanId));
+ }
+
+ public GetLoansLoanIdChargesTemplateResponse getLoanChargeTemplate(final
String loanExternalId) {
+ return ok(fineract().loanCharges.retrieveTemplate9(loanExternalId));
+ }
+
public List getRepaymentTemplate(final Integer loanId) {
final String GET_REPAYMENTS_URL = "/fineract-provider/api/v1/loans/" +
loanId + "/transactions/template?command=repayment&"
+ Utils.TENANT_IDENTIFIER;
@@ -503,9 +525,24 @@ public class LoanTransactionHelper extends IntegrationTest
{
return Utils.performServerPut(requestSpec, responseSpec, TRANSAC_URL,
"", "");
}
- public PostLoansLoanIdChargesChargeIdResponse waiveLoanCharge(final Long
loanId, final Long chargeId,
+ public PostLoansLoanIdChargesChargeIdResponse waiveLoanCharge(final Long
loanId, final Long loanChargeId,
+ final PostLoansLoanIdChargesChargeIdRequest request) {
+ return ok(fineract().loanCharges.executeLoanCharge2(loanId,
loanChargeId, request, "waive"));
+ }
+
+ public PostLoansLoanIdChargesChargeIdResponse waiveLoanCharge(final String
loanExternalId, final Long loanChargeId,
+ final PostLoansLoanIdChargesChargeIdRequest request) {
+ return ok(fineract().loanCharges.executeLoanCharge4(loanExternalId,
loanChargeId, request, "waive"));
+ }
+
+ public PostLoansLoanIdChargesChargeIdResponse waiveLoanCharge(final Long
loanId, final String loanChargeExternalId,
+ final PostLoansLoanIdChargesChargeIdRequest request) {
+ return ok(fineract().loanCharges.executeLoanCharge3(loanId,
loanChargeExternalId, request, "waive"));
+ }
+
+ public PostLoansLoanIdChargesChargeIdResponse waiveLoanCharge(final String
loanExternalId, final String loanChargeExternalId,
final PostLoansLoanIdChargesChargeIdRequest request) {
- return ok(fineract().loanCharges.executeLoanCharge1(loanId, chargeId,
request, "waive"));
+ return ok(fineract().loanCharges.executeLoanCharge5(loanExternalId,
loanChargeExternalId, request, "waive"));
}
public PostLoansLoanIdTransactionsResponse makeLoanRepayment(final String
loanExternalId,
@@ -674,6 +711,14 @@ public class LoanTransactionHelper extends IntegrationTest
{
getWithdrawLoanApplicationBodyAsJSON(date));
}
+ public PostLoansLoanIdChargesResponse addLoanCharge(final Long loanId,
final PostLoansLoanIdChargesRequest request) {
+ return ok(fineract().loanCharges.executeLoanCharge(loanId, request,
""));
+ }
+
+ public PostLoansLoanIdChargesResponse addLoanCharge(final String
loanExternalId, final PostLoansLoanIdChargesRequest request) {
+ return ok(fineract().loanCharges.executeLoanCharge1(loanExternalId,
request, ""));
+ }
+
public Integer addChargesForLoan(final Integer loanId, final String
request) {
log.info("--------------------------------- ADD CHARGES FOR LOAN
--------------------------------");
final String ADD_CHARGES_URL = LOAN_ACCOUNT_URL + "/" + loanId +
"/charges?" + Utils.TENANT_IDENTIFIER;
@@ -710,6 +755,42 @@ public class LoanTransactionHelper extends IntegrationTest
{
return (Integer) response.get("resourceId");
}
+ public PutLoansLoanIdChargesChargeIdResponse updateLoanCharge(final Long
loanId, final Long loanChargeId,
+ final PutLoansLoanIdChargesChargeIdRequest request) {
+ return ok(fineract().loanCharges.updateLoanCharge(loanId,
loanChargeId, request));
+ }
+
+ public PutLoansLoanIdChargesChargeIdResponse updateLoanCharge(final Long
loanId, final String loanChargeExternalId,
+ final PutLoansLoanIdChargesChargeIdRequest request) {
+ return ok(fineract().loanCharges.updateLoanCharge1(loanId,
loanChargeExternalId, request));
+ }
+
+ public PutLoansLoanIdChargesChargeIdResponse updateLoanCharge(final String
loanExternalId, final Long loanChargeId,
+ final PutLoansLoanIdChargesChargeIdRequest request) {
+ return ok(fineract().loanCharges.updateLoanCharge2(loanExternalId,
loanChargeId, request));
+ }
+
+ public PutLoansLoanIdChargesChargeIdResponse updateLoanCharge(final String
loanExternalId, final String loanChargeExternalId,
+ final PutLoansLoanIdChargesChargeIdRequest request) {
+ return ok(fineract().loanCharges.updateLoanCharge3(loanExternalId,
loanChargeExternalId, request));
+ }
+
+ public DeleteLoansLoanIdChargesChargeIdResponse deleteLoanCharge(final
Long loanId, final Long loanChargeId) {
+ return ok(fineract().loanCharges.deleteLoanCharge(loanId,
loanChargeId));
+ }
+
+ public DeleteLoansLoanIdChargesChargeIdResponse deleteLoanCharge(final
Long loanId, final String loanChargeExternalId) {
+ return ok(fineract().loanCharges.deleteLoanCharge1(loanId,
loanChargeExternalId));
+ }
+
+ public DeleteLoansLoanIdChargesChargeIdResponse deleteLoanCharge(final
String loanExternalId, final Long loanChargeId) {
+ return ok(fineract().loanCharges.deleteLoanCharge2(loanExternalId,
loanChargeId));
+ }
+
+ public DeleteLoansLoanIdChargesChargeIdResponse deleteLoanCharge(final
String loanExternalId, final String loanChargeExternalId) {
+ return ok(fineract().loanCharges.deleteLoanCharge3(loanExternalId,
loanChargeExternalId));
+ }
+
public Integer deleteChargesForLoan(final Integer loanId, final Integer
loanchargeId) {
log.info("--------------------------------- DELETE CHARGES FOR LOAN
--------------------------------");
final String DELETE_CHARGES_URL = "/fineract-provider/api/v1/loans/" +
loanId + "/charges/" + loanchargeId + "?"
@@ -735,7 +816,12 @@ public class LoanTransactionHelper extends IntegrationTest
{
public PostLoansLoanIdChargesChargeIdResponse chargeAdjustment(final Long
loanId, final Long chargeId,
final PostLoansLoanIdChargesChargeIdRequest request) {
- return ok(fineract().loanCharges.executeLoanCharge1(loanId, chargeId,
request, "adjustment"));
+ return ok(fineract().loanCharges.executeLoanCharge2(loanId, chargeId,
request, "adjustment"));
+ }
+
+ public PostLoansLoanIdChargesChargeIdResponse chargeAdjustment(final
String loanExternalId, final String loanChargeExternalId,
+ final PostLoansLoanIdChargesChargeIdRequest request) {
+ return ok(fineract().loanCharges.executeLoanCharge5(loanExternalId,
loanChargeExternalId, request, "adjustment"));
}
public Integer undoWaiveChargesForLoanReturnResourceId(final Integer
loanId, final Integer transactionId, final String body) {
@@ -754,11 +840,19 @@ public class LoanTransactionHelper extends
IntegrationTest {
return (Integer) response.get("resourceId");
}
- public HashMap payChargesForLoan(final Integer loanId, final Integer
loanchargeId, final String json, final String responseAttribute) {
- log.info("--------------------------------- WAIVE CHARGES FOR LOAN
--------------------------------");
- final String CHARGES_URL = "/fineract-provider/api/v1/loans/" + loanId
+ "/charges/" + loanchargeId + "?command=pay&"
- + Utils.TENANT_IDENTIFIER;
- return Utils.performServerPost(requestSpec, responseSpec, CHARGES_URL,
json, responseAttribute);
+ public PostLoansLoanIdChargesChargeIdResponse payLoanCharge(final Long
loanId, final Long loanChargeId,
+ final PostLoansLoanIdChargesChargeIdRequest request) {
+ return ok(fineract().loanCharges.executeLoanCharge2(loanId,
loanChargeId, request, "pay"));
+ }
+
+ public PostLoansLoanIdChargesChargeIdResponse payLoanCharge(final String
loanExternalId, final Long loanChargeId,
+ final PostLoansLoanIdChargesChargeIdRequest request) {
+ return ok(fineract().loanCharges.executeLoanCharge4(loanExternalId,
loanChargeId, request, "pay"));
+ }
+
+ public PostLoansLoanIdChargesChargeIdResponse payLoanCharge(final String
loanExternalId, final String loanChargeExternalId,
+ final PostLoansLoanIdChargesChargeIdRequest request) {
+ return ok(fineract().loanCharges.executeLoanCharge5(loanExternalId,
loanChargeExternalId, request, "pay"));
}
public ArrayList<HashMap> getLoanTransactionDetails(final
RequestSpecification requestSpec, final ResponseSpecification responseSpec,
@@ -774,6 +868,22 @@ public class LoanTransactionHelper extends IntegrationTest
{
return Utils.performServerGet(requestSpec, responseSpec,
GET_LOAN_CHARGES_URL, "");
}
+ public GetLoansLoanIdChargesChargeIdResponse getLoanCharge(final Long
loanId, final Long loanChargeId) {
+ return ok(fineract().loanCharges.retrieveLoanCharge(loanId,
loanChargeId));
+ }
+
+ public GetLoansLoanIdChargesChargeIdResponse getLoanCharge(final String
loanExternalId, final Long loanChargeId) {
+ return ok(fineract().loanCharges.retrieveLoanCharge2(loanExternalId,
loanChargeId));
+ }
+
+ public GetLoansLoanIdChargesChargeIdResponse getLoanCharge(final Long
loanId, final String loanChargeExternalId) {
+ return ok(fineract().loanCharges.retrieveLoanCharge1(loanId,
loanChargeExternalId));
+ }
+
+ public GetLoansLoanIdChargesChargeIdResponse getLoanCharge(final String
loanExternalId, final String loanChargeExternalId) {
+ return ok(fineract().loanCharges.retrieveLoanCharge3(loanExternalId,
loanChargeExternalId));
+ }
+
public Object getLoanTransactionDetails(final Integer loanId, final
Integer txnId, final String param) {
final String GET_LOAN_CHARGES_URL = "/fineract-provider/api/v1/loans/"
+ loanId + "/transactions/" + txnId + "?"
+ Utils.TENANT_IDENTIFIER;
@@ -997,16 +1107,24 @@ public class LoanTransactionHelper extends
IntegrationTest {
}
public static String getSpecifiedDueDateChargesForLoanAsJSON(final String
chargeId) {
- return getSpecifiedDueDateChargesForLoanAsJSON(chargeId, "12 January
2013", "100");
+ return getSpecifiedDueDateChargesForLoanAsJSON(chargeId, "12 January
2013", "100", null);
}
public static String getSpecifiedDueDateChargesForLoanAsJSON(final String
chargeId, final String dueDate, final String amount) {
+ return getSpecifiedDueDateChargesForLoanAsJSON(chargeId, dueDate,
amount, null);
+ }
+
+ public static String getSpecifiedDueDateChargesForLoanAsJSON(final String
chargeId, final String dueDate, final String amount,
+ final String externalId) {
final HashMap<String, String> map = new HashMap<>();
map.put("locale", "en_GB");
map.put("dateFormat", "dd MMMM yyyy");
map.put("amount", amount);
map.put("dueDate", dueDate);
map.put("chargeId", chargeId);
+ if (externalId != null) {
+ map.put("externalId", externalId);
+ }
String json = new Gson().toJson(map);
log.info("{}", json);
return json;