List of usage examples for org.joda.time LocalDate isBefore
public boolean isBefore(ReadablePartial partial)
From source file:org.apache.fineract.portfolio.account.service.StandingInstructionWritePlatformServiceImpl.java
License:Apache License
@Override @CronTarget(jobName = JobName.EXECUTE_STANDING_INSTRUCTIONS) public void executeStandingInstructions() throws JobExecutionException { Collection<StandingInstructionData> instructionDatas = this.standingInstructionReadPlatformService .retrieveAll(StandingInstructionStatus.ACTIVE.getValue()); final StringBuilder sb = new StringBuilder(); for (StandingInstructionData data : instructionDatas) { boolean isDueForTransfer = false; AccountTransferRecurrenceType recurrenceType = data.recurrenceType(); StandingInstructionType instructionType = data.instructionType(); LocalDate transactionDate = new LocalDate(); if (recurrenceType.isPeriodicRecurrence()) { final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator(); PeriodFrequencyType frequencyType = data.recurrenceFrequency(); LocalDate startDate = data.validFrom(); if (frequencyType.isMonthly()) { startDate = startDate.withDayOfMonth(data.recurrenceOnDay()); if (startDate.isBefore(data.validFrom())) { startDate = startDate.plusMonths(1); }/*from ww w. j ava2 s . c o m*/ } else if (frequencyType.isYearly()) { startDate = startDate.withDayOfMonth(data.recurrenceOnDay()) .withMonthOfYear(data.recurrenceOnMonth()); if (startDate.isBefore(data.validFrom())) { startDate = startDate.plusYears(1); } } isDueForTransfer = scheduledDateGenerator.isDateFallsInSchedule(frequencyType, data.recurrenceInterval(), startDate, transactionDate); } BigDecimal transactionAmount = data.amount(); if (data.toAccountType().isLoanAccount() && (recurrenceType.isDuesRecurrence() || (isDueForTransfer && instructionType.isDuesAmoutTransfer()))) { StandingInstructionDuesData standingInstructionDuesData = this.standingInstructionReadPlatformService .retriveLoanDuesData(data.toAccount().accountId()); if (data.instructionType().isDuesAmoutTransfer()) { transactionAmount = standingInstructionDuesData.totalDueAmount(); } if (recurrenceType.isDuesRecurrence()) { isDueForTransfer = new LocalDate().equals(standingInstructionDuesData.dueDate()); } } if (isDueForTransfer && transactionAmount != null && transactionAmount.compareTo(BigDecimal.ZERO) > 0) { final AccountTransferDetails accountTransferDetails = this.accountTransferDetailRepository .findOne(data.accountDetailId()); final SavingsAccount fromSavingsAccount = null; final boolean isRegularTransaction = true; final boolean isExceptionForBalanceCheck = false; accountTransferDetails.accountTransferStandingInstruction() .updateLatsRunDate(transactionDate.toDate()); AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount, data.fromAccountType(), data.toAccountType(), data.fromAccount().accountId(), data.toAccount().accountId(), data.name() + " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null, data.transferType().getValue(), accountTransferDetails, null, null, null, null, fromSavingsAccount, isRegularTransaction, isExceptionForBalanceCheck); transferAmount(sb, accountTransferDTO, data.getId()); } } if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); } }
From source file:org.apache.fineract.portfolio.calendar.domain.Calendar.java
License:Apache License
public Map<String, Object> updateStartDateAndDerivedFeilds(final LocalDate newMeetingStartDate) { final Map<String, Object> actualChanges = new LinkedHashMap<>(9); final LocalDate currentDate = DateUtils.getLocalDateOfTenant(); if (newMeetingStartDate.isBefore(currentDate)) { final String defaultUserMessage = "New meeting effective from date cannot be in past"; throw new CalendarDateException("new.start.date.cannot.be.in.past", defaultUserMessage, newMeetingStartDate, getStartDateLocalDate()); } else if (isStartDateAfter(newMeetingStartDate) && isStartDateBeforeOrEqual(currentDate)) { // new meeting date should be on or after start date or current // date// www.j av a 2s .c om final String defaultUserMessage = "New meeting effective from date cannot be a date before existing meeting start date"; throw new CalendarDateException("new.start.date.before.existing.date", defaultUserMessage, newMeetingStartDate, getStartDateLocalDate()); } else { actualChanges.put(CALENDAR_SUPPORTED_PARAMETERS.START_DATE.getValue(), newMeetingStartDate.toString()); this.startDate = newMeetingStartDate.toDate(); /* * If meeting start date is changed then there is possibilities of * recurring day may change, so derive the recurring day and update * it if it is changed. For weekly type is weekday and for monthly * type it is day of the month */ CalendarFrequencyType calendarFrequencyType = CalendarUtils.getFrequency(this.recurrence); Integer interval = CalendarUtils.getInterval(this.recurrence); Integer repeatsOnDay = null; /* * Repeats on day, need to derive based on the start date */ if (calendarFrequencyType.isWeekly()) { repeatsOnDay = newMeetingStartDate.getDayOfWeek(); } else if (calendarFrequencyType.isMonthly()) { repeatsOnDay = newMeetingStartDate.getDayOfMonth(); } // TODO cover other recurrence also this.recurrence = constructRecurrence(calendarFrequencyType, interval, repeatsOnDay); } return actualChanges; }
From source file:org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData.java
License:Apache License
private boolean occursBefore(final LocalDate date, final LocalDate target) { return target != null && target.isBefore(date); }
From source file:org.apache.fineract.portfolio.loanaccount.domain.Loan.java
License:Apache License
/** * Creates a loanTransaction for "Apply Charge Event" with transaction date * set to "suppliedTransactionDate". The newly created transaction is also * added to the Loan on which this method is called. * // w ww. java 2 s. c o m * If "suppliedTransactionDate" is not passed Id, the transaction date is * set to the loans due date if the due date is lesser than todays date. If * not, the transaction date is set to todays date * * @param loanCharge * @param suppliedTransactionDate * @return */ public LoanTransaction handleChargeAppliedTransaction(final LoanCharge loanCharge, final LocalDate suppliedTransactionDate, final AppUser currentUser) { final Money chargeAmount = loanCharge.getAmount(getCurrency()); Money feeCharges = chargeAmount; Money penaltyCharges = Money.zero(loanCurrency()); if (loanCharge.isPenaltyCharge()) { penaltyCharges = chargeAmount; feeCharges = Money.zero(loanCurrency()); } LocalDate transactionDate = null; if (suppliedTransactionDate != null) { transactionDate = suppliedTransactionDate; } else { transactionDate = loanCharge.getDueLocalDate(); final LocalDate currentDate = DateUtils.getLocalDateOfTenant(); // if loan charge is to be applied on a future date, the loan // transaction would show todays date as applied date if (transactionDate == null || currentDate.isBefore(transactionDate)) { transactionDate = currentDate; } } final LoanTransaction applyLoanChargeTransaction = LoanTransaction.accrueLoanCharge(this, getOffice(), chargeAmount, transactionDate, feeCharges, penaltyCharges, DateUtils.getLocalDateTimeOfTenant(), currentUser); Integer installmentNumber = null; final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(applyLoanChargeTransaction, loanCharge, loanCharge.getAmount(getCurrency()).getAmount(), installmentNumber); applyLoanChargeTransaction.getLoanChargesPaid().add(loanChargePaidBy); this.loanTransactions.add(applyLoanChargeTransaction); return applyLoanChargeTransaction; }
From source file:org.apache.fineract.portfolio.loanaccount.domain.Loan.java
License:Apache License
private BigDecimal constructFloatingInterestRates(final BigDecimal annualNominalInterestRate, final FloatingRateDTO floatingRateDTO, final List<LoanTermVariationsData> loanTermVariations) { final LocalDate dateValue = null; final boolean isSpecificToInstallment = false; BigDecimal interestRate = annualNominalInterestRate; if (loanProduct.isLinkedToFloatingInterestRate()) { Collection<FloatingRatePeriodData> applicableRates = loanProduct.fetchInterestRates(floatingRateDTO); LocalDate interestRateStartDate = DateUtils.getLocalDateOfTenant(); for (FloatingRatePeriodData periodData : applicableRates) { LoanTermVariationsData loanTermVariation = new LoanTermVariationsData( LoanEnumerations.loanvariationType(LoanTermVariationType.INTEREST_RATE), periodData.getFromDateAsLocalDate(), periodData.getInterestRate(), dateValue, isSpecificToInstallment); if (!interestRateStartDate.isBefore(periodData.getFromDateAsLocalDate())) { interestRateStartDate = periodData.getFromDateAsLocalDate(); interestRate = periodData.getInterestRate(); }/*www . j a va 2s.c o m*/ loanTermVariations.add(loanTermVariation); } } return interestRate; }
From source file:org.apache.fineract.portfolio.loanaccount.domain.Loan.java
License:Apache License
private void handleDisbursementTransaction(final LocalDate disbursedOn, final LocalDateTime createdDate, final AppUser currentUser) { // add repayment transaction to track incoming money from client to mfi // for (charges due at time of disbursement) /***//from w w w.ja va 2 s.com * TODO Vishwas: do we need to be able to pass in payment type details * for repayments at disbursements too? ***/ final Money totalFeeChargesDueAtDisbursement = this.summary .getTotalFeeChargesDueAtDisbursement(loanCurrency()); /** * all Charges repaid at disbursal is marked as repaid and * "APPLY Charge" transactions are created for all other fees ( which * are created during disbursal but not repaid) **/ Money disbursentMoney = Money.zero(getCurrency()); final LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(getOffice(), disbursentMoney, null, disbursedOn, null, createdDate, currentUser); final Integer installmentNumber = null; for (final LoanCharge charge : charges()) { Date actualDisbursementDate = getActualDisbursementDate(charge); if (charge.getCharge().getChargeTimeType() == ChargeTimeType.DISBURSEMENT.getValue() || (charge.getCharge().getChargeTimeType() == ChargeTimeType.TRANCHE_DISBURSEMENT.getValue() && disbursedOn.equals(new LocalDate(actualDisbursementDate)) && actualDisbursementDate != null && !charge.isWaived() && !charge.isFullyPaid())) { if (totalFeeChargesDueAtDisbursement.isGreaterThanZero() && !charge.getChargePaymentMode().isPaymentModeAccountTransfer()) { charge.markAsFullyPaid(); // Add "Loan Charge Paid By" details to this transaction final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, charge.amount(), installmentNumber); chargesPayment.getLoanChargesPaid().add(loanChargePaidBy); disbursentMoney = disbursentMoney.plus(charge.amount()); } } else if (disbursedOn.equals(new LocalDate(this.actualDisbursementDate))) { /** * create a Charge applied transaction if Up front Accrual, None * or Cash based accounting is enabled **/ if (isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct()) { handleChargeAppliedTransaction(charge, disbursedOn, currentUser); } } } if (disbursentMoney.isGreaterThanZero()) { final Money zero = Money.zero(getCurrency()); chargesPayment.updateComponentsAndTotal(zero, zero, disbursentMoney, zero); chargesPayment.updateLoan(this); this.loanTransactions.add(chargesPayment); updateLoanOutstandingBalaces(); } if (getApprovedOnDate() != null && disbursedOn.isBefore(getApprovedOnDate())) { final String errorMessage = "The date on which a loan is disbursed cannot be before its approval date: " + getApprovedOnDate().toString(); throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.before.approval.date", errorMessage, disbursedOn, getApprovedOnDate()); } if (getExpectedFirstRepaymentOnDate() != null && (disbursedOn.isAfter(this.fetchRepaymentScheduleInstallment(1).getDueDate()) || disbursedOn.isAfter(getExpectedFirstRepaymentOnDate())) && disbursedOn.toDate().equals(this.actualDisbursementDate)) { final String errorMessage = "submittedOnDate cannot be after the loans expectedFirstRepaymentOnDate: " + getExpectedFirstRepaymentOnDate().toString(); throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.after.expected.first.repayment.date", errorMessage, disbursedOn, getExpectedFirstRepaymentOnDate()); } validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_DISBURSED, disbursedOn); if (disbursedOn.isAfter(new LocalDate())) { final String errorMessage = "The date on which a loan with identifier : " + this.accountNumber + " is disbursed cannot be in the future."; throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.a.future.date", errorMessage, disbursedOn); } }
From source file:org.apache.fineract.portfolio.loanaccount.domain.Loan.java
License:Apache License
private ChangedTransactionDetail handleRepaymentOrRecoveryOrWaiverTransaction( final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction adjustedTransaction, final ScheduleGeneratorDTO scheduleGeneratorDTO, final AppUser currentUser) { ChangedTransactionDetail changedTransactionDetail = null; LoanStatus statusEnum = null;/*from w w w.j a v a 2 s . c om*/ LocalDate recalculateFrom = loanTransaction.getTransactionDate(); if (adjustedTransaction != null && adjustedTransaction.getTransactionDate().isBefore(recalculateFrom)) { recalculateFrom = adjustedTransaction.getTransactionDate(); } if (loanTransaction.isRecoveryRepayment()) { statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_RECOVERY_PAYMENT, LoanStatus.fromInt(this.loanStatus)); } else { statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, LoanStatus.fromInt(this.loanStatus)); } this.loanStatus = statusEnum.getValue(); loanTransaction.updateLoan(this); final boolean isTransactionChronologicallyLatest = isChronologicallyLatestRepaymentOrWaiver(loanTransaction, this.loanTransactions); if (loanTransaction.isNotZero(loanCurrency())) { this.loanTransactions.add(loanTransaction); } if (loanTransaction.isNotRepayment() && loanTransaction.isNotWaiver() && loanTransaction.isNotRecoveryRepayment()) { final String errorMessage = "A transaction of type repayment or recovery repayment or waiver was expected but not received."; throw new InvalidLoanTransactionTypeException("transaction", "is.not.a.repayment.or.waiver.or.recovery.transaction", errorMessage); } final LocalDate loanTransactionDate = loanTransaction.getTransactionDate(); if (loanTransactionDate.isBefore(getDisbursementDate())) { final String errorMessage = "The transaction date cannot be before the loan disbursement date: " + getApprovedOnDate().toString(); throw new InvalidLoanStateTransitionException("transaction", "cannot.be.before.disbursement.date", errorMessage, loanTransactionDate, getDisbursementDate()); } if (loanTransactionDate.isAfter(DateUtils.getLocalDateOfTenant())) { final String errorMessage = "The transaction date cannot be in the future."; throw new InvalidLoanStateTransitionException("transaction", "cannot.be.a.future.date", errorMessage, loanTransactionDate); } if (loanTransaction.isInterestWaiver()) { Money totalInterestOutstandingOnLoan = getTotalInterestOutstandingOnLoan(); if (adjustedTransaction != null) { totalInterestOutstandingOnLoan = totalInterestOutstandingOnLoan .plus(adjustedTransaction.getAmount(loanCurrency())); } if (loanTransaction.getAmount(loanCurrency()).isGreaterThan(totalInterestOutstandingOnLoan)) { final String errorMessage = "The amount of interest to waive cannot be greater than total interest outstanding on loan."; throw new InvalidLoanStateTransitionException("waive.interest", "amount.exceeds.total.outstanding.interest", errorMessage, loanTransaction.getAmount(loanCurrency()), totalInterestOutstandingOnLoan.getAmount()); } } if (this.loanProduct.isMultiDisburseLoan() && adjustedTransaction == null) { BigDecimal totalDisbursed = getDisbursedAmount(); if (totalDisbursed.compareTo(this.summary.getTotalPrincipalRepaid()) < 0) { final String errorMessage = "The transaction cannot be done before the loan disbursement: " + getApprovedOnDate().toString(); throw new InvalidLoanStateTransitionException("transaction", "cannot.be.done.before.disbursement", errorMessage); } } final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategy); final LoanRepaymentScheduleInstallment currentInstallment = fetchLoanRepaymentScheduleInstallment( loanTransaction.getTransactionDate()); boolean reprocess = true; if (isTransactionChronologicallyLatest && adjustedTransaction == null && loanTransaction.getTransactionDate().isEqual(LocalDate.now()) && currentInstallment != null && currentInstallment.getTotalOutstanding(getCurrency()) .isEqualTo(loanTransaction.getAmount(getCurrency()))) { reprocess = false; } if (isTransactionChronologicallyLatest && adjustedTransaction == null && (!reprocess || !this.repaymentScheduleDetail().isInterestRecalculationEnabled())) { loanRepaymentScheduleTransactionProcessor.handleTransaction(loanTransaction, getCurrency(), this.repaymentScheduleInstallments, charges()); reprocess = false; if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (currentInstallment == null || currentInstallment.isNotFullyPaidOff()) { reprocess = true; } else { final LoanRepaymentScheduleInstallment nextInstallment = fetchRepaymentScheduleInstallment( currentInstallment.getInstallmentNumber() + 1); if (nextInstallment != null && nextInstallment.getTotalPaidInAdvance(getCurrency()).isGreaterThanZero()) { reprocess = true; } } } } if (reprocess) { if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO, currentUser); } final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction( getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, charges()); for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail .getNewTransactionMappings().entrySet()) { mapEntry.getValue().updateLoan(this); } /*** * Commented since throwing exception if external id present for one * of the transactions. for this need to save the reversed * transactions first and then new transactions. */ this.loanTransactions.addAll(changedTransactionDetail.getNewTransactionMappings().values()); } updateLoanSummaryDerivedFields(); /** * FIXME: Vishwas, skipping post loan transaction checks for Loan * recoveries **/ if (loanTransaction.isNotRecoveryRepayment()) { doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine); } if (this.loanProduct.isMultiDisburseLoan()) { BigDecimal totalDisbursed = getDisbursedAmount(); if (totalDisbursed.compareTo(this.summary.getTotalPrincipalRepaid()) < 0 && this.repaymentScheduleDetail().getPrincipal().minus(totalDisbursed).isGreaterThanZero()) { final String errorMessage = "The transaction cannot be done before the loan disbursement: " + getApprovedOnDate().toString(); throw new InvalidLoanStateTransitionException("transaction", "cannot.be.done.before.disbursement", errorMessage); } } if (changedTransactionDetail != null) { this.loanTransactions.removeAll(changedTransactionDetail.getNewTransactionMappings().values()); } return changedTransactionDetail; }
From source file:org.apache.fineract.portfolio.loanaccount.domain.Loan.java
License:Apache License
public ChangedTransactionDetail closeAsWrittenOff(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, final AppUser currentUser, final ScheduleGeneratorDTO scheduleGeneratorDTO) { final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategy); ChangedTransactionDetail changedTransactionDetail = closeDisbursements(scheduleGeneratorDTO, loanRepaymentScheduleTransactionProcessor, currentUser); validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING); final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING, LoanStatus.fromInt(this.loanStatus)); LoanTransaction loanTransaction = null; if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) { this.loanStatus = statusEnum.getValue(); changes.put("status", LoanEnumerations.status(this.loanStatus)); existingTransactionIds.addAll(findExistingTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); final LocalDate writtenOffOnLocalDate = command.localDateValueOfParameterNamed("transactionDate"); final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId"); this.closedOnDate = writtenOffOnLocalDate.toDate(); this.writtenOffOnDate = writtenOffOnLocalDate.toDate(); this.closedBy = currentUser; changes.put("closedOnDate", command.stringValueOfParameterNamed("transactionDate")); changes.put("writtenOffOnDate", command.stringValueOfParameterNamed("transactionDate")); if (writtenOffOnLocalDate.isBefore(getDisbursementDate())) { final String errorMessage = "The date on which a loan is written off cannot be before the loan disbursement date: " + getDisbursementDate().toString(); throw new InvalidLoanStateTransitionException("writeoff", "cannot.be.before.submittal.date", errorMessage, writtenOffOnLocalDate, getDisbursementDate()); }/*from w w w .ja v a 2s .co m*/ validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.WRITE_OFF_OUTSTANDING, writtenOffOnLocalDate); if (writtenOffOnLocalDate.isAfter(DateUtils.getLocalDateOfTenant())) { final String errorMessage = "The date on which a loan is written off cannot be in the future."; throw new InvalidLoanStateTransitionException("writeoff", "cannot.be.a.future.date", errorMessage, writtenOffOnLocalDate); } LocalDateTime createdDate = DateUtils.getLocalDateTimeOfTenant(); loanTransaction = LoanTransaction.writeoff(this, getOffice(), writtenOffOnLocalDate, txnExternalId, createdDate, currentUser); LocalDate lastTransactionDate = getLastUserTransactionDate(); if (lastTransactionDate.isAfter(writtenOffOnLocalDate)) { final String errorMessage = "The date of the writeoff transaction must occur on or before previous transactions."; throw new InvalidLoanStateTransitionException("writeoff", "must.occur.on.or.after.other.transaction.dates", errorMessage, writtenOffOnLocalDate); } this.loanTransactions.add(loanTransaction); loanRepaymentScheduleTransactionProcessor.handleWriteOff(loanTransaction, loanCurrency(), this.repaymentScheduleInstallments); updateLoanSummaryDerivedFields(); } if (changedTransactionDetail == null) { changedTransactionDetail = new ChangedTransactionDetail(); } changedTransactionDetail.getNewTransactionMappings().put(0L, loanTransaction); return changedTransactionDetail; }
From source file:org.apache.fineract.portfolio.loanaccount.domain.Loan.java
License:Apache License
public ChangedTransactionDetail close(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO, final AppUser currentUser) { validateAccountStatus(LoanEvent.LOAN_CLOSED); existingTransactionIds.addAll(findExistingTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); final LocalDate closureDate = command.localDateValueOfParameterNamed("transactionDate"); final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId"); this.closedOnDate = closureDate.toDate(); changes.put("closedOnDate", command.stringValueOfParameterNamed("transactionDate")); validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.REPAID_IN_FULL, closureDate); if (closureDate.isBefore(getDisbursementDate())) { final String errorMessage = "The date on which a loan is closed cannot be before the loan disbursement date: " + getDisbursementDate().toString(); throw new InvalidLoanStateTransitionException("close", "cannot.be.before.submittal.date", errorMessage, closureDate, getDisbursementDate()); }//from w w w . ja v a 2 s.c om if (closureDate.isAfter(DateUtils.getLocalDateOfTenant())) { final String errorMessage = "The date on which a loan is closed cannot be in the future."; throw new InvalidLoanStateTransitionException("close", "cannot.be.a.future.date", errorMessage, closureDate); } final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategy); ChangedTransactionDetail changedTransactionDetail = closeDisbursements(scheduleGeneratorDTO, loanRepaymentScheduleTransactionProcessor, currentUser); LoanTransaction loanTransaction = null; if (isOpen()) { final Money totalOutstanding = this.summary.getTotalOutstanding(loanCurrency()); if (totalOutstanding.isGreaterThanZero() && getInArrearsTolerance().isGreaterThanOrEqualTo(totalOutstanding)) { final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, LoanStatus.fromInt(this.loanStatus)); if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) { this.loanStatus = statusEnum.getValue(); changes.put("status", LoanEnumerations.status(this.loanStatus)); } this.closedOnDate = closureDate.toDate(); loanTransaction = LoanTransaction.writeoff(this, getOffice(), closureDate, txnExternalId, DateUtils.getLocalDateTimeOfTenant(), currentUser); final boolean isLastTransaction = isChronologicallyLatestTransaction(loanTransaction, this.loanTransactions); if (!isLastTransaction) { final String errorMessage = "The closing date of the loan must be on or after latest transaction date."; throw new InvalidLoanStateTransitionException("close.loan", "must.occur.on.or.after.latest.transaction.date", errorMessage, closureDate); } this.loanTransactions.add(loanTransaction); loanRepaymentScheduleTransactionProcessor.handleWriteOff(loanTransaction, loanCurrency(), this.repaymentScheduleInstallments); updateLoanSummaryDerivedFields(); } else if (totalOutstanding.isGreaterThanZero()) { final String errorMessage = "A loan with money outstanding cannot be closed"; throw new InvalidLoanStateTransitionException("close", "loan.has.money.outstanding", errorMessage, totalOutstanding.toString()); } } if (isOverPaid()) { final Money totalLoanOverpayment = calculateTotalOverpayment(); if (totalLoanOverpayment.isGreaterThanZero() && getInArrearsTolerance().isGreaterThanOrEqualTo(totalLoanOverpayment)) { // TODO - KW - technically should set somewhere that this loan // has // 'overpaid' amount final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, LoanStatus.fromInt(this.loanStatus)); if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) { this.loanStatus = statusEnum.getValue(); changes.put("status", LoanEnumerations.status(this.loanStatus)); } this.closedOnDate = closureDate.toDate(); } else if (totalLoanOverpayment.isGreaterThanZero()) { final String errorMessage = "The loan is marked as 'Overpaid' and cannot be moved to 'Closed (obligations met)."; throw new InvalidLoanStateTransitionException("close", "loan.is.overpaid", errorMessage, totalLoanOverpayment.toString()); } } if (changedTransactionDetail == null) { changedTransactionDetail = new ChangedTransactionDetail(); } changedTransactionDetail.getNewTransactionMappings().put(0L, loanTransaction); return changedTransactionDetail; }
From source file:org.apache.fineract.portfolio.loanaccount.domain.Loan.java
License:Apache License
public void updateLoanRepaymentScheduleDates(final LocalDate meetingStartDate, final String recuringRule, final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays, final Boolean reschedulebasedOnMeetingDates, final LocalDate presentMeetingDate, final LocalDate newMeetingDate) { // first repayment's from date is same as disbursement date. /*/* ww w. j a va 2s. c o m*/ * meetingStartDate is used as seedDate Capture the seedDate from user * and use the seedDate as meetingStart date */ LocalDate tmpFromDate = getDisbursementDate(); final PeriodFrequencyType repaymentPeriodFrequencyType = this.loanRepaymentScheduleDetail .getRepaymentPeriodFrequencyType(); final Integer loanRepaymentInterval = this.loanRepaymentScheduleDetail.getRepayEvery(); final String frequency = CalendarUtils .getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType); LocalDate newRepaymentDate = null; Boolean isFirstTime = true; LocalDate latestRepaymentDate = null; for (final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : this.repaymentScheduleInstallments) { LocalDate oldDueDate = loanRepaymentScheduleInstallment.getDueDate(); if (oldDueDate.isEqual(presentMeetingDate) || oldDueDate.isAfter(presentMeetingDate)) { if (isFirstTime) { isFirstTime = false; newRepaymentDate = newMeetingDate; } else { // tmpFromDate.plusDays(1) is done to make sure // getNewRepaymentMeetingDate method returns next meeting // date and not the same as tmpFromDate newRepaymentDate = CalendarUtils.getNewRepaymentMeetingDate(recuringRule, tmpFromDate, tmpFromDate.plusDays(1), loanRepaymentInterval, frequency, workingDays); } if (isHolidayEnabled) { newRepaymentDate = HolidayUtil.getRepaymentRescheduleDateToIfHoliday(newRepaymentDate, holidays); } if (latestRepaymentDate == null || latestRepaymentDate.isBefore(newRepaymentDate)) { latestRepaymentDate = newRepaymentDate; } loanRepaymentScheduleInstallment.updateDueDate(newRepaymentDate); // reset from date to get actual daysInPeriod if (!isFirstTime) { loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate); } tmpFromDate = newRepaymentDate;// update with new repayment // date } else { tmpFromDate = oldDueDate; } } if (latestRepaymentDate != null) { this.expectedMaturityDate = latestRepaymentDate.toDate(); } }