Skip to content

Commit 98b7328

Browse files
budaidevadamsaghy
authored andcommitted
FINERACT-2311: Add buydown fees to loan
1 parent cb6a15c commit 98b7328

File tree

23 files changed

+729
-7
lines changed

23 files changed

+729
-7
lines changed

fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3858,4 +3858,13 @@ public CommandWrapperBuilder undoContractTermination(final Long loanId) {
38583858
this.href = "/loans/" + loanId;
38593859
return this;
38603860
}
3861+
3862+
public CommandWrapperBuilder makeLoanBuyDownFee(final Long loanId) {
3863+
this.actionName = "BUYDOWNFEE";
3864+
this.entityName = "LOAN";
3865+
this.entityId = null;
3866+
this.loanId = loanId;
3867+
this.href = "/loans/" + loanId + "/transactions/template?command=buyDownFee";
3868+
return this;
3869+
}
38613870
}

fineract-loan/src/main/java/org/apache/fineract/accounting/productaccountmapping/service/LoanProductToGLAccountMappingHelper.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,9 @@ public Map<String, Object> populateChangesForNewLoanProductToGLAccountMappingCre
173173
final Long incomeFromRecoveryAccountId = this.fromApiJsonHelper
174174
.extractLongNamed(LoanProductAccountingParams.INCOME_FROM_RECOVERY.getValue(), element);
175175

176+
final Long incomeFromBuyDownFeesAccountId = this.fromApiJsonHelper
177+
.extractLongNamed(LoanProductAccountingParams.INCOME_FROM_BUY_DOWN.getValue(), element);
178+
176179
final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(),
177180
element);
178181
final Long overPaymentAccountId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.OVERPAYMENT.getValue(),
@@ -198,12 +201,14 @@ public Map<String, Object> populateChangesForNewLoanProductToGLAccountMappingCre
198201
case ACCRUAL_PERIODIC:
199202
populateChangesForAccrualBasedAccounting(changes, fundAccountId, loanPortfolioAccountId, incomeFromInterestId,
200203
incomeFromFeeId, incomeFromPenaltyId, writeOffAccountId, overPaymentAccountId, transfersInSuspenseAccountId,
201-
incomeFromRecoveryAccountId, receivableInterestAccountId, receivableFeeAccountId, receivablePenaltyAccountId);
204+
incomeFromRecoveryAccountId, incomeFromBuyDownFeesAccountId, receivableInterestAccountId, receivableFeeAccountId,
205+
receivablePenaltyAccountId);
202206
break;
203207
case ACCRUAL_UPFRONT:
204208
populateChangesForAccrualBasedAccounting(changes, fundAccountId, loanPortfolioAccountId, incomeFromInterestId,
205209
incomeFromFeeId, incomeFromPenaltyId, writeOffAccountId, overPaymentAccountId, transfersInSuspenseAccountId,
206-
incomeFromRecoveryAccountId, receivableInterestAccountId, receivableFeeAccountId, receivablePenaltyAccountId);
210+
incomeFromRecoveryAccountId, incomeFromBuyDownFeesAccountId, receivableInterestAccountId, receivableFeeAccountId,
211+
receivablePenaltyAccountId);
207212
break;
208213
}
209214

@@ -213,8 +218,8 @@ public Map<String, Object> populateChangesForNewLoanProductToGLAccountMappingCre
213218
private void populateChangesForAccrualBasedAccounting(final Map<String, Object> changes, final Long fundAccountId,
214219
final Long loanPortfolioAccountId, final Long incomeFromInterestId, final Long incomeFromFeeId, final Long incomeFromPenaltyId,
215220
final Long writeOffAccountId, final Long overPaymentAccountId, final Long transfersInSuspenseAccountId,
216-
final Long incomeFromRecoveryAccountId, final Long receivableInterestAccountId, final Long receivableFeeAccountId,
217-
final Long receivablePenaltyAccountId) {
221+
final Long incomeFromRecoveryAccountId, final Long incomeFromBuyDownFeesAccountId, final Long receivableInterestAccountId,
222+
final Long receivableFeeAccountId, final Long receivablePenaltyAccountId) {
218223

219224
changes.put(LoanProductAccountingParams.INTEREST_RECEIVABLE.getValue(), receivableInterestAccountId);
220225
changes.put(LoanProductAccountingParams.FEES_RECEIVABLE.getValue(), receivableFeeAccountId);

fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ public interface LoanApiConstants {
181181
String CAPITALIZED_INCOME_ADJUSTMENT_TRANSACTION_COMMAND = "capitalizedIncomeAdjustment";
182182
String CONTRACT_TERMINATION_COMMAND = "contractTermination";
183183
String UNDO_CONTRACT_TERMINATION_COMMAND = "undoContractTermination";
184+
String BUY_DOWN_FEE_COMMAND = "buyDownFee";
185+
String BUY_DOWN_FEE_ADJUSTMENT_COMMAND = "buyDownFeeAdjustment";
184186

185187
// Data Validator names
186188
String LOAN_FRAUD_DATAVALIDATOR_PREFIX = "loans.fraud";

fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionEnumData.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ public class LoanTransactionEnumData implements Serializable {
7272
private final boolean capitalizedIncomeAdjustment;
7373
private final boolean capitalizedIncomeAmortizationAdjustment;
7474
private final boolean contractTermination;
75+
private final boolean buyDownFee;
76+
private final boolean buyDownFeeAdjustment;
7577

7678
public LoanTransactionEnumData(final Long id, final String code, final String value) {
7779
this.id = id;
@@ -114,6 +116,8 @@ public LoanTransactionEnumData(final Long id, final String code, final String va
114116
this.capitalizedIncomeAmortizationAdjustment = Long
115117
.valueOf(LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT.getValue()).equals(this.id);
116118
this.contractTermination = Long.valueOf(LoanTransactionType.CONTRACT_TERMINATION.getValue()).equals(this.id);
119+
this.buyDownFee = Long.valueOf(LoanTransactionType.BUY_DOWN_FEE.getValue()).equals(this.id);
120+
this.buyDownFeeAdjustment = Long.valueOf(LoanTransactionType.BUY_DOWN_FEE_ADJUSTMENT.getValue()).equals(this.id);
117121
}
118122

119123
public boolean isRepaymentType() {

fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,4 +961,13 @@ public void updateTransactionDate(final LocalDate transactionDate) {
961961
this.dateOf = transactionDate;
962962
}
963963

964+
public static LoanTransaction buyDownFee(final Loan loan, final Money amount, final PaymentDetail paymentDetail,
965+
final LocalDate transactionDate, final ExternalId externalId) {
966+
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.BUY_DOWN_FEE, paymentDetail, amount.getAmount(),
967+
transactionDate, externalId);
968+
}
969+
970+
public boolean isBuyDownFee() {
971+
return LoanTransactionType.BUY_DOWN_FEE.equals(this.typeOf);
972+
}
964973
}

fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionType.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ public enum LoanTransactionType {
7373
CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT(39, "loanTransactionType.capitalizedIncomeAmortizationAdjustment"), //
7474
// Kind of Final Transactions
7575
CONTRACT_TERMINATION(38, "loanTransactionType.contractTermination"), //
76+
77+
BUY_DOWN_FEE(40, "loanTransactionType.buyDownFee"), //
78+
BUY_DOWN_FEE_ADJUSTMENT(41, "loanTransactionType.buyDownFeeAdjustment"), //
7679
;
7780

7881
private final Integer value;
@@ -128,6 +131,8 @@ public static LoanTransactionType fromInt(final Integer transactionType) {
128131
case 37 -> LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT;
129132
case 38 -> LoanTransactionType.CONTRACT_TERMINATION;
130133
case 39 -> LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT;
134+
case 40 -> LoanTransactionType.BUY_DOWN_FEE;
135+
case 41 -> LoanTransactionType.BUY_DOWN_FEE_ADJUSTMENT;
131136
default -> LoanTransactionType.INVALID;
132137
};
133138
}
@@ -256,4 +261,12 @@ public boolean isCapitalizedIncomeAdjustment() {
256261
public boolean isContractTermination() {
257262
return this == LoanTransactionType.CONTRACT_TERMINATION;
258263
}
264+
265+
public boolean isBuyDownFee() {
266+
return this == LoanTransactionType.BUY_DOWN_FEE;
267+
}
268+
269+
public boolean isBuyDownFeeAdjustment() {
270+
return this == LoanTransactionType.BUY_DOWN_FEE_ADJUSTMENT;
271+
}
259272
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.fineract.portfolio.loanaccount.exception;
20+
21+
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
22+
23+
/**
24+
* {@link AbstractPlatformDomainRuleException} thrown when loan transaction processing violates a domain rule.
25+
*/
26+
public class LoanTransactionProcessingException extends AbstractPlatformDomainRuleException {
27+
28+
public LoanTransactionProcessingException(final String defaultUserMessage, final Object... defaultUserMessageArgs) {
29+
super("error.msg.loan.transaction.processing", defaultUserMessage, defaultUserMessageArgs);
30+
}
31+
32+
public LoanTransactionProcessingException(final String action, final String defaultUserMessage,
33+
final Object... defaultUserMessageArgs) {
34+
super("error.msg.loan.transaction." + action, defaultUserMessage, defaultUserMessageArgs);
35+
}
36+
}

fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,10 @@ public static LoanTransactionEnumData transactionType(final LoanTransactionType
338338
case CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT -> new LoanTransactionEnumData(
339339
LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT.getValue().longValue(),
340340
LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT.getCode(), "Capitalized Income Amortization Adjustment");
341+
case BUY_DOWN_FEE -> new LoanTransactionEnumData(LoanTransactionType.BUY_DOWN_FEE.getValue().longValue(),
342+
LoanTransactionType.BUY_DOWN_FEE.getCode(), "Buy Down Fee");
343+
case BUY_DOWN_FEE_ADJUSTMENT -> new LoanTransactionEnumData(LoanTransactionType.BUY_DOWN_FEE_ADJUSTMENT.getValue().longValue(),
344+
LoanTransactionType.BUY_DOWN_FEE_ADJUSTMENT.getCode(), "Buy Down Fee Adjustment");
341345
};
342346
}
343347

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.fineract.portfolio.loanaccount.handler;
20+
21+
import lombok.RequiredArgsConstructor;
22+
import org.apache.fineract.commands.annotation.CommandType;
23+
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
24+
import org.apache.fineract.infrastructure.DataIntegrityErrorHandler;
25+
import org.apache.fineract.infrastructure.core.api.JsonCommand;
26+
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
27+
import org.apache.fineract.portfolio.loanaccount.service.BuyDownFeePlatformService;
28+
import org.springframework.dao.DataIntegrityViolationException;
29+
import org.springframework.orm.jpa.JpaSystemException;
30+
import org.springframework.stereotype.Service;
31+
import org.springframework.transaction.annotation.Transactional;
32+
33+
@Service
34+
@RequiredArgsConstructor
35+
@CommandType(entity = "LOAN", action = "BUYDOWNFEE")
36+
public class AddBuyDownFeeCommandHandler implements NewCommandSourceHandler {
37+
38+
private final BuyDownFeePlatformService buyDownFeePlatformService;
39+
private final DataIntegrityErrorHandler dataIntegrityErrorHandler;
40+
41+
@Transactional
42+
@Override
43+
public CommandProcessingResult processCommand(final JsonCommand command) {
44+
45+
try {
46+
return this.buyDownFeePlatformService.makeLoanBuyDownFee(command.getLoanId(), command);
47+
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
48+
dataIntegrityErrorHandler.handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve, "loan.buy.down.fee",
49+
"Buy Down Fee");
50+
return CommandProcessingResult.empty();
51+
}
52+
}
53+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.fineract.portfolio.loanaccount.service;
20+
21+
import org.apache.fineract.infrastructure.core.api.JsonCommand;
22+
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
23+
import org.springframework.transaction.annotation.Transactional;
24+
25+
public interface BuyDownFeePlatformService {
26+
27+
@Transactional
28+
CommandProcessingResult makeLoanBuyDownFee(Long loanId, JsonCommand command);
29+
}

0 commit comments

Comments
 (0)