public EndOfDayBalance toEndOfDayBalanceBoundedBy(
      final Money openingBalance, final LocalDateInterval boundedBy) {

    MonetaryCurrency currency = openingBalance.getCurrency();
    Money endOfDayBalance = openingBalance.copy();

    int numberOfDaysOfBalance = this.balanceNumberOfDays;

    LocalDate balanceStartDate = getTransactionLocalDate();
    LocalDate balanceEndDate = getEndOfBalanceLocalDate();

    if (boundedBy.startDate().isAfter(balanceStartDate)) {
      balanceStartDate = boundedBy.startDate();
      LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
      numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
    } else {
      if (isDeposit()) {
        endOfDayBalance = openingBalance.plus(getAmount(currency));
      } else if (isWithdrawal() || isWithdrawalFee()) {
        endOfDayBalance = openingBalance.minus(getAmount(currency));
      }
    }

    if (balanceEndDate.isAfter(boundedBy.endDate())) {
      balanceEndDate = boundedBy.endDate();
      LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
      numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
    }

    return EndOfDayBalance.from(
        balanceStartDate, openingBalance, endOfDayBalance, numberOfDaysOfBalance);
  }
Example #2
0
 public Money calculateTotalInterestWrittenOff(
     final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
     final MonetaryCurrency currency) {
   Money total = Money.zero(currency);
   for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
     total = total.plus(installment.getInterestWrittenOff(currency));
   }
   return total;
 }
Example #3
0
 public Money calculateTotalPrincipalRepaid(
     final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
     final MonetaryCurrency currency) {
   Money total = Money.zero(currency);
   for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
     total = total.plus(installment.getPrincipalCompleted(currency));
   }
   return total;
 }
Example #4
0
 public Money calculateTotalPenaltyChargesWaived(
     final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
     final MonetaryCurrency currency) {
   Money total = Money.zero(currency);
   for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
     total = total.plus(installment.getPenaltyChargesWaived(currency));
   }
   return total;
 }
Example #5
0
 public Money calculateTotalInterestOverdueOn(
     final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
     final MonetaryCurrency currency,
     final LocalDate overdueAsOf) {
   Money total = Money.zero(currency);
   for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
     if (installment.isOverdueOn(overdueAsOf)) {
       total = total.plus(installment.getInterestOutstanding(currency));
     }
   }
   return total;
 }
  public EndOfDayBalance toEndOfDayBalance(final Money openingBalance) {
    MonetaryCurrency currency = openingBalance.getCurrency();
    Money endOfDayBalance = openingBalance.copy();
    if (isDeposit()) {
      endOfDayBalance = openingBalance.plus(getAmount(currency));
    } else if (isWithdrawal() || isWithdrawalFee()) {
      endOfDayBalance = openingBalance.minus(getAmount(currency));
    }

    return EndOfDayBalance.from(
        getTransactionLocalDate(), openingBalance, endOfDayBalance, this.balanceNumberOfDays);
  }
Example #7
0
 public LoanRepaymentScheduleInstallment fetchRepaymentInstallment(final Money trasferedAmount) {
   for (final LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) {
     if (loanChargePerInstallment.isPending()
         && trasferedAmount
             .getAmount()
             .equals(
                 loanChargePerInstallment
                     .getAmountThroughChargePayment(trasferedAmount.getCurrency())
                     .getAmount())) {
       return loanChargePerInstallment.getRepaymentInstallment();
     }
   }
   return null;
 }
 public void updateCumulativeBalanceAndDates(
     final MonetaryCurrency currency, final LocalDate endOfBalanceDate) {
   this.balanceEndDate = endOfBalanceDate.toDate();
   this.balanceNumberOfDays =
       LocalDateInterval.create(getTransactionLocalDate(), endOfBalanceDate)
           .daysInPeriodInclusiveOfEndDate();
   this.cumulativeBalance =
       Money.of(currency, this.runningBalance).multipliedBy(this.balanceNumberOfDays).getAmount();
 }
Example #9
0
  public Money calculateTotalOverdueOn(
      final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
      final MonetaryCurrency currency,
      final LocalDate overdueAsOf) {

    final Money principalOverdue =
        calculateTotalPrincipalOverdueOn(repaymentScheduleInstallments, currency, overdueAsOf);
    final Money interestOverdue =
        calculateTotalInterestOverdueOn(repaymentScheduleInstallments, currency, overdueAsOf);
    final Money feeChargesOverdue =
        calculateTotalFeeChargesOverdueOn(repaymentScheduleInstallments, currency, overdueAsOf);
    final Money penaltyChargesOverdue =
        calculateTotalPenaltyChargesOverdueOn(repaymentScheduleInstallments, currency, overdueAsOf);

    return principalOverdue
        .plus(interestOverdue)
        .plus(feeChargesOverdue)
        .plus(penaltyChargesOverdue);
  }
Example #10
0
  public void updateSummary(
      final MonetaryCurrency currency,
      final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
      final LoanSummaryWrapper summaryWrapper) {

    this.totalPrincipalOverdue =
        summaryWrapper
            .calculateTotalPrincipalOverdueOn(
                repaymentScheduleInstallments, currency, DateUtils.getLocalDateOfTenant())
            .getAmount();
    this.totalInterestOverdue =
        summaryWrapper
            .calculateTotalInterestOverdueOn(
                repaymentScheduleInstallments, currency, DateUtils.getLocalDateOfTenant())
            .getAmount();
    this.totalFeeChargesOverdue =
        summaryWrapper
            .calculateTotalFeeChargesOverdueOn(
                repaymentScheduleInstallments, currency, DateUtils.getLocalDateOfTenant())
            .getAmount();
    this.totalPenaltyChargesOverdue =
        summaryWrapper
            .calculateTotalPenaltyChargesOverdueOn(
                repaymentScheduleInstallments, currency, DateUtils.getLocalDateOfTenant())
            .getAmount();

    final Money totalOverdue =
        Money.of(currency, this.totalPrincipalOverdue)
            .plus(this.totalInterestOverdue)
            .plus(this.totalFeeChargesOverdue)
            .plus(this.totalPenaltyChargesOverdue);
    this.totalOverdue = totalOverdue.getAmount();

    final LocalDate overdueSinceLocalDate =
        summaryWrapper.determineOverdueSinceDateFrom(
            repaymentScheduleInstallments, currency, DateUtils.getLocalDateOfTenant());
    if (overdueSinceLocalDate != null) {
      this.overdueSinceDate = overdueSinceLocalDate.toDate();
    } else {
      this.overdueSinceDate = null;
    }
  }
  public EndOfDayBalance toEndOfDayBalance(
      final Money openingBalance, final LocalDate nextTransactionDate) {

    MonetaryCurrency currency = openingBalance.getCurrency();
    Money endOfDayBalance = openingBalance.copy();
    if (isDeposit()) {
      endOfDayBalance = openingBalance.plus(getAmount(currency));
    } else if (isWithdrawal() || isWithdrawalFee()) {
      endOfDayBalance = openingBalance.minus(getAmount(currency));
    }

    int numberOfDays =
        LocalDateInterval.create(getTransactionLocalDate(), nextTransactionDate)
            .daysInPeriodInclusiveOfEndDate();
    if (!openingBalance.isEqualTo(endOfDayBalance) && numberOfDays > 1) {
      numberOfDays = numberOfDays - 1;
    }
    return EndOfDayBalance.from(
        getTransactionLocalDate(), openingBalance, endOfDayBalance, numberOfDays);
  }
Example #12
0
  public LocalDate determineOverdueSinceDateFrom(
      final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
      final MonetaryCurrency currency,
      final LocalDate from) {

    LocalDate overdueSince = null;
    final Money totalOverdue =
        calculateTotalOverdueOn(repaymentScheduleInstallments, currency, from);
    if (totalOverdue.isGreaterThanZero()) {
      for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
        if (installment.isOverdueOn(from)) {
          if (overdueSince == null || overdueSince.isAfter(installment.getDueDate())) {
            overdueSince = installment.getDueDate();
          }
        }
      }
    }

    return overdueSince;
  }
 private SavingsAccountTransaction(
     final SavingsAccount savingsAccount,
     final Integer typeOf,
     final LocalDate dateOf,
     final Money amount,
     final boolean isReversed) {
   this.savingsAccount = savingsAccount;
   this.typeOf = typeOf;
   this.dateOf = dateOf.toDate();
   this.amount = amount.getAmount();
   this.reversed = isReversed;
 }
Example #14
0
 public Money waive(final MonetaryCurrency currency, final Integer loanInstallmentNumber) {
   if (isInstalmentFee()) {
     final LoanInstallmentCharge chargePerInstallment =
         getInstallmentLoanCharge(loanInstallmentNumber);
     final Money amountWaived = chargePerInstallment.waive(currency);
     if (this.amountWaived == null) {
       this.amountWaived = BigDecimal.ZERO;
     }
     this.amountWaived = this.amountWaived.add(amountWaived.getAmount());
     this.amountOutstanding = this.amountOutstanding.subtract(amountWaived.getAmount());
     if (determineIfFullyPaid()) {
       this.paid = false;
       this.waived = true;
     }
     return amountWaived;
   }
   this.amountWaived = this.amountOutstanding;
   this.amountOutstanding = BigDecimal.ZERO;
   this.paid = false;
   this.waived = true;
   return getAmountWaived(currency);
 }
 private SavingsAccountTransaction(
     final SavingsAccount savingsAccount,
     final PaymentDetail paymentDetail,
     final Integer typeOf,
     final LocalDate transactionLocalDate,
     final Money amount,
     final boolean isReversed) {
   this.savingsAccount = savingsAccount;
   this.typeOf = typeOf;
   this.dateOf = transactionLocalDate.toDate();
   this.amount = amount.getAmount();
   this.reversed = isReversed;
   this.paymentDetail = paymentDetail;
 }
  @Transactional
  @Override
  public EntityIdentifier adjustLoanTransaction(final AdjustLoanTransactionCommand command) {

    context.authenticatedUser();

    AdjustLoanTransactionCommandValidator validator =
        new AdjustLoanTransactionCommandValidator(command);
    validator.validate();

    Loan loan = this.loanRepository.findOne(command.getLoanId());
    if (loan == null) {
      throw new LoanNotFoundException(command.getLoanId());
    }

    LoanTransaction transactionToAdjust =
        this.loanTransactionRepository.findOne(command.getTransactionId());
    if (transactionToAdjust == null) {
      throw new LoanTransactionNotFoundException(command.getTransactionId());
    }

    final MonetaryCurrency currency = loan.repaymentScheduleDetail().getPrincipal().getCurrency();
    final Money transactionAmount = Money.of(currency, command.getTransactionAmount());

    // adjustment is only supported for repayments and waivers at present
    LocalDate transactionDate = command.getTransactionDate();
    LoanTransaction newTransactionDetail =
        LoanTransaction.repayment(transactionAmount, transactionDate);
    if (transactionToAdjust.isInterestWaiver()) {
      newTransactionDetail = LoanTransaction.waiver(loan, transactionAmount, transactionDate);
    }

    loan.adjustExistingTransaction(
        transactionToAdjust, newTransactionDetail, defaultLoanLifecycleStateMachine());

    if (newTransactionDetail.isGreaterThanZero(currency)) {
      this.loanTransactionRepository.save(newTransactionDetail);
    }

    this.loanRepository.save(loan);

    String noteText = command.getNote();
    if (StringUtils.isNotBlank(noteText)) {
      Note note = Note.loanTransactionNote(loan, newTransactionDetail, noteText);
      this.noteRepository.save(note);
    }

    return new EntityIdentifier(loan.getId());
  }
  @Transactional
  @Override
  public CommandProcessingResult officeTransaction(final JsonCommand command) {

    context.authenticatedUser();

    this.moneyTransferCommandFromApiJsonDeserializer.validateOfficeTransfer(command.json());

    Long officeId = null;
    Office fromOffice = null;
    final Long fromOfficeId = command.longValueOfParameterNamed("fromOfficeId");
    if (fromOfficeId != null) {
      fromOffice = this.officeRepository.findOne(fromOfficeId);
      officeId = fromOffice.getId();
    }
    Office toOffice = null;
    final Long toOfficeId = command.longValueOfParameterNamed("toOfficeId");
    if (toOfficeId != null) {
      toOffice = this.officeRepository.findOne(toOfficeId);
      officeId = toOffice.getId();
    }

    if (fromOffice == null && toOffice == null) {
      throw new OfficeNotFoundException(toOfficeId);
    }

    final String currencyCode = command.stringValueOfParameterNamed("currencyCode");
    final ApplicationCurrency appCurrency =
        this.applicationCurrencyRepository.findOneByCode(currencyCode);
    if (appCurrency == null) {
      throw new CurrencyNotFoundException(currencyCode);
    }

    final MonetaryCurrency currency =
        new MonetaryCurrency(appCurrency.getCode(), appCurrency.getDecimalPlaces());
    final Money amount =
        Money.of(currency, command.bigDecimalValueOfParameterNamed("transactionAmount"));

    final OfficeTransaction entity =
        OfficeTransaction.fromJson(fromOffice, toOffice, amount, command);

    this.officeTransactionRepository.save(entity);

    return new CommandProcessingResultBuilder() //
        .withCommandId(command.commandId()) //
        .withEntityId(entity.getId()) //
        .withOfficeId(officeId) //
        .build();
  }
  @Transactional
  @Override
  public EntityIdentifier withdrawDepositAccountInterestMoney(
      DepositAccountWithdrawInterestCommand command) {

    context.authenticatedUser();

    WithDrawDepositAccountInterestCommandValidator validator =
        new WithDrawDepositAccountInterestCommandValidator(command);
    validator.validate();

    DepositAccount account = this.depositAccountRepository.findOne(command.getAccountId());
    if (account == null || account.isDeleted()) {
      throw new DepositAccountNotFoundException(command.getAccountId());
    }
    if (account.isInterestWithdrawable() && !account.isInterestCompoundingAllowed()) {

      // BigDecimal totalAvailableInterestForWithdrawal =
      // getTotalWithdrawableInterestAvailable(account);
      // BigDecimal interestPaid = account.getInterstPaid();
      BigDecimal remainInterestForWithdrawal =
          account
              .getAvailableInterest(); // totalAvailableInterestForWithdrawal.subtract(interestPaid);

      if (remainInterestForWithdrawal.doubleValue() > 0) {
        if (remainInterestForWithdrawal.doubleValue() >= command.getWithdrawInterest().doubleValue()
            && command.getWithdrawInterest().doubleValue() > 0) {
          account.withdrawInterest(
              Money.of(account.getDeposit().getCurrency(), command.getWithdrawInterest()));
          this.depositAccountRepository.save(account);
        } else {
          throw new DepositAccountTransactionsException(
              "deposit.transaction.interest.withdrawal.exceed",
              "You can Withdraw " + remainInterestForWithdrawal + " only");
        }
      } else {
        throw new DepositAccountTransactionsException(
            "deposit.transaction.interest.withdrawal.insufficient.amount",
            "You don't have enough money for withdrawal");
      }
    } else {
      throw new DepositAccountTransactionsException(
          "deposit.transaction.interest.withdrawal.cannot.withdraw",
          "You can not withdraw interst for this account");
    }
    return new EntityIdentifier(account.getId());
  }
  public EndOfDayBalance toEndOfDayBalance(
      final LocalDateInterval periodInterval, final MonetaryCurrency currency) {

    Money endOfDayBalance = Money.of(currency, this.runningBalance);
    Money openingBalance = endOfDayBalance;

    LocalDate balanceDate = periodInterval.startDate();

    int numberOfDays = periodInterval.daysInPeriodInclusiveOfEndDate();
    if (periodInterval.contains(getTransactionLocalDate())) {
      balanceDate = getTransactionLocalDate();
      LocalDateInterval newInterval =
          LocalDateInterval.create(getTransactionLocalDate(), periodInterval.endDate());
      numberOfDays = newInterval.daysInPeriodInclusiveOfEndDate();
    }

    return EndOfDayBalance.from(balanceDate, openingBalance, endOfDayBalance, numberOfDays);
  }
  @Transactional
  @Override
  public EntityIdentifier makeLoanRepayment(final LoanTransactionCommand command) {

    AppUser currentUser = context.authenticatedUser();

    LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
    validator.validate();

    Loan loan = this.loanRepository.findOne(command.getLoanId());
    if (loan == null) {
      throw new LoanNotFoundException(command.getLoanId());
    }

    LocalDate transactionDate = command.getTransactionDate();
    if (this.isBeforeToday(transactionDate) && currentUser.canNotMakeRepaymentOnLoanInPast()) {
      throw new NoAuthorizationException(
          "error.msg.no.permission.to.make.repayment.on.loan.in.past");
    }

    Money repayment =
        Money.of(
            loan.repaymentScheduleDetail().getPrincipal().getCurrency(),
            command.getTransactionAmount());

    LoanTransaction loanRepayment = LoanTransaction.repayment(repayment, transactionDate);
    loan.makeRepayment(loanRepayment, defaultLoanLifecycleStateMachine());
    this.loanTransactionRepository.save(loanRepayment);
    this.loanRepository.save(loan);

    String noteText = command.getNote();
    if (StringUtils.isNotBlank(noteText)) {
      Note note = Note.loanTransactionNote(loan, loanRepayment, noteText);
      this.noteRepository.save(note);
    }

    return new EntityIdentifier(loan.getId());
  }
Example #21
0
 /**
  * @param percentageOf
  * @returns a minimum cap or maximum cap set on charges if the criteria fits else it returns the
  *     percentageOf if the amount is within min and max cap
  */
 private BigDecimal minimumAndMaximumCap(final BigDecimal percentageOf) {
   BigDecimal minMaxCap = BigDecimal.ZERO;
   if (this.minCap != null) {
     final int minimumCap = percentageOf.compareTo(this.minCap);
     if (minimumCap == -1) {
       minMaxCap = this.minCap;
       return minMaxCap;
     }
   }
   if (this.maxCap != null) {
     final int maximumCap = percentageOf.compareTo(this.maxCap);
     if (maximumCap == 1) {
       minMaxCap = this.maxCap;
       return minMaxCap;
     }
   }
   minMaxCap = percentageOf;
   // this will round the amount value
   if (this.loan != null && minMaxCap != null) {
     minMaxCap = Money.of(this.loan.getCurrency(), minMaxCap).getAmount();
   }
   return minMaxCap;
 }
Example #22
0
 public Money getTotalFeeChargesDueAtDisbursement(final MonetaryCurrency currency) {
   return Money.of(currency, this.totalFeeChargesDueAtDisbursement);
 }
 public boolean hasNotAmount(final Money amountToCheck) {
   final Money transactionAmount = getAmount(amountToCheck.getCurrency());
   return transactionAmount.isNotEqualTo(amountToCheck);
 }
 public void updateRunningBalance(final Money balance) {
   this.runningBalance = balance.getAmount();
 }
 public Money getAmount(final MonetaryCurrency currency) {
   return Money.of(currency, this.amount);
 }
Example #26
0
 public boolean isNotInArrears(final MonetaryCurrency currency) {
   return Money.of(currency, this.totalOverdue).isZero();
 }
Example #27
0
  public void updateSummary(
      final MonetaryCurrency currency,
      final Money principal,
      final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
      final LoanSummaryWrapper summaryWrapper,
      final Boolean disbursed) {

    this.totalPrincipalDisbursed = principal.getAmount();
    this.totalPrincipalRepaid =
        summaryWrapper
            .calculateTotalPrincipalRepaid(repaymentScheduleInstallments, currency)
            .getAmount();
    this.totalPrincipalWrittenOff =
        summaryWrapper
            .calculateTotalPrincipalWrittenOff(repaymentScheduleInstallments, currency)
            .getAmount();

    this.totalPrincipalOutstanding =
        principal.minus(this.totalPrincipalRepaid).minus(this.totalPrincipalWrittenOff).getAmount();

    final Money totalInterestCharged =
        summaryWrapper.calculateTotalInterestCharged(repaymentScheduleInstallments, currency);
    this.totalInterestCharged = totalInterestCharged.getAmount();
    this.totalInterestRepaid =
        summaryWrapper
            .calculateTotalInterestRepaid(repaymentScheduleInstallments, currency)
            .getAmount();
    this.totalInterestWaived =
        summaryWrapper
            .calculateTotalInterestWaived(repaymentScheduleInstallments, currency)
            .getAmount();
    this.totalInterestWrittenOff =
        summaryWrapper
            .calculateTotalInterestWrittenOff(repaymentScheduleInstallments, currency)
            .getAmount();

    this.totalInterestOutstanding =
        totalInterestCharged
            .minus(this.totalInterestRepaid)
            .minus(this.totalInterestWaived)
            .minus(this.totalInterestWrittenOff)
            .getAmount();

    final Money totalFeeChargesCharged =
        summaryWrapper
            .calculateTotalFeeChargesCharged(repaymentScheduleInstallments, currency)
            .plus(this.totalFeeChargesDueAtDisbursement);
    this.totalFeeChargesCharged = totalFeeChargesCharged.getAmount();

    Money totalFeeChargesRepaid =
        summaryWrapper.calculateTotalFeeChargesRepaid(repaymentScheduleInstallments, currency);
    if (disbursed) {
      totalFeeChargesRepaid = totalFeeChargesRepaid.plus(this.totalFeeChargesDueAtDisbursement);
    }
    this.totalFeeChargesRepaid = totalFeeChargesRepaid.getAmount();

    this.totalFeeChargesWaived =
        summaryWrapper
            .calculateTotalFeeChargesWaived(repaymentScheduleInstallments, currency)
            .getAmount();
    this.totalFeeChargesWrittenOff =
        summaryWrapper
            .calculateTotalFeeChargesWrittenOff(repaymentScheduleInstallments, currency)
            .getAmount();

    this.totalFeeChargesOutstanding =
        totalFeeChargesCharged
            .minus(this.totalFeeChargesRepaid)
            .minus(this.totalFeeChargesWaived)
            .minus(this.totalFeeChargesWrittenOff)
            .getAmount();

    final Money totalPenaltyChargesCharged =
        summaryWrapper.calculateTotalPenaltyChargesCharged(repaymentScheduleInstallments, currency);
    this.totalPenaltyChargesCharged = totalPenaltyChargesCharged.getAmount();
    this.totalPenaltyChargesRepaid =
        summaryWrapper
            .calculateTotalPenaltyChargesRepaid(repaymentScheduleInstallments, currency)
            .getAmount();
    this.totalPenaltyChargesWaived =
        summaryWrapper
            .calculateTotalPenaltyChargesWaived(repaymentScheduleInstallments, currency)
            .getAmount();
    this.totalPenaltyChargesWrittenOff =
        summaryWrapper
            .calculateTotalPenaltyChargesWrittenOff(repaymentScheduleInstallments, currency)
            .getAmount();

    this.totalPenaltyChargesOutstanding =
        totalPenaltyChargesCharged
            .minus(this.totalPenaltyChargesRepaid)
            .minus(this.totalPenaltyChargesWaived)
            .minus(this.totalPenaltyChargesWrittenOff)
            .getAmount();

    final Money totalExpectedRepayment =
        Money.of(currency, this.totalPrincipalDisbursed)
            .plus(this.totalInterestCharged)
            .plus(this.totalFeeChargesCharged)
            .plus(this.totalPenaltyChargesCharged);
    this.totalExpectedRepayment = totalExpectedRepayment.getAmount();

    final Money totalRepayment =
        Money.of(currency, this.totalPrincipalRepaid)
            .plus(this.totalInterestRepaid)
            .plus(this.totalFeeChargesRepaid)
            .plus(this.totalPenaltyChargesRepaid);
    this.totalRepayment = totalRepayment.getAmount();

    final Money totalExpectedCostOfLoan =
        Money.of(currency, this.totalInterestCharged)
            .plus(this.totalFeeChargesCharged)
            .plus(this.totalPenaltyChargesCharged);
    this.totalExpectedCostOfLoan = totalExpectedCostOfLoan.getAmount();

    final Money totalCostOfLoan =
        Money.of(currency, this.totalInterestRepaid)
            .plus(this.totalFeeChargesRepaid)
            .plus(this.totalPenaltyChargesRepaid);
    this.totalCostOfLoan = totalCostOfLoan.getAmount();

    final Money totalWaived =
        Money.of(currency, this.totalInterestWaived)
            .plus(this.totalFeeChargesWaived)
            .plus(this.totalPenaltyChargesWaived);
    this.totalWaived = totalWaived.getAmount();

    final Money totalWrittenOff =
        Money.of(currency, this.totalPrincipalWrittenOff)
            .plus(this.totalInterestWrittenOff)
            .plus(this.totalFeeChargesWrittenOff)
            .plus(this.totalPenaltyChargesWrittenOff);
    this.totalWrittenOff = totalWrittenOff.getAmount();

    final Money totalOutstanding =
        Money.of(currency, this.totalPrincipalOutstanding)
            .plus(this.totalInterestOutstanding)
            .plus(this.totalFeeChargesOutstanding)
            .plus(this.totalPenaltyChargesOutstanding);
    this.totalOutstanding = totalOutstanding.getAmount();
  }
  @Transactional
  @Override
  public LoanTransaction makeRepayment(
      final Long accountId,
      final CommandProcessingResultBuilder builderResult,
      final LocalDate transactionDate,
      final BigDecimal transactionAmount,
      final PaymentDetail paymentDetail,
      final String noteText,
      final String txnExternalId) {

    final Loan loan = this.loanAccountAssembler.assembleFrom(accountId);
    checkClientOrGroupActive(loan);

    // TODO: Is it required to validate transaction date with meeting dates
    // if repayments is synced with meeting?
    /*
     * if(loan.isSyncDisbursementWithMeeting()){ // validate actual
     * disbursement date against meeting date CalendarInstance
     * calendarInstance =
     * this.calendarInstanceRepository.findCalendarInstaneByLoanId
     * (loan.getId(), CalendarEntityType.LOANS.getValue());
     * this.loanEventApiJsonValidator
     * .validateRepaymentDateWithMeetingDate(transactionDate,
     * calendarInstance); }
     */

    final List<Long> existingTransactionIds = new ArrayList<Long>();
    final List<Long> existingReversedTransactionIds = new ArrayList<Long>();

    final Money repaymentAmount = Money.of(loan.getCurrency(), transactionAmount);
    final LoanTransaction newRepaymentTransaction =
        LoanTransaction.repayment(repaymentAmount, paymentDetail, transactionDate, txnExternalId);
    final boolean allowTransactionsOnHoliday =
        this.configurationDomainService.allowTransactionsOnHolidayEnabled();
    final List<Holiday> holidays =
        this.holidayRepository.findByOfficeIdAndGreaterThanDate(
            loan.getOfficeId(), transactionDate.toDate());
    final WorkingDays workingDays = this.workingDaysRepository.findOne();
    final boolean allowTransactionsOnNonWorkingDay =
        this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();

    final ChangedTransactionDetail changedTransactionDetail =
        loan.makeRepayment(
            newRepaymentTransaction,
            defaultLoanLifecycleStateMachine(),
            existingTransactionIds,
            existingReversedTransactionIds,
            allowTransactionsOnHoliday,
            holidays,
            workingDays,
            allowTransactionsOnNonWorkingDay);

    this.loanTransactionRepository.save(newRepaymentTransaction);

    /**
     * * TODO Vishwas Batch save is giving me a HibernateOptimisticLockingFailureException, looping
     * and saving for the time being, not a major issue for now as this loop is entered only in edge
     * cases (when a payment is made before the latest payment recorded against the loan) *
     */
    if (changedTransactionDetail != null) {
      for (final LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) {
        this.loanTransactionRepository.save(loanTransaction);
      }
    }
    this.loanRepository.save(loan);

    if (StringUtils.isNotBlank(noteText)) {
      final Note note = Note.loanTransactionNote(loan, newRepaymentTransaction, noteText);
      this.noteRepository.save(note);
    }

    postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);

    builderResult
        .withEntityId(newRepaymentTransaction.getId()) //
        .withOfficeId(loan.getOfficeId()) //
        .withClientId(loan.getClientId()) //
        .withGroupId(loan.getGroupId()); //

    return newRepaymentTransaction;
  }
Example #29
0
  private LoanApplicationTerms assembleLoanApplicationTermsFrom(
      final JsonElement element, final LoanProduct loanProduct) {

    final MonetaryCurrency currency = loanProduct.getCurrency();
    final ApplicationCurrency applicationCurrency =
        this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);

    // loan terms
    final Integer loanTermFrequency =
        fromApiJsonHelper.extractIntegerWithLocaleNamed("loanTermFrequency", element);
    final Integer loanTermFrequencyType =
        fromApiJsonHelper.extractIntegerWithLocaleNamed("loanTermFrequencyType", element);
    final PeriodFrequencyType loanTermPeriodFrequencyType =
        PeriodFrequencyType.fromInt(loanTermFrequencyType);

    final Integer numberOfRepayments =
        fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element);
    final Integer repaymentEvery =
        fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery", element);
    final Integer repaymentFrequencyType =
        fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentFrequencyType", element);
    final PeriodFrequencyType repaymentPeriodFrequencyType =
        PeriodFrequencyType.fromInt(repaymentFrequencyType);

    final Integer amortizationType =
        fromApiJsonHelper.extractIntegerWithLocaleNamed("amortizationType", element);
    final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(amortizationType);

    // interest terms
    final Integer interestType =
        fromApiJsonHelper.extractIntegerWithLocaleNamed("interestType", element);
    final InterestMethod interestMethod = InterestMethod.fromInt(interestType);

    final Integer interestCalculationPeriodType =
        fromApiJsonHelper.extractIntegerWithLocaleNamed("interestCalculationPeriodType", element);
    final InterestCalculationPeriodMethod interestCalculationPeriodMethod =
        InterestCalculationPeriodMethod.fromInt(interestCalculationPeriodType);

    final BigDecimal interestRatePerPeriod =
        fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
    final PeriodFrequencyType interestRatePeriodFrequencyType =
        loanProduct.getInterestPeriodFrequencyType();

    final BigDecimal annualNominalInterestRate =
        this.aprCalculator.calculateFrom(interestRatePeriodFrequencyType, interestRatePerPeriod);

    // disbursement details
    final BigDecimal principal =
        this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
    final Money principalMoney = Money.of(currency, principal);

    final LocalDate expectedDisbursementDate =
        this.fromApiJsonHelper.extractLocalDateNamed("expectedDisbursementDate", element);
    final LocalDate repaymentsStartingFromDate =
        this.fromApiJsonHelper.extractLocalDateNamed("repaymentsStartingFromDate", element);
    LocalDate calculatedRepaymentsStartingFromDate = repaymentsStartingFromDate;
    final Boolean synchDisbursement =
        fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", element);
    final Long calendarId = this.fromApiJsonHelper.extractLongNamed("calendarId", element);
    Calendar calendar = null;
    if ((synchDisbursement != null && synchDisbursement.booleanValue())
        || (calendarId != null && calendarId != 0)) {
      calendar = this.calendarRepository.findOne(calendarId);
      if (calendar == null) {
        throw new CalendarNotFoundException(calendarId);
      }

      // validate repayment frequency and interval with meeting frequency and interval
      PeriodFrequencyType meetingPeriodFrequency =
          CalendarHelper.getMeetingPeriodFrequencyType(calendar.getRecurrence());
      validateRepaymentFrequencyIsSameAsMeetingFrequency(
          meetingPeriodFrequency.getValue(),
          repaymentFrequencyType,
          CalendarHelper.getInterval(calendar.getRecurrence()),
          repaymentEvery);
    }

    if (synchDisbursement != null && synchDisbursement.booleanValue()) {
      validateDisbursementDateWithMeetingDates(expectedDisbursementDate, calendar);
    }

    // if calendar is attached (not null) then reschedule repayment dates according to meeting
    if (null != calendar) {
      // TODO : AA - Is it require to reset repaymentsStartingFromDate or
      // set only if it is not provided (or null)
      // Currently provided repaymentsStartingFromDate takes precedence over system generated next
      // meeting date
      if (calculatedRepaymentsStartingFromDate == null) {
        // FIXME: AA - Possibility of having next meeting date immediately after disbursement date,
        // need to have minimum number of days gap between disbursement and first repayment date.
        final String frequency =
            CalendarHelper.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType);
        calculatedRepaymentsStartingFromDate =
            CalendarHelper.getFirstRepaymentMeetingDate(
                calendar, expectedDisbursementDate, repaymentEvery, frequency);
      } else { // validate user provided repaymentsStartFromDate
        validateRepaymentsStartDateWithMeetingDates(repaymentsStartingFromDate, calendar);
      }
    }
    // grace details
    final Integer graceOnPrincipalPayment =
        this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnPrincipalPayment", element);
    final Integer graceOnInterestPayment =
        this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestPayment", element);
    final Integer graceOnInterestCharged =
        this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestCharged", element);
    final LocalDate interestChargedFromDate =
        fromApiJsonHelper.extractLocalDateNamed("interestChargedFromDate", element);

    // other
    final BigDecimal inArrearsTolerance =
        this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
    final Money inArrearsToleranceMoney = Money.of(currency, inArrearsTolerance);

    return LoanApplicationTerms.assembleFrom(
        applicationCurrency,
        loanTermFrequency,
        loanTermPeriodFrequencyType,
        numberOfRepayments,
        repaymentEvery,
        repaymentPeriodFrequencyType,
        amortizationMethod,
        interestMethod,
        interestRatePerPeriod,
        interestRatePeriodFrequencyType,
        annualNominalInterestRate,
        interestCalculationPeriodMethod,
        principalMoney,
        expectedDisbursementDate,
        repaymentsStartingFromDate,
        calculatedRepaymentsStartingFromDate,
        graceOnPrincipalPayment,
        graceOnInterestPayment,
        graceOnInterestCharged,
        interestChargedFromDate,
        inArrearsToleranceMoney);
  }
Example #30
0
 public Money getTotalOutstanding(final MonetaryCurrency currency) {
   return Money.of(currency, this.totalOutstanding);
 }