@Transactional
  @Override
  public CommandProcessingResult modifyApplication(
      final Long savingsId, final JsonCommand command) {
    try {
      this.savingsAccountDataValidator.validateForUpdate(command.json());

      final Map<String, Object> changes = new LinkedHashMap<String, Object>(20);

      final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
      checkClientOrGroupActive(account);
      account.modifyApplication(command, changes);
      account.validateNewApplicationState(DateUtils.getLocalDateOfTenant());
      account.validateAccountValuesWithProduct();

      if (!changes.isEmpty()) {

        if (changes.containsKey(SavingsApiConstants.clientIdParamName)) {
          final Long clientId =
              command.longValueOfParameterNamed(SavingsApiConstants.clientIdParamName);
          if (clientId != null) {
            final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
            if (client.isNotActive()) {
              throw new ClientNotActiveException(clientId);
            }
            account.update(client);
          } else {
            final Client client = null;
            account.update(client);
          }
        }

        if (changes.containsKey(SavingsApiConstants.groupIdParamName)) {
          final Long groupId =
              command.longValueOfParameterNamed(SavingsApiConstants.groupIdParamName);
          if (groupId != null) {
            final Group group = this.groupRepository.findOne(groupId);
            if (group == null) {
              throw new GroupNotFoundException(groupId);
            }
            if (group.isNotActive()) {
              if (group.isCenter()) {
                throw new CenterNotActiveException(groupId);
              }
              throw new GroupNotActiveException(groupId);
            }
            account.update(group);
          } else {
            final Group group = null;
            account.update(group);
          }
        }

        if (changes.containsKey(SavingsApiConstants.productIdParamName)) {
          final Long productId =
              command.longValueOfParameterNamed(SavingsApiConstants.productIdParamName);
          final SavingsProduct product = this.savingsProductRepository.findOne(productId);
          if (product == null) {
            throw new SavingsProductNotFoundException(productId);
          }

          account.update(product);
        }

        if (changes.containsKey(SavingsApiConstants.fieldOfficerIdParamName)) {
          final Long fieldOfficerId =
              command.longValueOfParameterNamed(SavingsApiConstants.fieldOfficerIdParamName);
          Staff fieldOfficer = null;
          if (fieldOfficerId != null) {
            fieldOfficer = this.staffRepository.findOneWithNotFoundDetection(fieldOfficerId);
          } else {
            changes.put(SavingsApiConstants.fieldOfficerIdParamName, "");
          }
          account.update(fieldOfficer);
        }

        if (changes.containsKey("charges")) {
          final Set<SavingsAccountCharge> charges =
              this.savingsAccountChargeAssembler.fromParsedJson(
                  command.parsedJson(), account.getCurrency().getCode());
          final boolean updated = account.update(charges);
          if (!updated) {
            changes.remove("charges");
          }
        }

        this.savingAccountRepository.saveAndFlush(account);
      }

      return new CommandProcessingResultBuilder() //
          .withCommandId(command.commandId()) //
          .withEntityId(savingsId) //
          .withOfficeId(account.officeId()) //
          .withClientId(account.clientId()) //
          .withGroupId(account.groupId()) //
          .withSavingsId(savingsId) //
          .with(changes) //
          .build();
    } catch (final DataAccessException dve) {
      handleDataIntegrityIssues(command, dve);
      return new CommandProcessingResult(Long.valueOf(-1));
    }
  }
  @Transactional
  @Override
  public EntityIdentifier modifyLoanApplication(final Long loanId, final JsonCommand command) {

    context.authenticatedUser();

    LoanApplicationCommand loanApplicationCommand =
        this.fromApiJsonDeserializer.commandFromApiJson(command.json());
    loanApplicationCommand.validate();

    CalculateLoanScheduleQuery calculateLoanScheduleQuery =
        this.calculateLoanScheduleQueryFromApiJsonDeserializer.commandFromApiJson(command.json());
    calculateLoanScheduleQuery.validate();

    final Loan existingLoanApplication = retrieveLoanBy(loanId);

    final Map<String, Object> changes =
        existingLoanApplication.modifyLoanApplication(
            command, loanApplicationCommand.getCharges(), this.aprCalculator);

    final String clientIdParamName = "clientId";
    if (changes.containsKey(clientIdParamName)) {
      final Long clientId = command.longValueOfParameterNamed(clientIdParamName);
      final Client client = this.clientRepository.findOne(clientId);
      if (client == null || client.isDeleted()) {
        throw new ClientNotFoundException(clientId);
      }

      existingLoanApplication.updateClient(client);
    }

    final String productIdParamName = "productId";
    if (changes.containsKey(productIdParamName)) {
      final Long productId = command.longValueOfParameterNamed(productIdParamName);
      final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
      if (loanProduct == null) {
        throw new LoanProductNotFoundException(productId);
      }

      existingLoanApplication.updateLoanProduct(loanProduct);
    }

    final String fundIdParamName = "fundId";
    if (changes.containsKey(fundIdParamName)) {
      final Long fundId = command.longValueOfParameterNamed(fundIdParamName);
      final Fund fund = this.loanAssembler.findFundByIdIfProvided(fundId);

      existingLoanApplication.updateFund(fund);
    }

    final String strategyIdParamName = "transactionProcessingStrategyId";
    if (changes.containsKey(strategyIdParamName)) {
      final Long strategyId = command.longValueOfParameterNamed(strategyIdParamName);
      final LoanTransactionProcessingStrategy strategy =
          this.loanAssembler.findStrategyByIdIfProvided(strategyId);

      existingLoanApplication.updateTransactionProcessingStrategy(strategy);
    }

    final String chargesParamName = "charges";
    if (changes.containsKey(chargesParamName)) {
      final Set<LoanCharge> loanCharges =
          this.loanChargeAssembler.fromParsedJson(command.parsedJson());
      existingLoanApplication.updateLoanCharges(loanCharges);
    }

    if (changes.containsKey("recalculateLoanSchedule")) {
      changes.remove("recalculateLoanSchedule");

      final JsonElement parsedQuery = this.fromJsonHelper.parse(command.json());
      final JsonQuery query = JsonQuery.from(command.json(), parsedQuery, this.fromJsonHelper);

      final LoanScheduleData loanSchedule =
          this.calculationPlatformService.calculateLoanSchedule(query);
      existingLoanApplication.updateLoanSchedule(loanSchedule);
      existingLoanApplication.updateLoanScheduleDependentDerivedFields();
    }

    this.loanRepository.save(existingLoanApplication);

    final String submittedOnNote = command.stringValueOfParameterNamed("submittedOnNote");
    if (StringUtils.isNotBlank(submittedOnNote)) {
      Note note = Note.loanNote(existingLoanApplication, submittedOnNote);
      this.noteRepository.save(note);
    }

    return EntityIdentifier.resourceResult(loanId, command.commandId(), changes);
  }