@GET
  @Path("{productId}")
  @Consumes({MediaType.APPLICATION_JSON})
  @Produces({MediaType.APPLICATION_JSON})
  public String retrieveOne(
      @PathParam("productId") final Long productId, @Context final UriInfo uriInfo) {

    this.context
        .authenticatedUser()
        .validateHasReadPermission(SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME);

    SavingsProductData savingProductData =
        this.savingProductReadPlatformService.retrieveOne(productId);

    final Collection<ChargeData> charges =
        this.chargeReadPlatformService.retrieveSavingsProductCharges(productId);

    savingProductData = SavingsProductData.withCharges(savingProductData, charges);

    final ApiRequestJsonSerializationSettings settings =
        this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());

    if (savingProductData.hasAccountingEnabled()) {
      final Map<String, Object> accountingMappings =
          this.accountMappingReadPlatformService.fetchAccountMappingDetailsForSavingsProduct(
              productId, savingProductData.accountingRuleTypeId());
      final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings =
          this.accountMappingReadPlatformService
              .fetchPaymentTypeToFundSourceMappingsForSavingsProduct(productId);
      Collection<ChargeToGLAccountMapper> feeToGLAccountMappings =
          this.accountMappingReadPlatformService.fetchFeeToIncomeAccountMappingsForSavingsProduct(
              productId);
      Collection<ChargeToGLAccountMapper> penaltyToGLAccountMappings =
          this.accountMappingReadPlatformService
              .fetchPenaltyToIncomeAccountMappingsForSavingsProduct(productId);
      savingProductData =
          SavingsProductData.withAccountingDetails(
              savingProductData,
              accountingMappings,
              paymentChannelToFundSourceMappings,
              feeToGLAccountMappings,
              penaltyToGLAccountMappings);
    }

    if (settings.isTemplate()) {
      savingProductData = handleTemplateRelatedData(savingProductData);
    }

    return this.toApiJsonSerializer.serialize(
        settings, savingProductData, SavingsApiConstants.SAVINGS_PRODUCT_RESPONSE_DATA_PARAMETERS);
  }
    @Override
    public SavingsProductData mapRow(
        final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {

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

      return SavingsProductData.lookup(id, name);
    }
 private Collection<SavingsProductData> retrieveSavingsProducts(
     Collection<JLGGroupData> groupsWithSavingsData) {
   List<SavingsProductData> savingsProducts = new ArrayList<>();
   for (JLGGroupData groupSavingsData : groupsWithSavingsData) {
     Collection<JLGClientData> clientsSavingsData = groupSavingsData.getClients();
     for (JLGClientData clientSavingsData : clientsSavingsData) {
       Collection<SavingsDueData> savingsDatas = clientSavingsData.getSavings();
       for (SavingsDueData savingsDueData : savingsDatas) {
         final SavingsProductData savingsProduct =
             SavingsProductData.lookup(savingsDueData.productId(), savingsDueData.productName());
         if (!savingsProducts.contains(savingsProduct)) {
           savingsProducts.add(savingsProduct);
         }
       }
     }
   }
   return savingsProducts;
 }
    @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);
    }
  private SavingsProductData handleTemplateRelatedData(final SavingsProductData savingsProduct) {

    final EnumOptionData interestCompoundingPeriodType =
        SavingsEnumerations.compoundingInterestPeriodType(
            SavingsCompoundingInterestPeriodType.DAILY);

    final EnumOptionData interestPostingPeriodType =
        SavingsEnumerations.interestPostingPeriodType(SavingsPostingInterestPeriodType.MONTHLY);

    final EnumOptionData interestCalculationType =
        SavingsEnumerations.interestCalculationType(SavingsInterestCalculationType.DAILY_BALANCE);

    final EnumOptionData interestCalculationDaysInYearType =
        SavingsEnumerations.interestCalculationDaysInYearType(
            SavingsInterestCalculationDaysInYearType.DAYS_365);

    final EnumOptionData accountingRule =
        AccountingEnumerations.accountingRuleType(AccountingRuleType.NONE);

    CurrencyData currency = CurrencyData.blank();
    final Collection<CurrencyData> currencyOptions =
        this.currencyReadPlatformService.retrieveAllowedCurrencies();
    if (currencyOptions.size() == 1) {
      currency = new ArrayList<CurrencyData>(currencyOptions).get(0);
    }

    final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions =
        this.dropdownReadPlatformService.retrieveCompoundingInterestPeriodTypeOptions();

    final Collection<EnumOptionData> interestPostingPeriodTypeOptions =
        this.dropdownReadPlatformService.retrieveInterestPostingPeriodTypeOptions();

    final Collection<EnumOptionData> interestCalculationTypeOptions =
        this.dropdownReadPlatformService.retrieveInterestCalculationTypeOptions();

    final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions =
        this.dropdownReadPlatformService.retrieveInterestCalculationDaysInYearTypeOptions();

    final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions =
        this.dropdownReadPlatformService.retrieveLockinPeriodFrequencyTypeOptions();

    final Collection<EnumOptionData> withdrawalFeeTypeOptions =
        this.dropdownReadPlatformService.retrievewithdrawalFeeTypeOptions();

    final Collection<CodeValueData> paymentTypeOptions =
        this.codeValueReadPlatformService.retrieveCodeValuesByCode(
            PaymentDetailConstants.paymentTypeCodeName);
    final Collection<EnumOptionData> accountingRuleOptions =
        this.accountingDropdownReadPlatformService.retrieveAccountingRuleTypeOptions();
    final Map<String, List<GLAccountData>> accountingMappingOptions =
        this.accountingDropdownReadPlatformService
            .retrieveAccountMappingOptionsForSavingsProducts();

    // charges
    final boolean feeChargesOnly = true;
    Collection<ChargeData> chargeOptions =
        this.chargeReadPlatformService.retrieveSavingsAccountApplicableCharges(feeChargesOnly);
    chargeOptions = CollectionUtils.isEmpty(chargeOptions) ? null : chargeOptions;

    Collection<ChargeData> penaltyOptions =
        this.chargeReadPlatformService.retrieveSavingsAccountApplicablePenalties();
    penaltyOptions = CollectionUtils.isEmpty(penaltyOptions) ? null : penaltyOptions;

    SavingsProductData savingsProductToReturn = null;
    if (savingsProduct != null) {
      savingsProductToReturn =
          SavingsProductData.withTemplate(
              savingsProduct,
              currencyOptions,
              interestCompoundingPeriodTypeOptions,
              interestPostingPeriodTypeOptions,
              interestCalculationTypeOptions,
              interestCalculationDaysInYearTypeOptions,
              lockinPeriodFrequencyTypeOptions,
              withdrawalFeeTypeOptions,
              paymentTypeOptions,
              accountingRuleOptions,
              accountingMappingOptions,
              chargeOptions,
              penaltyOptions);
    } else {
      savingsProductToReturn =
          SavingsProductData.template(
              currency,
              interestCompoundingPeriodType,
              interestPostingPeriodType,
              interestCalculationType,
              interestCalculationDaysInYearType,
              accountingRule,
              currencyOptions,
              interestCompoundingPeriodTypeOptions,
              interestPostingPeriodTypeOptions,
              interestCalculationTypeOptions,
              interestCalculationDaysInYearTypeOptions,
              lockinPeriodFrequencyTypeOptions,
              withdrawalFeeTypeOptions,
              paymentTypeOptions,
              accountingRuleOptions,
              accountingMappingOptions,
              chargeOptions,
              penaltyOptions);
    }

    return savingsProductToReturn;
  }
    @Override
    public SavingsProductData 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 shortName = rs.getString("shortName");
      final String description = rs.getString("description");

      final String currencyCode = rs.getString("currencyCode");
      final String currencyName = rs.getString("currencyName");
      final String currencyNameCode = rs.getString("currencyNameCode");
      final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
      final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
      final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
      final CurrencyData currency =
          new CurrencyData(
              currencyCode,
              currencyName,
              currencyDigits,
              inMultiplesOf,
              currencyDisplaySymbol,
              currencyNameCode);
      final BigDecimal nominalAnnualInterestRate = rs.getBigDecimal("nominalAnnualInterestRate");

      final Integer compoundingInterestPeriodTypeValue =
          JdbcSupport.getInteger(rs, "compoundingInterestPeriodType");
      final EnumOptionData compoundingInterestPeriodType =
          SavingsEnumerations.compoundingInterestPeriodType(compoundingInterestPeriodTypeValue);

      final Integer interestPostingPeriodTypeValue =
          JdbcSupport.getInteger(rs, "interestPostingPeriodType");
      final EnumOptionData interestPostingPeriodType =
          SavingsEnumerations.interestPostingPeriodType(interestPostingPeriodTypeValue);

      final Integer interestCalculationTypeValue =
          JdbcSupport.getInteger(rs, "interestCalculationType");
      final EnumOptionData interestCalculationType =
          SavingsEnumerations.interestCalculationType(interestCalculationTypeValue);

      EnumOptionData interestCalculationDaysInYearType = null;
      final Integer interestCalculationDaysInYearTypeValue =
          JdbcSupport.getInteger(rs, "interestCalculationDaysInYearType");
      if (interestCalculationDaysInYearTypeValue != null) {
        interestCalculationDaysInYearType =
            SavingsEnumerations.interestCalculationDaysInYearType(
                interestCalculationDaysInYearTypeValue);
      }

      final Integer accountingRuleId = JdbcSupport.getInteger(rs, "accountingType");
      final EnumOptionData accountingRuleType =
          AccountingEnumerations.accountingRuleType(accountingRuleId);

      final BigDecimal minRequiredOpeningBalance = rs.getBigDecimal("minRequiredOpeningBalance");

      final Integer lockinPeriodFrequency = JdbcSupport.getInteger(rs, "lockinPeriodFrequency");
      EnumOptionData lockinPeriodFrequencyType = null;
      final Integer lockinPeriodFrequencyTypeValue =
          JdbcSupport.getInteger(rs, "lockinPeriodFrequencyType");
      if (lockinPeriodFrequencyTypeValue != null) {
        lockinPeriodFrequencyType =
            SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodFrequencyTypeValue);
      }

      final boolean withdrawalFeeForTransfers = rs.getBoolean("withdrawalFeeForTransfers");
      final boolean allowOverdraft = rs.getBoolean("allowOverdraft");
      final BigDecimal overdraftLimit = rs.getBigDecimal("overdraftLimit");
      return SavingsProductData.instance(
          id,
          name,
          shortName,
          description,
          currency,
          nominalAnnualInterestRate,
          compoundingInterestPeriodType,
          interestPostingPeriodType,
          interestCalculationType,
          interestCalculationDaysInYearType,
          minRequiredOpeningBalance,
          lockinPeriodFrequency,
          lockinPeriodFrequencyType,
          withdrawalFeeForTransfers,
          accountingRuleType,
          allowOverdraft,
          overdraftLimit);
    }