FINERACT-2090: restructure loan approvals

This commit is contained in:
Kristof Jozsa
2024-06-24 11:30:53 +02:00
committed by Adam Saghy
parent 9152408c24
commit fac4832723
31 changed files with 438 additions and 384 deletions
@@ -284,7 +284,7 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
}
@Override
public Long retreivePeroidInNumberOfDaysForSkipMeetingDate() {
public Long retreivePeriodInNumberOfDaysForSkipMeetingDate() {
final String propertyName = "skip-repayment-on-first-day-of-month";
final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(propertyName);
return property.getValue();
@@ -249,7 +249,7 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ
Integer numberOfDays = 0;
boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
if (isSkipRepaymentOnFirstMonthEnabled) {
numberOfDays = this.configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
numberOfDays = this.configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
}
final Collection<LocalDate> recurringDates = CalendarUtils.getRecurringDates(rrule, seedDate, periodStartDate, periodEndDate,
@@ -328,7 +328,7 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ
Integer numberOfDays = 0;
boolean isSkipRepaymentOnFirstMonthEnabled = configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
if (isSkipRepaymentOnFirstMonthEnabled) {
numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
numberOfDays = configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
}
if (lastMeetingDate != null && !calendarData.isBetweenStartAndEndDate(lastMeetingDate)
@@ -344,7 +344,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe
Integer numberOfDays = 0;
boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
if (isSkipRepaymentOnFirstMonthEnabled) {
numberOfDays = this.configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
numberOfDays = this.configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
isSkipMeetingOnFirstDay = this.calendarReadPlatformService.isCalendarAssociatedWithEntity(entityId, calendar.getId(),
entityType.getValue().longValue());
}
@@ -510,7 +510,7 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService
Integer numberOfDays = 0;
boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
if (isSkipRepaymentOnFirstMonthEnabled) {
numberOfDays = this.configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
numberOfDays = this.configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
}
for (CenterData centerData : centerDataArray) {
if (centerData.getCollectionMeetingCalendar().isValidRecurringDate(meetingDate, isSkipRepaymentOnFirstMonthEnabled,
@@ -18,6 +18,13 @@
*/
package org.apache.fineract.portfolio.loanaccount.loanschedule.service;
import static org.apache.fineract.portfolio.loanaccount.domain.Loan.APPROVED_ON_DATE;
import static org.apache.fineract.portfolio.loanaccount.domain.Loan.DATE_FORMAT;
import static org.apache.fineract.portfolio.loanaccount.domain.Loan.EVENT_DATE;
import static org.apache.fineract.portfolio.loanaccount.domain.Loan.EXPECTED_DISBURSEMENT_DATE;
import static org.apache.fineract.portfolio.loanaccount.domain.Loan.LOCALE;
import static org.apache.fineract.portfolio.loanaccount.domain.Loan.PARAM_STATUS;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@@ -36,6 +43,7 @@ import java.util.Set;
import java.util.TreeSet;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
@@ -87,7 +95,11 @@ import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
import org.apache.fineract.portfolio.loanaccount.domain.LoanOfficerAssignmentHistory;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
@@ -118,6 +130,7 @@ import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyTy
import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType;
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.stereotype.Service;
@Service
@@ -141,6 +154,8 @@ public class LoanScheduleAssembler {
private final CalendarInstanceRepository calendarInstanceRepository;
private final LoanUtilService loanUtilService;
private final LoanDisbursementDetailsAssembler loanDisbursementDetailsAssembler;
private final LoanRepositoryWrapper loanRepositoryWrapper;
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
public LoanApplicationTerms assembleLoanTerms(final JsonElement element) {
final Long loanProductId = this.fromApiJsonHelper.extractLongNamed("productId", element);
@@ -228,7 +243,6 @@ public class LoanScheduleAssembler {
LocalDate calculatedRepaymentsStartingFromDate = repaymentsStartingFromDate;
final Boolean synchDisbursement = this.fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", element);
final Long calendarId = this.fromApiJsonHelper.extractLongNamed("calendarId", element);
Calendar calendar = null;
@@ -290,19 +304,12 @@ public class LoanScheduleAssembler {
if (isSkipRepaymentOnFirstMonthEnabled) {
isSkipMeetingOnFirstDay = this.loanUtilService.isLoanRepaymentsSyncWithMeeting(group, calendar);
if (isSkipMeetingOnFirstDay) {
numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
numberOfDays = configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
}
}
if ((loanType.isJLGAccount() || loanType.isGroupAccount()) && calendar != null) {
validateRepaymentsStartDateWithMeetingDates(calculatedRepaymentsStartingFromDate, calendar, isSkipMeetingOnFirstDay,
numberOfDays);
/*
* If disbursement is synced on meeting, make sure disbursement date is on a meeting date
*/
if (synchDisbursement != null && synchDisbursement.booleanValue()) {
validateDisbursementDateWithMeetingDates(expectedDisbursementDate, calendar, isSkipMeetingOnFirstDay, numberOfDays);
}
}
if (RepaymentStartDateType.DISBURSEMENT_DATE.equals(repaymentStartDateType)) {
@@ -601,20 +608,9 @@ public class LoanScheduleAssembler {
}
}
public void validateDisbursementDateWithMeetingDates(final LocalDate expectedDisbursementDate, final Calendar calendar,
Boolean isSkipRepaymentOnFirstMonth, Integer numberOfDays) {
// disbursement date should fall on a meeting date
if (calendar != null && !calendar.isValidRecurringDate(expectedDisbursementDate, isSkipRepaymentOnFirstMonth, numberOfDays)) {
final String errorMessage = "Expected disbursement date '" + expectedDisbursementDate + "' do not fall on a meeting date";
throw new LoanApplicationDateException("disbursement.date.do.not.match.meeting.date", errorMessage, expectedDisbursementDate);
}
}
private void validateRepaymentFrequencyIsSameAsMeetingFrequency(final Integer meetingFrequency, final Integer repaymentFrequency,
final Integer meetingInterval, final Integer repaymentInterval) {
// meeting with daily frequency should allow loan products with any
// frequency.
// meeting with daily frequency should allow loan products with any frequency.
if (!PeriodFrequencyType.DAYS.getValue().equals(meetingFrequency)) {
// repayment frequency must match with meeting frequency
if (!meetingFrequency.equals(repaymentFrequency)) {
@@ -886,7 +882,7 @@ public class LoanScheduleAssembler {
if (isSkipRepaymentOnFirstMonthEnabled) {
isSkipRepaymentOnFirstMonth = this.loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), loanCalendar);
if (isSkipRepaymentOnFirstMonth) {
numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
numberOfDays = configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
}
}
final Integer minGap = installmentConfig.getMinimumGap();
@@ -1138,7 +1134,7 @@ public class LoanScheduleAssembler {
final LocalDate refernceDateForCalculatingFirstRepaymentDate, final PeriodFrequencyType repaymentPeriodFrequencyType,
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final Calendar calendar, final LocalDate submittedOnDate) {
boolean isMeetingSkipOnFirstDayOfMonth = configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
int numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
int numberOfDays = configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
final String frequency = CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType);
final LocalDate derivedFirstRepayment = CalendarUtils.getFirstRepaymentMeetingDate(calendar,
refernceDateForCalculatingFirstRepaymentDate, repaymentEvery, frequency, isMeetingSkipOnFirstDayOfMonth, numberOfDays);
@@ -1384,4 +1380,70 @@ public class LoanScheduleAssembler {
loanProductRelatedDetail.setEqualAmortization(newValue);
}
}
public Pair<Loan, Map<String, Object>> assembleLoanApproval(AppUser currentUser, JsonCommand command, Long loanId) {
final JsonArray disbursementDataArray = command.arrayOfParameterNamed(LoanApiConstants.disbursementDataParameterName);
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
final Map<String, Object> actualChanges = new HashMap<>();
defaultLoanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVED, loan);
actualChanges.put(PARAM_STATUS, LoanEnumerations.status(loan.getStatus()));
LocalDate approvedOn = command.localDateValueOfParameterNamed(APPROVED_ON_DATE);
String approvedOnDateChange = command.stringValueOfParameterNamed(APPROVED_ON_DATE);
if (approvedOn == null) {
approvedOn = command.localDateValueOfParameterNamed(EVENT_DATE);
approvedOnDateChange = command.stringValueOfParameterNamed(EVENT_DATE);
}
LocalDate expectedDisbursementDate = command.localDateValueOfParameterNamed(EXPECTED_DISBURSEMENT_DATE);
BigDecimal approvedLoanAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.approvedLoanAmountParameterName);
if (approvedLoanAmount != null) {
/*
* All the calculations are done based on the principal amount, so it is necessary to set principal amount
* to approved amount
*/
loan.setApprovedPrincipal(approvedLoanAmount);
loan.getLoanRepaymentScheduleDetail().setPrincipal(approvedLoanAmount);
actualChanges.put(LoanApiConstants.approvedLoanAmountParameterName, approvedLoanAmount);
actualChanges.put(LoanApiConstants.disbursementPrincipalParameterName, approvedLoanAmount);
actualChanges.put(LoanApiConstants.disbursementNetDisbursalAmountParameterName, loan.getNetDisbursalAmount());
if (disbursementDataArray != null) {
loan.updateDisbursementDetails(command, actualChanges);
}
}
loan.recalculateAllCharges();
loan.setApprovedOnDate(approvedOn);
loan.setApprovedBy(currentUser);
actualChanges.put(LOCALE, command.locale());
actualChanges.put(DATE_FORMAT, command.dateFormat());
actualChanges.put(APPROVED_ON_DATE, approvedOnDateChange);
if (expectedDisbursementDate != null) {
loan.setExpectedDisbursementDate(expectedDisbursementDate);
actualChanges.put(EXPECTED_DISBURSEMENT_DATE, expectedDisbursementDate);
}
if (loan.getLoanOfficer() != null) {
final LoanOfficerAssignmentHistory loanOfficerAssignmentHistory = LoanOfficerAssignmentHistory.createNew(loan,
loan.getLoanOfficer(), approvedOn);
loan.getLoanOfficerHistory().add(loanOfficerAssignmentHistory);
}
loan.adjustNetDisbursalAmount(loan.getApprovedPrincipal());
if (!actualChanges.isEmpty()) {
if (actualChanges.containsKey(LoanApiConstants.approvedLoanAmountParameterName)
|| actualChanges.containsKey("recalculateLoanSchedule") || actualChanges.containsKey("expectedDisbursementDate")) {
loan.regenerateRepaymentSchedule(loanUtilService.buildScheduleGeneratorDTO(loan, null));
}
}
return Pair.of(loan, actualChanges);
}
}
@@ -52,6 +52,9 @@ import org.apache.fineract.infrastructure.core.exception.UnsupportedParameterExc
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService;
import org.apache.fineract.infrastructure.entityaccess.FineractEntityAccessConstants;
import org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityAccessType;
import org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityRelation;
@@ -68,6 +71,10 @@ import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
import org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
import org.apache.fineract.organisation.workingdays.service.WorkingDaysUtil;
import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
import org.apache.fineract.portfolio.calendar.domain.Calendar;
import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
@@ -83,9 +90,12 @@ import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagement;
import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException;
@@ -99,6 +109,7 @@ import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementData
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
import org.apache.fineract.portfolio.loanproduct.data.LoanProductData;
import org.apache.fineract.portfolio.loanproduct.domain.AdvancedPaymentAllocationsValidator;
@@ -182,6 +193,11 @@ public final class LoanApplicationValidator {
private final WorkingDaysRepositoryWrapper workingDaysRepository;
private final HolidayRepository holidayRepository;
private final SavingsAccountRepositoryWrapper savingsAccountRepository;
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
private final LoanSummaryWrapper loanSummaryWrapper;
private final CalendarInstanceRepository calendarInstanceRepository;
private final LoanUtilService loanUtilService;
private final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService;
public void validateForCreate(final Loan loan) {
final LocalDate expectedFirstRepaymentOnDate = loan.getExpectedFirstRepaymentOnDate();
@@ -238,6 +254,7 @@ public final class LoanApplicationValidator {
final Long groupId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.groupIdParameterName, element);
final Client client = clientId != null ? this.clientRepository.findOneWithNotFoundDetection(clientId) : null;
final Group group = groupId != null ? this.groupRepository.findOneWithNotFoundDetection(groupId) : null;
validateClientOrGroup(client, group, productId);
validateOrThrow("loan", baseDataValidator -> {
@@ -528,7 +545,7 @@ public final class LoanApplicationValidator {
// charges
loanChargeApiJsonValidator.validateLoanCharges(element, loanProduct, baseDataValidator);
/**
/*
* TODO: Add collaterals for other loan accounts if needed. For now it's only applicable for individual
* accounts. (loanType.isJLG() || loanType.isGLIM())
*/
@@ -725,7 +742,7 @@ public final class LoanApplicationValidator {
}
checkForProductMixRestrictions(element);
validateSubmittedOnDate(element, null, loanProduct);
validateSubmittedOnDate(element, submittedOnDate, loanProduct);
validateDisbursementDetails(loanProduct, element);
validateCollateral(element);
// validate if disbursement date is a holiday or a non-working day
@@ -1381,7 +1398,6 @@ public final class LoanApplicationValidator {
validateDisbursementDetails(loanProduct, element);
validateSubmittedOnDate(element, loan.getSubmittedOnDate(), loanProduct);
validateClientOrGroup(client, group, productId);
// validate if disbursement date is a holiday or a non-working day
@@ -1439,7 +1455,7 @@ public final class LoanApplicationValidator {
}
}
if (disbursementDataSize > loanProduct.maxTrancheCount()) {
final String errorMessage = "Number of tranche shouldn't be greter than " + loanProduct.maxTrancheCount();
final String errorMessage = "Number of tranche shouldn't be greater than " + loanProduct.maxTrancheCount();
throw new ExceedingTrancheCountException(LoanApiConstants.disbursementDataParameterName, errorMessage,
loanProduct.maxTrancheCount(), disbursementDataSize);
}
@@ -1897,6 +1913,199 @@ public final class LoanApplicationValidator {
loan.adjustNetDisbursalAmount(netDisbursalAmount);
}
public void validateApproval(JsonCommand command, Long loanId) {
String json = command.json();
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Set<String> disbursementParameters = new HashSet<>(
Arrays.asList(LoanApiConstants.loanIdTobeApproved, LoanApiConstants.approvedLoanAmountParameterName,
LoanApiConstants.approvedOnDateParameterName, LoanApiConstants.disbursementNetDisbursalAmountParameterName,
LoanApiConstants.noteParameterName, LoanApiConstants.localeParameterName, LoanApiConstants.dateFormatParameterName,
LoanApiConstants.disbursementDataParameterName, LoanApiConstants.expectedDisbursementDateParameterName));
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
validateOrThrow("loanapplication", baseDataValidator -> {
final JsonElement element = this.fromApiJsonHelper.parse(json);
final BigDecimal principal = this.fromApiJsonHelper
.extractBigDecimalWithLocaleNamed(LoanApiConstants.approvedLoanAmountParameterName, element);
baseDataValidator.reset().parameter(LoanApiConstants.approvedLoanAmountParameterName).value(principal).ignoreIfNull()
.positiveAmount();
final BigDecimal netDisbursalAmount = this.fromApiJsonHelper
.extractBigDecimalWithLocaleNamed(LoanApiConstants.disbursementNetDisbursalAmountParameterName, element);
baseDataValidator.reset().parameter(LoanApiConstants.disbursementNetDisbursalAmountParameterName).value(netDisbursalAmount)
.ignoreIfNull().positiveAmount();
final LocalDate approvedOnDate = this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.approvedOnDateParameterName,
element);
baseDataValidator.reset().parameter(LoanApiConstants.approvedOnDateParameterName).value(approvedOnDate).notNull();
LocalDate expectedDisbursementDate = this.fromApiJsonHelper
.extractLocalDateNamed(LoanApiConstants.expectedDisbursementDateParameterName, element);
baseDataValidator.reset().parameter(LoanApiConstants.expectedDisbursementDateParameterName).value(expectedDisbursementDate)
.ignoreIfNull();
final String note = this.fromApiJsonHelper.extractStringNamed(LoanApiConstants.noteParameterName, element);
baseDataValidator.reset().parameter(LoanApiConstants.noteParameterName).value(note).notExceedingLengthOf(1000);
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
loan.setHelpers(defaultLoanLifecycleStateMachine, this.loanSummaryWrapper,
this.loanRepaymentScheduleTransactionProcessorFactory);
final Client client = loan.client();
if (client != null && client.isNotActive()) {
throw new ClientNotActiveException(client.getId());
}
final Group group = loan.group();
if (group != null && group.isNotActive()) {
throw new GroupNotActiveException(group.getId());
}
if (expectedDisbursementDate == null) {
expectedDisbursementDate = loan.getExpectedDisbursedOnLocalDate();
}
if (DateUtils.isBefore(approvedOnDate, loan.getSubmittedOnDate())) {
final String errorMessage = "Loan approval date " + approvedOnDate + " can not be before its submittal date: "
+ loan.getSubmittedOnDate();
throw new InvalidLoanStateTransitionException("approval", "cannot.be.before.submittal.date", errorMessage, approvedOnDate,
loan.getSubmittedOnDate());
}
LoanProduct loanProduct = loan.loanProduct();
if (loanProduct.isMultiDisburseLoan()) {
validateLoanMultiDisbursementDate(element, expectedDisbursementDate, principal);
final JsonArray disbursementDataArray = this.fromApiJsonHelper
.extractJsonArrayNamed(LoanApiConstants.disbursementDataParameterName, element);
int disbursementDataSize = disbursementDataArray != null ? disbursementDataArray.size() : 0;
if (disbursementDataSize > loanProduct.maxTrancheCount()) {
final String errorMessage = "Number of tranche shouldn't be greater than " + loanProduct.maxTrancheCount();
throw new ExceedingTrancheCountException(LoanApiConstants.disbursementDataParameterName, errorMessage,
loanProduct.maxTrancheCount(), disbursementDataSize);
}
}
boolean isSkipRepaymentOnFirstMonth;
int numberOfDays = 0;
if (loan.isSyncDisbursementWithMeeting() && (loan.isGroupLoan() || loan.isJLGLoan())) {
Calendar calendar = getCalendarInstance(loan);
isSkipRepaymentOnFirstMonth = isLoanRepaymentsSyncWithMeeting(loan, calendar);
if (isSkipRepaymentOnFirstMonth) {
numberOfDays = configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
}
validateDisbursementDateWithMeetingDates(expectedDisbursementDate, calendar, isSkipRepaymentOnFirstMonth, numberOfDays);
}
entityDatatableChecksWritePlatformService.runTheCheckForProduct(loanId, EntityTables.LOAN.getName(),
StatusEnum.APPROVE.getCode().longValue(), EntityTables.LOAN.getForeignKeyColumnNameOnDatatable(), loan.productId());
if (loan.isTopup() && loan.getClientId() != null) {
validateTopupLoan(loan, expectedDisbursementDate);
}
if (!loan.getStatus().isSubmittedAndPendingApproval()) {
final String defaultUserMessage = "Loan Account Approval is not allowed. Loan Account is not in submitted and pending approval state.";
final ApiParameterError error = ApiParameterError
.generalError("error.msg.loan.approve.account.is.not.submitted.and.pending.state", defaultUserMessage);
baseDataValidator.getDataValidationErrors().add(error);
}
BigDecimal approvedLoanAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.approvedLoanAmountParameterName);
if (approvedLoanAmount != null) {
compareApprovedToProposedPrincipal(loan, approvedLoanAmount);
}
if (expectedDisbursementDate != null) {
if (DateUtils.isBefore(expectedDisbursementDate, approvedOnDate)) {
final String errorMessage = "The expected disbursement date " + expectedDisbursementDate
+ " should be either on or after the approval date: " + approvedOnDate;
throw new InvalidLoanStateTransitionException("expecteddisbursal", "should.be.on.or.after.approval.date", errorMessage,
approvedOnDate, expectedDisbursementDate);
}
}
if (client != null && client.getOfficeJoiningDate() != null) {
final LocalDate clientOfficeJoiningDate = client.getOfficeJoiningDate();
if (DateUtils.isBefore(approvedOnDate, clientOfficeJoiningDate)) {
throw new InvalidLoanStateTransitionException("approval", "cannot.be.before.client.transfer.date",
"The date on which a loan is approved cannot be earlier than client's transfer date to this office",
clientOfficeJoiningDate);
}
}
if (DateUtils.isDateInTheFuture(approvedOnDate)) {
final String errorMessage = "The date on which a loan is approved cannot be in the future.";
throw new InvalidLoanStateTransitionException("approval", "cannot.be.a.future.date", errorMessage, approvedOnDate);
}
final LoanStatus newStatus = defaultLoanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_APPROVED, loan);
if (newStatus.hasStateOf(loan.getStatus())) {
final String defaultUserMessage = "Loan is already approved.";
final ApiParameterError error = ApiParameterError
.generalError("error.msg.loan.approve.account.is.not.submitted.and.pending.state", defaultUserMessage);
baseDataValidator.getDataValidationErrors().add(error);
}
}); // end validation
}
private void compareApprovedToProposedPrincipal(Loan loan, BigDecimal approvedLoanAmount) {
if (loan.loanProduct().isDisallowExpectedDisbursements() && loan.loanProduct().isAllowApprovedDisbursedAmountsOverApplied()) {
BigDecimal maxApprovedLoanAmount = getOverAppliedMax(loan);
if (approvedLoanAmount.compareTo(maxApprovedLoanAmount) > 0) {
final String errorMessage = "Loan approved amount can't be greater than maximum applied loan amount calculation.";
throw new InvalidLoanStateTransitionException("approval",
"amount.can't.be.greater.than.maximum.applied.loan.amount.calculation", errorMessage, approvedLoanAmount,
maxApprovedLoanAmount);
}
} else {
if (approvedLoanAmount.compareTo(loan.getProposedPrincipal()) > 0) {
final String errorMessage = "Loan approved amount can't be greater than loan amount demanded.";
throw new InvalidLoanStateTransitionException("approval", "amount.can't.be.greater.than.loan.amount.demanded", errorMessage,
loan.getProposedPrincipal(), approvedLoanAmount);
}
}
}
private BigDecimal getOverAppliedMax(Loan loan) {
LoanProduct loanProduct = loan.getLoanProduct();
if ("percentage".equals(loanProduct.getOverAppliedCalculationType())) {
BigDecimal overAppliedNumber = BigDecimal.valueOf(loanProduct.getOverAppliedNumber());
BigDecimal totalPercentage = BigDecimal.valueOf(1).add(overAppliedNumber.divide(BigDecimal.valueOf(100)));
return loan.getProposedPrincipal().multiply(totalPercentage);
} else {
return loan.getProposedPrincipal().add(BigDecimal.valueOf(loanProduct.getOverAppliedNumber()));
}
}
/**
* validate disbursement date should fall on a meeting date
*/
public void validateDisbursementDateWithMeetingDates(final LocalDate expectedDisbursementDate, final Calendar calendar,
Boolean isSkipRepaymentOnFirstMonth, Integer numberOfDays) {
if (calendar != null && !calendar.isValidRecurringDate(expectedDisbursementDate, isSkipRepaymentOnFirstMonth, numberOfDays)) {
final String errorMessage = "Expected disbursement date '" + expectedDisbursementDate + "' do not fall on a meeting date";
throw new LoanApplicationDateException("disbursement.date.do.not.match.meeting.date", errorMessage, expectedDisbursementDate);
}
}
private Calendar getCalendarInstance(Loan loan) {
CalendarInstance calendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
CalendarEntityType.LOANS.getValue());
return calendarInstance != null ? calendarInstance.getCalendar() : null;
}
private boolean isLoanRepaymentsSyncWithMeeting(Loan loan, Calendar calendar) {
return configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled()
&& loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), calendar);
}
public static void validateOrThrow(String resource, Consumer<DataValidatorBuilder> baseDataValidator) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder dataValidatorBuilder = new DataValidatorBuilder(dataValidationErrors).resource(resource);
@@ -31,23 +31,18 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService;
@@ -85,7 +80,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler;
@@ -109,7 +103,6 @@ import org.springframework.transaction.annotation.Transactional;
public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements LoanApplicationWritePlatformService {
private final PlatformSecurityContext context;
private final FromJsonHelper fromJsonHelper;
private final LoanApplicationTransitionValidator loanApplicationTransitionValidator;
private final LoanApplicationValidator loanApplicationValidator;
private final LoanRepositoryWrapper loanRepositoryWrapper;
@@ -121,9 +114,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
private final CalendarInstanceRepository calendarInstanceRepository;
private final SavingsAccountRepositoryWrapper savingsAccountRepository;
private final AccountAssociationsRepository accountAssociationsRepository;
private final LoanReadPlatformService loanReadPlatformService;
private final BusinessEventNotifierService businessEventNotifierService;
private final ConfigurationDomainService configurationDomainService;
private final LoanScheduleAssembler loanScheduleAssembler;
private final LoanUtilService loanUtilService;
private final CalendarReadPlatformService calendarReadPlatformService;
@@ -510,19 +501,6 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
.build();
}
public void validateMultiDisbursementData(final JsonCommand command, LocalDate expectedDisbursementDate) {
final String json = command.json();
final JsonElement element = this.fromJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
final BigDecimal principal = this.fromJsonHelper.extractBigDecimalWithLocaleNamed("approvedLoanAmount", element);
loanApplicationValidator.validateLoanMultiDisbursementDate(element, baseDataValidator, expectedDisbursementDate, principal);
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
@Transactional
@Override
public CommandProcessingResult approveGLIMLoanAppication(final Long loanId, final JsonCommand command) {
@@ -572,100 +550,16 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
@Transactional
@Override
public CommandProcessingResult approveApplication(final Long loanId, final JsonCommand command) {
final AppUser currentUser = getAppUserIfPresent();
LocalDate expectedDisbursementDate = null;
loanApplicationValidator.validateApproval(command, loanId);
this.loanApplicationTransitionValidator.validateApproval(command.json());
Loan loan = retrieveLoanBy(loanId);
final JsonArray disbursementDataArray = command.arrayOfParameterNamed(LoanApiConstants.disbursementDataParameterName);
expectedDisbursementDate = command.localDateValueOfParameterNamed(LoanApiConstants.expectedDisbursementDateParameterName);
if (expectedDisbursementDate == null) {
expectedDisbursementDate = loan.getExpectedDisbursedOnLocalDate();
}
if (loan.loanProduct().isMultiDisburseLoan()) {
this.validateMultiDisbursementData(command, expectedDisbursementDate);
}
loanApplicationTransitionValidator.checkClientOrGroupActive(loan);
Boolean isSkipRepaymentOnFirstMonth = false;
Integer numberOfDays = 0;
// validate expected disbursement date against meeting date
if (loan.isSyncDisbursementWithMeeting() && (loan.isGroupLoan() || loan.isJLGLoan())) {
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
CalendarEntityType.LOANS.getValue());
Calendar calendar = null;
if (calendarInstance != null) {
calendar = calendarInstance.getCalendar();
}
// final Calendar calendar = calendarInstance.getCalendar();
boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
if (isSkipRepaymentOnFirstMonthEnabled) {
isSkipRepaymentOnFirstMonth = this.loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), calendar);
if (isSkipRepaymentOnFirstMonth) {
numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
}
}
this.loanScheduleAssembler.validateDisbursementDateWithMeetingDates(expectedDisbursementDate, calendar,
isSkipRepaymentOnFirstMonth, numberOfDays);
}
final Map<String, Object> changes = loan.loanApplicationApproval(currentUser, command, disbursementDataArray,
defaultLoanLifecycleStateMachine);
entityDatatableChecksWritePlatformService.runTheCheckForProduct(loanId, EntityTables.LOAN.getName(),
StatusEnum.APPROVE.getCode().longValue(), EntityTables.LOAN.getForeignKeyColumnNameOnDatatable(), loan.productId());
Pair<Loan, Map<String, Object>> loanAndChanges = loanScheduleAssembler.assembleLoanApproval(currentUser, command, loanId);
final Loan loan = loanAndChanges.getLeft();
final Map<String, Object> changes = loanAndChanges.getRight();
if (!changes.isEmpty()) {
// If loan approved amount less than loan demanded amount, then need
// to recompute the schedule
if (changes.containsKey(LoanApiConstants.approvedLoanAmountParameterName) || changes.containsKey("recalculateLoanSchedule")
|| changes.containsKey("expectedDisbursementDate")) {
LocalDate recalculateFrom = null;
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO);
}
if (loan.isTopup() && loan.getClientId() != null) {
final Long loanIdToClose = loan.getTopupLoanDetails().getLoanIdToClose();
final Loan loanToClose = this.loanRepositoryWrapper.findNonClosedLoanThatBelongsToClient(loanIdToClose, loan.getClientId());
if (loanToClose == null) {
throw new GeneralPlatformDomainRuleException("error.msg.loan.to.be.closed.with.topup.is.not.active",
"Loan to be closed with this topup is not active.");
}
final LocalDate lastUserTransactionOnLoanToClose = loanToClose.getLastUserTransactionDate();
if (DateUtils.isBefore(loan.getDisbursementDate(), lastUserTransactionOnLoanToClose)) {
throw new GeneralPlatformDomainRuleException(
"error.msg.loan.disbursal.date.should.be.after.last.transaction.date.of.loan.to.be.closed",
"Disbursal date of this loan application " + loan.getDisbursementDate()
+ " should be after last transaction date of loan to be closed " + lastUserTransactionOnLoanToClose);
}
BigDecimal loanOutstanding = this.loanReadPlatformService
.retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, loanIdToClose, expectedDisbursementDate).getAmount();
final BigDecimal firstDisbursalAmount = loan.getFirstDisbursalAmount();
if (loanOutstanding.compareTo(firstDisbursalAmount) > 0) {
throw new GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
"Topup loan amount should be greater than outstanding amount of loan to be closed.");
}
BigDecimal netDisbursalAmount = loan.getApprovedPrincipal().subtract(loanOutstanding);
loan.adjustNetDisbursalAmount(netDisbursalAmount);
}
loan = loanRepository.saveAndFlush(loan);
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
final Note note = Note.loanNote(loan, noteText);
changes.put("note", noteText);
this.noteRepository.save(note);
}
createNote(noteText, loan).ifPresent(note -> changes.put("note", noteText));
businessEventNotifierService.notifyPostBusinessEvent(new LoanApprovedBusinessEvent(loan));
}
@@ -944,10 +838,13 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
}
}
private void createNote(String submittedOnNote, Loan newLoanApplication) {
private Optional<Note> createNote(String submittedOnNote, Loan newLoanApplication) {
if (StringUtils.isNotBlank(submittedOnNote)) {
final Note note = Note.loanNote(newLoanApplication, submittedOnNote);
this.noteRepository.save(note);
return Optional.of(note);
} else {
return Optional.empty();
}
}
@@ -108,7 +108,7 @@ public class LoanUtilService {
if (isSkipRepaymentOnFirstMonthEnabled) {
isSkipRepaymentOnFirstMonth = isLoanRepaymentsSyncWithMeeting(loan.group(), calendar);
if (isSkipRepaymentOnFirstMonth) {
numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
numberOfDays = configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
}
}
final Boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled = this.configurationDomainService
@@ -251,7 +251,7 @@ public class LoanUtilService {
boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService
.isSkippingMeetingOnFirstDayOfMonthEnabled();
if (isSkipRepaymentOnFirstMonthEnabled) {
numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
numberOfDays = configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
isSkipRepaymentOnFirstMonth = isLoanRepaymentsSyncWithMeeting(loan.group(), calendar);
}
calculatedRepaymentsStartingFromDate = CalendarUtils.getFirstRepaymentMeetingDate(calendar, actualDisbursementDate,
@@ -279,7 +279,7 @@ public class LoanUtilService {
Integer numberOfDays = 0;
boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
if (isSkipRepaymentOnFirstMonthEnabled) {
numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
numberOfDays = configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
isSkipRepaymentOnFirstMonth = isLoanRepaymentsSyncWithMeeting(loan.group(), historyList.get(0).getCalendar());
}
calculatedRepaymentsStartingFromDate = CalendarUtils.getNextRepaymentMeetingDate(historyList.get(0).getRecurrence(),
@@ -2326,7 +2326,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (isSkipRepaymentOnFirstMonthEnabled) {
isSkipRepaymentOnFirstMonth = this.loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), calendar);
if (isSkipRepaymentOnFirstMonth) {
numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
numberOfDays = configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
}
}
@@ -216,12 +216,12 @@ public class LoanAccountConfiguration {
EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService, GLIMAccountInfoRepository glimRepository,
LoanRepository loanRepository, GSIMReadPlatformService gsimReadPlatformService,
LoanLifecycleStateMachine defaultLoanLifecycleStateMachine) {
return new LoanApplicationWritePlatformServiceJpaRepositoryImpl(context, fromJsonHelper, loanApplicationTransitionValidator,
return new LoanApplicationWritePlatformServiceJpaRepositoryImpl(context, loanApplicationTransitionValidator,
loanApplicationValidator, loanRepositoryWrapper, noteRepository, loanAssembler, loanSummaryWrapper,
loanRepaymentScheduleTransactionProcessorFactory, calendarRepository, calendarInstanceRepository, savingsAccountRepository,
accountAssociationsRepository, loanReadPlatformService, businessEventNotifierService, configurationDomainService,
loanScheduleAssembler, loanUtilService, calendarReadPlatformService, entityDatatableChecksWritePlatformService,
glimRepository, loanRepository, gsimReadPlatformService, defaultLoanLifecycleStateMachine);
accountAssociationsRepository, businessEventNotifierService, loanScheduleAssembler, loanUtilService,
calendarReadPlatformService, entityDatatableChecksWritePlatformService, glimRepository, loanRepository,
gsimReadPlatformService, defaultLoanLifecycleStateMachine);
}
@Bean
@@ -100,7 +100,7 @@ public class MeetingWritePlatformServiceJpaRepositoryImpl implements MeetingWrit
isSkipRepaymentOnFirstMonth = true;
}
if (isSkipRepaymentOnFirstMonth) {
numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
numberOfDays = configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
}
}
final Meeting newMeeting = Meeting.createNew(calendarInstance, meetingDate, isTransactionDateOnNonMeetingDate,
@@ -229,7 +229,7 @@ public class MeetingWritePlatformServiceJpaRepositoryImpl implements MeetingWrit
if (isSkipRepaymentOnFirstMonthEnabled) {
if (calendarInstance != null) {
isSkipRepaymentOnFirstMonth = true;
numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
numberOfDays = configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
}
}
final Meeting meetingForUpdate = this.meetingRepositoryWrapper.findOneWithNotFoundDetection(command.entityId());
@@ -305,7 +305,7 @@ public class MeetingWritePlatformServiceJpaRepositoryImpl implements MeetingWrit
if (isSkipRepaymentOnFirstMonthEnabled) {
isSkipRepaymentOnFirstMonth = true;
if (isSkipRepaymentOnFirstMonth) {
numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
numberOfDays = configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
}
}
// create new meeting