Example #1
0
 private void validateDisbursementDateWithMeetingDates(
     final LocalDate expectedDisbursementDate, final Calendar calendar) {
   // disbursement date should fall on a meeting date
   if (!CalendarHelper.isValidRedurringDate(
       calendar.getRecurrence(), calendar.getStartDateLocalDate(), expectedDisbursementDate)) {
     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);
   }
 }
Example #2
0
 private void validateRepaymentsStartDateWithMeetingDates(
     final LocalDate repaymentsStartingFromDate, final Calendar calendar) {
   if (repaymentsStartingFromDate != null
       && !CalendarHelper.isValidRedurringDate(
           calendar.getRecurrence(),
           calendar.getStartDateLocalDate(),
           repaymentsStartingFromDate)) {
     final String errorMessage =
         "First repayment date '" + repaymentsStartingFromDate + "' do not fall on a meeting date";
     throw new LoanApplicationDateException(
         "first.repayment.date.do.not.match.meeting.date",
         errorMessage,
         repaymentsStartingFromDate);
   }
 }
Example #3
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);
  }
  @Override
  public JLGCollectionSheetData generateGroupCollectionSheet(
      final Long groupId, final JsonQuery query) {

    this.collectionSheetGenerateCommandFromApiJsonDeserializer.validateForGenerateCollectionSheet(
        query.json());

    final Long calendarId = query.longValueOfParameterNamed(calendarIdParamName);
    final LocalDate transactionDate =
        query.localDateValueOfParameterNamed(transactionDateParamName);
    final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
    final String transactionDateStr = df.format(transactionDate.toDate());

    final Calendar calendar =
        this.calendarRepositoryWrapper.findOneWithNotFoundDetection(calendarId);
    // check if transaction against calendar effective from date

    if (!calendar.isValidRecurringDate(transactionDate)) {
      throw new NotValidRecurringDateException(
          "collectionsheet",
          "The date '" + transactionDate + "' is not a valid meeting date.",
          transactionDate);
    }

    final AppUser currentUser = this.context.authenticatedUser();
    final String hierarchy = currentUser.getOffice().getHierarchy();
    final String officeHierarchy = hierarchy + "%";

    final GroupGeneralData group = this.groupReadPlatformService.retrieveOne(groupId);

    final JLGCollectionSheetFaltDataMapper mapper = new JLGCollectionSheetFaltDataMapper();

    // entityType should be center if it's within a center
    final CalendarEntityType entityType =
        (group.isChildGroup()) ? CalendarEntityType.CENTERS : CalendarEntityType.GROUPS;

    final SqlParameterSource namedParameters =
        new MapSqlParameterSource()
            .addValue("dueDate", transactionDateStr)
            .addValue("groupId", group.getId())
            .addValue("officeHierarchy", officeHierarchy)
            .addValue("entityTypeId", entityType.getValue());

    final Collection<JLGCollectionSheetFlatData> collectionSheetFlatDatas =
        this.namedParameterjdbcTemplate.query(
            mapper.collectionSheetSchema(false), namedParameters, mapper);

    // loan data for collection sheet
    JLGCollectionSheetData collectionSheetData =
        buildJLGCollectionSheet(transactionDate, collectionSheetFlatDatas);

    // mandatory savings data for collection sheet
    Collection<JLGGroupData> groupsWithSavingsData =
        this.namedParameterjdbcTemplate.query(
            mandatorySavingsExtractor.collectionSheetSchema(false),
            namedParameters,
            mandatorySavingsExtractor);

    // merge savings data into loan data
    mergeSavingsGroupDataIntoCollectionsheetData(groupsWithSavingsData, collectionSheetData);

    collectionSheetData =
        JLGCollectionSheetData.withSavingsProducts(
            collectionSheetData, retrieveSavingsProducts(groupsWithSavingsData));

    return collectionSheetData;
  }