A new rates module was added to define new rates that can be used to set min and max nominal interest rate, when a new loan account is created they can be used to determine which rates may be applicable for the loan account.
This commit is contained in:
committed by
Michael Vorburger ⛑️
parent
f484f884a0
commit
a7d4c83e92
+17
@@ -3147,4 +3147,21 @@ public class CommandWrapperBuilder {
|
||||
this.href = "/self/pocket?command="+PocketApiConstants.delinkAccountsFromPocketCommandParam;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createRate() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "RATE";
|
||||
this.entityId = null;
|
||||
this.href = "/rates/template";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder updateRate(final Long rateId) {
|
||||
this.actionName = "UPDATE";
|
||||
this.entityName = "RATE";
|
||||
this.entityId = rateId;
|
||||
this.href = "/rates/" + rateId;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
@@ -92,4 +92,6 @@ public interface ConfigurationDomainService {
|
||||
Integer retrieveOTPCharacterLength();
|
||||
|
||||
Integer retrieveOTPLiveTime();
|
||||
|
||||
boolean isSubRatesEnabled();
|
||||
}
|
||||
+11
@@ -332,4 +332,15 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
|
||||
}
|
||||
return configurations.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSubRatesEnabled() {
|
||||
GlobalConfigurationPropertyData configuration = getGlobalConfigurationPropertyData("sub-rates");
|
||||
if (configuration==null){
|
||||
return false;
|
||||
}else{
|
||||
return configuration.isEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-1
@@ -54,6 +54,7 @@ import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSer
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
|
||||
import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadPlatformService;
|
||||
@@ -82,19 +83,21 @@ public class LoanTransactionsApiResource {
|
||||
private final DefaultToApiJsonSerializer<LoanTransactionData> toApiJsonSerializer;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final PaymentTypeReadPlatformService paymentTypeReadPlatformService;
|
||||
private final LoanChargePaidByReadPlatformService loanChargePaidByReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanTransactionsApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService,
|
||||
final ApiRequestParameterHelper apiRequestParameterHelper,
|
||||
final DefaultToApiJsonSerializer<LoanTransactionData> toApiJsonSerializer,
|
||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
|
||||
PaymentTypeReadPlatformService paymentTypeReadPlatformService) {
|
||||
PaymentTypeReadPlatformService paymentTypeReadPlatformService, LoanChargePaidByReadPlatformService loanChargePaidByReadPlatformService) {
|
||||
this.context = context;
|
||||
this.loanReadPlatformService = loanReadPlatformService;
|
||||
this.apiRequestParameterHelper = apiRequestParameterHelper;
|
||||
this.toApiJsonSerializer = toApiJsonSerializer;
|
||||
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
||||
this.paymentTypeReadPlatformService = paymentTypeReadPlatformService;
|
||||
this.loanChargePaidByReadPlatformService = loanChargePaidByReadPlatformService;
|
||||
}
|
||||
|
||||
private boolean is(final String commandParam, final String commandValue) {
|
||||
@@ -170,6 +173,7 @@ public class LoanTransactionsApiResource {
|
||||
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
|
||||
|
||||
LoanTransactionData transactionData = this.loanReadPlatformService.retrieveLoanTransaction(loanId, transactionId);
|
||||
transactionData.setLoanChargePaidByList(this.loanChargePaidByReadPlatformService.getLoanChargesPaidByTransactionId(transactionId));
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
if (settings.isTemplate()) {
|
||||
final Collection<PaymentTypeData> paymentTypeOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
|
||||
|
||||
+39
-24
@@ -64,6 +64,7 @@ import org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookP
|
||||
import org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookService;
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonQuery;
|
||||
@@ -131,6 +132,8 @@ import org.apache.fineract.portfolio.loanproduct.service.LoanProductReadPlatform
|
||||
import org.apache.fineract.portfolio.note.data.NoteData;
|
||||
import org.apache.fineract.portfolio.note.domain.NoteType;
|
||||
import org.apache.fineract.portfolio.note.service.NoteReadPlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.rate.data.RateData;
|
||||
import org.apache.fineract.portfolio.rate.service.RateReadService;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -259,7 +262,7 @@ public class LoansApiResource {
|
||||
"syncDisbursementWithMeeting", "loanCounter", "loanProductCounter", "notes", "accountLinkingOptions", "linkedAccount",
|
||||
"interestRateDifferential", "isFloatingInterestRate", "interestRatesPeriods", LoanApiConstants.canUseForTopup,
|
||||
LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.topupAmount, LoanApiConstants.clientActiveLoanOptions,
|
||||
LoanApiConstants.datatables));
|
||||
LoanApiConstants.datatables, LoanProductConstants.ratesParamName));
|
||||
|
||||
private final Set<String> LOAN_APPROVAL_DATA_PARAMETERS = new HashSet<>(Arrays.asList("approvalDate", "approvalAmount"));
|
||||
private final String resourceNameForPermissions = "LOAN";
|
||||
@@ -291,30 +294,32 @@ public class LoansApiResource {
|
||||
private final EntityDatatableChecksReadService entityDatatableChecksReadService;
|
||||
private final BulkImportWorkbookService bulkImportWorkbookService;
|
||||
private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService;
|
||||
|
||||
private final RateReadService rateReadService;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
|
||||
@Autowired
|
||||
public LoansApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService,
|
||||
final LoanProductReadPlatformService loanProductReadPlatformService,
|
||||
final LoanDropdownReadPlatformService dropdownReadPlatformService, final FundReadPlatformService fundReadPlatformService,
|
||||
final ChargeReadPlatformService chargeReadPlatformService, final LoanChargeReadPlatformService loanChargeReadPlatformService,
|
||||
final CollateralReadPlatformService loanCollateralReadPlatformService,
|
||||
final LoanScheduleCalculationPlatformService calculationPlatformService,
|
||||
final GuarantorReadPlatformService guarantorReadPlatformService,
|
||||
final CodeValueReadPlatformService codeValueReadPlatformService, final GroupReadPlatformService groupReadPlatformService,
|
||||
final DefaultToApiJsonSerializer<LoanAccountData> toApiJsonSerializer,
|
||||
final DefaultToApiJsonSerializer<LoanApprovalData> loanApprovalDataToApiJsonSerializer,
|
||||
final DefaultToApiJsonSerializer<LoanScheduleData> loanScheduleToApiJsonSerializer,
|
||||
final ApiRequestParameterHelper apiRequestParameterHelper, final FromJsonHelper fromJsonHelper,
|
||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
|
||||
final CalendarReadPlatformService calendarReadPlatformService, final NoteReadPlatformServiceImpl noteReadPlatformService,
|
||||
final PortfolioAccountReadPlatformService portfolioAccountReadPlatformServiceImpl,
|
||||
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
|
||||
final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService,
|
||||
final AccountDetailsReadPlatformService accountDetailsReadPlatformService,
|
||||
final EntityDatatableChecksReadService entityDatatableChecksReadService,
|
||||
final BulkImportWorkbookService bulkImportWorkbookService,
|
||||
final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService) {
|
||||
final LoanProductReadPlatformService loanProductReadPlatformService,
|
||||
final LoanDropdownReadPlatformService dropdownReadPlatformService, final FundReadPlatformService fundReadPlatformService,
|
||||
final ChargeReadPlatformService chargeReadPlatformService, final LoanChargeReadPlatformService loanChargeReadPlatformService,
|
||||
final CollateralReadPlatformService loanCollateralReadPlatformService,
|
||||
final LoanScheduleCalculationPlatformService calculationPlatformService,
|
||||
final GuarantorReadPlatformService guarantorReadPlatformService,
|
||||
final CodeValueReadPlatformService codeValueReadPlatformService, final GroupReadPlatformService groupReadPlatformService,
|
||||
final DefaultToApiJsonSerializer<LoanAccountData> toApiJsonSerializer,
|
||||
final DefaultToApiJsonSerializer<LoanApprovalData> loanApprovalDataToApiJsonSerializer,
|
||||
final DefaultToApiJsonSerializer<LoanScheduleData> loanScheduleToApiJsonSerializer,
|
||||
final ApiRequestParameterHelper apiRequestParameterHelper, final FromJsonHelper fromJsonHelper,
|
||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
|
||||
final CalendarReadPlatformService calendarReadPlatformService, final NoteReadPlatformServiceImpl noteReadPlatformService,
|
||||
final PortfolioAccountReadPlatformService portfolioAccountReadPlatformServiceImpl,
|
||||
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
|
||||
final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService,
|
||||
final AccountDetailsReadPlatformService accountDetailsReadPlatformService,
|
||||
final EntityDatatableChecksReadService entityDatatableChecksReadService,
|
||||
final BulkImportWorkbookService bulkImportWorkbookService,
|
||||
final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService, final RateReadService rateReadService,
|
||||
final ConfigurationDomainService configurationDomainService) {
|
||||
this.context = context;
|
||||
this.loanReadPlatformService = loanReadPlatformService;
|
||||
this.loanProductReadPlatformService = loanProductReadPlatformService;
|
||||
@@ -340,8 +345,10 @@ public class LoansApiResource {
|
||||
this.loanScheduleHistoryReadPlatformService = loanScheduleHistoryReadPlatformService;
|
||||
this.accountDetailsReadPlatformService = accountDetailsReadPlatformService;
|
||||
this.entityDatatableChecksReadService = entityDatatableChecksReadService;
|
||||
this.rateReadService = rateReadService;
|
||||
this.bulkImportWorkbookService=bulkImportWorkbookService;
|
||||
this.bulkImportWorkbookPopulatorService=bulkImportWorkbookPopulatorService;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -397,6 +404,7 @@ public class LoansApiResource {
|
||||
LoanAccountData newLoanAccount = null;
|
||||
Long officeId = null;
|
||||
Collection<PortfolioAccountData> accountLinkingOptions = null;
|
||||
boolean isRatesEnabled = this.configurationDomainService.isSubRatesEnabled();
|
||||
|
||||
if (productId != null) {
|
||||
newLoanAccount = this.loanReadPlatformService.retrieveLoanProductDetailsTemplate(productId, clientId, groupId);
|
||||
@@ -474,7 +482,7 @@ public class LoansApiResource {
|
||||
// add product options, allowed loan officers and calendar options
|
||||
// (calendar options will be null in individual loan)
|
||||
newLoanAccount = LoanAccountData.associationsAndTemplate(newLoanAccount, productOptions, allowedLoanOfficers, calendarOptions,
|
||||
accountLinkingOptions);
|
||||
accountLinkingOptions, isRatesEnabled);
|
||||
}
|
||||
final List<DatatableData> datatableTemplates = this.entityDatatableChecksReadService
|
||||
.retrieveTemplates(StatusEnum.CREATE.getCode().longValue(), EntityTables.LOAN.getName(), productId);
|
||||
@@ -736,13 +744,20 @@ public class LoansApiResource {
|
||||
|
||||
paidInAdvanceTemplate = this.loanReadPlatformService.retrieveTotalPaidInAdvance(loanId);
|
||||
|
||||
//Get rates from Loan
|
||||
boolean isRatesEnabled = this.configurationDomainService.isSubRatesEnabled();
|
||||
List<RateData> rates = null;
|
||||
if (isRatesEnabled){
|
||||
rates = this.rateReadService.retrieveLoanRates(loanId);
|
||||
}
|
||||
|
||||
final LoanAccountData loanAccount = LoanAccountData.associationsAndTemplate(loanBasicDetails, repaymentSchedule, loanRepayments,
|
||||
charges, collateral, guarantors, meeting, productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
|
||||
repaymentFrequencyNthDayTypeOptions, repaymentFrequencyDayOfWeekTypeOptions, repaymentStrategyOptions,
|
||||
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
||||
fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, loanPurposeOptions, loanCollateralOptions,
|
||||
calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations,
|
||||
overdueCharges, paidInAdvanceTemplate, interestRatesPeriods, clientActiveLoanOptions);
|
||||
overdueCharges, paidInAdvanceTemplate, interestRatesPeriods, clientActiveLoanOptions, rates, isRatesEnabled);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
|
||||
mandatoryResponseParameters);
|
||||
|
||||
+53
-18
@@ -50,6 +50,7 @@ import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrat
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductConfigurableAttributes;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductValueConditionType;
|
||||
import org.apache.fineract.portfolio.note.data.NoteData;
|
||||
import org.apache.fineract.portfolio.rate.data.RateData;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@@ -219,6 +220,10 @@ public class LoanAccountData {
|
||||
private List<DatatableData> datatables = null;
|
||||
private final Boolean isEqualAmortization;
|
||||
|
||||
//Rate
|
||||
private final List<RateData> rates;
|
||||
private final Boolean isRatesEnabled;
|
||||
|
||||
//import fields
|
||||
private String dateFormat;
|
||||
private String locale;
|
||||
@@ -311,6 +316,7 @@ public class LoanAccountData {
|
||||
this.groupId=groupId;
|
||||
this.expectedDisbursementDate=expectedDisbursementDate;
|
||||
this.charges = charges;
|
||||
this.rates=null;
|
||||
this.id = null;
|
||||
this.accountNo = null;
|
||||
|
||||
@@ -418,6 +424,7 @@ public class LoanAccountData {
|
||||
this.minimumGap = null;
|
||||
this.maximumGap = null;
|
||||
this.isEqualAmortization = null;
|
||||
this.isRatesEnabled = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -580,6 +587,9 @@ public class LoanAccountData {
|
||||
final String closureLoanAccountNo = null;
|
||||
final BigDecimal topupAmount = null;
|
||||
final boolean isEqualAmortization = false;
|
||||
final List<RateData> rates = null;
|
||||
final Boolean isRatesEnabled = false;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
|
||||
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
||||
loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, principal,
|
||||
@@ -598,7 +608,7 @@ public class LoanAccountData {
|
||||
maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
|
||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
|
||||
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization);
|
||||
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, rates, isRatesEnabled);
|
||||
|
||||
}
|
||||
|
||||
@@ -720,6 +730,9 @@ public class LoanAccountData {
|
||||
final String closureLoanAccountNo = null;
|
||||
final BigDecimal topupAmount = null;
|
||||
final boolean isEqualAmortization = false;
|
||||
final List<RateData> rates = null;
|
||||
final Boolean isRatesEnabled = false;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
|
||||
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
||||
loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, principal,
|
||||
@@ -738,7 +751,8 @@ public class LoanAccountData {
|
||||
maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
|
||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
|
||||
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization);
|
||||
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization,
|
||||
rates, isRatesEnabled);
|
||||
|
||||
}
|
||||
|
||||
@@ -768,7 +782,8 @@ public class LoanAccountData {
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
|
||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization,
|
||||
acc.rates, acc.isRatesEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -891,6 +906,9 @@ public class LoanAccountData {
|
||||
final String closureLoanAccountNo = null;
|
||||
final BigDecimal topupAmount = null;
|
||||
final boolean isEqualAmortization = false;
|
||||
final List<RateData> rates = null;
|
||||
final Boolean isRatesEnabled = false;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
|
||||
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
||||
loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, principal,
|
||||
@@ -909,7 +927,8 @@ public class LoanAccountData {
|
||||
maxOutstandingBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
|
||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
|
||||
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization);
|
||||
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization,
|
||||
rates, isRatesEnabled);
|
||||
|
||||
}
|
||||
|
||||
@@ -939,7 +958,8 @@ public class LoanAccountData {
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
|
||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization,
|
||||
acc.rates, acc.isRatesEnabled);
|
||||
|
||||
}
|
||||
|
||||
@@ -1069,6 +1089,9 @@ public class LoanAccountData {
|
||||
final Long closureLoanId = null;
|
||||
final String closureLoanAccountNo = null;
|
||||
final BigDecimal topupAmount = null;
|
||||
final List<RateData> rates = null;
|
||||
final Boolean isRatesEnabled = false;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
|
||||
loanType, product.getId(), product.getName(), product.getDescription(), product.isLinkedToFloatingInterestRates(),
|
||||
product.getFundId(), product.getFundName(), loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
@@ -1093,7 +1116,7 @@ public class LoanAccountData {
|
||||
originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods,
|
||||
product.isVariableInstallmentsAllowed(), product.getMinimumGapBetweenInstallments(),
|
||||
product.getMaximumGapBetweenInstallments(), subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId,
|
||||
closureLoanAccountNo, topupAmount, product.isEqualAmortization());
|
||||
closureLoanAccountNo, topupAmount, product.isEqualAmortization(),rates, isRatesEnabled);
|
||||
}
|
||||
|
||||
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
|
||||
@@ -1153,7 +1176,8 @@ public class LoanAccountData {
|
||||
product.toLoanInterestRecalculationData(), acc.originalSchedule, acc.createStandingInstructionAtDisbursement,
|
||||
paidInAdvance, acc.interestRatesPeriods, product.isVariableInstallmentsAllowed(),
|
||||
product.getMinimumGapBetweenInstallments(), product.getMaximumGapBetweenInstallments(), acc.subStatus, acc.canUseForTopup,
|
||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, product.isEqualAmortization());
|
||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, product.isEqualAmortization(), acc.rates,
|
||||
acc.isRatesEnabled);
|
||||
|
||||
}
|
||||
|
||||
@@ -1222,6 +1246,8 @@ public class LoanAccountData {
|
||||
final PaidInAdvanceData paidInAdvance = null;
|
||||
final Collection<InterestRatePeriodData> interestRatesPeriods = null;
|
||||
final Collection<LoanAccountSummaryData> clientActiveLoanOptions = null;
|
||||
final List<RateData> rates = null;
|
||||
final Boolean isRatesEnabled = false;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
|
||||
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
||||
@@ -1241,7 +1267,8 @@ public class LoanAccountData {
|
||||
outstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
|
||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
|
||||
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization);
|
||||
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization,
|
||||
rates, isRatesEnabled);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1265,7 +1292,7 @@ public class LoanAccountData {
|
||||
final PortfolioAccountData linkedAccount, final Collection<DisbursementData> disbursementDetails,
|
||||
final Collection<LoanTermVariationsData> emiAmountVariations, final Collection<ChargeData> overdueCharges,
|
||||
final PaidInAdvanceData paidInAdvance, Collection<InterestRatePeriodData> interestRatesPeriods,
|
||||
final Collection<LoanAccountSummaryData> clientActiveLoanOptions) {
|
||||
final Collection<LoanAccountSummaryData> clientActiveLoanOptions,final List<RateData> rates, final Boolean isRatesEnabled) {
|
||||
LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
|
||||
if (acc.product != null) {
|
||||
loanProductConfigurableAttributes = acc.product.getloanProductConfigurableAttributes();
|
||||
@@ -1294,12 +1321,12 @@ public class LoanAccountData {
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, acc.isVariableInstallmentsAllowed,
|
||||
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, clientActiveLoanOptions, acc.isTopup,
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, rates, isRatesEnabled);
|
||||
}
|
||||
|
||||
public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
|
||||
final Collection<StaffData> allowedLoanOfficers, final Collection<CalendarData> calendarOptions,
|
||||
final Collection<PortfolioAccountData> accountLinkingOptions) {
|
||||
final Collection<PortfolioAccountData> accountLinkingOptions, final Boolean isRatesEnabled) {
|
||||
return associationsAndTemplate(acc, acc.repaymentSchedule, acc.transactions, acc.charges, acc.collateral, acc.guarantors,
|
||||
acc.meeting, productOptions, acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions,
|
||||
acc.repaymentFrequencyNthDayTypeOptions, acc.repaymentFrequencyDaysOfWeekTypeOptions,
|
||||
@@ -1307,7 +1334,7 @@ public class LoanAccountData {
|
||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||
allowedLoanOfficers, acc.loanPurposeOptions, acc.loanCollateralOptions, calendarOptions, acc.notes, accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.emiAmountVariations, acc.overdueCharges, acc.paidInAdvance,
|
||||
acc.interestRatesPeriods, acc.clientActiveLoanOptions);
|
||||
acc.interestRatesPeriods, acc.clientActiveLoanOptions,acc.rates, isRatesEnabled);
|
||||
}
|
||||
|
||||
public static LoanAccountData associateGroup(final LoanAccountData acc, final GroupGeneralData group) {
|
||||
@@ -1336,7 +1363,8 @@ public class LoanAccountData {
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
|
||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates,
|
||||
acc.isRatesEnabled);
|
||||
}
|
||||
|
||||
public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) {
|
||||
@@ -1401,7 +1429,8 @@ public class LoanAccountData {
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
|
||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates,
|
||||
acc.isRatesEnabled);
|
||||
|
||||
}
|
||||
|
||||
@@ -1435,7 +1464,8 @@ public class LoanAccountData {
|
||||
acc.isInterestRecalculationEnabled, interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
|
||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates,
|
||||
acc.isRatesEnabled);
|
||||
}
|
||||
|
||||
public static LoanAccountData withLoanCalendarData(final LoanAccountData acc, final CalendarData calendarData) {
|
||||
@@ -1463,7 +1493,8 @@ public class LoanAccountData {
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
||||
acc.originalSchedule, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
|
||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization,
|
||||
acc.rates, acc.isRatesEnabled);
|
||||
}
|
||||
|
||||
public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) {
|
||||
@@ -1492,7 +1523,8 @@ public class LoanAccountData {
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
|
||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization,
|
||||
acc.rates, acc.isRatesEnabled);
|
||||
}
|
||||
|
||||
private LoanAccountData(final Long id, //
|
||||
@@ -1542,7 +1574,8 @@ public class LoanAccountData {
|
||||
final Collection<InterestRatePeriodData> interestRatesPeriods, final Boolean isVariableInstallmentsAllowed,
|
||||
final Integer minimumGap, final Integer maximumGap, final EnumOptionData subStatus, final Boolean canUseForTopup,
|
||||
final Collection<LoanAccountSummaryData> clientActiveLoanOptions, final boolean isTopup,
|
||||
final Long closureLoanId, final String closureLoanAccountNo, final BigDecimal topupAmount, final boolean isEqualAmortization) {
|
||||
final Long closureLoanId, final String closureLoanAccountNo, final BigDecimal topupAmount, final boolean isEqualAmortization,
|
||||
final List<RateData> rates, final Boolean isRatesEnabled) {
|
||||
|
||||
this.id = id;
|
||||
this.accountNo = accountNo;
|
||||
@@ -1622,6 +1655,7 @@ public class LoanAccountData {
|
||||
this.amortizationTypeOptions = amortizationTypeOptions;
|
||||
this.interestTypeOptions = interestTypeOptions;
|
||||
this.interestCalculationPeriodTypeOptions = interestCalculationPeriodTypeOptions;
|
||||
this.isRatesEnabled = isRatesEnabled;
|
||||
|
||||
if (CollectionUtils.isEmpty(transactionProcessingStrategyOptions)) {
|
||||
this.transactionProcessingStrategyOptions = null;
|
||||
@@ -1725,6 +1759,7 @@ public class LoanAccountData {
|
||||
this.closureLoanAccountNo = closureLoanAccountNo;
|
||||
this.topupAmount = topupAmount;
|
||||
this.isEqualAmortization = isEqualAmortization;
|
||||
this.rates = rates;
|
||||
|
||||
}
|
||||
|
||||
|
||||
+15
@@ -27,6 +27,17 @@ public class LoanChargePaidByData {
|
||||
private final Integer installmentNumber;
|
||||
private final Long chargeId;
|
||||
private final Long transactionId;
|
||||
private final String name;
|
||||
|
||||
public LoanChargePaidByData(final Long id, final BigDecimal amount, final Integer installmentNumber, final Long chargeId,
|
||||
final Long transactionId, String name) {
|
||||
this.id = id;
|
||||
this.amount = amount;
|
||||
this.installmentNumber = installmentNumber;
|
||||
this.chargeId = chargeId;
|
||||
this.transactionId = transactionId;
|
||||
this.name=name;
|
||||
}
|
||||
|
||||
public LoanChargePaidByData(final Long id, final BigDecimal amount, final Integer installmentNumber, final Long chargeId,
|
||||
final Long transactionId) {
|
||||
@@ -35,6 +46,7 @@ public class LoanChargePaidByData {
|
||||
this.installmentNumber = installmentNumber;
|
||||
this.chargeId = chargeId;
|
||||
this.transactionId = transactionId;
|
||||
this.name=null;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
@@ -57,4 +69,7 @@ public class LoanChargePaidByData {
|
||||
return this.transactionId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -59,6 +59,8 @@ public class LoanTransactionData {
|
||||
private final boolean manuallyReversed;
|
||||
private final LocalDate possibleNextRepaymentDate;
|
||||
|
||||
private Collection<LoanChargePaidByData> loanChargePaidByList;
|
||||
|
||||
// templates
|
||||
final Collection<PaymentTypeData> paymentTypeOptions;
|
||||
|
||||
@@ -339,4 +341,12 @@ public class LoanTransactionData {
|
||||
public void setWriteOffReasonOptions(Collection<CodeValueData> writeOffReasonOptions){
|
||||
this.writeOffReasonOptions =writeOffReasonOptions;
|
||||
}
|
||||
|
||||
public Collection<LoanChargePaidByData> getLoanChargePaidByList() {
|
||||
return loanChargePaidByList;
|
||||
}
|
||||
|
||||
public void setLoanChargePaidByList(Collection<LoanChargePaidByData> loanChargePaidByList) {
|
||||
this.loanChargePaidByList = loanChargePaidByList;
|
||||
}
|
||||
}
|
||||
+31
-7
@@ -133,6 +133,7 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanTransactionProcessin
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
||||
import org.apache.fineract.portfolio.rate.domain.Rate;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.joda.time.Days;
|
||||
import org.joda.time.LocalDate;
|
||||
@@ -388,20 +389,24 @@ public class Loan extends AbstractPersistableCustom<Long> {
|
||||
@OneToOne(cascade = CascadeType.ALL, mappedBy = "loan", optional = true, orphanRemoval = true, fetch=FetchType.EAGER)
|
||||
private LoanTopupDetails loanTopupDetails;
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY)
|
||||
@JoinTable(name = "m_loan_rate", joinColumns = @JoinColumn(name = "loan_id"), inverseJoinColumns = @JoinColumn(name = "rate_id"))
|
||||
private List<Rate> rates;
|
||||
|
||||
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType,
|
||||
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
|
||||
final LoanTransactionProcessingStrategy transactionProcessingStrategy,
|
||||
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges,
|
||||
final Set<LoanCollateral> collateral, final BigDecimal fixedEmiAmount, final List<LoanDisbursementDetails> disbursementDetails,
|
||||
final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement,
|
||||
final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential) {
|
||||
final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential, final List<Rate> rates) {
|
||||
final LoanStatus status = null;
|
||||
final Group group = null;
|
||||
final Boolean syncDisbursementWithMeeting = null;
|
||||
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
|
||||
loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
|
||||
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
|
||||
interestRateDifferential);
|
||||
interestRateDifferential, rates);
|
||||
}
|
||||
|
||||
public static Loan newGroupLoanApplication(final String accountNo, final Group group, final Integer loanType,
|
||||
@@ -411,13 +416,13 @@ public class Loan extends AbstractPersistableCustom<Long> {
|
||||
final Set<LoanCollateral> collateral, final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount,
|
||||
final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
|
||||
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
|
||||
final BigDecimal interestRateDifferential) {
|
||||
final BigDecimal interestRateDifferential, final List<Rate> rates) {
|
||||
final LoanStatus status = null;
|
||||
final Client client = null;
|
||||
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
|
||||
loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
|
||||
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
|
||||
interestRateDifferential);
|
||||
interestRateDifferential, rates);
|
||||
}
|
||||
|
||||
public static Loan newIndividualLoanApplicationFromGroup(final String accountNo, final Client client, final Group group,
|
||||
@@ -427,12 +432,12 @@ public class Loan extends AbstractPersistableCustom<Long> {
|
||||
final Set<LoanCollateral> collateral, final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount,
|
||||
final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
|
||||
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
|
||||
final BigDecimal interestRateDifferential) {
|
||||
final BigDecimal interestRateDifferential, final List<Rate> rates) {
|
||||
final LoanStatus status = null;
|
||||
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
|
||||
loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
|
||||
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
|
||||
interestRateDifferential);
|
||||
interestRateDifferential,rates);
|
||||
}
|
||||
|
||||
protected Loan() {
|
||||
@@ -445,7 +450,7 @@ public class Loan extends AbstractPersistableCustom<Long> {
|
||||
final Set<LoanCharge> loanCharges, final Set<LoanCollateral> collateral, final Boolean syncDisbursementWithMeeting,
|
||||
final BigDecimal fixedEmiAmount, final List<LoanDisbursementDetails> disbursementDetails,
|
||||
final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement,
|
||||
final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential) {
|
||||
final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential, final List<Rate> rates) {
|
||||
|
||||
this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail;
|
||||
this.loanRepaymentScheduleDetail.validateRepaymentPeriodWithGraceSettings();
|
||||
@@ -503,6 +508,9 @@ public class Loan extends AbstractPersistableCustom<Long> {
|
||||
|
||||
this.proposedPrincipal = this.loanRepaymentScheduleDetail.getPrincipal().getAmount();
|
||||
|
||||
//rates added here
|
||||
this.rates = rates;
|
||||
|
||||
}
|
||||
|
||||
private LoanSummary updateSummaryWithTotalFeeChargesDueAtDisbursement(final BigDecimal feeChargesDueAtDisbursement) {
|
||||
@@ -1166,6 +1174,14 @@ public class Loan extends AbstractPersistableCustom<Long> {
|
||||
this.collateral.addAll(associateWithThisLoan(loanCollateral));
|
||||
}
|
||||
|
||||
public void updateLoanRates(final List<Rate> loanRates) {
|
||||
if (this.rates == null) {
|
||||
this.rates = new ArrayList<>();
|
||||
}
|
||||
this.rates.clear();
|
||||
this.rates.addAll(loanRates);
|
||||
}
|
||||
|
||||
public void updateLoanSchedule(final LoanScheduleModel modifiedLoanSchedule, AppUser currentUser) {
|
||||
this.repaymentScheduleInstallments.clear();
|
||||
for (final LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) {
|
||||
@@ -6533,4 +6549,12 @@ public class Loan extends AbstractPersistableCustom<Long> {
|
||||
}
|
||||
|
||||
public boolean isIndividualLoan(){return AccountType.fromInt(this.loanType).isIndividualAccount();}
|
||||
|
||||
public void setRates(List<Rate> rates) {
|
||||
this.rates = rates;
|
||||
}
|
||||
|
||||
public List<Rate> getRates() {
|
||||
return rates;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -67,7 +67,8 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
|
||||
LoanProductConstants.graceOnArrearsAgeingParameterName, LoanApiConstants.createStandingInstructionAtDisbursementParameterName,
|
||||
LoanApiConstants.isFloatingInterestRateParameterName, LoanApiConstants.interestRateDifferentialParameterName,
|
||||
LoanApiConstants.repaymentFrequencyNthDayTypeParameterName, LoanApiConstants.repaymentFrequencyDayOfWeekTypeParameterName,
|
||||
LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.datatables, LoanApiConstants.isEqualAmortizationParam));
|
||||
LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.datatables, LoanApiConstants.isEqualAmortizationParam,
|
||||
LoanProductConstants.ratesParamName));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
|
||||
+1
-1
@@ -91,7 +91,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
LoanApiConstants.linkAccountIdParameterName, LoanApiConstants.disbursementDataParameterName,
|
||||
LoanApiConstants.emiAmountParameterName, LoanApiConstants.maxOutstandingBalanceParameterName,
|
||||
LoanProductConstants.graceOnArrearsAgeingParameterName, LoanApiConstants.createStandingInstructionAtDisbursementParameterName,
|
||||
LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.datatables, LoanApiConstants.isEqualAmortizationParam));
|
||||
LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.datatables, LoanApiConstants.isEqualAmortizationParam, LoanProductConstants.ratesParamName));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final CalculateLoanScheduleQueryFromApiJsonHelper apiJsonHelper;
|
||||
|
||||
+11
-2
@@ -126,6 +126,7 @@ import org.apache.fineract.portfolio.loanproduct.serialization.LoanProductDataVa
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.note.domain.Note;
|
||||
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
||||
import org.apache.fineract.portfolio.rate.service.RateAssembler;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
@@ -178,6 +179,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
private final FineractEntityToEntityMappingRepository repository;
|
||||
private final FineractEntityRelationRepository fineractEntityRelationRepository;
|
||||
private final LoanProductReadPlatformService loanProductReadPlatformService;
|
||||
private final RateAssembler rateAssembler;
|
||||
|
||||
@Autowired
|
||||
public LoanApplicationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper,
|
||||
@@ -200,7 +202,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
final LoanScheduleAssembler loanScheduleAssembler, final LoanUtilService loanUtilService,
|
||||
final CalendarReadPlatformService calendarReadPlatformService, final GlobalConfigurationRepositoryWrapper globalConfigurationRepository,
|
||||
final FineractEntityToEntityMappingRepository repository, final FineractEntityRelationRepository fineractEntityRelationRepository,
|
||||
final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService, final LoanProductReadPlatformService loanProductReadPlatformService) {
|
||||
final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService, final LoanProductReadPlatformService loanProductReadPlatformService,
|
||||
final RateAssembler rateAssembler) {
|
||||
this.context = context;
|
||||
this.fromJsonHelper = fromJsonHelper;
|
||||
this.loanApplicationTransitionApiJsonValidator = loanApplicationTransitionApiJsonValidator;
|
||||
@@ -236,7 +239,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
this.repository = repository;
|
||||
this.fineractEntityRelationRepository = fineractEntityRelationRepository;
|
||||
this.loanProductReadPlatformService = loanProductReadPlatformService;
|
||||
|
||||
this.rateAssembler = rateAssembler;
|
||||
}
|
||||
|
||||
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
|
||||
@@ -858,6 +861,12 @@ public void checkForProductMixRestrictions(final Loan loan) {
|
||||
existingLoanApplication.recalculateAllCharges();
|
||||
}
|
||||
|
||||
//Changes to modify loan rates.
|
||||
if (command.hasParameter(LoanProductConstants.ratesParamName)) {
|
||||
existingLoanApplication.updateLoanRates(rateAssembler.fromParsedJson(command.parsedJson()));
|
||||
}
|
||||
|
||||
|
||||
this.fromApiJsonDeserializer.validateLoanTermAndRepaidEveryValues(existingLoanApplication.getTermFrequency(),
|
||||
existingLoanApplication.getTermPeriodFrequencyType(), productRelatedDetail.getNumberOfRepayments(),
|
||||
productRelatedDetail.getRepayEvery(), productRelatedDetail.getRepaymentPeriodFrequencyType().getValue(),
|
||||
|
||||
+14
-4
@@ -25,6 +25,8 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
@@ -71,6 +73,7 @@ import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementData
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler;
|
||||
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
@@ -78,6 +81,8 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanTransactionProcessin
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.InvalidCurrencyException;
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.LinkedAccountRequiredException;
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.rate.domain.Rate;
|
||||
import org.apache.fineract.portfolio.rate.service.RateAssembler;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -104,6 +109,7 @@ public class LoanAssembler {
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final RateAssembler rateAssembler;
|
||||
|
||||
@Autowired
|
||||
public LoanAssembler(final FromJsonHelper fromApiJsonHelper, final LoanRepositoryWrapper loanRepository,
|
||||
@@ -115,7 +121,7 @@ public class LoanAssembler {
|
||||
final CollateralAssembler loanCollateralAssembler, final LoanSummaryWrapper loanSummaryWrapper,
|
||||
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
final HolidayRepository holidayRepository, final ConfigurationDomainService configurationDomainService,
|
||||
final WorkingDaysRepositoryWrapper workingDaysRepository, final LoanUtilService loanUtilService) {
|
||||
final WorkingDaysRepositoryWrapper workingDaysRepository, final LoanUtilService loanUtilService, RateAssembler rateAssembler) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
this.loanRepository = loanRepository;
|
||||
this.loanProductRepository = loanProductRepository;
|
||||
@@ -134,6 +140,7 @@ public class LoanAssembler {
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.workingDaysRepository = workingDaysRepository;
|
||||
this.loanUtilService = loanUtilService;
|
||||
this.rateAssembler = rateAssembler;
|
||||
}
|
||||
|
||||
public Loan assembleFrom(final Long accountId) {
|
||||
@@ -222,6 +229,9 @@ public class LoanAssembler {
|
||||
Client client = null;
|
||||
Group group = null;
|
||||
|
||||
//Here we add Rates to LoanApplication
|
||||
final List<Rate> rates = this.rateAssembler.fromParsedJson(element);
|
||||
|
||||
final LoanProductRelatedDetail loanProductRelatedDetail = this.loanScheduleAssembler.assembleLoanProductRelatedDetail(element);
|
||||
|
||||
final BigDecimal interestRateDifferential = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.interestRateDifferentialParameterName, element);
|
||||
@@ -250,21 +260,21 @@ public class LoanAssembler {
|
||||
loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanType.getId().intValue(),
|
||||
loanProduct, fund, loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges,
|
||||
collateral, syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance,
|
||||
createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential);
|
||||
createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates);
|
||||
|
||||
} else if (group != null) {
|
||||
|
||||
loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanType.getId().intValue(), loanProduct, fund, loanOfficer,
|
||||
loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, collateral,
|
||||
syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance,
|
||||
createStandingInstructionAtDisbursement,isFloatingInterestRate, interestRateDifferential);
|
||||
createStandingInstructionAtDisbursement,isFloatingInterestRate, interestRateDifferential, rates);
|
||||
|
||||
} else if (client != null) {
|
||||
|
||||
loanApplication = Loan.newIndividualLoanApplication(accountNo, client, loanType.getId().intValue(), loanProduct, fund,
|
||||
loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, collateral,
|
||||
fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement,
|
||||
isFloatingInterestRate, interestRateDifferential);
|
||||
isFloatingInterestRate, interestRateDifferential, rates);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
|
||||
|
||||
public interface LoanChargePaidByReadPlatformService {
|
||||
|
||||
List<LoanChargePaidByData> getLoanChargesPaidByTransactionId(Long id);
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class LoanChargePaidByReadPlatformServiceImpl implements
|
||||
LoanChargePaidByReadPlatformService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final PlatformSecurityContext context;
|
||||
|
||||
@Autowired
|
||||
public LoanChargePaidByReadPlatformServiceImpl(final PlatformSecurityContext context,
|
||||
final RoutingDataSource dataSource) {
|
||||
this.context = context;
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LoanChargePaidByData> getLoanChargesPaidByTransactionId(Long transactionId) {
|
||||
this.context.authenticatedUser();
|
||||
final LoanChargePaidByMapper rm = new LoanChargePaidByMapper();
|
||||
final String sql = "select " + rm.loanChargePaidBySchema() + " where lcpd.loan_transaction_id = ?";
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[]{transactionId});
|
||||
}
|
||||
|
||||
private static final class LoanChargePaidByMapper implements RowMapper<LoanChargePaidByData> {
|
||||
|
||||
public String loanChargePaidBySchema() {
|
||||
return "lcpd.id as id, lcpd.amount as amount, lcpd.installment_number as installmentNumber," +
|
||||
" lcpd.loan_charge_id as chargeId, lcpd.loan_transaction_id as transactionId, " +
|
||||
" c.name as chargeName"
|
||||
+ " from m_loan_charge_paid_by lcpd"
|
||||
+ " join m_loan_charge lc on lc.id=lcpd.loan_charge_Id"
|
||||
+ " join m_charge c on c.id=lc.charge_id";
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanChargePaidByData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum)
|
||||
throws SQLException {
|
||||
final Long id = rs.getLong("id");
|
||||
final BigDecimal amount = rs.getBigDecimal("amount");
|
||||
final Integer installmentNumber = rs.getInt("installmentNumber");
|
||||
final Long chargeId = rs.getLong("chargeId");
|
||||
final Long transactionId = rs.getLong("transactionId");
|
||||
final String chargeName = rs.getString("chargeName");
|
||||
return new LoanChargePaidByData(id, amount, installmentNumber, chargeId, transactionId, chargeName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -134,5 +134,6 @@ public interface LoanProductConstants {
|
||||
|
||||
public static final String isEqualAmortizationParam = "isEqualAmortization";
|
||||
|
||||
public static final String ratesParamName = "rates";
|
||||
|
||||
}
|
||||
|
||||
+20
-4
@@ -52,6 +52,7 @@ import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToG
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
@@ -77,6 +78,8 @@ import org.apache.fineract.portfolio.loanproduct.service.LoanDropdownReadPlatfor
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
|
||||
import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadPlatformService;
|
||||
import org.apache.fineract.portfolio.rate.data.RateData;
|
||||
import org.apache.fineract.portfolio.rate.service.RateReadService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -102,7 +105,8 @@ public class LoanProductsApiResource {
|
||||
"interestCalculationPeriodTypeOptions", "transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions",
|
||||
"accountingRuleOptions", "accountingMappingOptions", "floatingRateOptions", "isLinkedToFloatingInterestRates",
|
||||
"floatingRatesId", "interestRateDifferential", "minDifferentialLendingRate", "defaultDifferentialLendingRate",
|
||||
"maxDifferentialLendingRate", "isFloatingInterestRateCalculationAllowed", LoanProductConstants.canUseForTopup, LoanProductConstants.isEqualAmortizationParam));
|
||||
"maxDifferentialLendingRate", "isFloatingInterestRateCalculationAllowed", LoanProductConstants.canUseForTopup, LoanProductConstants.isEqualAmortizationParam,
|
||||
LoanProductConstants.ratesParamName));
|
||||
|
||||
private final Set<String> PRODUCT_MIX_DATA_PARAMETERS = new HashSet<>(Arrays.asList("productId", "productName", "restrictedProducts",
|
||||
"allowedProducts", "productOptions"));
|
||||
@@ -125,6 +129,9 @@ public class LoanProductsApiResource {
|
||||
private final DropdownReadPlatformService commonDropdownReadPlatformService;
|
||||
private final PaymentTypeReadPlatformService paymentTypeReadPlatformService;
|
||||
private final FloatingRatesReadPlatformService floatingRateReadPlatformService;
|
||||
private final RateReadService rateReadService;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
|
||||
|
||||
@Autowired
|
||||
public LoanProductsApiResource(final PlatformSecurityContext context, final LoanProductReadPlatformService readPlatformService,
|
||||
@@ -139,7 +146,8 @@ public class LoanProductsApiResource {
|
||||
final ProductMixReadPlatformService productMixReadPlatformService,
|
||||
final DropdownReadPlatformService commonDropdownReadPlatformService,
|
||||
PaymentTypeReadPlatformService paymentTypeReadPlatformService,
|
||||
final FloatingRatesReadPlatformService floatingRateReadPlatformService) {
|
||||
final FloatingRatesReadPlatformService floatingRateReadPlatformService, final RateReadService rateReadService,
|
||||
final ConfigurationDomainService configurationDomainService) {
|
||||
this.context = context;
|
||||
this.loanProductReadPlatformService = readPlatformService;
|
||||
this.chargeReadPlatformService = chargeReadPlatformService;
|
||||
@@ -156,6 +164,8 @@ public class LoanProductsApiResource {
|
||||
this.commonDropdownReadPlatformService = commonDropdownReadPlatformService;
|
||||
this.paymentTypeReadPlatformService = paymentTypeReadPlatformService;
|
||||
this.floatingRateReadPlatformService = floatingRateReadPlatformService;
|
||||
this.rateReadService = rateReadService;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
}
|
||||
|
||||
@POST
|
||||
@@ -288,6 +298,12 @@ public class LoanProductsApiResource {
|
||||
penaltyOptions = null;
|
||||
}
|
||||
|
||||
boolean isRatesEnabled = this.configurationDomainService.isSubRatesEnabled();
|
||||
Collection<RateData> rateOptions = this.rateReadService.retrieveLoanApplicableRates();
|
||||
if(rateOptions.isEmpty()){
|
||||
rateOptions = null;
|
||||
}
|
||||
|
||||
final Collection<CurrencyData> currencyOptions = this.currencyReadPlatformService.retrieveAllowedCurrencies();
|
||||
final List<EnumOptionData> amortizationTypeOptions = this.dropdownReadPlatformService.retrieveLoanAmortizationTypeOptions();
|
||||
final List<EnumOptionData> interestTypeOptions = this.dropdownReadPlatformService.retrieveLoanInterestTypeOptions();
|
||||
@@ -331,11 +347,11 @@ public class LoanProductsApiResource {
|
||||
|
||||
return new LoanProductData(productData, chargeOptions, penaltyOptions, paymentTypeOptions, currencyOptions,
|
||||
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions,
|
||||
interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions, accountOptions,
|
||||
interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions, rateOptions, accountOptions,
|
||||
accountingRuleTypeOptions, loanCycleValueConditionTypeOptions, daysInMonthTypeOptions, daysInYearTypeOptions,
|
||||
interestRecalculationCompoundingTypeOptions, rescheduleStrategyTypeOptions, interestRecalculationFrequencyTypeOptions,
|
||||
preCloseInterestCalculationStrategyOptions, floatingRateOptions, interestRecalculationNthDayTypeOptions,
|
||||
interestRecalculationDayOfWeekTypeOptions);
|
||||
interestRecalculationDayOfWeekTypeOptions, isRatesEnabled);
|
||||
}
|
||||
|
||||
}
|
||||
+34
-9
@@ -47,6 +47,7 @@ import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductConfigurableAttributes;
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
|
||||
import org.apache.fineract.portfolio.rate.data.RateData;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@@ -133,6 +134,10 @@ public class LoanProductData implements Serializable {
|
||||
private Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings;
|
||||
private Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings;
|
||||
|
||||
// rates
|
||||
private final boolean isRatesEnabled;
|
||||
private final Collection<RateData> rates;
|
||||
|
||||
// template related
|
||||
private final Collection<FundData> fundOptions;
|
||||
@SuppressWarnings("unused")
|
||||
@@ -146,6 +151,7 @@ public class LoanProductData implements Serializable {
|
||||
private final List<EnumOptionData> interestCalculationPeriodTypeOptions;
|
||||
private final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions;
|
||||
private final Collection<ChargeData> chargeOptions;
|
||||
private final Collection<RateData> rateOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<ChargeData> penaltyOptions;
|
||||
@SuppressWarnings("unused")
|
||||
@@ -261,6 +267,9 @@ public class LoanProductData implements Serializable {
|
||||
final boolean syncExpectedWithDisbursementDate = false;
|
||||
final boolean canUseForTopup = false;
|
||||
final boolean isEqualAmortization = false;
|
||||
final Collection<RateData> rateOptions = null;
|
||||
final Collection<RateData> rates = null;
|
||||
final boolean isRatesEnabled= false;
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType,
|
||||
@@ -275,7 +284,7 @@ public class LoanProductData implements Serializable {
|
||||
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId, floatingRateName,
|
||||
interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, maxDifferentialLendingRate,
|
||||
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
||||
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
|
||||
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled);
|
||||
|
||||
}
|
||||
|
||||
@@ -353,6 +362,9 @@ public class LoanProductData implements Serializable {
|
||||
final boolean syncExpectedWithDisbursementDate = false;
|
||||
final boolean canUseForTopup = false;
|
||||
final boolean isEqualAmortization = false;
|
||||
final Collection<RateData> rateOptions = null;
|
||||
final Collection<RateData> rates = null;
|
||||
final boolean isRatesEnabled = false;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -368,7 +380,7 @@ public class LoanProductData implements Serializable {
|
||||
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId, floatingRateName,
|
||||
interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, maxDifferentialLendingRate,
|
||||
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
||||
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
|
||||
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled);
|
||||
|
||||
}
|
||||
|
||||
@@ -453,6 +465,9 @@ public class LoanProductData implements Serializable {
|
||||
final boolean syncExpectedWithDisbursementDate = false;
|
||||
final boolean canUseForTopup = false;
|
||||
final boolean isEqualAmortization = false;
|
||||
final Collection<RateData> rateOptions = null;
|
||||
final Collection<RateData> rates = null;
|
||||
final boolean isRatesEnabled = false;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -468,7 +483,7 @@ public class LoanProductData implements Serializable {
|
||||
installmentAmountInMultiplesOf, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
||||
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
|
||||
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled);
|
||||
|
||||
}
|
||||
|
||||
@@ -547,6 +562,9 @@ public class LoanProductData implements Serializable {
|
||||
final boolean syncExpectedWithDisbursementDate = false;
|
||||
final boolean canUseForTopup = false;
|
||||
final boolean isEqualAmortization = false;
|
||||
final Collection<RateData> rateOptions = null;
|
||||
final Collection<RateData> rates = null;
|
||||
final boolean isRatesEnabled = false;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -562,7 +580,7 @@ public class LoanProductData implements Serializable {
|
||||
installmentAmountInMultiplesOf, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
||||
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
|
||||
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled);
|
||||
|
||||
}
|
||||
|
||||
@@ -601,7 +619,8 @@ public class LoanProductData implements Serializable {
|
||||
BigDecimal minDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate, BigDecimal maxDifferentialLendingRate,
|
||||
boolean isFloatingInterestRateCalculationAllowed, final boolean isVariableInstallmentsAllowed,
|
||||
final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments,
|
||||
final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup, final boolean isEqualAmortization) {
|
||||
final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup, final boolean isEqualAmortization, Collection<RateData> rateOptions, Collection<RateData> rates,
|
||||
final boolean isRatesEnabled) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
@@ -653,6 +672,9 @@ public class LoanProductData implements Serializable {
|
||||
this.status = status;
|
||||
this.externalId = externalId;
|
||||
this.minimumDaysBetweenDisbursalAndFirstRepayment = minimumDaysBetweenDisbursalAndFirstRepayment;
|
||||
this.rateOptions = rateOptions;
|
||||
this.rates = rates;
|
||||
this.isRatesEnabled = isRatesEnabled;
|
||||
|
||||
this.chargeOptions = null;
|
||||
this.penaltyOptions = null;
|
||||
@@ -715,7 +737,7 @@ public class LoanProductData implements Serializable {
|
||||
final List<EnumOptionData> interestTypeOptions, final List<EnumOptionData> interestCalculationPeriodTypeOptions,
|
||||
final List<EnumOptionData> repaymentFrequencyTypeOptions, final List<EnumOptionData> interestRateFrequencyTypeOptions,
|
||||
final Collection<FundData> fundOptions, final Collection<TransactionProcessingStrategyData> transactionStrategyOptions,
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions, final List<EnumOptionData> accountingRuleOptions,
|
||||
final Collection<RateData> rateOptions, final Map<String, List<GLAccountData>> accountingMappingOptions, final List<EnumOptionData> accountingRuleOptions,
|
||||
final List<EnumOptionData> valueConditionTypeOptions, final List<EnumOptionData> daysInMonthTypeOptions,
|
||||
final List<EnumOptionData> daysInYearTypeOptions, final List<EnumOptionData> interestRecalculationCompoundingTypeOptions,
|
||||
final List<EnumOptionData> rescheduleStrategyTypeOptions, final List<EnumOptionData> interestRecalculationFrequencyTypeOptions,
|
||||
@@ -780,6 +802,7 @@ public class LoanProductData implements Serializable {
|
||||
this.currency = productData.currency;
|
||||
this.fundOptions = fundOptions;
|
||||
this.transactionProcessingStrategyOptions = transactionStrategyOptions;
|
||||
this.rateOptions = rateOptions;
|
||||
this.floatingRateOptions = floatingRateOptions;
|
||||
if (this.transactionProcessingStrategyOptions != null && this.transactionProcessingStrategyOptions.size() == 1) {
|
||||
final List<TransactionProcessingStrategyData> listOfOptions = new ArrayList<>(this.transactionProcessingStrategyOptions);
|
||||
@@ -843,6 +866,8 @@ public class LoanProductData implements Serializable {
|
||||
this.syncExpectedWithDisbursementDate = productData.syncExpectedWithDisbursementDate;
|
||||
this.canUseForTopup = productData.canUseForTopup;
|
||||
this.isEqualAmortization = productData.isEqualAmortization;
|
||||
this.rates = productData.rates;
|
||||
this.isRatesEnabled = isRatesEnabled;
|
||||
}
|
||||
|
||||
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
|
||||
@@ -1198,9 +1223,9 @@ public class LoanProductData implements Serializable {
|
||||
return syncExpectedWithDisbursementDate;
|
||||
}
|
||||
|
||||
public boolean canUseForTopup() {
|
||||
return this.canUseForTopup;
|
||||
}
|
||||
public boolean canUseForTopup() {
|
||||
return this.canUseForTopup;
|
||||
}
|
||||
|
||||
public BigDecimal getInterestRateDifferential() {
|
||||
return this.interestRateDifferential;
|
||||
|
||||
+48
-3
@@ -61,6 +61,7 @@ import org.apache.fineract.portfolio.floatingrates.domain.FloatingRate;
|
||||
import org.apache.fineract.portfolio.fund.domain.Fund;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.apache.fineract.portfolio.rate.domain.Rate;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
/**
|
||||
@@ -99,6 +100,10 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
||||
@JoinTable(name = "m_product_loan_charge", joinColumns = @JoinColumn(name = "product_loan_id"), inverseJoinColumns = @JoinColumn(name = "charge_id"))
|
||||
private List<Charge> charges;
|
||||
|
||||
@ManyToMany(fetch = FetchType.LAZY)
|
||||
@JoinTable(name = "m_product_loan_rate", joinColumns = @JoinColumn(name = "product_loan_id"), inverseJoinColumns = @JoinColumn(name = "rate_id"))
|
||||
private List<Rate> rates;
|
||||
|
||||
@Embedded
|
||||
private LoanProductRelatedDetail loanProductRelatedDetail;
|
||||
|
||||
@@ -184,7 +189,8 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
||||
private boolean isEqualAmortization = false;
|
||||
|
||||
public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
|
||||
final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator, FloatingRate floatingRate) {
|
||||
final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator, FloatingRate floatingRate,
|
||||
final List<Rate> productRates) {
|
||||
|
||||
final String name = command.stringValueOfParameterNamed("name");
|
||||
final String shortName = command.stringValueOfParameterNamed(LoanProductConstants.shortName);
|
||||
@@ -350,7 +356,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
||||
installmentAmountInMultiplesOf, loanConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRate,
|
||||
interestRateDifferential, minDifferentialLendingRate, maxDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGapBetweenInstallments,
|
||||
maximumGapBetweenInstallments, syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
|
||||
maximumGapBetweenInstallments, syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, productRates);
|
||||
|
||||
}
|
||||
|
||||
@@ -580,7 +586,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
||||
BigDecimal minDifferentialLendingRate, BigDecimal maxDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate,
|
||||
Boolean isFloatingInterestRateCalculationAllowed, final Boolean isVariableInstallmentsAllowed,
|
||||
final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments,
|
||||
final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup, final boolean isEqualAmortization) {
|
||||
final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup, final boolean isEqualAmortization, final List<Rate> rates) {
|
||||
this.fund = fund;
|
||||
this.transactionProcessingStrategy = transactionProcessingStrategy;
|
||||
this.name = name.trim();
|
||||
@@ -658,6 +664,10 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
||||
syncExpectedWithDisbursementDate;
|
||||
this.canUseForTopup = canUseForTopup;
|
||||
this.isEqualAmortization = isEqualAmortization;
|
||||
|
||||
if(rates != null){
|
||||
this.rates = rates;
|
||||
}
|
||||
}
|
||||
|
||||
public MonetaryCurrency getCurrency() {
|
||||
@@ -699,6 +709,25 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
||||
return updated;
|
||||
}
|
||||
|
||||
public boolean updateRates(final List<Rate> newProductRates) {
|
||||
if (newProductRates == null) { return false; }
|
||||
|
||||
boolean updated = false;
|
||||
if (this.rates != null) {
|
||||
final Set<Rate> currentSetOfCharges = new HashSet<>(this.rates);
|
||||
final Set<Rate> newSetOfCharges = new HashSet<>(newProductRates);
|
||||
|
||||
if (!currentSetOfCharges.equals(newSetOfCharges)) {
|
||||
updated = true;
|
||||
this.rates = newProductRates;
|
||||
}
|
||||
} else {
|
||||
updated = true;
|
||||
this.rates = newProductRates;
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
public Integer getAccountingType() {
|
||||
return this.accountingRule;
|
||||
}
|
||||
@@ -1043,6 +1072,13 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
||||
this.canUseForTopup = newValue;
|
||||
}
|
||||
|
||||
if (command.hasParameter(LoanProductConstants.ratesParamName)) {
|
||||
final JsonArray jsonArray = command.arrayOfParameterNamed(LoanProductConstants.ratesParamName);
|
||||
if (jsonArray != null) {
|
||||
actualChanges.put(LoanProductConstants.ratesParamName, command.jsonFragment(LoanProductConstants.ratesParamName));
|
||||
}
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
@@ -1379,4 +1415,13 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
||||
this.isEqualAmortization = isEqualAmortization;
|
||||
}
|
||||
|
||||
|
||||
public List<Rate> getRates() {
|
||||
return rates;
|
||||
}
|
||||
|
||||
public void setRates(List<Rate> rates) {
|
||||
this.rates = rates;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
@@ -163,4 +163,12 @@ public class LoanProductMinMaxConstraints {
|
||||
this.maxNominalInterestRatePerPeriod = null;
|
||||
}
|
||||
|
||||
|
||||
public void setMinNominalInterestRatePerPeriod(BigDecimal minNominalInterestRatePerPeriod) {
|
||||
this.minNominalInterestRatePerPeriod = minNominalInterestRatePerPeriod;
|
||||
}
|
||||
|
||||
public void setMaxNominalInterestRatePerPeriod(BigDecimal maxNominalInterestRatePerPeriod) {
|
||||
this.maxNominalInterestRatePerPeriod = maxNominalInterestRatePerPeriod;
|
||||
}
|
||||
}
|
||||
+3
@@ -666,4 +666,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
this.isEqualAmortization = isEqualAmortization;
|
||||
}
|
||||
|
||||
public void setNominalInterestRatePerPeriod(BigDecimal nominalInterestRatePerPeriod) {
|
||||
this.nominalInterestRatePerPeriod = nominalInterestRatePerPeriod;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -106,7 +106,8 @@ public final class LoanProductDataValidator {
|
||||
LoanProductConstants.recalculationRestFrequencyWeekdayParamName,
|
||||
LoanProductConstants.recalculationRestFrequencyNthDayParamName, LoanProductConstants.recalculationRestFrequencyOnDayParamName,
|
||||
LoanProductConstants.isCompoundingToBePostedAsTransactionParamName, LoanProductConstants.allowCompoundingOnEodParamName,
|
||||
LoanProductConstants.canUseForTopup, LoanProductConstants.isEqualAmortizationParam));
|
||||
LoanProductConstants.canUseForTopup, LoanProductConstants.isEqualAmortizationParam,
|
||||
LoanProductConstants.ratesParamName));
|
||||
|
||||
private static final String[] supportedloanConfigurableAttributes = {LoanProductConstants.amortizationTypeParamName,
|
||||
LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyIdParamName,
|
||||
|
||||
+16
-6
@@ -42,6 +42,8 @@ import org.apache.fineract.portfolio.loanproduct.data.LoanProductInterestRecalcu
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductConfigurableAttributes;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductParamType;
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.rate.data.RateData;
|
||||
import org.apache.fineract.portfolio.rate.service.RateReadService;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
@@ -55,16 +57,18 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
private final PlatformSecurityContext context;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final ChargeReadPlatformService chargeReadPlatformService;
|
||||
private final RateReadService rateReadService;
|
||||
private final FineractEntityAccessUtil fineractEntityAccessUtil;
|
||||
|
||||
@Autowired
|
||||
public LoanProductReadPlatformServiceImpl(final PlatformSecurityContext context,
|
||||
final ChargeReadPlatformService chargeReadPlatformService, final RoutingDataSource dataSource,
|
||||
final FineractEntityAccessUtil fineractEntityAccessUtil) {
|
||||
final FineractEntityAccessUtil fineractEntityAccessUtil, final RateReadService rateReadService) {
|
||||
this.context = context;
|
||||
this.chargeReadPlatformService = chargeReadPlatformService;
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
this.fineractEntityAccessUtil = fineractEntityAccessUtil;
|
||||
this.rateReadService=rateReadService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,8 +76,9 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
|
||||
try {
|
||||
final Collection<ChargeData> charges = this.chargeReadPlatformService.retrieveLoanProductCharges(loanProductId);
|
||||
final Collection<RateData> rates = this.rateReadService.retrieveProductLoanRates(loanProductId);
|
||||
final Collection<LoanProductBorrowerCycleVariationData> borrowerCycleVariationDatas = retrieveLoanProductBorrowerCycleVariations(loanProductId);
|
||||
final LoanProductMapper rm = new LoanProductMapper(charges, borrowerCycleVariationDatas);
|
||||
final LoanProductMapper rm = new LoanProductMapper(charges, borrowerCycleVariationDatas, rates);
|
||||
final String sql = "select " + rm.loanProductSchema() + " where lp.id = ?";
|
||||
|
||||
return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { loanProductId });
|
||||
@@ -95,7 +100,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final LoanProductMapper rm = new LoanProductMapper(null, null);
|
||||
final LoanProductMapper rm = new LoanProductMapper(null, null, null);
|
||||
|
||||
String sql = "select " + rm.loanProductSchema();
|
||||
|
||||
@@ -172,10 +177,13 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
|
||||
private final Collection<LoanProductBorrowerCycleVariationData> borrowerCycleVariationDatas;
|
||||
|
||||
private final Collection<RateData> rates;
|
||||
|
||||
public LoanProductMapper(final Collection<ChargeData> charges,
|
||||
final Collection<LoanProductBorrowerCycleVariationData> borrowerCycleVariationDatas) {
|
||||
final Collection<LoanProductBorrowerCycleVariationData> borrowerCycleVariationDatas, final Collection<RateData> rates) {
|
||||
this.charges = charges;
|
||||
this.borrowerCycleVariationDatas = borrowerCycleVariationDatas;
|
||||
this.rates = rates;
|
||||
}
|
||||
|
||||
public String loanProductSchema() {
|
||||
@@ -448,6 +456,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
final boolean syncExpectedWithDisbursementDate = rs.getBoolean("syncExpectedWithDisbursementDate");
|
||||
|
||||
final boolean canUseForTopup = rs.getBoolean("canUseForTopup");
|
||||
final Collection<RateData> rateOptions= null;
|
||||
final boolean isRatesEnabled = false;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -464,7 +474,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
installmentAmountInMultiplesOf, allowAttributeOverrides, isLinkedToFloatingInterestRates, floatingRateId,
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableIntallmentsAllowed, minimumGap,
|
||||
maximumGap, syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
|
||||
maximumGap, syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, this.rates, isRatesEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,7 +544,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
public Collection<LoanProductData> retrieveAllLoanProductsForCurrency(String currencyCode) {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final LoanProductMapper rm = new LoanProductMapper(null, null);
|
||||
final LoanProductMapper rm = new LoanProductMapper(null, null, null);
|
||||
|
||||
String sql = "select " + rm.loanProductSchema() + " where lp.currency_code= ? ";
|
||||
|
||||
|
||||
+38
-2
@@ -49,6 +49,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionProcessingStrategyRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionProcessingStrategyNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
|
||||
@@ -57,6 +58,8 @@ import org.apache.fineract.portfolio.loanproduct.exception.LoanProductCannotBeMo
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductDateException;
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanproduct.serialization.LoanProductDataValidator;
|
||||
import org.apache.fineract.portfolio.rate.domain.Rate;
|
||||
import org.apache.fineract.portfolio.rate.domain.RateRepositoryWrapper;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -76,6 +79,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
||||
private final FundRepository fundRepository;
|
||||
private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
|
||||
private final ChargeRepositoryWrapper chargeRepository;
|
||||
private final RateRepositoryWrapper rateRepository;
|
||||
private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService;
|
||||
private final FineractEntityAccessUtil fineractEntityAccessUtil;
|
||||
private final FloatingRateRepositoryWrapper floatingRateRepository;
|
||||
@@ -87,7 +91,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
||||
final LoanProductDataValidator fromApiJsonDeserializer, final LoanProductRepository loanProductRepository,
|
||||
final AprCalculator aprCalculator, final FundRepository fundRepository,
|
||||
final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
|
||||
final ChargeRepositoryWrapper chargeRepository,
|
||||
final ChargeRepositoryWrapper chargeRepository, final RateRepositoryWrapper rateRepository,
|
||||
final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService,
|
||||
final FineractEntityAccessUtil fineractEntityAccessUtil,
|
||||
final FloatingRateRepositoryWrapper floatingRateRepository,
|
||||
@@ -100,6 +104,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
||||
this.fundRepository = fundRepository;
|
||||
this.loanTransactionProcessingStrategyRepository = loanTransactionProcessingStrategyRepository;
|
||||
this.chargeRepository = chargeRepository;
|
||||
this.rateRepository = rateRepository;
|
||||
this.accountMappingWritePlatformService = accountMappingWritePlatformService;
|
||||
this.fineractEntityAccessUtil = fineractEntityAccessUtil;
|
||||
this.floatingRateRepository = floatingRateRepository;
|
||||
@@ -125,6 +130,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
||||
|
||||
final String currencyCode = command.stringValueOfParameterNamed("currencyCode");
|
||||
final List<Charge> charges = assembleListOfProductCharges(command, currencyCode);
|
||||
final List<Rate> rates = assembleListOfProductRates(command);
|
||||
|
||||
FloatingRate floatingRate = null;
|
||||
if(command.parameterExists("floatingRatesId")){
|
||||
@@ -132,7 +138,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
||||
.findOneWithNotFoundDetection(command.longValueOfParameterNamed("floatingRatesId"));
|
||||
}
|
||||
final LoanProduct loanproduct = LoanProduct.assembleFromJson(fund, loanTransactionProcessingStrategy, charges, command,
|
||||
this.aprCalculator, floatingRate);
|
||||
this.aprCalculator, floatingRate, rates);
|
||||
loanproduct.updateLoanProductInRelatedClasses();
|
||||
|
||||
this.loanProductRepository.save(loanproduct);
|
||||
@@ -237,6 +243,14 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
||||
.updateLoanProductToGLAccountMapping(product.getId(), command, accountingTypeChanged, product.getAccountingType());
|
||||
changes.putAll(accountingMappingChanges);
|
||||
|
||||
if (changes.containsKey(LoanProductConstants.ratesParamName)) {
|
||||
final List<Rate> productRates = assembleListOfProductRates(command);
|
||||
final boolean updated = product.updateRates(productRates);
|
||||
if (!updated) {
|
||||
changes.remove(LoanProductConstants.ratesParamName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!changes.isEmpty()) {
|
||||
this.loanProductRepository.saveAndFlush(product);
|
||||
}
|
||||
@@ -299,6 +313,28 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
||||
return charges;
|
||||
}
|
||||
|
||||
private List<Rate> assembleListOfProductRates(final JsonCommand command) {
|
||||
|
||||
final List<Rate> rates = new ArrayList<>();
|
||||
|
||||
if (command.parameterExists("rates")) {
|
||||
final JsonArray ratesArray = command.arrayOfParameterNamed("rates");
|
||||
if (ratesArray != null) {
|
||||
List<Long> idList = new ArrayList<>();
|
||||
for (int i = 0; i < ratesArray.size(); i++) {
|
||||
|
||||
final JsonObject jsonObject = ratesArray.get(i).getAsJsonObject();
|
||||
if (jsonObject.has("id")) {
|
||||
idList.add(jsonObject.get("id").getAsLong());
|
||||
}
|
||||
}
|
||||
rates.addAll(this.rateRepository.findMultipleWithNotFoundDetection(idList));
|
||||
}
|
||||
}
|
||||
|
||||
return rates;
|
||||
}
|
||||
|
||||
/*
|
||||
* Guaranteed to throw an exception no matter what the data integrity issue
|
||||
* is.
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.rate.api;
|
||||
|
||||
public class RateApiConstants {
|
||||
|
||||
public static final String approveUserIdParamName = "approveUserId";
|
||||
public static final String rate = "rate";
|
||||
public static final String rateName = "name";
|
||||
public static final String ratePercentage = "percentage";
|
||||
public static final String rateProductApply = "productApply";
|
||||
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.fineract.portfolio.rate.api;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.rate.data.RateData;
|
||||
import org.apache.fineract.portfolio.rate.service.RateReadService;
|
||||
import org.apache.fineract.portfolio.rate.service.RateWriteService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.ws.rs.*;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Bowpi GT Created by Jose on 19/07/2017.
|
||||
*/
|
||||
|
||||
@Path("/rates")
|
||||
@Component
|
||||
@Scope("singleton")
|
||||
public class RateApiResource {
|
||||
|
||||
private final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(
|
||||
Arrays.asList("id", "name", "percentage", "productApply", "active"));
|
||||
private final String resourceNameForPermissions = "RATE";
|
||||
private final PlatformSecurityContext context;
|
||||
private final RateReadService readPlatformService;
|
||||
private final DefaultToApiJsonSerializer<RateData> toApiJsonSerializer;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public RateApiResource(final PlatformSecurityContext context,
|
||||
final RateReadService rateReadService,
|
||||
final DefaultToApiJsonSerializer<RateData> toApiJsonSerializer,
|
||||
final ApiRequestParameterHelper apiRequestParameterHelper,
|
||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
|
||||
this.context = context;
|
||||
this.readPlatformService = rateReadService;
|
||||
this.toApiJsonSerializer = toApiJsonSerializer;
|
||||
this.apiRequestParameterHelper = apiRequestParameterHelper;
|
||||
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{rateId}")
|
||||
@Consumes({MediaType.APPLICATION_JSON})
|
||||
@Produces({MediaType.APPLICATION_JSON})
|
||||
public String retrieveRate(@PathParam("rateId") Long rateId, @Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
|
||||
|
||||
final RateData rate = this.readPlatformService.retrieveOne(rateId);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper
|
||||
.process(uriInfo.getQueryParameters());
|
||||
|
||||
return this.toApiJsonSerializer.serialize(settings, rate, this.RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({MediaType.APPLICATION_JSON})
|
||||
@Produces({MediaType.APPLICATION_JSON})
|
||||
public String createRate(final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().createRate()
|
||||
.withJson(apiRequestBodyAsJson).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService
|
||||
.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes({MediaType.APPLICATION_JSON})
|
||||
@Produces({MediaType.APPLICATION_JSON})
|
||||
public String getAllRates(@Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
|
||||
|
||||
Collection<RateData> rates = this.readPlatformService.retrieveAllRates();
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper
|
||||
.process(uriInfo.getQueryParameters());
|
||||
|
||||
return this.toApiJsonSerializer.serialize(settings, rates, this.RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{rateId}")
|
||||
@Consumes({MediaType.APPLICATION_JSON})
|
||||
@Produces({MediaType.APPLICATION_JSON})
|
||||
public String updateRate(@PathParam("rateId") Long rateId, final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateRate(rateId)
|
||||
.withJson(apiRequestBodyAsJson).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService
|
||||
.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.fineract.portfolio.rate.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Bowpi GT Created by Jose on 19/07/2017.
|
||||
*/
|
||||
public class RateData implements Serializable {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
private BigDecimal percentage;
|
||||
|
||||
private String productApply;
|
||||
|
||||
private boolean active;
|
||||
|
||||
public static RateData instance(final Long id, final String name, final BigDecimal percentage,
|
||||
final String productApply, final boolean active) {
|
||||
return new RateData(id, name, percentage, productApply, active);
|
||||
}
|
||||
|
||||
private RateData(final Long id, final String name, final BigDecimal percentage,
|
||||
final String productApply, final boolean active) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.percentage = percentage;
|
||||
this.productApply = productApply;
|
||||
this.active = active;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.fineract.portfolio.rate.domain;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.math.BigDecimal;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
|
||||
|
||||
/**
|
||||
* Bowpi GT Created by Jose on 19/07/2017.
|
||||
*/
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_rate", uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = {"name"}, name = "name")})
|
||||
public class Rate extends AbstractAuditableCustom<AppUser, Long> {
|
||||
|
||||
@Column(name = "name", length = 250, unique = true)
|
||||
private String name;
|
||||
|
||||
@Column(name = "percentage", scale = 10, precision = 2, nullable = false)
|
||||
private BigDecimal percentage;
|
||||
|
||||
@Column(name = "product_apply", length = 100)
|
||||
private String productApply;
|
||||
|
||||
@Column(name = "active", nullable = false)
|
||||
private boolean active;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "approve_user", nullable = true)
|
||||
private AppUser approveUser;
|
||||
|
||||
|
||||
public Rate() {
|
||||
}
|
||||
|
||||
|
||||
public Rate(String name, BigDecimal percentage, String productApply, boolean active,
|
||||
AppUser approveUser) {
|
||||
this.name = name;
|
||||
this.percentage = percentage;
|
||||
this.productApply = productApply;
|
||||
this.active = active;
|
||||
this.approveUser = approveUser;
|
||||
}
|
||||
|
||||
public Rate(String name, BigDecimal percentage, String productApply, boolean active) {
|
||||
this.name = name;
|
||||
this.percentage = percentage;
|
||||
this.productApply = productApply;
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public BigDecimal getPercentage() {
|
||||
return percentage;
|
||||
}
|
||||
|
||||
public void setPercentage(BigDecimal percentage) {
|
||||
this.percentage = percentage;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setActive(boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
public AppUser getApproveUser() {
|
||||
return approveUser;
|
||||
}
|
||||
|
||||
public void setApproveUser(AppUser approveUser) {
|
||||
this.approveUser = approveUser;
|
||||
}
|
||||
|
||||
public String getProductApply() {
|
||||
return productApply;
|
||||
}
|
||||
|
||||
public void setProductApply(String productApply) {
|
||||
this.productApply = productApply;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Rate{" +
|
||||
"name='" + name + '\'' +
|
||||
", percentage=" + percentage +
|
||||
", productApply='" + productApply + '\'' +
|
||||
", active=" + active +
|
||||
", approveUser=" + approveUser +
|
||||
'}';
|
||||
}
|
||||
|
||||
public static Rate from(String name, BigDecimal percentage, String productApply, Boolean active) {
|
||||
return new Rate(name, percentage, productApply, active);
|
||||
}
|
||||
|
||||
public static Rate fromJson(final JsonCommand command, AppUser user) {
|
||||
|
||||
final String name = command.stringValueOfParameterNamed("name");
|
||||
|
||||
final BigDecimal percentage = command.bigDecimalValueOfParameterNamed("percentage");
|
||||
|
||||
final String productApply = command.stringValueOfParameterNamed("productApply");
|
||||
|
||||
final boolean active = command.booleanPrimitiveValueOfParameterNamed("active");
|
||||
|
||||
return new Rate(name, percentage, productApply, active, user);
|
||||
}
|
||||
|
||||
public Map<String, Object> update(final JsonCommand command) {
|
||||
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<>(7);
|
||||
|
||||
final String nameParamName = "name";
|
||||
if (command.isChangeInStringParameterNamed(nameParamName, this.name)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(nameParamName);
|
||||
actualChanges.put(nameParamName, newValue);
|
||||
this.name = StringUtils.defaultIfEmpty(newValue, null);
|
||||
}
|
||||
|
||||
final String percentageParamName = "percentage";
|
||||
if (command.isChangeInBigDecimalParameterNamed(percentageParamName, this.percentage)) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(percentageParamName);
|
||||
actualChanges.put(percentageParamName, newValue);
|
||||
this.percentage = newValue;
|
||||
}
|
||||
|
||||
final String productApplyParamName = "productApply";
|
||||
if (command.isChangeInStringParameterNamed(productApplyParamName, this.productApply)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(productApplyParamName);
|
||||
actualChanges.put(productApplyParamName, newValue);
|
||||
this.productApply = StringUtils.defaultIfEmpty(newValue, null);
|
||||
}
|
||||
|
||||
final String activeParamName = "active";
|
||||
if (command.isChangeInBooleanParameterNamed(activeParamName, this.active)) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(activeParamName);
|
||||
actualChanges.put(activeParamName, newValue);
|
||||
this.active = newValue;
|
||||
}
|
||||
|
||||
final String approveUserParamName = "approveUserId";
|
||||
if (command.isChangeInLongParameterNamed(approveUserParamName, getApproveUserId())) {
|
||||
final Long newValue = command.longValueOfParameterNamed(approveUserParamName);
|
||||
actualChanges.put(approveUserParamName, newValue);
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
private Long getApproveUserId() {
|
||||
Long approveUserId = null;
|
||||
if (this.approveUser != null) {
|
||||
approveUserId = this.approveUser.getId();
|
||||
}
|
||||
return approveUserId;
|
||||
}
|
||||
|
||||
public void assembleFrom(String name, BigDecimal percentage, String productApply, boolean active){
|
||||
this.name = name;
|
||||
this.percentage = percentage;
|
||||
this.productApply = productApply;
|
||||
this.active = active;
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.rate.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Bowpi GT
|
||||
* Rate repository to save on m_rate table (custom change for Credi Chapin)
|
||||
* Created by Jose on 19/07/2017.
|
||||
*/
|
||||
public interface RateRepository extends JpaRepository<Rate, Long>, JpaSpecificationExecutor<Rate> {
|
||||
|
||||
Rate findByName(String name);
|
||||
List<Rate> findAllByActiveAndProductApply(boolean active, String productApply);
|
||||
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.rate.domain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import org.apache.fineract.portfolio.rate.exception.RateNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class RateRepositoryWrapper {
|
||||
|
||||
private final RateRepository repository;
|
||||
|
||||
@Autowired
|
||||
public RateRepositoryWrapper(final RateRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public Rate findOneWithNotFoundDetection(final Long rateId) {
|
||||
|
||||
final Rate rate = this.repository.findOne(rateId);
|
||||
if (rate == null) {
|
||||
throw new RateNotFoundException(rateId);
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
public List<Rate> findMultipleWithNotFoundDetection(final List<Long> rateIds) {
|
||||
List<Rate> rates = new ArrayList<>();
|
||||
if (rateIds != null && !rateIds.isEmpty()) {
|
||||
final List<Rate> foundRates = this.repository.findAll(rateIds);
|
||||
for (Long rateId : rateIds) {
|
||||
Boolean found = false;
|
||||
for (Rate foundRate : foundRates) {
|
||||
if (Objects.equals(foundRate.getId(),
|
||||
rateId)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
throw new RateNotFoundException(rateId);
|
||||
}
|
||||
}
|
||||
rates.addAll(foundRates);
|
||||
}
|
||||
return rates;
|
||||
}
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.rate.exception;
|
||||
|
||||
/**
|
||||
* Bowpi GT
|
||||
* Created by Jose on 24/07/2017.
|
||||
*/
|
||||
public class RateAlreadyExistException extends Exception{
|
||||
|
||||
public RateAlreadyExistException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.fineract.portfolio.rate.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
public class RateNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
|
||||
public RateNotFoundException(final Long id) {
|
||||
super("error.msg.rate.id.invalid", "Rate with identifier " + id + " does not exist", id);
|
||||
}
|
||||
|
||||
public RateNotFoundException(final String name) {
|
||||
super("error.msg.rate.id.invalid", "Rate with name " + name + " does not exist", name);
|
||||
}
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.rate.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.rate.service.RateWriteService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* Bowpi GT
|
||||
* Created by Jose on 19/07/2017.
|
||||
*/
|
||||
@Service
|
||||
@CommandType(entity = "RATE", action = "CREATE")
|
||||
public class CreateRateCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final RateWriteService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CreateRateCommandHandler(final RateWriteService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.createRate(command);
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.rate.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.rate.service.RateWriteService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Bowpi GT
|
||||
* Created by Jose on 19/07/2017.
|
||||
*/
|
||||
@Service
|
||||
@CommandType(entity = "RATE", action = "UPDATE")
|
||||
public class UpdateRateCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final RateWriteService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public UpdateRateCommandHandler(final RateWriteService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.updateRate(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.rate.serialization;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.portfolio.rate.api.RateApiConstants;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class RateDefinitionCommandFromApiJsonDeserializer {
|
||||
|
||||
/**
|
||||
* The parameters supported for this command.
|
||||
*/
|
||||
private final Set<String> supportedParameters = new HashSet<>(
|
||||
Arrays.asList("id", "name", "percentage", "productApply", "active", "approveUser", "locale"));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@Autowired
|
||||
public RateDefinitionCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
}
|
||||
|
||||
public void validateForCreate(final String json) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
|
||||
}.getType();
|
||||
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource(RateApiConstants.rateName);
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(RateApiConstants.rateName, element);
|
||||
baseDataValidator.reset().parameter(RateApiConstants.rateName).value(name).notBlank().notExceedingLengthOf(250);
|
||||
|
||||
final BigDecimal percentage = this.fromApiJsonHelper
|
||||
.extractBigDecimalWithLocaleNamed(RateApiConstants.ratePercentage, element);
|
||||
baseDataValidator.reset().parameter(RateApiConstants.ratePercentage).value(percentage).notBlank();
|
||||
|
||||
final String productApply = this.fromApiJsonHelper.extractStringNamed(RateApiConstants.rateProductApply, element);
|
||||
baseDataValidator.reset().parameter(RateApiConstants.rateProductApply).value(productApply).notBlank()
|
||||
.notExceedingLengthOf(100);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateForUpdate(final String json) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
|
||||
}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource(RateApiConstants.rate);
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(RateApiConstants.rateName, element)) {
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(RateApiConstants.rateName, element);
|
||||
baseDataValidator.reset().parameter(RateApiConstants.rateName).value(name).notBlank().notExceedingLengthOf(250);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(RateApiConstants.ratePercentage, element)) {
|
||||
final BigDecimal percentage = this.fromApiJsonHelper
|
||||
.extractBigDecimalWithLocaleNamed(RateApiConstants.ratePercentage, element);
|
||||
baseDataValidator.reset().parameter(RateApiConstants.ratePercentage).value(percentage).notBlank();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(RateApiConstants.rateProductApply, element)) {
|
||||
final String productApply = this.fromApiJsonHelper.extractStringNamed(RateApiConstants.rateProductApply, element);
|
||||
baseDataValidator.reset().parameter(RateApiConstants.rateProductApply).value(productApply).notBlank();
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(
|
||||
final List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.rate.service;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.apache.fineract.portfolio.rate.domain.Rate;
|
||||
import org.apache.fineract.portfolio.rate.domain.RateRepositoryWrapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class RateAssembler {
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final RateRepositoryWrapper rateRepository;
|
||||
|
||||
@Autowired
|
||||
public RateAssembler(final FromJsonHelper fromApiJsonHelper,
|
||||
final RateRepositoryWrapper rateRepository) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
this.rateRepository = rateRepository;
|
||||
}
|
||||
|
||||
public List<Rate> fromParsedJson(final JsonElement element) {
|
||||
|
||||
final List<Rate> rateItems = new ArrayList<>();
|
||||
|
||||
if (element.isJsonObject()) {
|
||||
final JsonObject topLevelJsonElement = element.getAsJsonObject();
|
||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
|
||||
|
||||
if (topLevelJsonElement.has(LoanProductConstants.ratesParamName) && topLevelJsonElement
|
||||
.get(LoanProductConstants.ratesParamName)
|
||||
.isJsonArray()) {
|
||||
final JsonArray array = topLevelJsonElement.get(LoanProductConstants.ratesParamName)
|
||||
.getAsJsonArray();
|
||||
List<Long> idList = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
|
||||
final JsonObject rateElement = array.get(i).getAsJsonObject();
|
||||
|
||||
final Long id = this.fromApiJsonHelper.extractLongNamed("id", rateElement);
|
||||
|
||||
if (id != null) {
|
||||
final Long rateId = this.fromApiJsonHelper.extractLongNamed("id", rateElement);
|
||||
idList.add(rateId);
|
||||
}
|
||||
}
|
||||
rateItems.addAll(rateRepository.findMultipleWithNotFoundDetection(idList));
|
||||
}
|
||||
}
|
||||
|
||||
return rateItems;
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.rate.service;
|
||||
|
||||
import org.apache.fineract.portfolio.rate.data.RateData;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Bowpi GT
|
||||
* Created by Jose on 19/07/2017.
|
||||
*/
|
||||
public interface RateReadService {
|
||||
|
||||
Collection<RateData> retrieveAllRates();
|
||||
|
||||
Collection<RateData> retrieveLoanApplicableRates();
|
||||
|
||||
RateData retrieveOne(Long rateId);
|
||||
|
||||
RateData retrieveByName(String name);
|
||||
|
||||
List<RateData> retrieveProductLoanRates(Long loanId);
|
||||
|
||||
List<RateData> retrieveLoanRates(Long loanId);
|
||||
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.fineract.portfolio.rate.service;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.rate.exception.RateNotFoundException;
|
||||
import org.apache.fineract.portfolio.rate.domain.Rate;
|
||||
import org.apache.fineract.portfolio.rate.data.RateData;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Bowpi GT Created by Jose on 19/07/2017.
|
||||
*/
|
||||
@Service
|
||||
public class RateReadServiceImpl implements RateReadService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final PlatformSecurityContext context;
|
||||
|
||||
@Autowired
|
||||
public RateReadServiceImpl(PlatformSecurityContext context, final RoutingDataSource dataSource) {
|
||||
this.context = context;
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RateData> retrieveAllRates() {
|
||||
this.context.authenticatedUser();
|
||||
final RateMapper rm = new RateMapper();
|
||||
final String sql = "select " + rm.rateSchema();
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[]{});
|
||||
}
|
||||
|
||||
@Override
|
||||
public RateData retrieveOne(Long rateId) {
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
final RateMapper rm = new RateMapper();
|
||||
final String sql = "select " + rm.rateSchema() + " where r.id = ?";
|
||||
final RateData selectedRate = this.jdbcTemplate.queryForObject(sql, rm, new Object[]{rateId});
|
||||
return selectedRate;
|
||||
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new RateNotFoundException(rateId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RateData retrieveByName(String name) {
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
final RateMapper rm = new RateMapper();
|
||||
final String sql = "select " + rm.rateSchema() + " where r.name = ?";
|
||||
final RateData selectedRate = this.jdbcTemplate.queryForObject(sql, rm, new Object[]{name});
|
||||
return selectedRate;
|
||||
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new RateNotFoundException(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RateData> retrieveLoanApplicableRates() {
|
||||
this.context.authenticatedUser();
|
||||
final RateMapper rm = new RateMapper();
|
||||
final String sql = "select " + rm.rateSchema() + " where r.active = ? and product_apply=?";
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[]{true, "m_loan"});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RateData> retrieveLoanRates(Long loanId) {
|
||||
final RateMapper rm = new RateMapper();
|
||||
final String sql = "select " + rm.loanRateSchema() + " where lr.loan_id = ?";
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[]{loanId});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RateData> retrieveProductLoanRates(Long loanId) {
|
||||
final RateMapper rm = new RateMapper();
|
||||
final String sql = "select " + rm.productLoanRateSchema() + " where lr.product_loan_id = ?";
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[]{loanId});
|
||||
}
|
||||
|
||||
private static final class RateMapper implements RowMapper<RateData> {
|
||||
|
||||
|
||||
public String rateSchema() {
|
||||
return " r.id as id, r.name as name, r.percentage as percentage, " +
|
||||
"r.product_apply as productApply, r.active as active from m_rate r ";
|
||||
}
|
||||
|
||||
public String loanRateSchema() {
|
||||
return rateSchema() + " join m_loan_rate lr on lr.rate_id = r.id";
|
||||
}
|
||||
|
||||
public String productLoanRateSchema() {
|
||||
return rateSchema() + " join m_product_loan_rate lr on lr.rate_id = r.id";
|
||||
}
|
||||
|
||||
public RateMapper() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RateData mapRow(ResultSet resultSet, int i) throws SQLException {
|
||||
final Long id = resultSet.getLong("id");
|
||||
final String name = resultSet.getString("name");
|
||||
final BigDecimal percentage = resultSet.getBigDecimal("percentage");
|
||||
final String productApply = resultSet.getString("productApply");
|
||||
final boolean active = resultSet.getBoolean("active");
|
||||
return RateData.instance(id, name, percentage, productApply, active);
|
||||
}
|
||||
|
||||
public RateData mapRow(Rate rateResponse, int i) {
|
||||
final Long id = rateResponse.getId();
|
||||
final String name = rateResponse.getName();
|
||||
final BigDecimal percentage = rateResponse.getPercentage();
|
||||
final String productApply = rateResponse.getProductApply();
|
||||
final boolean active = rateResponse.isActive();
|
||||
return RateData.instance(id, name, percentage, productApply, active);
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.rate.service;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
|
||||
/**
|
||||
* Bowpi GT
|
||||
* Created by Jose on 19/07/2017.
|
||||
*/
|
||||
public interface RateWriteService {
|
||||
|
||||
CommandProcessingResult createRate(final JsonCommand command);
|
||||
|
||||
CommandProcessingResult updateRate(Long rateId, JsonCommand command);
|
||||
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.rate.service;
|
||||
|
||||
import static org.apache.fineract.portfolio.rate.api.RateApiConstants.approveUserIdParamName;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.persistence.PersistenceException;
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.rate.domain.Rate;
|
||||
import org.apache.fineract.portfolio.rate.domain.RateRepository;
|
||||
import org.apache.fineract.portfolio.rate.exception.RateNotFoundException;
|
||||
import org.apache.fineract.portfolio.rate.serialization.RateDefinitionCommandFromApiJsonDeserializer;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.apache.fineract.useradministration.domain.AppUserRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Bowpi GT Created by Jose on 19/07/2017.
|
||||
*/
|
||||
@Service
|
||||
public class RateWriteServiceImpl implements RateWriteService {
|
||||
|
||||
private final static Logger logger = LoggerFactory
|
||||
.getLogger(RateWriteServiceImpl.class);
|
||||
private final RateRepository rateRepository;
|
||||
private final AppUserRepository appUserRepository;
|
||||
private final PlatformSecurityContext context;
|
||||
private final RateDefinitionCommandFromApiJsonDeserializer fromApiJsonDeserializer;
|
||||
|
||||
@Autowired
|
||||
public RateWriteServiceImpl(RateRepository rateRepository, AppUserRepository appUserRepository,
|
||||
final RateDefinitionCommandFromApiJsonDeserializer fromApiJsonDeserializer,
|
||||
PlatformSecurityContext context) {
|
||||
this.rateRepository = rateRepository;
|
||||
this.appUserRepository = appUserRepository;
|
||||
this.context = context;
|
||||
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult createRate(JsonCommand command) {
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
this.fromApiJsonDeserializer.validateForCreate(command.json());
|
||||
|
||||
final Long approveUserId = command.longValueOfParameterNamed(approveUserIdParamName);
|
||||
AppUser approveUser = null;
|
||||
if (approveUserId != null) {
|
||||
approveUser = this.appUserRepository.findOne(approveUserId);
|
||||
}
|
||||
final Rate rate = Rate.fromJson(command, approveUser);
|
||||
|
||||
this.rateRepository.save(rate);
|
||||
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId())
|
||||
.withEntityId(rate.getId()).build();
|
||||
|
||||
} catch (final DataIntegrityViolationException dve) {
|
||||
handleRateDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
||||
return CommandProcessingResult.empty();
|
||||
} catch (final PersistenceException dve) {
|
||||
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
||||
handleRateDataIntegrityIssues(command, throwable, dve);
|
||||
return CommandProcessingResult.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult updateRate(final Long rateId, final JsonCommand command) {
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final Rate rateToUpdate = this.rateRepository.findOne(rateId);
|
||||
if (rateToUpdate == null) {
|
||||
throw new RateNotFoundException(rateId);
|
||||
}
|
||||
|
||||
final Map<String, Object> changes = rateToUpdate.update(command);
|
||||
|
||||
this.fromApiJsonDeserializer.validateForUpdate(command.json());
|
||||
|
||||
if (changes.containsKey(approveUserIdParamName)) {
|
||||
final Long newValue = (Long) changes.get(approveUserIdParamName);
|
||||
AppUser newApproveUser = null;
|
||||
if (newValue != null) {
|
||||
newApproveUser = this.appUserRepository.findOne(newValue);
|
||||
}
|
||||
rateToUpdate.setApproveUser(newApproveUser);
|
||||
}
|
||||
if (!changes.isEmpty()) {
|
||||
this.rateRepository.saveAndFlush(rateToUpdate);
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(rateId) //
|
||||
.with(changes) //
|
||||
.build();
|
||||
|
||||
} catch (final DataIntegrityViolationException dve) {
|
||||
handleRateDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
||||
return new CommandProcessingResult(Long.valueOf(-1));
|
||||
} catch (final PersistenceException dve) {
|
||||
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
||||
handleRateDataIntegrityIssues(command, throwable, dve);
|
||||
return CommandProcessingResult.empty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Guaranteed to throw an exception no matter what the data integrity issue
|
||||
* is.
|
||||
*/
|
||||
private void handleRateDataIntegrityIssues(final JsonCommand command, final Throwable realCause,
|
||||
final Exception dve) {
|
||||
if (realCause.getMessage().contains("rate_name_org")) {
|
||||
final String name = command.stringValueOfParameterNamed("name");
|
||||
throw new PlatformDataIntegrityException("error.msg.fund.duplicate.externalId",
|
||||
"A rate with name '" + name
|
||||
+ "' already exists", "name", name);
|
||||
}
|
||||
|
||||
logger.error(dve.getMessage(), dve);
|
||||
throw new PlatformDataIntegrityException("error.msg.fund.unknown.data.integrity.issue",
|
||||
"Unknown data integrity issue with resource: " + realCause.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
--
|
||||
-- Licensed to the Apache Software Foundation (ASF) under one
|
||||
-- or more contributor license agreements. See the NOTICE file
|
||||
-- distributed with this work for additional information
|
||||
-- regarding copyright ownership. The ASF licenses this file
|
||||
-- to you under the Apache License, Version 2.0 (the
|
||||
-- "License"); you may not use this file except in compliance
|
||||
-- with the License. You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing,
|
||||
-- software distributed under the License is distributed on an
|
||||
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
-- KIND, either express or implied. See the License for the
|
||||
-- specific language governing permissions and limitations
|
||||
-- under the License.
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `m_rate` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(250) NOT NULL,
|
||||
`percentage` decimal(10,2) NOT NULL,
|
||||
`active` tinyint(1) DEFAULT '0',
|
||||
`product_apply` varchar(100) NOT NULL,
|
||||
`created_date` datetime NULL DEFAULT NULL,
|
||||
`createdby_id` bigint(20) NOT NULL,
|
||||
`lastmodifiedby_id` bigint(20) NOT NULL,
|
||||
`lastmodified_date` datetime NULL DEFAULT NULL,
|
||||
`approve_user` bigint(20) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `FK_M_RATE_CREATE_USER` (`createdby_id`),
|
||||
KEY `FK_M_RATE_APPROVE_USER` (`approve_user`),
|
||||
CONSTRAINT `FK_M_RATE_APPROVE_USER` FOREIGN KEY (`approve_user`) REFERENCES `m_appuser` (`id`),
|
||||
CONSTRAINT `FK_M_RATE_CREATE_USER` FOREIGN KEY (`createdby_id`) REFERENCES `m_appuser` (`id`)
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `m_loan_rate` (
|
||||
`loan_id` bigint(20) NOT NULL,
|
||||
`rate_id` bigint(20) NOT NULL,
|
||||
PRIMARY KEY (`loan_id`,`rate_id`),
|
||||
KEY `FK_M_LOAN_RATE_RATE` (`rate_id`),
|
||||
CONSTRAINT `FK_M_LOAN_RATE_LOAN` FOREIGN KEY (`loan_id`) REFERENCES `m_loan` (`id`),
|
||||
CONSTRAINT `FK_M_LOAN_RATE_RATE` FOREIGN KEY (`rate_id`) REFERENCES `m_rate` (`id`)
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `m_product_loan_rate` (
|
||||
`product_loan_id` bigint(20) NOT NULL,
|
||||
`rate_id` bigint(20) NOT NULL,
|
||||
PRIMARY KEY (`product_loan_id`,`rate_id`),
|
||||
KEY `FK_M_PRODUCT_LOAN_RATE_RATE` (`rate_id`),
|
||||
CONSTRAINT `FK_M_PRODUCT_LOAN_RATE_LOAN` FOREIGN KEY (`product_loan_id`) REFERENCES `m_product_loan` (`id`),
|
||||
CONSTRAINT `FK_M_PRODUCT_LOAN_RATE_RATE` FOREIGN KEY (`rate_id`) REFERENCES `m_rate` (`id`)
|
||||
);
|
||||
|
||||
|
||||
INSERT INTO `m_permission`
|
||||
(`grouping`,`code`,`entity_name`,`action_name`,`can_maker_checker`) VALUES
|
||||
('organisation', 'CREATE_RATE', 'RATE', 'CREATE', '1'),
|
||||
('organisation', 'UPDATE_RATE', 'RATE', 'UPDATE', '1');
|
||||
|
||||
INSERT INTO `c_configuration`
|
||||
(`name`, `value`, `enabled`, `is_trap_door`, `description`) VALUES
|
||||
('vat-tax', 12, 0, 0, 'VAT tax'),
|
||||
('sub-rates', 12, 0, 0, 'Enable Rates Module');;
|
||||
Reference in New Issue
Block a user