FINERACT-1760: Enhanced external id support for loan charges

This commit is contained in:
Adam Saghy
2022-11-28 12:02:00 +01:00
committed by Arnold Gálovics
parent 2f4dcbdd6c
commit 25c0b6a08d
16 changed files with 879 additions and 296 deletions
@@ -28,6 +28,7 @@ import java.util.Map;
import lombok.Getter; import lombok.Getter;
import org.apache.fineract.accounting.glaccount.data.GLAccountData; import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; 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.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType; import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData; 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, 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() { public SavingsAccountChargeData toSavingsAccountChargeData() {
@@ -43,15 +43,18 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.commands.domain.CommandWrapper; import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder; import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper; import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; 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.exception.UnrecognizedQueryParamException;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.service.CommandParameterUtil; 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.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.charge.data.ChargeData; import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType; 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.LoanChargeData;
import org.apache.fineract.portfolio.loanaccount.data.LoanInstallmentChargeData; import org.apache.fineract.portfolio.loanaccount.data.LoanInstallmentChargeData;
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformService; 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.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Path("/loans/{loanId}/charges") @Path("/loans")
@Component @Component
@Scope("singleton") @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" @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" + "\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") + "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 class LoanChargesApiResource {
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"));
private static final String RESOURCE_NAME_FOR_PERMISSIONS = "LOAN";
public static final String COMMAND_PAY = "pay"; public static final String COMMAND_PAY = "pay";
public static final String COMMAND_WAIVE = "waive"; public static final String COMMAND_WAIVE = "waive";
public static final String COMMAND_ADJUSTMENT = "adjustment"; 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", "externalId"));
private static final String RESOURCE_NAME_FOR_PERMISSIONS = "LOAN";
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final ChargeReadPlatformService chargeReadPlatformService; private final ChargeReadPlatformService chargeReadPlatformService;
private final LoanChargeReadPlatformService loanChargeReadPlatformService; private final LoanChargeReadPlatformService loanChargeReadPlatformService;
private final DefaultToApiJsonSerializer<LoanChargeData> toApiJsonSerializer; private final DefaultToApiJsonSerializer<LoanChargeData> toApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper; private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final LoanReadPlatformService loanReadPlatformService;
@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;
}
@GET @GET
@Path("{loanId}/charges")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ 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" @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, public String retrieveAllLoanCharges(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId,
@Context final UriInfo uriInfo) { @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);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, loanCharges, RESPONSE_DATA_PARAMETERS);
} }
@GET @GET
@Path("template") @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) {
return retrieveAllLoanCharges(null, loanExternalId, uriInfo);
}
@GET
@Path("{loanId}/charges/template")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ 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" @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, public String retrieveTemplate(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId,
@Context final UriInfo uriInfo) { @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);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, loanChargeTemplate, RESPONSE_DATA_PARAMETERS);
} }
@GET @GET
@Path("{chargeId}") @Path("external-id/{loanExternalId}/charges/template")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ 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 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) {
return retrieveTemplate(null, loanExternalId, uriInfo);
}
@GET
@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 Loan Charge ID"
+ "Example Requests:\n" + "\n" + "/loans/1/charges/1\n" + "\n" + "\n" + "/loans/1/charges/1?fields=name,amountOrPercentage") + "Example Requests:\n" + "\n" + "/loans/1/charges/1\n" + "\n" + "\n" + "/loans/1/charges/1?fields=name,amountOrPercentage")
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanChargesApiResourceSwagger.GetLoansLoanIdChargesChargeIdResponse.class))) }) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanChargesApiResourceSwagger.GetLoansLoanIdChargesChargeIdResponse.class))) })
public String retrieveLoanCharge(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId, 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 return retrieveLoanCharge(loanId, null, null, loanChargeExternalId, uriInfo);
.retrieveInstallmentLoanCharges(loanChargeId, true); }
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 retrieveLoanCharge(null, loanExternalId, loanChargeId, null, uriInfo);
return this.toApiJsonSerializer.serialize(settings, loanChargeData, RESPONSE_DATA_PARAMETERS); }
@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 @POST
@Path("{loanId}/charges")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ 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))) @RequestBody(required = true, content = @Content(schema = @Schema(implementation = LoanChargesApiResourceSwagger.PostLoansLoanIdChargesRequest.class)))
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanChargesApiResourceSwagger.PostLoansLoanIdChargesResponse.class))) }) @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, @QueryParam("command") @Parameter(description = "command") final String commandParam,
@Parameter(hidden = true) final String apiRequestBodyAsJson) { @Parameter(hidden = true) final String apiRequestBodyAsJson) {
CommandProcessingResult result; return handleExecuteLoanCharge(loanId, null, commandParam, apiRequestBodyAsJson);
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 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 @PUT
@Path("{chargeId}") @Path("{loanId}/charges/{loanChargeId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ 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") @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({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanChargesApiResourceSwagger.PutLoansLoanIdChargesChargeIdResponse.class))) }) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanChargesApiResourceSwagger.PutLoansLoanIdChargesChargeIdResponse.class))) })
public String updateLoanCharge(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId, 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) { @Parameter(hidden = true) final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateLoanCharge(loanId, loanChargeId) return updateLoanCharge(loanId, null, loanChargeId, null, apiRequestBodyAsJson);
.withJson(apiRequestBodyAsJson).build(); }
@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 updateLoanCharge(loanId, null, null, loanChargeExternalId, apiRequestBodyAsJson);
}
@PUT
@Path("external-id/{loanExternalId}/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")
@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("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); final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result); return this.toApiJsonSerializer.serialize(result);
} }
@POST private String retrieveLoanCharge(final Long loanId, final String loanExternalIdStr, final Long loanChargeId,
@Path("{chargeId}") final String loanChargeExternalIdStr, final UriInfo uriInfo) {
@Consumes({ MediaType.APPLICATION_JSON }) this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Pay Loan Charge", description = "Loan Charge will be paid if the loan is linked with a savings account") ExternalId loanExternalId = ExternalIdFactory.produce(loanExternalIdStr);
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = LoanChargesApiResourceSwagger.PostLoansLoanIdChargesChargeIdRequest.class))) ExternalId loanChargeExternalId = ExternalIdFactory.produce(loanChargeExternalIdStr);
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanChargesApiResourceSwagger.PostLoansLoanIdChargesChargeIdResponse.class))) }) Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
public String executeLoanCharge(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId, Long resolvedLoanChargeId = getResolvedLoanChargeId(loanChargeId, loanChargeExternalId);
@PathParam("chargeId") @Parameter(description = "chargeId") final Long loanChargeId,
@QueryParam("command") @Parameter(description = "command") final String commandParam, final LoanChargeData loanCharge = this.loanChargeReadPlatformService.retrieveLoanChargeDetails(resolvedLoanChargeId,
@Parameter(hidden = true) final String apiRequestBodyAsJson) { 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); final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
CommandProcessingResult result; CommandProcessingResult result;
if (CommandParameterUtil.is(commandParam, COMMAND_WAIVE)) { 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); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (CommandParameterUtil.is(commandParam, COMMAND_PAY)) { } 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); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (CommandParameterUtil.is(commandParam, COMMAND_ADJUSTMENT)) { } 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); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else { } else {
throw new UnrecognizedQueryParamException("command", commandParam); throw new UnrecognizedQueryParamException("command", commandParam);
@@ -243,20 +501,64 @@ public class LoanChargesApiResource {
return this.toApiJsonSerializer.serialize(result); return this.toApiJsonSerializer.serialize(result);
} }
@DELETE private String updateLoanCharge(final Long loanId, final String loanExternalIdStr, final Long loanChargeId,
@Path("{chargeId}") final String loanChargeExternalIdStr, final String apiRequestBodyAsJson) {
@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) {
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); final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result); 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;
}
} }
@@ -105,6 +105,8 @@ final class LoanChargesApiResourceSwagger {
public Boolean penalty; public Boolean penalty;
@Schema(example = "27 March 2013") @Schema(example = "27 March 2013")
public LocalDate submittedOnDate; public LocalDate submittedOnDate;
@Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7")
public String externalId;
} }
@Schema(description = "GetLoansLoanIdChargesTemplateResponse") @Schema(description = "GetLoansLoanIdChargesTemplateResponse")
@@ -199,6 +201,8 @@ final class LoanChargesApiResourceSwagger {
public Long loanId; public Long loanId;
@Schema(example = "31") @Schema(example = "31")
public Long resourceId; public Long resourceId;
@Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7")
public String resourceExternalId;
} }
@Schema(description = " PutLoansLoanIdChargesChargeIdRequest") @Schema(description = " PutLoansLoanIdChargesChargeIdRequest")
@@ -229,6 +233,8 @@ final class LoanChargesApiResourceSwagger {
public Long loanId; public Long loanId;
@Schema(example = "6") @Schema(example = "6")
public Long resourceId; public Long resourceId;
@Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7")
public String resourceExternalId;
public PutLoansLoanIdChargesChargeIdRequest changes; public PutLoansLoanIdChargesChargeIdRequest changes;
} }
@@ -325,5 +331,7 @@ final class LoanChargesApiResourceSwagger {
public Long loanId; public Long loanId;
@Schema(example = "2") @Schema(example = "2")
public Long resourceId; public Long resourceId;
@Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7")
public String resourceExternalId;
} }
} }
@@ -25,6 +25,7 @@ import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; 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.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.charge.data.ChargeData; import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode; import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
@@ -89,11 +90,11 @@ public class LoanChargeData {
private BigDecimal amountUnrecognized; private BigDecimal amountUnrecognized;
private String externalId; private final ExternalId externalId;
public static LoanChargeData template(final Collection<ChargeData> chargeOptions) { 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, 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, public static LoanChargeData newLoanChargeDetails(final Long chargeId, final String name, final CurrencyData currency,
final BigDecimal amount, final BigDecimal percentage, final EnumOptionData chargeTimeType, final BigDecimal amount, final BigDecimal percentage, final EnumOptionData chargeTimeType,
final EnumOptionData chargeCalculationType, final boolean penalty, final EnumOptionData chargePaymentMode, 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, 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, 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 LocalDate dueDate, final EnumOptionData chargeCalculationType, final BigDecimal percentage,
final BigDecimal amountPercentageAppliedTo, final boolean penalty, final EnumOptionData chargePaymentMode, final boolean paid, 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, 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.id = id;
this.chargeId = chargeId; this.chargeId = chargeId;
this.name = name; this.name = name;
@@ -158,7 +159,7 @@ public class LoanChargeData {
final BigDecimal percentage, final EnumOptionData chargeTimeType, final EnumOptionData chargeCalculationType, final BigDecimal percentage, final EnumOptionData chargeTimeType, final EnumOptionData chargeCalculationType,
final Collection<ChargeData> chargeOptions, final boolean penalty, final EnumOptionData chargePaymentMode, final boolean paid, 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, 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.id = id;
this.chargeId = chargeId; this.chargeId = chargeId;
this.name = name; this.name = name;
@@ -197,10 +198,11 @@ public class LoanChargeData {
this.installmentChargeData = installmentChargeData; this.installmentChargeData = installmentChargeData;
this.amountAccrued = null; this.amountAccrued = null;
this.amountUnrecognized = null; this.amountUnrecognized = null;
this.externalId = externalId;
} }
public LoanChargeData(final Long id, final LocalDate dueAsOfDate, final BigDecimal amountOutstanding, EnumOptionData chargeTimeType, 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.id = id;
this.chargeId = null; this.chargeId = null;
this.name = null; this.name = null;
@@ -229,10 +231,12 @@ public class LoanChargeData {
this.installmentChargeData = installmentChargeData; this.installmentChargeData = installmentChargeData;
this.amountAccrued = null; this.amountAccrued = null;
this.amountUnrecognized = null; this.amountUnrecognized = null;
this.externalId = externalId;
} }
public LoanChargeData(final Long id, final Long chargeId, final LocalDate dueAsOfDate, EnumOptionData chargeTimeType, 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.id = id;
this.chargeId = chargeId; this.chargeId = chargeId;
this.name = null; this.name = null;
@@ -261,6 +265,7 @@ public class LoanChargeData {
this.installmentChargeData = null; this.installmentChargeData = null;
this.amountAccrued = amountAccrued; this.amountAccrued = amountAccrued;
this.amountUnrecognized = null; this.amountUnrecognized = null;
this.externalId = externalId;
} }
public LoanChargeData(final BigDecimal amountUnrecognized, final LoanChargeData chargeData) { public LoanChargeData(final BigDecimal amountUnrecognized, final LoanChargeData chargeData) {
@@ -292,6 +297,7 @@ public class LoanChargeData {
this.installmentChargeData = null; this.installmentChargeData = null;
this.amountAccrued = chargeData.amountAccrued; this.amountAccrued = chargeData.amountAccrued;
this.amountUnrecognized = amountUnrecognized; this.amountUnrecognized = amountUnrecognized;
this.externalId = chargeData.externalId;
} }
public LoanChargeData(LoanChargeData chargeData, Collection<LoanInstallmentChargeData> installmentChargeData) { public LoanChargeData(LoanChargeData chargeData, Collection<LoanInstallmentChargeData> installmentChargeData) {
@@ -356,6 +362,7 @@ public class LoanChargeData {
this.installmentChargeData = null; this.installmentChargeData = null;
this.amountAccrued = null; this.amountAccrued = null;
this.amountUnrecognized = null; this.amountUnrecognized = null;
this.externalId = ExternalId.empty();
} }
public LoanChargeData(final Long id, final LocalDate dueAsOfDate, final BigDecimal amountOrPercentage) { public LoanChargeData(final Long id, final LocalDate dueAsOfDate, final BigDecimal amountOrPercentage) {
@@ -387,6 +394,7 @@ public class LoanChargeData {
this.installmentChargeData = null; this.installmentChargeData = null;
this.amountAccrued = null; this.amountAccrued = null;
this.amountUnrecognized = null; this.amountUnrecognized = null;
this.externalId = ExternalId.empty();
} }
public boolean isChargePayable() { public boolean isChargePayable() {
@@ -1950,9 +1950,12 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
this.disbursementDetails.add(disbursementDetails); this.disbursementDetails.add(disbursementDetails);
for (LoanTrancheCharge trancheCharge : trancheCharges) { for (LoanTrancheCharge trancheCharge : trancheCharges) {
Charge chargeDefinition = trancheCharge.getCharge(); Charge chargeDefinition = trancheCharge.getCharge();
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, principal, null, null, null, ExternalId externalId = ExternalId.empty();
expectedDisbursementDate, null, null); if (TemporaryConfigurationServiceContainer.isExternalIdAutoGenerationEnabled()) {
loanCharge.update(this); 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, LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge,
disbursementDetails); disbursementDetails);
loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge); loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge);
@@ -27,7 +27,6 @@ import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; 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.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; 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.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.Money;
@@ -131,7 +131,7 @@ public class LoanCharge extends AbstractPersistableCustom {
private boolean active = true; private boolean active = true;
@Column(name = "external_id") @Column(name = "external_id")
private String externalId; private ExternalId externalId;
@OneToOne(mappedBy = "loancharge", cascade = CascadeType.ALL, optional = true, orphanRemoval = true, fetch = FetchType.EAGER) @OneToOne(mappedBy = "loancharge", cascade = CascadeType.ALL, optional = true, orphanRemoval = true, fetch = FetchType.EAGER)
private LoanOverdueInstallmentCharge overdueInstallmentCharge; private LoanOverdueInstallmentCharge overdueInstallmentCharge;
@@ -142,99 +142,14 @@ public class LoanCharge extends AbstractPersistableCustom {
@OneToMany(mappedBy = "loanCharge", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) @OneToMany(mappedBy = "loanCharge", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private Set<LoanChargePaidBy> loanChargePaidBySet; 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() { protected LoanCharge() {
// //
} }
public LoanCharge(final Loan loan, final Charge chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount, public LoanCharge(final Loan loan, final Charge chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount,
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate, 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.loan = loan;
this.charge = chargeDefinition; this.charge = chargeDefinition;
this.submittedOnDate = DateUtils.getBusinessLocalDate(); this.submittedOnDate = DateUtils.getBusinessLocalDate();
@@ -278,6 +193,7 @@ public class LoanCharge extends AbstractPersistableCustom {
populateDerivedFields(loanPrincipal, chargeAmount, numberOfRepayments, loanCharge); populateDerivedFields(loanPrincipal, chargeAmount, numberOfRepayments, loanCharge);
this.paid = determineIfFullyPaid(); this.paid = determineIfFullyPaid();
this.externalId = externalId;
} }
private void populateDerivedFields(final BigDecimal amountPercentageAppliedTo, final BigDecimal chargeAmount, private void populateDerivedFields(final BigDecimal amountPercentageAppliedTo, final BigDecimal chargeAmount,
@@ -1093,14 +1009,10 @@ public class LoanCharge extends AbstractPersistableCustom {
this.waived = false; this.waived = false;
} }
public String getExternalId() { public ExternalId getExternalId() {
return externalId; return externalId;
} }
public void setExternalId(String externalId) {
this.externalId = externalId;
}
public ChargeTimeType getChargeTimeType() { public ChargeTimeType getChargeTimeType() {
return ChargeTimeType.fromInt(this.chargeTime); return ChargeTimeType.fromInt(this.chargeTime);
} }
@@ -18,9 +18,16 @@
*/ */
package org.apache.fineract.portfolio.loanaccount.domain; 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.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; 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> { 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);
} }
@@ -67,7 +67,7 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
@Override @Override
@Transactional @Transactional
public void addAccrualAccounting(final Long loanId, final Collection<LoanScheduleAccrualData> loanScheduleAccrualData) { 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<LoanSchedulePeriodData> loanWaiverScheduleData = new ArrayList<>(1);
Collection<LoanTransactionData> loanWaiverTransactionData = 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) { public void addPeriodicAccruals(final LocalDate tillDate, Long loanId, Collection<LoanScheduleAccrualData> loanScheduleAccrualData) {
boolean firstTime = true; boolean firstTime = true;
LocalDate accruedTill = null; LocalDate accruedTill = null;
Collection<LoanChargeData> chargeData = this.loanChargeReadPlatformService.retrieveLoanChargesForAccural(loanId); Collection<LoanChargeData> chargeData = this.loanChargeReadPlatformService.retrieveLoanChargesForAccrual(loanId);
Collection<LoanSchedulePeriodData> loanWaiverScheduleData = new ArrayList<>(1); Collection<LoanSchedulePeriodData> loanWaiverScheduleData = new ArrayList<>(1);
Collection<LoanTransactionData> loanWaiverTransactionData = new ArrayList<>(1); Collection<LoanTransactionData> loanWaiverTransactionData = new ArrayList<>(1);
for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualData) { for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualData) {
@@ -29,14 +29,20 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Set; 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.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.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType; import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode; import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper; import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType; import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeAddedException; 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.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.LoanCharge;
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository; import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails; 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.LoanProduct;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException; import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
@RequiredArgsConstructor
public class LoanChargeAssembler { public class LoanChargeAssembler {
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
private final ChargeRepositoryWrapper chargeRepository; private final ChargeRepositoryWrapper chargeRepository;
private final LoanChargeRepository loanChargeRepository; private final LoanChargeRepository loanChargeRepository;
private final LoanProductRepository loanProductRepository; private final LoanProductRepository loanProductRepository;
private final ExternalIdFactory externalIdFactory;
@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;
}
public Set<LoanCharge> fromParsedJson(final JsonElement element, List<LoanDisbursementDetails> disbursementDetails) { public Set<LoanCharge> fromParsedJson(final JsonElement element, List<LoanDisbursementDetails> disbursementDetails) {
JsonArray jsonDisbursement = this.fromApiJsonHelper.extractJsonArrayNamed("disbursementData", element); JsonArray jsonDisbursement = this.fromApiJsonHelper.extractJsonArrayNamed("disbursementData", element);
@@ -117,6 +115,8 @@ public class LoanChargeAssembler {
locale); locale);
final Integer chargePaymentMode = this.fromApiJsonHelper.extractIntegerNamed("chargePaymentMode", loanChargeElement, final Integer chargePaymentMode = this.fromApiJsonHelper.extractIntegerNamed("chargePaymentMode", loanChargeElement,
locale); locale);
final String externalIdStr = this.fromApiJsonHelper.extractStringNamed("externalId", loanChargeElement);
final ExternalId externalId = externalIdFactory.create(externalIdStr);
if (id == null) { if (id == null) {
final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeId); final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeId);
@@ -140,8 +140,8 @@ public class LoanChargeAssembler {
chargePaymentModeEnum = ChargePaymentMode.fromInt(chargePaymentMode); chargePaymentModeEnum = ChargePaymentMode.fromInt(chargePaymentMode);
} }
if (!isMultiDisbursal) { if (!isMultiDisbursal) {
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, principal, amount, chargeTime, final LoanCharge loanCharge = createNewWithoutLoan(chargeDefinition, principal, amount, chargeTime,
chargeCalculation, dueDate, chargePaymentModeEnum, numberOfRepayments); chargeCalculation, dueDate, chargePaymentModeEnum, numberOfRepayments, externalId);
loanCharges.add(loanCharge); loanCharges.add(loanCharge);
} else { } else {
if (topLevelJsonElement.has("disbursementData") && topLevelJsonElement.get("disbursementData").isJsonArray()) { if (topLevelJsonElement.has("disbursementData") && topLevelJsonElement.get("disbursementData").isJsonArray()) {
@@ -159,8 +159,8 @@ public class LoanChargeAssembler {
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = null; LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = null;
if (chargeDefinition.isPercentageOfApprovedAmount() if (chargeDefinition.isPercentageOfApprovedAmount()
&& disbursementDetail.expectedDisbursementDateAsLocalDate().equals(expectedDisbursementDate)) { && disbursementDetail.expectedDisbursementDateAsLocalDate().equals(expectedDisbursementDate)) {
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, principal, amount, final LoanCharge loanCharge = createNewWithoutLoan(chargeDefinition, principal, amount, chargeTime,
chargeTime, chargeCalculation, dueDate, chargePaymentModeEnum, numberOfRepayments); chargeCalculation, dueDate, chargePaymentModeEnum, numberOfRepayments, externalId);
loanCharges.add(loanCharge); loanCharges.add(loanCharge);
if (loanCharge.isTrancheDisbursementCharge()) { if (loanCharge.isTrancheDisbursementCharge()) {
loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge, loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge,
@@ -169,10 +169,10 @@ public class LoanChargeAssembler {
} }
} else { } else {
if (disbursementDetail.expectedDisbursementDateAsLocalDate().equals(expectedDisbursementDate)) { if (disbursementDetail.expectedDisbursementDateAsLocalDate().equals(expectedDisbursementDate)) {
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, final LoanCharge loanCharge = createNewWithoutLoan(chargeDefinition,
disbursementDetail.principal(), amount, chargeTime, chargeCalculation, disbursementDetail.principal(), amount, chargeTime, chargeCalculation,
disbursementDetail.expectedDisbursementDateAsLocalDate(), chargePaymentModeEnum, disbursementDetail.expectedDisbursementDateAsLocalDate(), chargePaymentModeEnum,
numberOfRepayments); numberOfRepayments, externalId);
loanCharges.add(loanCharge); loanCharges.add(loanCharge);
if (loanCharge.isTrancheDisbursementCharge()) { if (loanCharge.isTrancheDisbursementCharge()) {
loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge, loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge,
@@ -186,18 +186,18 @@ public class LoanChargeAssembler {
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = null; LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = null;
for (LoanDisbursementDetails disbursementDetail : disbursementDetails) { for (LoanDisbursementDetails disbursementDetail : disbursementDetails) {
if (ChargeTimeType.TRANCHE_DISBURSEMENT.getValue().equals(chargeDefinition.getChargeTimeType())) { if (ChargeTimeType.TRANCHE_DISBURSEMENT.getValue().equals(chargeDefinition.getChargeTimeType())) {
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, final LoanCharge loanCharge = createNewWithoutLoan(chargeDefinition, disbursementDetail.principal(),
disbursementDetail.principal(), amount, chargeTime, chargeCalculation, amount, chargeTime, chargeCalculation,
disbursementDetail.expectedDisbursementDateAsLocalDate(), chargePaymentModeEnum, disbursementDetail.expectedDisbursementDateAsLocalDate(), chargePaymentModeEnum,
numberOfRepayments); numberOfRepayments, externalId);
loanCharges.add(loanCharge); loanCharges.add(loanCharge);
loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge, disbursementDetail); loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge, disbursementDetail);
loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge); loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge);
} }
} }
} else { } else {
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, principal, amount, final LoanCharge loanCharge = createNewWithoutLoan(chargeDefinition, principal, amount, chargeTime,
chargeTime, chargeCalculation, dueDate, chargePaymentModeEnum, numberOfRepayments); chargeCalculation, dueDate, chargePaymentModeEnum, numberOfRepayments, externalId);
loanCharges.add(loanCharge); loanCharges.add(loanCharge);
} }
} }
@@ -239,4 +239,88 @@ public class LoanChargeAssembler {
} }
return associatedChargesForLoan; 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);
}
} }
@@ -19,6 +19,7 @@
package org.apache.fineract.portfolio.loanaccount.service; package org.apache.fineract.portfolio.loanaccount.service;
import java.util.Collection; 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.data.ChargeData;
import org.apache.fineract.portfolio.charge.domain.Charge; import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData; 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<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, Collection<LoanChargePaidByData> retrieveLoanChargesPaidBy(Long chargeId, LoanTransactionType transactionType,
Integer installmentNumber); Integer installmentNumber);
Long retrieveLoanChargeIdByExternalId(ExternalId loanChargeExternalId);
} }
@@ -30,7 +30,9 @@ import java.util.Map;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.glaccount.data.GLAccountData; import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; 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.domain.JdbcSupport;
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.charge.data.ChargeData; import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.domain.Charge; 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.data.LoanInstallmentChargeData;
import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; 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.loanaccount.domain.LoanTransactionType;
import org.apache.fineract.portfolio.tax.data.TaxGroupData; import org.apache.fineract.portfolio.tax.data.TaxGroupData;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
@@ -55,23 +58,24 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
private final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService; private final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService;
private final DropdownReadPlatformService dropdownReadPlatformService; private final DropdownReadPlatformService dropdownReadPlatformService;
private final LoanChargeRepository loanChargeRepository;
private static final class LoanChargeMapper implements RowMapper<LoanChargeData> { private static final class LoanChargeMapper implements RowMapper<LoanChargeData> {
public String schema() { 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, " 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 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.amount_writtenoff_derived as amountWrittenOff, lc.amount_outstanding_derived as amountOutstanding, " //
+ "lc.calculation_percentage as percentageOf, lc.calculation_on_amount as amountPercentageAppliedTo, " + "lc.calculation_percentage as percentageOf, lc.calculation_on_amount as amountPercentageAppliedTo, " //
+ "lc.charge_time_enum as chargeTime, " + "lc.is_penalty as penalty, " + "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.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.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.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, " + "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, " + "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.decimal_places as currencyDecimalPlaces, oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, " //
+ "oc.internationalized_name_code as currencyNameCode from m_charge c " + "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 " + "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 "; + "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) { if (disbursementDate != null) {
dueAsOfDate = disbursementDate; 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, return new LoanChargeData(id, chargeId, name, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding,
chargeTimeType, submittedOnDate, dueAsOfDate, chargeCalculationType, percentageOf, amountPercentageAppliedTo, penalty, chargeTimeType, submittedOnDate, dueAsOfDate, chargeCalculationType, percentageOf, amountPercentageAppliedTo, penalty,
@@ -192,9 +197,9 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
private static final class LoanChargeMapperWithLoanId implements RowMapper<LoanChargeData> { private static final class LoanChargeMapperWithLoanId implements RowMapper<LoanChargeData> {
public String schema() { public String schema() {
return "lc.id as id, lc.due_for_collection_as_of_date as dueAsOfDate, " + "lc.amount_outstanding_derived as amountOutstanding, " 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 " + " 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 "; + " join m_loan loan on loan.id = lc.loan_id ";
} }
@Override @Override
@@ -206,8 +211,10 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding"); final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
final int chargeTime = rs.getInt("chargeTime"); final int chargeTime = rs.getInt("chargeTime");
final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(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() { public String schema() {
return " lsi.installment as installmentNumber, lsi.duedate as dueAsOfDate, " 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_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_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 "; + "join m_loan_repayment_schedule lsi on lsi.id = lic.loan_schedule_id ";
} }
@@ -262,7 +269,7 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
} }
@Override @Override
public Collection<LoanChargeData> retrieveLoanChargesForAccural(final Long loanId) { public Collection<LoanChargeData> retrieveLoanChargesForAccrual(final Long loanId) {
final LoanChargeAccrualMapper rm = new LoanChargeAccrualMapper(); final LoanChargeAccrualMapper rm = new LoanChargeAccrualMapper();
@@ -298,20 +305,20 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
LoanChargeAccrualMapper() { LoanChargeAccrualMapper() {
StringBuilder sb = new StringBuilder(50); StringBuilder sb = new StringBuilder(50);
sb.append("lc.id as id, lc.charge_id as chargeId, "); 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 as amountDue, ");
sb.append("lc.amount_waived_derived as amountWaived, "); sb.append(" lc.amount_waived_derived as amountWaived, ");
sb.append("lc.charge_time_enum as chargeTime, "); sb.append(" lc.charge_time_enum as chargeTime, ");
sb.append(" sum(cp.amount) as amountAccrued, "); sb.append(" sum(cp.amount) as amountAccrued, ");
sb.append("lc.is_penalty as penalty, "); sb.append(" lc.is_penalty as penalty, ");
sb.append("lc.due_for_collection_as_of_date as dueAsOfDate "); sb.append(" lc.due_for_collection_as_of_date as dueAsOfDate ");
sb.append(" from m_loan_charge lc "); sb.append(" from m_loan_charge lc ");
sb.append("left join ("); sb.append(" left join ( ");
sb.append("select lcp.loan_charge_id, lcp.amount"); sb.append(" select lcp.loan_charge_id, lcp.amount ");
sb.append(" from m_loan_charge_paid_by lcp "); sb.append(" from m_loan_charge_paid_by lcp ");
sb.append( 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 = ?"); " 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 "); sb.append(" ) cp on cp.loan_charge_id= lc.id ");
schemaSql = sb.toString(); schemaSql = sb.toString();
} }
@@ -335,7 +342,10 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate"); final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
final boolean penalty = rs.getBoolean("penalty"); 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()); 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 static final class LoanChargesPaidByMapper implements RowMapper<LoanChargePaidByData> {
private final String schemaSql; private final String schemaSql;
@@ -152,8 +152,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
private final ConfigurationDomainService configurationDomainService; private final ConfigurationDomainService configurationDomainService;
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory; private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
private final ExternalIdFactory externalIdFactory; private final ExternalIdFactory externalIdFactory;
private final AccountTransferDetailRepository accountTransferDetailRepository; private final AccountTransferDetailRepository accountTransferDetailRepository;
private final LoanChargeAssembler loanChargeAssembler;
private static boolean isPartOfThisInstallment(LoanCharge loanCharge, LoanRepaymentScheduleInstallment e) { private static boolean isPartOfThisInstallment(LoanCharge loanCharge, LoanRepaymentScheduleInstallment e) {
return e.getFromDate().isBefore(loanCharge.getDueDate()) && !loanCharge.getDueDate().isAfter(e.getDueDate()); return e.getFromDate().isBefore(loanCharge.getDueDate()) && !loanCharge.getDueDate().isAfter(e.getDueDate());
@@ -185,10 +185,17 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
LocalDate recalculateFrom = loan.fetchInterestRecalculateFromDate(); LocalDate recalculateFrom = loan.fetchInterestRecalculateFromDate();
if (chargeDefinition.isPercentageOfDisbursementAmount()) { if (chargeDefinition.isPercentageOfDisbursementAmount()) {
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge; LoanTrancheDisbursementCharge loanTrancheDisbursementCharge;
ExternalId externalId = externalIdFactory.createFromCommand(command, "externalId");
boolean needToGenerateNewExternalId = false;
for (LoanDisbursementDetails disbursementDetail : loanDisburseDetails) { for (LoanDisbursementDetails disbursementDetail : loanDisburseDetails) {
if (disbursementDetail.actualDisbursementDate() == null) { if (disbursementDetail.actualDisbursementDate() == null) {
loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, disbursementDetail.principal(), null, null, null, // If multiple charges to be applied, only the first one will get the provided externalId, for the
disbursementDetail.expectedDisbursementDateAsLocalDate(), null, null); // 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); loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge, disbursementDetail);
loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge); loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge);
businessEventNotifierService.notifyPreBusinessEvent(new LoanAddChargeBusinessEvent(loanCharge)); businessEventNotifierService.notifyPreBusinessEvent(new LoanAddChargeBusinessEvent(loanCharge));
@@ -198,6 +205,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
if (recalculateFrom.isAfter(disbursementDetail.expectedDisbursementDateAsLocalDate())) { if (recalculateFrom.isAfter(disbursementDetail.expectedDisbursementDateAsLocalDate())) {
recalculateFrom = disbursementDetail.expectedDisbursementDateAsLocalDate(); recalculateFrom = disbursementDetail.expectedDisbursementDateAsLocalDate();
} }
needToGenerateNewExternalId = true;
} }
} }
if (loanCharge == null) { if (loanCharge == null) {
@@ -207,7 +215,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
} }
loan.addTrancheLoanCharge(chargeDefinition); loan.addTrancheLoanCharge(chargeDefinition);
} else { } else {
loanCharge = LoanCharge.createNewFromJson(loan, chargeDefinition, command); loanCharge = loanChargeAssembler.createNewFromJson(loan, chargeDefinition, command);
businessEventNotifierService.notifyPreBusinessEvent(new LoanAddChargeBusinessEvent(loanCharge)); businessEventNotifierService.notifyPreBusinessEvent(new LoanAddChargeBusinessEvent(loanCharge));
validateAddLoanCharge(loan, chargeDefinition, loanCharge); validateAddLoanCharge(loan, chargeDefinition, loanCharge);
@@ -252,6 +260,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
businessEventNotifierService.notifyPostBusinessEvent(new LoanAddChargeBusinessEvent(loanCharge)); businessEventNotifierService.notifyPostBusinessEvent(new LoanAddChargeBusinessEvent(loanCharge));
return new CommandProcessingResultBuilder().withCommandId(command.commandId()) // return new CommandProcessingResultBuilder().withCommandId(command.commandId()) //
.withEntityId(loanCharge.getId()) // .withEntityId(loanCharge.getId()) //
.withEntityExternalId(loanCharge.getExternalId()) //
.withOfficeId(loan.getOfficeId()) // .withOfficeId(loan.getOfficeId()) //
.withClientId(loan.getClientId()) // .withClientId(loan.getClientId()) //
.withGroupId(loan.getGroupId()) // .withGroupId(loan.getGroupId()) //
@@ -346,7 +355,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
LoanChargeWaiveCannotBeReversedException.LoanChargeWaiveCannotUndoReason.LOAN_INACTIVE, loanCharge.getId()); 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)); businessEventNotifierService.notifyPreBusinessEvent(new LoanWaiveChargeUndoBusinessEvent(loanCharge));
@@ -367,6 +376,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) // .withCommandId(command.commandId()) //
.withEntityId(loanCharge.getId()) // .withEntityId(loanCharge.getId()) //
.withEntityExternalId(loanCharge.getExternalId()) //
.withSubEntityId(loanTransaction.getId()) // .withSubEntityId(loanTransaction.getId()) //
.withSubEntityExternalId(loanTransaction.getExternalId()) // .withSubEntityExternalId(loanTransaction.getExternalId()) //
.withLoanId(loanId) // .withLoanId(loanId) //
@@ -399,6 +409,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) // .withCommandId(command.commandId()) //
.withEntityId(loanChargeId) // .withEntityId(loanChargeId) //
.withEntityExternalId(loanCharge.getExternalId()) //
.withOfficeId(loan.getOfficeId()) // .withOfficeId(loan.getOfficeId()) //
.withClientId(loan.getClientId()) // .withClientId(loan.getClientId()) //
.withGroupId(loan.getGroupId()) // .withGroupId(loan.getGroupId()) //
@@ -458,7 +469,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
loanInstallmentNumber = chargePerInstallment.getRepaymentInstallment().getInstallmentNumber(); loanInstallmentNumber = chargePerInstallment.getRepaymentInstallment().getInstallmentNumber();
} }
final Map<String, Object> changes = new LinkedHashMap<>(3); final Map<String, Object> changes = new LinkedHashMap<>();
changes.put(LoanApiConstants.externalIdParameterName, externalId); changes.put(LoanApiConstants.externalIdParameterName, externalId);
final List<Long> existingTransactionIds = new ArrayList<>(); final List<Long> existingTransactionIds = new ArrayList<>();
@@ -490,6 +501,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) // .withCommandId(command.commandId()) //
.withEntityId(loanChargeId) // .withEntityId(loanChargeId) //
.withEntityExternalId(loanCharge.getExternalId()) //
.withSubEntityId(waiveTransaction.getId()) // .withSubEntityId(waiveTransaction.getId()) //
.withSubEntityExternalId(waiveTransaction.getExternalId()) // .withSubEntityExternalId(waiveTransaction.getExternalId()) //
.withOfficeId(loan.getOfficeId()) // .withOfficeId(loan.getOfficeId()) //
@@ -523,6 +535,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) // .withCommandId(command.commandId()) //
.withEntityId(loanChargeId) // .withEntityId(loanChargeId) //
.withEntityExternalId(loanCharge.getExternalId()) //
.withOfficeId(loan.getOfficeId()) // .withOfficeId(loan.getOfficeId()) //
.withClientId(loan.getClientId()) // .withClientId(loan.getClientId()) //
.withGroupId(loan.getGroupId()) // .withGroupId(loan.getGroupId()) //
@@ -613,6 +626,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) // .withCommandId(command.commandId()) //
.withEntityId(loanChargeId) // .withEntityId(loanChargeId) //
.withEntityExternalId(loanCharge.getExternalId()) //
.withSubEntityId(loanTransaction.getId()).withSubEntityExternalId(loanTransaction.getExternalId()) .withSubEntityId(loanTransaction.getId()).withSubEntityExternalId(loanTransaction.getExternalId())
.withOfficeId(loan.getOfficeId()) // .withOfficeId(loan.getOfficeId()) //
.withClientId(loan.getClientId()) // .withClientId(loan.getClientId()) //
@@ -629,7 +643,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId); final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
final LocalDate transactionDate = DateUtils.getBusinessLocalDate(); final LocalDate transactionDate = DateUtils.getBusinessLocalDate();
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("amount"); 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(); final String locale = command.locale();
loanChargeAdjustmentEntranceValidation(loanCharge, transactionAmount); loanChargeAdjustmentEntranceValidation(loanCharge, transactionAmount);
@@ -637,7 +651,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
final CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder(); 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. // Update loan transaction on repayment.
if (AccountType.fromInt(loan.getLoanType()).isIndividualAccount()) { if (AccountType.fromInt(loan.getLoanType()).isIndividualAccount()) {
@@ -658,13 +672,15 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
businessEventNotifierService.notifyPostBusinessEvent(new LoanChargeAdjustmentPostBusinessEvent(loanTransaction)); businessEventNotifierService.notifyPostBusinessEvent(new LoanChargeAdjustmentPostBusinessEvent(loanTransaction));
Map<String, Object> changes = new HashMap<>(); Map<String, Object> changes = new HashMap<>();
changes.put("externalId", txnExternalId); changes.put("externalId", externalId);
changes.put("amount", transactionAmount); changes.put("amount", transactionAmount);
changes.put("transactionDate", transactionDate); changes.put("transactionDate", transactionDate);
changes.put("locale", locale); changes.put("locale", locale);
return commandProcessingResultBuilder.withCommandId(command.commandId()) // return commandProcessingResultBuilder.withCommandId(command.commandId()) //
.withLoanId(loanId) // .withLoanId(loanId) //
.withEntityId(loanChargeId).withSubEntityId(loanTransaction.getId()) // .withEntityId(loanChargeId) //
.withEntityExternalId(loanCharge.getExternalId()) //
.withSubEntityId(loanTransaction.getId()) //
.withSubEntityExternalId(loanTransaction.getExternalId()) // .withSubEntityExternalId(loanTransaction.getExternalId()) //
.with(changes) // .with(changes) //
.build(); .build();
@@ -1008,7 +1024,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
businessEventNotifierService.notifyPreBusinessEvent(new LoanApplyOverdueChargeBusinessEvent(loan)); businessEventNotifierService.notifyPreBusinessEvent(new LoanApplyOverdueChargeBusinessEvent(loan));
for (Map.Entry<Integer, LocalDate> entry : scheduleDates.entrySet()) { 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) { if (BigDecimal.ZERO.compareTo(loanCharge.amount()) == 0) {
continue; continue;
@@ -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.CalendarRepository;
import org.apache.fineract.portfolio.calendar.domain.CalendarType; import org.apache.fineract.portfolio.calendar.domain.CalendarType;
import org.apache.fineract.portfolio.calendar.exception.CalendarParameterUpdateNotSupportedException; 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.domain.Client;
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException; import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement; 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.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService; import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; 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.LoanCollateralManagement;
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails; import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetailsRepository; import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetailsRepository;
@@ -209,8 +207,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final LoanTransactionRepository loanTransactionRepository; private final LoanTransactionRepository loanTransactionRepository;
private final LoanTransactionRelationRepository loanTransactionRelationRepository; private final LoanTransactionRelationRepository loanTransactionRelationRepository;
private final LoanAssembler loanAssembler; private final LoanAssembler loanAssembler;
private final ChargeRepositoryWrapper chargeRepository;
private final LoanChargeRepository loanChargeRepository;
private final JournalEntryWritePlatformService journalEntryWritePlatformService; private final JournalEntryWritePlatformService journalEntryWritePlatformService;
private final CalendarInstanceRepository calendarInstanceRepository; private final CalendarInstanceRepository calendarInstanceRepository;
private final PaymentDetailWritePlatformService paymentDetailWritePlatformService; private final PaymentDetailWritePlatformService paymentDetailWritePlatformService;
@@ -220,7 +216,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final AccountTransfersWritePlatformService accountTransfersWritePlatformService; private final AccountTransfersWritePlatformService accountTransfersWritePlatformService;
private final AccountTransfersReadPlatformService accountTransfersReadPlatformService; private final AccountTransfersReadPlatformService accountTransfersReadPlatformService;
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService; private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
private final LoanChargeReadPlatformService loanChargeReadPlatformService;
private final LoanReadPlatformService loanReadPlatformService; private final LoanReadPlatformService loanReadPlatformService;
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
private final AccountTransferRepository accountTransferRepository; private final AccountTransferRepository accountTransferRepository;
@@ -245,7 +240,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository; private final LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository;
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine; private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
private final LoanAccountLockService loanAccountLockService; private final LoanAccountLockService loanAccountLockService;
private final ExternalIdFactory externalIdFactory; private final ExternalIdFactory externalIdFactory;
@Transactional @Transactional
@@ -25,6 +25,7 @@ import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; 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.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType; import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode; import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
@@ -83,6 +84,6 @@ public class LoanTest {
private LoanCharge buildLoanCharge() { private LoanCharge buildLoanCharge() {
return new LoanCharge(mock(Loan.class), mock(Charge.class), new BigDecimal(100), new BigDecimal(100), 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, ChargeTimeType.TRANCHE_DISBURSEMENT, ChargeCalculationType.FLAT, LocalDate.of(2022, 6, 27), ChargePaymentMode.REGULAR, 1,
new BigDecimal(100)); new BigDecimal(100), ExternalId.generate());
} }
} }
@@ -30,14 +30,22 @@ import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeFormatterBuilder;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.UUID; 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.GetLoansLoanIdTransactionsTransactionIdResponse;
import org.apache.fineract.client.models.PostClientsResponse; import org.apache.fineract.client.models.PostClientsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest; import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest;
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse; 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.PostLoansLoanIdTransactionsRequest;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse; import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest; 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.client.IntegrationTest;
import org.apache.fineract.integrationtests.common.ClientHelper; import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper; import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
@@ -113,11 +121,38 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
LocalDate targetDate = LocalDate.of(2022, 9, 7); LocalDate targetDate = LocalDate.of(2022, 9, 7);
final String penaltyCharge1AddedDate = dateFormatter.format(targetDate); final String penaltyCharge1AddedDate = dateFormatter.format(targetDate);
String penalty1LoanChargeExternalId = UUID.randomUUID().toString();
Integer penalty1LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanId, Integer penalty1LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), penaltyCharge1AddedDate, "10")); LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), penaltyCharge1AddedDate, "10",
penalty1LoanChargeExternalId));
Integer penalty2LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanId, // Get loan charges
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), penaltyCharge1AddedDate, "10")); 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) // Check whether we can fetch transaction templates with proper result http code (HTTP 200..300)
ok(fineract().loanTransactions.retrieveTransactionTemplate1(loanExternalIdStr, "repayment", null, null, null)); ok(fineract().loanTransactions.retrieveTransactionTemplate1(loanExternalIdStr, "repayment", null, null, null));
@@ -135,6 +170,7 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
PostLoansLoanIdChargesChargeIdResponse waiveLoanChargeResult = loanTransactionHelper.waiveLoanCharge((long) loanId, PostLoansLoanIdChargesChargeIdResponse waiveLoanChargeResult = loanTransactionHelper.waiveLoanCharge((long) loanId,
(long) penalty1LoanChargeId, new PostLoansLoanIdChargesChargeIdRequest().externalId(waiveChargeExternalIdStr)); (long) penalty1LoanChargeId, new PostLoansLoanIdChargesChargeIdRequest().externalId(waiveChargeExternalIdStr));
assertEquals(waiveChargeExternalIdStr, waiveLoanChargeResult.getSubResourceExternalId()); assertEquals(waiveChargeExternalIdStr, waiveLoanChargeResult.getSubResourceExternalId());
assertEquals(penalty1LoanChargeExternalId, waiveLoanChargeResult.getResourceExternalId());
GetLoansLoanIdTransactionsTransactionIdResponse response = loanTransactionHelper.getLoanTransactionDetails((long) loanId, GetLoansLoanIdTransactionsTransactionIdResponse response = loanTransactionHelper.getLoanTransactionDetails((long) loanId,
waiveChargeExternalIdStr); waiveChargeExternalIdStr);
@@ -157,9 +193,10 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
assertEquals(waiveChargeExternalIdStr, response.getExternalId()); assertEquals(waiveChargeExternalIdStr, response.getExternalId());
// Check whether an external id was generated // Check whether an external id was generated
waiveLoanChargeResult = loanTransactionHelper.waiveLoanCharge((long) loanId, (long) penalty1LoanChargeId, waiveLoanChargeResult = loanTransactionHelper.waiveLoanCharge(loanExternalIdStr, (long) penalty1LoanChargeId,
new PostLoansLoanIdChargesChargeIdRequest()); new PostLoansLoanIdChargesChargeIdRequest());
assertNotNull(waiveLoanChargeResult.getSubResourceExternalId()); assertNotNull(waiveLoanChargeResult.getSubResourceExternalId());
assertEquals(penalty1LoanChargeExternalId, waiveLoanChargeResult.getResourceExternalId());
// Check whether an external id was generated // Check whether an external id was generated
undoWaiveLoanChargeResult = loanTransactionHelper.undoWaiveLoanCharge(loanExternalIdStr, waiveLoanChargeResult.getSubResourceId()); undoWaiveLoanChargeResult = loanTransactionHelper.undoWaiveLoanCharge(loanExternalIdStr, waiveLoanChargeResult.getSubResourceId());
@@ -167,9 +204,10 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
// Check whether an external id was generated // Check whether an external id was generated
waiveChargeExternalIdStr = UUID.randomUUID().toString(); waiveChargeExternalIdStr = UUID.randomUUID().toString();
waiveLoanChargeResult = loanTransactionHelper.waiveLoanCharge((long) loanId, (long) penalty1LoanChargeId, waiveLoanChargeResult = loanTransactionHelper.waiveLoanCharge(loanExternalIdStr, penalty1LoanChargeExternalId,
new PostLoansLoanIdChargesChargeIdRequest().externalId(waiveChargeExternalIdStr)); new PostLoansLoanIdChargesChargeIdRequest().externalId(waiveChargeExternalIdStr));
assertEquals(waiveChargeExternalIdStr, waiveLoanChargeResult.getSubResourceExternalId()); assertEquals(waiveChargeExternalIdStr, waiveLoanChargeResult.getSubResourceExternalId());
assertEquals(penalty1LoanChargeExternalId, waiveLoanChargeResult.getResourceExternalId());
// Check whether an external id was generated // Check whether an external id was generated
undoWaiveLoanChargeResult = loanTransactionHelper.undoWaiveLoanCharge(loanExternalIdStr, undoWaiveLoanChargeResult = loanTransactionHelper.undoWaiveLoanCharge(loanExternalIdStr,
@@ -389,7 +427,73 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
final HashMap loanWithInterest = this.loanTransactionHelper.getLoanId(loanApplicationJSON, ""); final HashMap loanWithInterest = this.loanTransactionHelper.getLoanId(loanApplicationJSON, "");
Integer loanWithInterestId = (Integer) loanWithInterest.get("resourceId"); 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); this.loanTransactionHelper.approveLoan(formattedDate, loanWithInterestId);
final HashMap disbursedLoanWithInterestResult = this.loanTransactionHelper.disburseLoan(formattedDate, loanWithInterestId, "1000", final HashMap disbursedLoanWithInterestResult = this.loanTransactionHelper.disburseLoan(formattedDate, loanWithInterestId, "1000",
null); null);
// Check whether an external id was generated // Check whether an external id was generated
@@ -397,14 +501,16 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
LocalDate aMonthBeforePlus3Days = aMonthBefore.plusDays(3); LocalDate aMonthBeforePlus3Days = aMonthBefore.plusDays(3);
formattedDate = dateFormatter.format(aMonthBeforePlus3Days); formattedDate = dateFormatter.format(aMonthBeforePlus3Days);
Integer penalty3LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanWithInterestId, String penalty3LoanChargeExternalId = UUID.randomUUID().toString();
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), formattedDate, "10")); Integer penalty3LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanWithInterestId, LoanTransactionHelper
.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), formattedDate, "10", penalty3LoanChargeExternalId));
Integer penalty4LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanWithInterestId, Integer penalty4LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanWithInterestId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty2), formattedDate, "1000")); LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty2), formattedDate, "1000"));
Integer penalty5LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanWithInterestId, String penalty5LoanChargeExternalId = UUID.randomUUID().toString();
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty2), formattedDate, "1000")); Integer penalty5LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanWithInterestId, LoanTransactionHelper
.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty2), formattedDate, "1000", penalty5LoanChargeExternalId));
// Check whether an external id was generated // Check whether an external id was generated
final PostLoansLoanIdTransactionsResponse waiveInterestResult = loanTransactionHelper.makeWaiveInterest(loanExternalIdStr, final PostLoansLoanIdTransactionsResponse waiveInterestResult = loanTransactionHelper.makeWaiveInterest(loanExternalIdStr,
@@ -520,6 +626,7 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
(long) penalty3LoanChargeId, (long) penalty3LoanChargeId,
new PostLoansLoanIdChargesChargeIdRequest().externalId(chargeAdjustmentExternalIdStr).amount(1.0).locale("en")); new PostLoansLoanIdChargesChargeIdRequest().externalId(chargeAdjustmentExternalIdStr).amount(1.0).locale("en"));
assertEquals(chargeAdjustmentExternalIdStr, chargeAdjustmentResult.getSubResourceExternalId()); assertEquals(chargeAdjustmentExternalIdStr, chargeAdjustmentResult.getSubResourceExternalId());
assertEquals(penalty3LoanChargeExternalId, chargeAdjustmentResult.getResourceExternalId());
response = loanTransactionHelper.getLoanTransactionDetails((long) loanWithInterestId, chargeAdjustmentExternalIdStr); response = loanTransactionHelper.getLoanTransactionDetails((long) loanWithInterestId, chargeAdjustmentExternalIdStr);
assertEquals(chargeAdjustmentExternalIdStr, response.getExternalId()); assertEquals(chargeAdjustmentExternalIdStr, response.getExternalId());
@@ -529,23 +636,27 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
assertEquals(chargeAdjustmentExternalIdStr, response.getExternalId()); assertEquals(chargeAdjustmentExternalIdStr, response.getExternalId());
// Check whether an external id was generated // 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")); new PostLoansLoanIdChargesChargeIdRequest().amount(1.0).locale("en"));
assertNotNull(chargeAdjustmentResult.getSubResourceExternalId()); assertNotNull(chargeAdjustmentResult.getSubResourceExternalId());
assertEquals(penalty3LoanChargeExternalId, chargeAdjustmentResult.getResourceExternalId());
HashMap payChargeResult = this.loanTransactionHelper.payChargesForLoan(loanWithInterestId, penalty4LoanChargeId, PostLoansLoanIdChargesChargeIdResponse payChargeResult = this.loanTransactionHelper.payLoanCharge(loanExternalIdStr,
LoanTransactionHelper.getPayChargeJSON(formattedDate, null), ""); (long) penalty4LoanChargeId,
assertNotNull(payChargeResult.get("subResourceExternalId")); new PostLoansLoanIdChargesChargeIdRequest().locale("en").dateFormat("dd MMMM yyyy").transactionDate(formattedDate));
assertNotNull(payChargeResult.getSubResourceExternalId());
assertNotNull(payChargeResult.getResourceExternalId());
String payChargeExternalIdStr = UUID.randomUUID().toString(); String payChargeExternalIdStr = UUID.randomUUID().toString();
payChargeResult = this.loanTransactionHelper.payChargesForLoan(loanWithInterestId, penalty5LoanChargeId, payChargeResult = this.loanTransactionHelper.payLoanCharge(loanExternalIdStr, penalty5LoanChargeExternalId,
LoanTransactionHelper.getPayChargeJSON(formattedDate, null, payChargeExternalIdStr), ""); new PostLoansLoanIdChargesChargeIdRequest().locale("en").dateFormat("dd MMMM yyyy").transactionDate(formattedDate)
assertEquals(payChargeExternalIdStr, payChargeResult.get("subResourceExternalId")); .externalId(payChargeExternalIdStr));
assertEquals(payChargeExternalIdStr, payChargeResult.getSubResourceExternalId());
assertEquals(penalty5LoanChargeExternalId, payChargeResult.getResourceExternalId());
response = loanTransactionHelper.getLoanTransactionDetails((long) loanWithInterestId, payChargeExternalIdStr); response = loanTransactionHelper.getLoanTransactionDetails((long) loanWithInterestId, payChargeExternalIdStr);
assertEquals(payChargeExternalIdStr, response.getExternalId()); assertEquals(payChargeExternalIdStr, response.getExternalId());
response = loanTransactionHelper.getLoanTransactionDetails(loanExternalIdStr, response = loanTransactionHelper.getLoanTransactionDetails(loanExternalIdStr, payChargeResult.getSubResourceId());
Long.valueOf(payChargeResult.get("subResourceId").toString()));
assertEquals(payChargeExternalIdStr, response.getExternalId()); assertEquals(payChargeExternalIdStr, response.getExternalId());
response = loanTransactionHelper.getLoanTransactionDetails(loanExternalIdStr, payChargeExternalIdStr); response = loanTransactionHelper.getLoanTransactionDetails(loanExternalIdStr, payChargeExternalIdStr);
assertEquals(payChargeExternalIdStr, response.getExternalId()); assertEquals(payChargeExternalIdStr, response.getExternalId());
@@ -42,8 +42,11 @@ import java.util.Map;
import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import lombok.extern.slf4j.Slf4j; 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.GetDelinquencyTagHistoryResponse;
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse; 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.GetLoansLoanIdCollectionData;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod; import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule; 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.GetPaymentTypesResponse;
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest; import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest;
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse; 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.PostLoansLoanIdChargesResponse;
import org.apache.fineract.client.models.PostLoansLoanIdResponse; import org.apache.fineract.client.models.PostLoansLoanIdResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest; import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse; import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest; 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.models.PutLoansLoanIdResponse;
import org.apache.fineract.client.util.JSON; import org.apache.fineract.client.util.JSON;
import org.apache.fineract.integrationtests.client.IntegrationTest; 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, ""); 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) { public List getRepaymentTemplate(final Integer loanId) {
final String GET_REPAYMENTS_URL = "/fineract-provider/api/v1/loans/" + loanId + "/transactions/template?command=repayment&" final String GET_REPAYMENTS_URL = "/fineract-provider/api/v1/loans/" + loanId + "/transactions/template?command=repayment&"
+ Utils.TENANT_IDENTIFIER; + Utils.TENANT_IDENTIFIER;
@@ -503,9 +525,24 @@ public class LoanTransactionHelper extends IntegrationTest {
return Utils.performServerPut(requestSpec, responseSpec, TRANSAC_URL, "", ""); 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) { final PostLoansLoanIdChargesChargeIdRequest request) {
return ok(fineract().loanCharges.executeLoanCharge1(loanId, chargeId, request, "waive")); 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.executeLoanCharge5(loanExternalId, loanChargeExternalId, request, "waive"));
} }
public PostLoansLoanIdTransactionsResponse makeLoanRepayment(final String loanExternalId, public PostLoansLoanIdTransactionsResponse makeLoanRepayment(final String loanExternalId,
@@ -674,6 +711,14 @@ public class LoanTransactionHelper extends IntegrationTest {
getWithdrawLoanApplicationBodyAsJSON(date)); 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) { public Integer addChargesForLoan(final Integer loanId, final String request) {
log.info("--------------------------------- ADD CHARGES FOR LOAN --------------------------------"); log.info("--------------------------------- ADD CHARGES FOR LOAN --------------------------------");
final String ADD_CHARGES_URL = LOAN_ACCOUNT_URL + "/" + loanId + "/charges?" + Utils.TENANT_IDENTIFIER; 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"); 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) { public Integer deleteChargesForLoan(final Integer loanId, final Integer loanchargeId) {
log.info("--------------------------------- DELETE CHARGES FOR LOAN --------------------------------"); log.info("--------------------------------- DELETE CHARGES FOR LOAN --------------------------------");
final String DELETE_CHARGES_URL = "/fineract-provider/api/v1/loans/" + loanId + "/charges/" + loanchargeId + "?" 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, public PostLoansLoanIdChargesChargeIdResponse chargeAdjustment(final Long loanId, final Long chargeId,
final PostLoansLoanIdChargesChargeIdRequest request) { 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) { 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"); return (Integer) response.get("resourceId");
} }
public HashMap payChargesForLoan(final Integer loanId, final Integer loanchargeId, final String json, final String responseAttribute) { public PostLoansLoanIdChargesChargeIdResponse payLoanCharge(final Long loanId, final Long loanChargeId,
log.info("--------------------------------- WAIVE CHARGES FOR LOAN --------------------------------"); final PostLoansLoanIdChargesChargeIdRequest request) {
final String CHARGES_URL = "/fineract-provider/api/v1/loans/" + loanId + "/charges/" + loanchargeId + "?command=pay&" return ok(fineract().loanCharges.executeLoanCharge2(loanId, loanChargeId, request, "pay"));
+ Utils.TENANT_IDENTIFIER; }
return Utils.performServerPost(requestSpec, responseSpec, CHARGES_URL, json, responseAttribute);
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, 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, ""); 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) { 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 + "?" final String GET_LOAN_CHARGES_URL = "/fineract-provider/api/v1/loans/" + loanId + "/transactions/" + txnId + "?"
+ Utils.TENANT_IDENTIFIER; + Utils.TENANT_IDENTIFIER;
@@ -997,16 +1107,24 @@ public class LoanTransactionHelper extends IntegrationTest {
} }
public static String getSpecifiedDueDateChargesForLoanAsJSON(final String chargeId) { 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) { 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<>(); final HashMap<String, String> map = new HashMap<>();
map.put("locale", "en_GB"); map.put("locale", "en_GB");
map.put("dateFormat", "dd MMMM yyyy"); map.put("dateFormat", "dd MMMM yyyy");
map.put("amount", amount); map.put("amount", amount);
map.put("dueDate", dueDate); map.put("dueDate", dueDate);
map.put("chargeId", chargeId); map.put("chargeId", chargeId);
if (externalId != null) {
map.put("externalId", externalId);
}
String json = new Gson().toJson(map); String json = new Gson().toJson(map);
log.info("{}", json); log.info("{}", json);
return json; return json;