budaidev commented on code in PR #5760:
URL: https://github.com/apache/fineract/pull/5760#discussion_r3079653379
##########
fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedAmortizationScheduleModel.java:
##########
@@ -184,6 +203,37 @@ public ProjectedAmortizationScheduleModel regenerate(final
BigDecimal newDiscoun
return newModel;
}
+ public void recalculateNetAmortizationAndDeferredBalanceFrom(final
LocalDate repaymentDate) {
+ if (repaymentDate == null || payments == null || payments.isEmpty()) {
+ return;
+ }
+ final ProjectedPayment lastRepayment = payments.stream().filter(p ->
p.paymentNo() > 0).filter(p -> repaymentDate.equals(p.date()))
+ .reduce((a, b) -> b).orElse(null);
+
+ if (lastRepayment == null) {
+ return;
Review Comment:
minor: maybe we can add a warn log here, since it's unexpected at this point
##########
fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalLoanAccountStepDef.java:
##########
@@ -1309,4 +1315,143 @@ private Long
createWorkingCapitalProductForBreachOverride(final boolean breachOv
() ->
fineractClient.workingCapitalLoanProducts().createWorkingCapitalLoanProduct(productRequest,
Map.of()));
return productResponse.getResourceId();
}
+
+ @Then("Customer makes repayment on {string} with {double} transaction
amount on Working Capital loan")
+ public void makeWorkingCapitalLoanRepayment(final String transactionDate,
final double transactionAmount) {
+ final Long loanId = getCreatedLoanId();
+ final PostWorkingCapitalLoanTransactionsRequest repaymentRequest =
buildRepaymentRequest(transactionDate, transactionAmount, null);
+ final PostWorkingCapitalLoanTransactionsResponse response =
executeRepaymentById(loanId, repaymentRequest);
+ validateRepaymentResponse(response, transactionAmount,
transactionDate, loanId);
+ }
+
+ @Then("Customer makes repayment by loan external ID on {string} with
{double} transaction amount on Working Capital loan")
+ public void makeWorkingCapitalLoanRepaymentByExternalId(final String
transactionDate, final double transactionAmount) {
+ final Long loanId = getCreatedLoanId();
+ final String loanExternalId = retrieveLoanExternalId(loanId);
+ final PostWorkingCapitalLoanTransactionsRequest repaymentRequest =
buildRepaymentRequest(transactionDate, transactionAmount, null);
+ final PostWorkingCapitalLoanTransactionsResponse response =
executeRepaymentByExternalId(loanExternalId, repaymentRequest);
+ validateRepaymentResponse(response, transactionAmount,
transactionDate, loanExternalId);
+ }
+
+ @Then("Customer makes repayment on {string} with {double} transaction
amount on Working Capital loan with the following payment details:")
+ public void makeWorkingCapitalLoanRepaymentWithPaymentDetails(final String
transactionDate, final double transactionAmount,
+ final DataTable table) {
+ final Long loanId = getCreatedLoanId();
+ final PostWorkingCapitalLoanTransactionsPaymentDetailRequest
paymentDetails = buildPaymentDetailsFromTable(table);
+ final PostWorkingCapitalLoanTransactionsRequest repaymentRequest =
buildRepaymentRequest(transactionDate, transactionAmount,
+ paymentDetails);
+ final PostWorkingCapitalLoanTransactionsResponse response =
executeRepaymentById(loanId, repaymentRequest);
+ validateRepaymentResponse(response, transactionAmount,
transactionDate, loanId);
+ }
+
+ private PostWorkingCapitalLoanTransactionsRequest
buildRepaymentRequest(final String transactionDate, final double
transactionAmount,
+ final PostWorkingCapitalLoanTransactionsPaymentDetailRequest
paymentDetails) {
+ final PostWorkingCapitalLoanTransactionsRequest request =
workingCapitalProductRequestFactory
+
.defaultWorkingCapitalLoanRepaymentRequest().transactionDate(transactionDate)
+ .transactionAmount(BigDecimal.valueOf(transactionAmount));
+
+ if (paymentDetails != null) {
+ request.paymentDetails(paymentDetails);
+ }
+
+ return request;
+ }
+
+ private PostWorkingCapitalLoanTransactionsResponse
executeRepaymentById(final Long loanId,
+ final PostWorkingCapitalLoanTransactionsRequest repaymentRequest) {
+ log.debug("Making repayment for loan ID: {}, transactionDate: {},
transactionAmount: {}", loanId,
+ repaymentRequest.getTransactionDate(),
repaymentRequest.getTransactionAmount());
+
+ return ok(() ->
fineractClient.workingCapitalLoanTransactions().executeWorkingCapitalLoanTransactionById(loanId,
"repayment",
+ repaymentRequest));
+ }
+
+ private PostWorkingCapitalLoanTransactionsResponse
executeRepaymentByExternalId(final String loanExternalId,
+ final PostWorkingCapitalLoanTransactionsRequest repaymentRequest) {
+ log.debug("Making repayment for loan externalId: {}, transactionDate:
{}, transactionAmount: {}", loanExternalId,
+ repaymentRequest.getTransactionDate(),
repaymentRequest.getTransactionAmount());
+
+ return ok(() ->
fineractClient.workingCapitalLoanTransactions().executeWorkingCapitalLoanTransactionByExternalId(loanExternalId,
+ "repayment", repaymentRequest));
+ }
+
+ private void validateRepaymentResponse(final
PostWorkingCapitalLoanTransactionsResponse response, final double
transactionAmount,
+ final String transactionDate, final Object loanIdentifier) {
+ assertNotNull(response, "Repayment response should not be null");
+ assertNotNull(response.getResourceId(), "Repayment transaction ID
should not be null");
+ log.debug("Working Capital loan repayment of {} made on {} for loan
{}, transaction ID: {}", transactionAmount, transactionDate,
+ loanIdentifier, response.getResourceId());
+ }
+
+ private PostWorkingCapitalLoanTransactionsPaymentDetailRequest
buildPaymentDetailsFromTable(final DataTable table) {
+ final Map<String, String> paymentDetailsMap =
convertDataTableToMap(table);
+ return buildPaymentDetailsObject(paymentDetailsMap);
+ }
+
+ private Map<String, String> convertDataTableToMap(final DataTable table) {
+ final List<List<String>> rows = table.asLists(String.class);
+ final List<String> headers = rows.get(0);
+ final List<String> values = rows.get(1);
+
+ final Map<String, String> map = new java.util.HashMap<>();
Review Comment:
you can just import it, no need for full qualified name
##########
fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWritePlatformServiceImpl.java:
##########
@@ -415,6 +418,77 @@ public CommandProcessingResult updateDiscount(final Long
loanId, final JsonComma
.withLoanId(loanId).with(changes).build();
}
+ @Override
+ public CommandProcessingResult makeRepayment(final Long loanId, final
JsonCommand command) {
+ final WorkingCapitalLoan loan = this.loanRepository.findById(loanId)
+ .orElseThrow(() -> new
WorkingCapitalLoanNotFoundException(loanId));
+ this.validator.validateRepayment(command.json(), loan);
+
+ if (loan.getLoanStatus() != LoanStatus.ACTIVE && loan.getLoanStatus()
!= LoanStatus.OVERPAID) {
+ throw new
PlatformApiDataValidationException("validation.msg.wc.loan.transition.not.allowed",
+ "Repayment is allowed only for active/overpaid loans",
"loanStatus");
+ }
+
+ final LocalDate transactionDate =
command.localDateValueOfParameterNamed(WorkingCapitalLoanConstants.transactionDateParamName);
+ final BigDecimal transactionAmount = this.fromApiJsonHelper
+
.extractBigDecimalNamed(WorkingCapitalLoanConstants.transactionAmountParamName,
command.parsedJson(), new HashSet<>());
+ final Map<String, Object> changes = new LinkedHashMap<>();
+ changes.put(WorkingCapitalLoanConstants.transactionDateParamName,
transactionDate);
+ changes.put(WorkingCapitalLoanConstants.transactionAmountParamName,
transactionAmount);
+ final PaymentDetail paymentDetail =
createAndPersistPaymentDetailFromCommand(command, changes);
+
+ final Long classificationId =
command.longValueOfParameterNamed(WorkingCapitalLoanConstants.classificationIdParamName);
+ final CodeValue classification = classificationId != null
+ ?
codeValueRepository.findByCodeNameAndId(WorkingCapitalLoanConstants.REPAYMENT_CLASSIFICATION_CODE_NAME,
classificationId)
+ : null;
+ changes.put(WorkingCapitalLoanConstants.classificationIdParamName,
classificationId);
+
+ final ExternalId txnExternalId =
this.externalIdFactory.createFromCommand(command,
+ WorkingCapitalLoanConstants.externalIdParameterName);
+ final WorkingCapitalLoanTransaction repaymentTransaction =
WorkingCapitalLoanTransaction.repayment(loan, transactionAmount,
+ paymentDetail, transactionDate, classification, txnExternalId);
+ this.transactionRepository.saveAndFlush(repaymentTransaction);
+
+ final WorkingCapitalLoanTransactionAllocation allocation =
WorkingCapitalLoanTransactionAllocation
+ .forPrincipalAllocation(repaymentTransaction,
transactionAmount);
+ this.allocationRepository.saveAndFlush(allocation);
+
+ final RepaymentAmortizationData amortizationData =
amortizationScheduleWriteService.applyRepayment(loan, transactionDate,
+ transactionAmount);
+ updateBalanceOnRepayment(loan, transactionAmount, amortizationData);
+ internalWorkingCapitalLoanPaymentService.makePayment(loanId,
transactionAmount, transactionDate);
+
+ if (loan.getBalance() != null &&
loan.getBalance().getPrincipalOutstanding() != null) {
Review Comment:
when setting the status should we use
WorkingCapitalLoanLifecycleStateMachine? Migth be more consistent
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]