public static LoanProductData withAccountingDetails(
     final LoanProductData productData,
     final Map<String, Object> accountingMappings,
     final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings,
     final Collection<ChargeToGLAccountMapper> feeToGLAccountMappings,
     final Collection<ChargeToGLAccountMapper> penaltyToGLAccountMappings) {
   productData.accountingMappings = accountingMappings;
   productData.paymentChannelToFundSourceMappings = paymentChannelToFundSourceMappings;
   productData.feeToIncomeAccountMappings = feeToGLAccountMappings;
   productData.penaltyToIncomeAccountMappings = penaltyToGLAccountMappings;
   return productData;
 }
    @Override
    public LoanProductData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum)
        throws SQLException {

      final Long id = rs.getLong("id");
      final String name = rs.getString("name");

      return LoanProductData.lookup(id, name);
    }
    @Override
    public AutoPostingData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum)
        throws SQLException {

      final Long id = rs.getLong("id");
      final String name = rs.getString("name");
      final String description = rs.getString("description");
      final Long officeId = JdbcSupport.getLong(rs, "officeId");
      final String officeName = rs.getString("officeName");
      final int productTypeEnum = rs.getInt("productTypeEnum");
      final Long productId = JdbcSupport.getLong(rs, "productId");
      final Long chargeId = JdbcSupport.getLong(rs, "chargeId");
      final Long accountingRuleId = JdbcSupport.getLong(rs, "accountingRuleId");
      final Long eventId = JdbcSupport.getLong(rs, "eventId");
      final Long eventAttributeId = JdbcSupport.getLong(rs, "eventAttributeId");
      final Long debitAccountId = JdbcSupport.getLong(rs, "debitAccountId");
      final Long creditAccountId = JdbcSupport.getLong(rs, "creditAccountId");
      final String savingProductName = rs.getString("savingProductName");
      final String loanProductName = rs.getString("loanProductName");
      final String chargeName = rs.getString("chargeName");
      final String eventName = rs.getString("eventName");
      final String eventAttributeName = rs.getString("eventAttributeName");
      final boolean chargeIsPenalty = rs.getBoolean("isPenalty");

      OfficeData officeData = null;
      if (officeId != null) {
        officeData = OfficeData.dropdown(officeId, officeName, null);
      }

      SavingsProductData savingsProductData = null;
      LoanProductData loanProductData = null;

      PortfolioProductType portfolioProductType = PortfolioProductType.fromInt(productTypeEnum);
      EnumOptionData productType =
          AccountingEnumerations.portfolioProductType(portfolioProductType);
      if (portfolioProductType.isLoanProduct() && productId != null) {
        savingsProductData = SavingsProductData.lookup(productId, savingProductName);
      } else if (portfolioProductType.isSavingProduct() && productId != null) {
        loanProductData = LoanProductData.lookup(productId, loanProductName);
      }

      ChargeData chargeData = null;
      if (chargeId != null) {
        chargeData = ChargeData.lookup(chargeId, chargeName, chargeIsPenalty);
      }

      CodeData event = CodeData.instance(eventId, eventName, true);

      CodeValueData eventAttribute = null;
      if (eventAttributeId != null) {
        eventAttribute = CodeValueData.instance(eventAttributeId, eventAttributeName);
      }

      AccountingRuleData accountingRuleData =
          new AccountingRuleData(accountingRuleId, debitAccountId, creditAccountId);

      return new AutoPostingData(
          id,
          name,
          description,
          officeData,
          productType,
          loanProductData,
          savingsProductData,
          chargeData,
          event,
          eventAttribute,
          accountingRuleData);
    }
  public LoanProductData(
      final LoanProductData productData,
      final Collection<ChargeData> chargeOptions,
      final Collection<ChargeData> penaltyOptions,
      final Collection<CodeValueData> paymentTypeOptions,
      final Collection<CurrencyData> currencyOptions,
      final List<EnumOptionData> amortizationTypeOptions,
      final List<EnumOptionData> interestTypeOptions,
      final List<EnumOptionData> interestCalculationPeriodTypeOptions,
      final List<EnumOptionData> repaymentFrequencyTypeOptions,
      final List<EnumOptionData> interestRateFrequencyTypeOptions,
      final Collection<FundData> fundOptions,
      final Collection<TransactionProcessingStrategyData> transactionStrategyOptions,
      final Map<String, List<GLAccountData>> accountingMappingOptions,
      final List<EnumOptionData> accountingRuleOptions,
      final List<EnumOptionData> valueConditionTypeOptions) {
    this.id = productData.id;
    this.name = productData.name;
    this.shortName = productData.shortName;
    this.description = productData.description;
    this.fundId = productData.fundId;
    this.fundName = productData.fundName;

    this.principal = productData.principal;
    this.minPrincipal = productData.minPrincipal;
    this.maxPrincipal = productData.maxPrincipal;
    this.inArrearsTolerance = productData.inArrearsTolerance;
    this.numberOfRepayments = productData.numberOfRepayments;
    this.minNumberOfRepayments = productData.minNumberOfRepayments;
    this.maxNumberOfRepayments = productData.maxNumberOfRepayments;
    this.repaymentEvery = productData.repaymentEvery;
    this.interestRatePerPeriod = productData.interestRatePerPeriod;
    this.minInterestRatePerPeriod = productData.minInterestRatePerPeriod;
    this.maxInterestRatePerPeriod = productData.maxInterestRatePerPeriod;
    this.annualInterestRate = productData.annualInterestRate;
    this.repaymentFrequencyType = productData.repaymentFrequencyType;
    this.interestRateFrequencyType = productData.interestRateFrequencyType;
    this.amortizationType = productData.amortizationType;
    this.interestType = productData.interestType;
    this.interestCalculationPeriodType = productData.interestCalculationPeriodType;
    this.startDate = productData.startDate;
    this.closeDate = productData.closeDate;
    this.status = productData.status;
    this.externalId = productData.externalId;

    this.charges = nullIfEmpty(productData.charges());
    this.principalVariationsForBorrowerCycle = productData.principalVariationsForBorrowerCycle;
    this.interestRateVariationsForBorrowerCycle =
        productData.interestRateVariationsForBorrowerCycle;
    this.numberOfRepaymentVariationsForBorrowerCycle =
        productData.numberOfRepaymentVariationsForBorrowerCycle;
    this.accountingRule = productData.accountingRule;
    this.accountingMappings = productData.accountingMappings;
    this.paymentChannelToFundSourceMappings = productData.paymentChannelToFundSourceMappings;
    this.feeToIncomeAccountMappings = productData.feeToIncomeAccountMappings;
    this.penaltyToIncomeAccountMappings = productData.penaltyToIncomeAccountMappings;

    this.chargeOptions = chargeOptions;
    this.penaltyOptions = penaltyOptions;
    this.paymentTypeOptions = paymentTypeOptions;
    this.currencyOptions = currencyOptions;
    this.currency = productData.currency;
    this.fundOptions = fundOptions;
    this.transactionProcessingStrategyOptions = transactionStrategyOptions;
    if (this.transactionProcessingStrategyOptions != null
        && this.transactionProcessingStrategyOptions.size() == 1) {
      final List<TransactionProcessingStrategyData> listOfOptions =
          new ArrayList<TransactionProcessingStrategyData>(
              this.transactionProcessingStrategyOptions);

      this.transactionProcessingStrategyId = listOfOptions.get(0).id();
      this.transactionProcessingStrategyName = listOfOptions.get(0).name();
    } else {
      this.transactionProcessingStrategyId = productData.transactionProcessingStrategyId;
      this.transactionProcessingStrategyName = productData.transactionProcessingStrategyName;
    }

    this.graceOnPrincipalPayment = productData.graceOnPrincipalPayment;
    this.graceOnInterestPayment = productData.graceOnInterestPayment;
    this.graceOnInterestCharged = productData.graceOnInterestCharged;
    this.includeInBorrowerCycle = productData.includeInBorrowerCycle;
    this.useBorrowerCycle = productData.useBorrowerCycle;
    this.multiDisburseLoan = productData.multiDisburseLoan;
    this.maxTrancheCount = productData.maxTrancheCount;
    this.outstandingLoanBalance = productData.outstandingLoanBalance;

    this.amortizationTypeOptions = amortizationTypeOptions;
    this.interestTypeOptions = interestTypeOptions;
    this.interestCalculationPeriodTypeOptions = interestCalculationPeriodTypeOptions;
    this.repaymentFrequencyTypeOptions = repaymentFrequencyTypeOptions;
    this.interestRateFrequencyTypeOptions = interestRateFrequencyTypeOptions;

    this.accountingMappingOptions = accountingMappingOptions;
    this.accountingRuleOptions = accountingRuleOptions;
    this.valueConditionTypeOptions = valueConditionTypeOptions;
    this.graceOnArrearsAgeing = productData.graceOnArrearsAgeing;
  }
  /*
   * Reads all the loans which are due for disbursement or collection and
   * builds hierarchical data structure for collections sheet with hierarchy
   * Groups >> Clients >> Loans.
   */
  @SuppressWarnings("null")
  private JLGCollectionSheetData buildJLGCollectionSheet(
      final LocalDate dueDate,
      final Collection<JLGCollectionSheetFlatData> jlgCollectionSheetFlatData) {

    boolean firstTime = true;
    Long prevGroupId = null;
    Long prevClientId = null;

    final List<JLGGroupData> jlgGroupsData = new ArrayList<>();
    List<JLGClientData> clientsData = new ArrayList<>();
    List<LoanDueData> loansDueData = new ArrayList<>();

    JLGCollectionSheetData jlgCollectionSheetData = null;
    JLGCollectionSheetFlatData prevCollectioSheetFlatData = null;
    JLGCollectionSheetFlatData corrCollectioSheetFlatData = null;
    final Set<LoanProductData> loanProducts = new HashSet<>();
    if (jlgCollectionSheetFlatData != null) {

      for (final JLGCollectionSheetFlatData collectionSheetFlatData : jlgCollectionSheetFlatData) {

        if (collectionSheetFlatData.getProductId() != null) {
          loanProducts.add(
              LoanProductData.lookupWithCurrency(
                  collectionSheetFlatData.getProductId(),
                  collectionSheetFlatData.getProductShortName(),
                  collectionSheetFlatData.getCurrency()));
        }
        corrCollectioSheetFlatData = collectionSheetFlatData;

        if (firstTime || collectionSheetFlatData.getGroupId().equals(prevGroupId)) {
          if (firstTime || collectionSheetFlatData.getClientId().equals(prevClientId)) {
            if (collectionSheetFlatData.getLoanId() != null) {
              loansDueData.add(collectionSheetFlatData.getLoanDueData());
            }
          } else {
            final JLGClientData clientData = prevCollectioSheetFlatData.getClientData();
            clientData.setLoans(loansDueData);
            clientsData.add(clientData);
            loansDueData = new ArrayList<>();

            if (collectionSheetFlatData.getLoanId() != null) {
              loansDueData.add(collectionSheetFlatData.getLoanDueData());
            }
          }
        } else {

          final JLGClientData clientData = prevCollectioSheetFlatData.getClientData();
          clientData.setLoans(loansDueData);
          clientsData.add(clientData);

          final JLGGroupData jlgGroupData = prevCollectioSheetFlatData.getJLGGroupData();
          jlgGroupData.setClients(clientsData);

          jlgGroupsData.add(jlgGroupData);

          loansDueData = new ArrayList<>();
          clientsData = new ArrayList<>();

          if (collectionSheetFlatData.getLoanId() != null) {
            loansDueData.add(collectionSheetFlatData.getLoanDueData());
          }
        }

        prevClientId = collectionSheetFlatData.getClientId();
        prevGroupId = collectionSheetFlatData.getGroupId();
        prevCollectioSheetFlatData = collectionSheetFlatData;
        firstTime = false;
      }

      // FIXME Need to check last loan is added under previous
      // client/group or new client / previous group or new client / new
      // group
      if (corrCollectioSheetFlatData != null) {
        final JLGClientData lastClientData = corrCollectioSheetFlatData.getClientData();
        lastClientData.setLoans(loansDueData);
        clientsData.add(lastClientData);

        final JLGGroupData jlgGroupData = corrCollectioSheetFlatData.getJLGGroupData();
        jlgGroupData.setClients(clientsData);
        jlgGroupsData.add(jlgGroupData);
      }

      jlgCollectionSheetData =
          JLGCollectionSheetData.instance(
              dueDate,
              loanProducts,
              jlgGroupsData,
              this.attendanceDropdownReadPlatformService.retrieveAttendanceTypeOptions());
    }

    return jlgCollectionSheetData;
  }
 @Override
 public LoanProductData retrieveNewLoanProductDetails() {
   return LoanProductData.sensibleDefaultsForNewLoanProductCreation();
 }