Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1102,34 +1102,10 @@ public static String undoDisbursalDisallowedFailure(String status) {
return String.format("Transition LOAN_DISBURSAL_UNDO is not allowed from status %s", status);
}

public static String discountAmountExceedFailure() {
return "Failed data validation due to: amount.cannot.exceed.created.discount.";
}

public static String discountAmountExceedApprovedFailure() {
return "Failed data validation due to: amount.cannot.exceed.approved.discount.";
}

public static String discountAlreadySetBeforeDisburseFailure() {
return "Discount was already set before disbursement and cannot be added again";
}

public static String discountDiffDateFromDisburseFailure() {
return "Failed data validation due to: transaction.date.must.be.equal.disbursement.date.";
}

public static String overrideDisallowedByProductFailure() {
return "Failed data validation due to: override.not.allowed.by.product.";
}

public static String discountExceedCreatedDiscountFailure() {
return "Failed data validation due to: amount.cannot.exceed.created.discount.";
}

public static String discountExceedProductDiscountFailure() {
return "Failed data validation due to: amount.cannot.exceed.product.discount.";
}

public static String nearBreachCannotEnableWithoutBreachFailure() {
return "Failed data validation due to: cannot.enable.near.breach.without.breach.";
}
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -368,5 +368,8 @@ public abstract class TestContextKey {
public static final String WORKING_CAPITAL_LOAN_RATE_CHANGE_ID = "wcLoanRateChangeId";
public static final String WORKING_CAPITAL_CHARGE_ID = "workingCapitalChargeId";
public static final String WORKING_CAPITAL_CHARGE_TEMPLATE = "workingCapitalChargeTemplate";
public static final String WORKING_CAPITAL_LOAN_DISBURSE_DISCOUNT_EXTERNAL_ID_USER_GENERATED = "workingCapitalLoanDisburseDiscountExternalIdUserGenerated";
public static final String WORKING_CAPITAL_LOAN_DISCOUNT_FEE_EXTERNAL_ID_USER_GENERATED = "workingCapitalLoanDiscountFeeExternalIdUserGenerated";
public static final String WORKING_CAPITAL_LOAN_DISCOUNT_FEE_RESPONSE = "workingCapitalLoanDiscountFeeResponse";
public static final String LAST_SAVINGS_ACCOUNT_ID = "lastSavingsAccountId";
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ private WorkingCapitalLoanConstants() {
public static final String approvedLoanAmountParamName = "approvedLoanAmount";
public static final String expectedDisbursementDateParamName = "expectedDisbursementDate";
public static final String discountAmountParamName = "discountAmount";
public static final String discountExternalIdParameterName = "discountExternalId";
public static final String noteParamName = "note";
public static final String rejectedOnDateParamName = "rejectedOnDate";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,8 @@ private PostWorkingCapitalLoansLoanIdRequest() {}
public Long classificationId;
@Schema(example = "ext-disburse-001", description = "External ID; optional for disburse")
public String externalId;
@Schema(example = "ext-discount-001", description = "External ID for the discount fee transaction created during disburse; optional. Only accepted when discountAmount is greater than 0. When omitted and auto-generation is enabled, a UUID is generated.")
public String discountExternalId;
@Schema(description = "Payment details (Account No, Cheque No, Routing Code, Receipt No, Bank code)")
public PostWorkingCapitalLoansLoanIdDisbursementPaymentDetails paymentDetails;
@Schema(description = "Related resource ID for transaction, e.g., related transaction ID")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ public class WorkingCapitalLoanDataValidator {
private static final Set<String> UNDO_APPROVAL_SUPPORTED_PARAMETERS = new HashSet<>(
Arrays.asList("locale", "dateFormat", WorkingCapitalLoanConstants.noteParamName));

private static final Set<String> DISBURSAL_SUPPORTED_PARAMETERS = new HashSet<>(
Arrays.asList("locale", "dateFormat", WorkingCapitalLoanConstants.actualDisbursementDateParamName,
WorkingCapitalLoanConstants.transactionAmountParamName, WorkingCapitalLoanConstants.discountAmountParamName,
WorkingCapitalLoanConstants.noteParamName, WorkingCapitalLoanConstants.paymentDetailsParamName,
WorkingCapitalLoanConstants.externalIdParameterName, WorkingCapitalLoanConstants.classificationIdParamName));
private static final Set<String> DISBURSAL_SUPPORTED_PARAMETERS = new HashSet<>(Arrays.asList("locale", "dateFormat",
WorkingCapitalLoanConstants.actualDisbursementDateParamName, WorkingCapitalLoanConstants.transactionAmountParamName,
WorkingCapitalLoanConstants.discountAmountParamName, WorkingCapitalLoanConstants.noteParamName,
WorkingCapitalLoanConstants.paymentDetailsParamName, WorkingCapitalLoanConstants.externalIdParameterName,
WorkingCapitalLoanConstants.discountExternalIdParameterName, WorkingCapitalLoanConstants.classificationIdParamName));

private static final Set<String> PAYMENT_DETAILS_SUPPORTED_PARAMETERS = new HashSet<>(
Arrays.asList(WorkingCapitalLoanConstants.paymentTypeIdParamName, WorkingCapitalLoanConstants.accountNumberParamName,
Expand All @@ -94,7 +94,7 @@ public class WorkingCapitalLoanDataValidator {
Arrays.asList("locale", "dateFormat", WorkingCapitalLoanConstants.noteParamName,
WorkingCapitalLoanConstants.transactionAmountParamName, WorkingCapitalLoanConstants.classificationIdParamName,
WorkingCapitalLoanConstants.relatedResourceIdParamName, WorkingCapitalLoanConstants.paymentDetailsParamName,
WorkingCapitalLoanConstants.noteParamName, WorkingCapitalLoanConstants.transactionDateParamName));
WorkingCapitalLoanConstants.transactionDateParamName, WorkingCapitalLoanConstants.externalIdParameterName));
private static final Set<String> CREDIT_BALANCE_REFUND_SUPPORTED_PARAMETERS = new HashSet<>(REPAYMENT_SUPPORTED_PARAMETERS);

private static final Set<String> UPDATE_RATE_SUPPORTED_PARAMETERS = new HashSet<>(
Expand All @@ -119,6 +119,8 @@ public void validateDiscountTransaction(final WorkingCapitalLoan loan, final Str
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(WorkingCapitalLoanConstants.RESOURCE_NAME);

final JsonElement element = this.fromApiJsonHelper.parse(json);

if (isDiscountOverrideAllowed(loan)) {
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.discountAmountParamName)
.failWithCode("override.not.allowed.by.product");
Expand Down Expand Up @@ -149,6 +151,8 @@ public void validateDiscountTransaction(final WorkingCapitalLoan loan, final Str
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.noteParamName).value(note).ignoreIfNull()
.notExceedingLengthOf(NOTE_MAX_LENGTH);

validateTransactionExternalId(baseDataValidator, element, WorkingCapitalLoanConstants.externalIdParameterName);

throwExceptionIfValidationWarningsExist(dataValidationErrors);
}

Expand Down Expand Up @@ -202,7 +206,7 @@ public void validateApproval(final String json, final WorkingCapitalLoan loan) {
.extractLocalDateNamed(WorkingCapitalLoanConstants.expectedDisbursementDateParamName, element);
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.expectedDisbursementDateParamName).value(expectedDisbursementDate)
.notNull();
if (expectedDisbursementDate != null && approvedOnDate != null && DateUtils.isBefore(expectedDisbursementDate, approvedOnDate)) {
if (expectedDisbursementDate != null && DateUtils.isBefore(expectedDisbursementDate, approvedOnDate)) {
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.expectedDisbursementDateParamName)
.failWithCode("cannot.be.before.approval.date");
}
Expand Down Expand Up @@ -381,18 +385,10 @@ public void validateDisbursement(final String json, final WorkingCapitalLoan loa
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.noteParamName).value(note).ignoreIfNull()
.notExceedingLengthOf(NOTE_MAX_LENGTH);

if (this.fromApiJsonHelper.parameterHasValue(WorkingCapitalLoanConstants.externalIdParameterName, element)) {
final String externalIdStr = this.fromApiJsonHelper.extractStringNamed(WorkingCapitalLoanConstants.externalIdParameterName,
element);
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.externalIdParameterName).value(externalIdStr).ignoreIfNull()
.notExceedingLengthOf(EXTERNAL_ID_MAX_LENGTH);
if (externalIdStr != null && !externalIdStr.isBlank()) {
final ExternalId externalId = ExternalIdFactory.produce(externalIdStr);
if (!externalId.isEmpty() && this.transactionRepository.existsByExternalId(externalId)) {
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.externalIdParameterName).failWithCode("already.exists");
}
}
}
validateTransactionExternalId(baseDataValidator, element, WorkingCapitalLoanConstants.externalIdParameterName);
validateTransactionExternalId(baseDataValidator, element, WorkingCapitalLoanConstants.discountExternalIdParameterName);
validateDiscountExternalIdRequiresPositiveDiscount(baseDataValidator, element);
validateDisbursementAndDiscountExternalIdsDiffer(baseDataValidator, element);

validatePaymentDetails(baseDataValidator, element);

Expand Down Expand Up @@ -433,6 +429,55 @@ private void validatePaymentDetails(final DataValidatorBuilder baseDataValidator
}
}

private void validateDiscountExternalIdRequiresPositiveDiscount(final DataValidatorBuilder baseDataValidator,
final JsonElement element) {
if (!this.fromApiJsonHelper.parameterHasValue(WorkingCapitalLoanConstants.discountExternalIdParameterName, element)) {
return;
}
final String discountExternalIdStr = this.fromApiJsonHelper
.extractStringNamed(WorkingCapitalLoanConstants.discountExternalIdParameterName, element);
if (StringUtils.isBlank(discountExternalIdStr)) {
return;
}
final BigDecimal discountAmount = this.fromApiJsonHelper
.parameterHasValue(WorkingCapitalLoanConstants.discountAmountParamName, element)
? this.fromApiJsonHelper.extractBigDecimalNamed(WorkingCapitalLoanConstants.discountAmountParamName, element,
new HashSet<>())
: null;
if (discountAmount == null || discountAmount.signum() == 0) {
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.discountExternalIdParameterName)
.failWithCode("not.allowed.without.positive.discount");
}
}

private void validateDisbursementAndDiscountExternalIdsDiffer(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
final String disbursementExternalId = this.fromApiJsonHelper.extractStringNamed(WorkingCapitalLoanConstants.externalIdParameterName,
element);
final String discountExternalId = this.fromApiJsonHelper
.extractStringNamed(WorkingCapitalLoanConstants.discountExternalIdParameterName, element);
if (StringUtils.isNotBlank(disbursementExternalId) && StringUtils.isNotBlank(discountExternalId)
&& disbursementExternalId.equals(discountExternalId)) {
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.discountExternalIdParameterName)
.failWithCode("must.differ.from.disbursement.external.id");
}
}

private void validateTransactionExternalId(final DataValidatorBuilder baseDataValidator, final JsonElement element,
final String paramName) {
if (!this.fromApiJsonHelper.parameterHasValue(paramName, element)) {
return;
}
final String externalIdStr = this.fromApiJsonHelper.extractStringNamed(paramName, element);
baseDataValidator.reset().parameter(paramName).value(externalIdStr).ignoreIfNull().notExceedingLengthOf(EXTERNAL_ID_MAX_LENGTH);
if (externalIdStr == null || externalIdStr.isBlank()) {
return;
}
final ExternalId externalId = ExternalIdFactory.produce(externalIdStr);
if (!externalId.isEmpty() && this.transactionRepository.existsByExternalId(externalId)) {
baseDataValidator.reset().parameter(paramName).failWithCode("already.exists");
}
}

private JsonElement resolvePaymentDetailsElement(final JsonElement element) {
if (element != null && element.isJsonObject()) {
final JsonObject root = element.getAsJsonObject();
Expand Down Expand Up @@ -523,18 +568,7 @@ public void validateRepayment(final String json, final WorkingCapitalLoan loan,
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.noteParamName).value(note).ignoreIfNull()
.notExceedingLengthOf(NOTE_MAX_LENGTH);

if (this.fromApiJsonHelper.parameterHasValue(WorkingCapitalLoanConstants.externalIdParameterName, element)) {
final String externalIdStr = this.fromApiJsonHelper.extractStringNamed(WorkingCapitalLoanConstants.externalIdParameterName,
element);
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.externalIdParameterName).value(externalIdStr).ignoreIfNull()
.notExceedingLengthOf(EXTERNAL_ID_MAX_LENGTH);
if (externalIdStr != null && !externalIdStr.isBlank()) {
final ExternalId externalId = ExternalIdFactory.produce(externalIdStr);
if (!externalId.isEmpty() && this.transactionRepository.existsByExternalId(externalId)) {
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.externalIdParameterName).failWithCode("already.exists");
}
}
}
validateTransactionExternalId(baseDataValidator, element, WorkingCapitalLoanConstants.externalIdParameterName);

validatePaymentDetails(baseDataValidator, element);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
Expand Down Expand Up @@ -618,18 +652,7 @@ public void validateCreditBalanceRefund(final String json, final WorkingCapitalL
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.noteParamName).value(note).ignoreIfNull()
.notExceedingLengthOf(NOTE_MAX_LENGTH);

if (this.fromApiJsonHelper.parameterHasValue(WorkingCapitalLoanConstants.externalIdParameterName, element)) {
final String externalIdStr = this.fromApiJsonHelper.extractStringNamed(WorkingCapitalLoanConstants.externalIdParameterName,
element);
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.externalIdParameterName).value(externalIdStr).ignoreIfNull()
.notExceedingLengthOf(EXTERNAL_ID_MAX_LENGTH);
if (externalIdStr != null && !externalIdStr.isBlank()) {
final ExternalId externalId = ExternalIdFactory.produce(externalIdStr);
if (!externalId.isEmpty() && this.transactionRepository.existsByExternalId(externalId)) {
baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.externalIdParameterName).failWithCode("already.exists");
}
}
}
validateTransactionExternalId(baseDataValidator, element, WorkingCapitalLoanConstants.externalIdParameterName);

validatePaymentDetails(baseDataValidator, element);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,11 +317,12 @@ public CommandProcessingResult disburseLoan(final Long loanId, final JsonCommand
ExternalId discountTxnExternalId = null;

if (discount != null && discount.compareTo(BigDecimal.ZERO) > 0) {
WorkingCapitalLoanTransaction discountTransaction = createAndPersistDiscountFeeTransaction(loan, disbursementTransaction, null,
discount, actualDisbursementDate, null, null);
final ExternalId discountExternalId = externalIdFactory.createFromCommand(command,
WorkingCapitalLoanConstants.discountExternalIdParameterName);
final WorkingCapitalLoanTransaction discountTransaction = createAndPersistDiscountFeeTransaction(loan, disbursementTransaction,
discountExternalId, discount, actualDisbursementDate, null, null);
discountTransactionId = discountTransaction.getId();
discountTxnExternalId = discountTransaction.getExternalId();
changes.put(WorkingCapitalLoanConstants.discountAmountParamName, discount);
}
updateBalanceOnDisburse(loan, transactionAmount);
amortizationScheduleWriteService.generateAndSaveAmortizationScheduleOnDisbursement(loan, transactionAmount, actualDisbursementDate);
Expand Down
Loading