mariiaKraievska commented on code in PR #6143:
URL: https://github.com/apache/fineract/pull/6143#discussion_r3690892224
##########
fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/serialization/WorkingCapitalLoanDataValidator.java:
##########
@@ -952,4 +959,101 @@ public void validateUndoTransaction(JsonCommand command,
WorkingCapitalLoan loan
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
+
+ public void validateChargeOff(final JsonCommand command, final
WorkingCapitalLoan loan) {
+ final String json = command.getJsonCommand();
+ if (StringUtils.isBlank(json)) {
+ throw new InvalidJsonException();
+ }
+ final Type typeOfMap = new TypeToken<Map<String, Object>>()
{}.getType();
+ this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
CHARGE_OFF_SUPPORTED_PARAMETERS);
+
+ final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+ final DataValidatorBuilder baseDataValidator = new
DataValidatorBuilder(dataValidationErrors)
+ .resource(WorkingCapitalLoanConstants.RESOURCE_NAME);
+ final JsonElement element = this.fromApiJsonHelper.parse(json);
+
+ // The loan must be active; charge-off keeps it ACTIVE and has no
portfolio impact.
+ if (!LoanStatus.ACTIVE.equals(loan.getLoanStatus())) {
+
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.loanStatusParamName)
+ .failWithCode("error.msg.wc.loan.is.not.active");
+ }
+ // A loan cannot be charged off twice.
+ if (loan.isChargedOff()) {
+
baseDataValidator.reset().parameter("chargedOff").failWithCode("error.msg.wc.loan.is.already.charged.off");
+ }
+
+ final LocalDate transactionDate =
this.fromApiJsonHelper.extractLocalDateNamed(WorkingCapitalLoanConstants.transactionDateParamName,
+ element);
+
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.transactionDateParamName).value(transactionDate).notNull();
+ if (transactionDate != null) {
+ // Charge-off can be backdated, but not into the future nor before
the last transaction.
+ if (DateUtils.isDateInTheFuture(transactionDate)) {
+
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.transactionDateParamName).value(transactionDate)
+ .failWithCode("cannot.be.a.future.date");
+ }
+ final LocalDate lastTransactionDate =
findLastTransactionDate(loan);
+ if (lastTransactionDate != null &&
DateUtils.isBefore(transactionDate, lastTransactionDate)) {
+
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.transactionDateParamName).value(transactionDate)
+
.failWithCode("cannot.be.before.last.transaction.date");
+ }
+ }
+
+ // Charge-off reason is optional.
+ final Long chargeOffReasonId =
this.fromApiJsonHelper.extractLongNamed(WorkingCapitalLoanConstants.chargeOffReasonIdParamName,
+ element);
+
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.chargeOffReasonIdParamName).value(chargeOffReasonId).ignoreIfNull()
+ .integerGreaterThanZero();
+
+ final String note =
this.fromApiJsonHelper.extractStringNamed(WorkingCapitalLoanConstants.noteParamName,
element);
+
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.noteParamName).value(note).ignoreIfNull()
+ .notExceedingLengthOf(1000);
+
+ validateTransactionExternalId(baseDataValidator, element,
WorkingCapitalLoanConstants.externalIdParameterName);
+
+ throwExceptionIfValidationWarningsExist(dataValidationErrors);
+ }
+
+ public void validateUndoChargeOff(final JsonCommand command, final
WorkingCapitalLoan loan) {
+ final String json = command.getJsonCommand();
+ final boolean hasBody = StringUtils.isNotBlank(json);
+ if (hasBody) {
+ final Type typeOfMap = new TypeToken<Map<String, Object>>()
{}.getType();
+ this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap,
json, UNDO_CHARGE_OFF_SUPPORTED_PARAMETERS);
+ }
+
+ final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+ final DataValidatorBuilder baseDataValidator = new
DataValidatorBuilder(dataValidationErrors)
+ .resource(WorkingCapitalLoanConstants.RESOURCE_NAME);
+
+ if (!loan.isChargedOff()) {
+
baseDataValidator.reset().parameter("chargedOff").failWithCode("error.msg.wc.loan.is.not.charged.off");
+ } else {
+ // Undo is only allowed when no monetary transaction has been
posted after the charge-off date.
+ final LocalDate chargeOffDate = loan.getChargedOffOnDate();
+ final boolean hasMonetaryActivityAfterChargeOff =
this.transactionRepository
+
.findByWcLoan_IdOrderByTransactionDateAscIdAsc(loan.getId()).stream().filter(t
-> !t.isReversed())
+ .filter(t -> t.getTypeOf().isRepaymentType())
+ .anyMatch(t -> chargeOffDate != null &&
DateUtils.isAfter(t.getTransactionDate(), chargeOffDate));
+ if (hasMonetaryActivityAfterChargeOff) {
+
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.transactionDateParamName)
+
.failWithCode("error.msg.wc.loan.charge.off.is.not.the.last.transaction");
+ }
+ }
+
+ if (hasBody) {
+ final JsonElement element = this.fromApiJsonHelper.parse(json);
+ validateTransactionExternalId(baseDataValidator, element,
WorkingCapitalLoanConstants.reversalExternalIdParamName);
+ final String note =
this.fromApiJsonHelper.extractStringNamed(WorkingCapitalLoanConstants.noteParamName,
element);
+
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.noteParamName).value(note).ignoreIfNull()
+ .notExceedingLengthOf(1000);
+ }
+
+ throwExceptionIfValidationWarningsExist(dataValidationErrors);
+ }
+
+ private LocalDate findLastTransactionDate(final WorkingCapitalLoan loan) {
+ return
this.transactionRepository.findByWcLoan_IdOrderByTransactionDateAscIdAsc(loan.getId()).stream().filter(t
-> !t.isReversed())
+
.map(WorkingCapitalLoanTransaction::getTransactionDate).max(LocalDate::compareTo).orElse(null);
Review Comment:
findLastTransactionDate includes system transactions (eg.
accrual/amortization). After COB, that can push the earliest allowed charge-off
date forward even when there was no user activity. Term/progressive loans use
last user transaction date instead. Is including system txns intentional here?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]