/*
   * Guaranteed to throw an exception no matter what the data integrity issue
   * is.
   */
  private void handleDataIntegrityIssues(
      final JsonCommand command, final Throwable realCause, final Exception dae) {

    if (realCause.getMessage().contains("sp_unq_name")) {

      final String name = command.stringValueOfParameterNamed("name");
      throw new PlatformDataIntegrityException(
          "error.msg.product.savings.duplicate.name",
          "Savings product with name `" + name + "` already exists",
          "name",
          name);
    } else if (realCause.getMessage().contains("sp_unq_short_name")) {

      final String shortName = command.stringValueOfParameterNamed("shortName");
      throw new PlatformDataIntegrityException(
          "error.msg.product.savings.duplicate.short.name",
          "Savings product with short name `" + shortName + "` already exists",
          "shortName",
          shortName);
    }

    logAsErrorUnexpectedDataIntegrityException(dae);
    throw new PlatformDataIntegrityException(
        "error.msg.savingsproduct.unknown.data.integrity.issue",
        "Unknown data integrity issue with resource.");
  }
  public Map<String, Object> update(final JsonCommand command) {

    final Map<String, Object> actualChanges = new LinkedHashMap<>(3);

    if (command.isChangeInStringParameterNamed(PaymentTypeApiResourceConstants.NAME, this.name)) {
      final String newValue =
          command.stringValueOfParameterNamed(PaymentTypeApiResourceConstants.NAME);
      actualChanges.put(PaymentTypeApiResourceConstants.NAME, newValue);
      this.name = StringUtils.defaultIfEmpty(newValue, null);
    }

    if (command.isChangeInStringParameterNamed(
        PaymentTypeApiResourceConstants.DESCRIPTION, this.description)) {
      final String newDescription =
          command.stringValueOfParameterNamed(PaymentTypeApiResourceConstants.DESCRIPTION);
      actualChanges.put(PaymentTypeApiResourceConstants.DESCRIPTION, newDescription);
      this.description = StringUtils.defaultIfEmpty(newDescription, null);
    }

    if (command.isChangeInBooleanParameterNamed(
        PaymentTypeApiResourceConstants.ISCASHPAYMENT, this.isCashPayment)) {
      final Boolean newCashPaymentType =
          command.booleanObjectValueOfParameterNamed(PaymentTypeApiResourceConstants.ISCASHPAYMENT);
      actualChanges.put(PaymentTypeApiResourceConstants.ISCASHPAYMENT, newCashPaymentType);
      this.isCashPayment = newCashPaymentType.booleanValue();
    }

    if (command.isChangeInLongParameterNamed(
        PaymentTypeApiResourceConstants.POSITION, this.position)) {
      final Long newPosition =
          command.longValueOfParameterNamed(PaymentTypeApiResourceConstants.POSITION);
      actualChanges.put(PaymentTypeApiResourceConstants.POSITION, newPosition);
      this.position = newPosition;
    }

    return actualChanges;
  }
  @Transactional
  @Override
  public Long handleRDAccountPreMatureClosure(
      final RecurringDepositAccount account,
      final PaymentDetail paymentDetail,
      final AppUser user,
      final JsonCommand command,
      final LocalDate tenantsTodayDate,
      final Map<String, Object> changes) {

    final boolean isSavingsInterestPostingAtCurrentPeriodEnd =
        this.configurationDomainService.isSavingsInterestPostingAtCurrentPeriodEnd();
    final Integer financialYearBeginningMonth =
        this.configurationDomainService.retrieveFinancialYearBeginningMonth();

    boolean isAccountTransfer = false;
    final boolean isPreMatureClosure = true;
    boolean isRegularTransaction = false;
    final Set<Long> existingTransactionIds = new HashSet<>();
    final Set<Long> existingReversedTransactionIds = new HashSet<>();
    updateExistingTransactionsDetails(
        account, existingTransactionIds, existingReversedTransactionIds);

    final LocalDate closedDate =
        command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName);
    final Locale locale = command.extractLocale();
    final DateTimeFormatter fmt =
        DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
    Long savingsTransactionId = null;
    // post interest
    account.postPreMaturityInterest(
        closedDate,
        isPreMatureClosure,
        isSavingsInterestPostingAtCurrentPeriodEnd,
        financialYearBeginningMonth);

    final Integer closureTypeValue =
        command.integerValueOfParameterNamed(DepositsApiConstants.onAccountClosureIdParamName);
    DepositAccountOnClosureType closureType = DepositAccountOnClosureType.fromInt(closureTypeValue);

    if (closureType.isTransferToSavings()) {
      final boolean isExceptionForBalanceCheck = false;
      final Long toSavingsId = command.longValueOfParameterNamed(toSavingsAccountIdParamName);
      final String transferDescription =
          command.stringValueOfParameterNamed(transferDescriptionParamName);
      final SavingsAccount toSavingsAccount =
          this.depositAccountAssembler.assembleFrom(
              toSavingsId, DepositAccountType.SAVINGS_DEPOSIT);
      final AccountTransferDTO accountTransferDTO =
          new AccountTransferDTO(
              closedDate,
              account.getAccountBalance(),
              PortfolioAccountType.SAVINGS,
              PortfolioAccountType.SAVINGS,
              null,
              null,
              transferDescription,
              locale,
              fmt,
              null,
              null,
              null,
              null,
              null,
              AccountTransferType.ACCOUNT_TRANSFER.getValue(),
              null,
              null,
              null,
              null,
              toSavingsAccount,
              account,
              isRegularTransaction,
              isExceptionForBalanceCheck);
      this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
    } else {
      final SavingsAccountTransaction withdrawal =
          this.handleWithdrawal(
              account,
              fmt,
              closedDate,
              account.getAccountBalance(),
              paymentDetail,
              false,
              isRegularTransaction);
      savingsTransactionId = withdrawal.getId();
    }

    /** * Update account transactionIds for post journal entries. */
    updateExistingTransactionsDetails(
        account, existingTransactionIds, existingReversedTransactionIds);
    account.prematureClosure(user, command, tenantsTodayDate, changes);
    this.savingsAccountRepository.save(account);
    postJournalEntries(
        account, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
    return savingsTransactionId;
  }
  @Transactional
  @Override
  public Long handleRDAccountClosure(
      final RecurringDepositAccount account,
      final PaymentDetail paymentDetail,
      final AppUser user,
      final JsonCommand command,
      final LocalDate tenantsTodayDate,
      final Map<String, Object> changes) {

    final boolean isSavingsInterestPostingAtCurrentPeriodEnd =
        this.configurationDomainService.isSavingsInterestPostingAtCurrentPeriodEnd();
    final Integer financialYearBeginningMonth =
        this.configurationDomainService.retrieveFinancialYearBeginningMonth();

    boolean isRegularTransaction = false;
    boolean isAccountTransfer = false;
    final boolean isPreMatureClosure = false;
    final Set<Long> existingTransactionIds = new HashSet<>();
    final Set<Long> existingReversedTransactionIds = new HashSet<>();
    updateExistingTransactionsDetails(
        account, existingTransactionIds, existingReversedTransactionIds);

    final MathContext mc = MathContext.DECIMAL64;
    final Locale locale = command.extractLocale();
    final DateTimeFormatter fmt =
        DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
    final LocalDate closedDate =
        command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName);
    Long savingsTransactionId = null;
    account.postMaturityInterest(
        isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, closedDate);
    final BigDecimal transactionAmount = account.getAccountBalance();
    final Integer onAccountClosureId =
        command.integerValueOfParameterNamed(onAccountClosureIdParamName);
    final DepositAccountOnClosureType onClosureType =
        DepositAccountOnClosureType.fromInt(onAccountClosureId);
    if (onClosureType.isReinvest()) {
      RecurringDepositAccount reinvestedDeposit = account.reInvest(transactionAmount);
      depositAccountAssembler.assignSavingAccountHelpers(reinvestedDeposit);
      reinvestedDeposit.updateMaturityDateAndAmount(
          mc,
          isPreMatureClosure,
          isSavingsInterestPostingAtCurrentPeriodEnd,
          financialYearBeginningMonth);
      reinvestedDeposit.processAccountUponActivation(fmt, user);
      reinvestedDeposit.updateMaturityDateAndAmount(
          mc,
          isPreMatureClosure,
          isSavingsInterestPostingAtCurrentPeriodEnd,
          financialYearBeginningMonth);
      this.savingsAccountRepository.save(reinvestedDeposit);

      Money amountForDeposit = reinvestedDeposit.activateWithBalance();
      if (amountForDeposit.isGreaterThanZero()) {
        handleRDDeposit(
            reinvestedDeposit,
            fmt,
            reinvestedDeposit.getActivationLocalDate(),
            amountForDeposit.getAmount(),
            paymentDetail,
            isRegularTransaction);
      }
      reinvestedDeposit.updateMaturityDateAndAmount(
          mc,
          isPreMatureClosure,
          isSavingsInterestPostingAtCurrentPeriodEnd,
          financialYearBeginningMonth);
      this.savingsAccountRepository.save(reinvestedDeposit);
      autoGenerateAccountNumber(reinvestedDeposit);

      final SavingsAccountTransaction withdrawal =
          this.handleWithdrawal(
              account,
              fmt,
              closedDate,
              account.getAccountBalance(),
              paymentDetail,
              false,
              isRegularTransaction);
      savingsTransactionId = withdrawal.getId();

    } else if (onClosureType.isTransferToSavings()) {
      final Long toSavingsId = command.longValueOfParameterNamed(toSavingsAccountIdParamName);
      final String transferDescription =
          command.stringValueOfParameterNamed(transferDescriptionParamName);
      final SavingsAccount toSavingsAccount =
          this.depositAccountAssembler.assembleFrom(
              toSavingsId, DepositAccountType.SAVINGS_DEPOSIT);
      final boolean isExceptionForBalanceCheck = false;
      final AccountTransferDTO accountTransferDTO =
          new AccountTransferDTO(
              closedDate,
              transactionAmount,
              PortfolioAccountType.SAVINGS,
              PortfolioAccountType.SAVINGS,
              null,
              null,
              transferDescription,
              locale,
              fmt,
              null,
              null,
              null,
              null,
              null,
              AccountTransferType.ACCOUNT_TRANSFER.getValue(),
              null,
              null,
              null,
              null,
              toSavingsAccount,
              account,
              isRegularTransaction,
              isExceptionForBalanceCheck);
      this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
    } else {
      final SavingsAccountTransaction withdrawal =
          this.handleWithdrawal(
              account,
              fmt,
              closedDate,
              account.getAccountBalance(),
              paymentDetail,
              false,
              isRegularTransaction);
      savingsTransactionId = withdrawal.getId();
    }

    /** * Update account transactionIds for post journal entries. */
    updateExistingTransactionsDetails(
        account, existingTransactionIds, existingReversedTransactionIds);
    account.close(user, command, tenantsTodayDate, changes);

    this.savingsAccountRepository.save(account);

    postJournalEntries(
        account, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);

    return savingsTransactionId;
  }