@Transactional
  @Override
  public EntityIdentifier applicantWithdrawsFromLoanApplication(
      final Long loanId, final JsonCommand command) {

    final AppUser currentUser = context.authenticatedUser();

    final LoanStateTransitionCommand applicantWithdrawsFromLoanApplication =
        this.loanStateTransitionCommandFromApiJsonDeserializer.commandFromApiJson(command.json());
    applicantWithdrawsFromLoanApplication.validate();

    final Loan loan = retrieveLoanBy(loanId);

    final LocalDate eventDate = applicantWithdrawsFromLoanApplication.getWithdrawnOnDate();
    if (this.isBeforeToday(eventDate) && currentUser.canNotWithdrawByClientLoanInPast()) {
      throw new NoAuthorizationException(
          "User has no authority to mark loan as withdrawn by applicant with a date in the past.");
    }

    final Map<String, Object> changes =
        loan.loanApplicationWithdrawnByApplicant(command, defaultLoanLifecycleStateMachine());
    this.loanRepository.save(loan);

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

    return EntityIdentifier.resourceResult(loanId, command.commandId(), changes);
  }
  @Transactional
  @Override
  public EntityIdentifier disburseLoan(final Long loanId, final JsonCommand command) {

    final AppUser currentUser = context.authenticatedUser();

    final LoanStateTransitionCommand disburseLoan =
        this.loanStateTransitionCommandFromApiJsonDeserializer.commandFromApiJson(command.json());
    disburseLoan.validate();

    final Loan loan = retrieveLoanBy(loanId);

    final String noteText = command.stringValueOfParameterNamed("note");
    final LocalDate actualDisbursementDate = disburseLoan.getDisbursedOnDate();
    if (this.isBeforeToday(actualDisbursementDate) && currentUser.canNotDisburseLoanInPast()) {
      throw new NoAuthorizationException(
          "User has no authority to disburse loan with a date in the past.");
    }

    final ApplicationCurrency currency =
        this.applicationCurrencyRepository.findOneByCode(loan.getPrincpal().getCurrencyCode());

    final Map<String, Object> changes =
        loan.disburse(command, defaultLoanLifecycleStateMachine(), currency);
    this.loanRepository.save(loan);

    if (StringUtils.isNotBlank(noteText)) {
      Note note = Note.loanNote(loan, noteText);
      this.noteRepository.save(note);
    }

    return EntityIdentifier.resourceResult(loanId, command.commandId(), changes);
  }
  @Transactional
  @Override
  public CommandProcessingResult createOffice(final JsonCommand command) {

    try {
      final AppUser currentUser = context.authenticatedUser();

      this.fromApiJsonDeserializer.validateForCreate(command.json());

      Long parentId = null;
      if (command.parameterExists("parentId")) {
        parentId = command.longValueOfParameterNamed("parentId");
      }

      final Office parent = validateUserPriviledgeOnOfficeAndRetrieve(currentUser, parentId);
      final Office office = Office.fromJson(parent, command);

      // pre save to generate id for use in office hierarchy
      this.officeRepository.save(office);

      office.generateHierarchy();

      this.officeRepository.save(office);

      return new CommandProcessingResultBuilder() //
          .withCommandId(command.commandId()) //
          .withEntityId(office.getId()) //
          .withOfficeId(office.getId()) //
          .build();
    } catch (DataIntegrityViolationException dve) {
      handleOfficeDataIntegrityIssues(command, dve);
      return CommandProcessingResult.empty();
    }
  }
  @Transactional
  @Override
  public CommandProcessingResult createHoliday(final JsonCommand command) {

    try {
      this.context.authenticatedUser();
      this.fromApiJsonDeserializer.validateForCreate(command.json());

      validateInputDates(command);

      final Set<Office> offices = getSelectedOffices(command);

      final Holiday holiday = Holiday.createNew(offices, command);

      this.holidayRepository.save(holiday);

      return new CommandProcessingResultBuilder()
          .withCommandId(command.commandId())
          .withEntityId(holiday.getId())
          .build();
    } catch (final DataIntegrityViolationException dve) {
      handleDataIntegrityIssues(command, dve);
      return CommandProcessingResult.empty();
    }
  }
  @Transactional
  @Override
  public CommandProcessingResult updateCharge(final Long chargeId, final JsonCommand command) {

    try {
      this.context.authenticatedUser();

      this.fromApiJsonDeserializer.validateForUpdate(command.json());

      final Charge chargeForUpdate = this.chargeRepository.findOne(chargeId);
      if (chargeForUpdate == null) {
        throw new ChargeNotFoundException(chargeId);
      }

      final Map<String, Object> changes = chargeForUpdate.update(command);

      if (!changes.isEmpty()) {
        this.chargeRepository.save(chargeForUpdate);
      }

      return new CommandProcessingResultBuilder()
          .withCommandId(command.commandId())
          .withEntityId(chargeId)
          .with(changes)
          .build();
    } catch (DataIntegrityViolationException dve) {
      handleDataIntegrityIssues(command, dve);
      return CommandProcessingResult.empty();
    }
  }
 @Transactional
 @Override
 public CommandProcessingResult createEventPricing(JsonCommand command) {
   try {
     this.context.authenticatedUser();
     this.apiJsonDeserializer.validateForCreate(command.json());
     Long eventId = command.longValueOfParameterNamed("eventId");
     EventMaster eventMaster = this.eventMasterRepository.findOne(eventId);
     final EventPricing eventPricing = EventPricing.fromJson(command, eventMaster);
     List<EventPricingData> eventDetails =
         this.eventPricingReadPlatformService.retrieventPriceData(command.entityId());
     for (EventPricingData eventDetail : eventDetails) {
       if (eventPricing.getFormatType().equalsIgnoreCase(eventDetail.getFormatType())
           && eventPricing.getClientType() == eventDetail.getClientType()
           && eventPricing.getOptType().equalsIgnoreCase(eventDetail.getOptType())) {
         throw new DuplicatEventPrice(eventPricing.getFormatType());
       }
     }
     this.eventPricingRepository.save(eventPricing);
     return new CommandProcessingResultBuilder()
         .withCommandId(command.commandId())
         .withEntityId(eventPricing.getId())
         .build();
   } catch (DataIntegrityViolationException dve) {
     handleDataIntegrityIssues(command, dve);
     return new CommandProcessingResult(Long.valueOf(-1));
   }
 }
  @Transactional
  @Override
  public EntityIdentifier submitLoanApplication(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 newLoanApplication = loanAssembler.assembleFrom(command);

    this.loanRepository.save(newLoanApplication);

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

    return EntityIdentifier.resourceResult(newLoanApplication.getId(), command.commandId());
  }
  @Transactional
  @Override
  public CommandProcessingResult rejectApplication(
      final Long savingsId, final JsonCommand command) {

    final AppUser currentUser = this.context.authenticatedUser();

    this.savingsAccountApplicationTransitionApiJsonValidator.validateRejection(command.json());

    final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsId);
    checkClientOrGroupActive(savingsAccount);

    final Map<String, Object> changes =
        savingsAccount.rejectApplication(currentUser, command, DateUtils.getLocalDateOfTenant());
    if (!changes.isEmpty()) {
      this.savingAccountRepository.save(savingsAccount);

      final String noteText = command.stringValueOfParameterNamed("note");
      if (StringUtils.isNotBlank(noteText)) {
        final Note note = Note.savingNote(savingsAccount, noteText);
        changes.put("note", noteText);
        this.noteRepository.save(note);
      }
    }

    return new CommandProcessingResultBuilder() //
        .withCommandId(command.commandId()) //
        .withEntityId(savingsId) //
        .withOfficeId(savingsAccount.officeId()) //
        .withClientId(savingsAccount.clientId()) //
        .withGroupId(savingsAccount.groupId()) //
        .withSavingsId(savingsId) //
        .with(changes) //
        .build();
  }
  @Transactional
  @Override
  public CommandProcessingResult updateGLClosure(
      final Long glClosureId, final JsonCommand command) {
    final GLClosureCommand closureCommand =
        this.fromApiJsonDeserializer.commandFromApiJson(command.json());
    closureCommand.validateForUpdate();

    // is the glClosure valid
    final GLClosure glClosure = this.glClosureRepository.findOne(glClosureId);
    if (glClosure == null) {
      throw new GLClosureNotFoundException(glClosureId);
    }

    final Map<String, Object> changesOnly = glClosure.update(command);

    if (!changesOnly.isEmpty()) {
      this.glClosureRepository.saveAndFlush(glClosure);
    }

    return new CommandProcessingResultBuilder()
        .withCommandId(command.commandId())
        .withOfficeId(glClosure.getOffice().getId())
        .withEntityId(glClosure.getId())
        .with(changesOnly)
        .build();
  }
  /* (non-Javadoc)
   * @see #updateTaxMap(org.mifosplatform.infrastructure.core.api.JsonCommand, java.lang.Long)
   */
  @Transactional
  @Override
  public CommandProcessingResult updateTaxMap(final JsonCommand command, final Long taxMapId) {
    TaxMap taxMap = null;
    try {
      this.context.authenticatedUser();
      this.apiJsonDeserializer.validateForCreate(command);
      taxMap = retrieveTaxMapById(taxMapId);
      final Map<String, Object> changes = taxMap.update(command);

      if (!changes.isEmpty()) {
        this.taxMapRepository.saveAndFlush(taxMap);
      }

      return new CommandProcessingResultBuilder()
          .withCommandId(command.commandId())
          .withEntityId(taxMap.getId())
          .with(changes)
          .build();
    } catch (final DataIntegrityViolationException dve) {
      if (dve.getCause() instanceof ConstraintViolationException) {
        handleDataIntegrityIssues(command, dve);
      }
      return new CommandProcessingResult(Long.valueOf(-1));
    }
  }
  @Transactional
  @Override
  public CommandProcessingResult submitApplication(final JsonCommand command) {
    try {
      this.savingsAccountDataValidator.validateForSubmit(command.json());
      final AppUser submittedBy = this.context.authenticatedUser();

      final SavingsAccount account = this.savingAccountAssembler.assembleFrom(command, submittedBy);
      this.savingAccountRepository.save(account);

      if (account.isAccountNumberRequiresAutoGeneration()) {
        final AccountNumberGenerator accountNoGenerator =
            this.accountIdentifierGeneratorFactory.determineSavingsAccountNoGenerator(
                account.getId());
        account.updateAccountNo(accountNoGenerator.generate());

        this.savingAccountRepository.save(account);
      }

      final Long savingsId = account.getId();
      return new CommandProcessingResultBuilder() //
          .withCommandId(command.commandId()) //
          .withEntityId(savingsId) //
          .withOfficeId(account.officeId()) //
          .withClientId(account.clientId()) //
          .withGroupId(account.groupId()) //
          .withSavingsId(savingsId) //
          .build();
    } catch (final DataAccessException dve) {
      handleDataIntegrityIssues(command, dve);
      return CommandProcessingResult.empty();
    }
  }
  @Transactional
  @Override
  public CommandProcessingResult followUpProspect(
      final JsonCommand command, final Long prospectId) {
    try {
      context.authenticatedUser();
      this.clientProspectCommandFromApiJsonDeserializer.validateForUpdate(command.json());

      final ProspectDetail prospectDetail = ProspectDetail.fromJson(command, prospectId);
      prospectDetailJpaRepository.save(prospectDetail);

      return new CommandProcessingResultBuilder()
          .withCommandId(command.commandId())
          .withEntityId(prospectDetail.getProspectId())
          .build();

    } catch (DataIntegrityViolationException dve) {
      handleDataIntegrityIssues(command, dve);
      return CommandProcessingResult.empty();
    } catch (ParseException e) {
      throw new PlatformDataIntegrityException(
          "invalid.date.and.time.format",
          "invalid.date.and.time.format",
          "invalid.date.and.time.format");
    }
  }
  @Transactional
  @Override
  public CommandProcessingResult createProspect(JsonCommand command) {

    try {
      context.authenticatedUser();
      this.clientProspectCommandFromApiJsonDeserializer.validateForCreate(command.json());

      final ClientProspect entity = ClientProspect.fromJson(fromApiJsonHelper, command);
      this.clientProspectJpaRepository.save(entity);

      return new CommandProcessingResultBuilder()
          .withCommandId(command.commandId())
          .withEntityId(entity.getId())
          .build();

    } catch (DataIntegrityViolationException dve) {
      handleDataIntegrityIssues(command, dve);
      return new CommandProcessingResult(Long.valueOf(-1));
    } catch (ParseException pe) {
      throw new PlatformDataIntegrityException(
          "invalid.date.and.time.format",
          "invalid.date.and.time.format",
          "invalid.date.and.time.format");
    }
  }
  @Override
  public CommandProcessingResult updateProspect(JsonCommand command) {

    try {
      context.authenticatedUser();
      this.clientProspectCommandFromApiJsonDeserializer.validateForCreate(command.json());

      final ClientProspect pros = retrieveCodeBy(command.entityId());
      final Map<String, Object> changes = pros.update(command);

      if (!changes.isEmpty()) {
        this.clientProspectJpaRepository.save(pros);
      }

      return new CommandProcessingResultBuilder() //
          .withCommandId(command.commandId()) //
          .withEntityId(pros.getId()) //
          .with(changes) //
          .build();

    } catch (DataIntegrityViolationException dve) {
      handleDataIntegrityIssues(command, dve);
    }
    return new CommandProcessingResultBuilder().withEntityId(-1L).build();
  }
  @Transactional
  @Override
  @CacheEvict(value = "hooks", allEntries = true)
  public CommandProcessingResult updateHook(final Long hookId, final JsonCommand command) {

    try {
      this.context.authenticatedUser();

      this.fromApiJsonDeserializer.validateForUpdate(command.json());

      final Hook hook = retrieveHookBy(hookId);
      final HookTemplate template = hook.getHookTemplate();
      final Map<String, Object> changes = hook.update(command);

      if (!changes.isEmpty()) {

        if (changes.containsKey(templateIdParamName)) {
          final Long ugdTemplateId = command.longValueOfParameterNamed(templateIdParamName);
          final Template ugdTemplate = this.ugdTemplateRepository.findOne(ugdTemplateId);
          if (ugdTemplate == null) {
            changes.remove(templateIdParamName);
            throw new TemplateNotFoundException(ugdTemplateId);
          }
          hook.updateUgdTemplate(ugdTemplate);
        }

        if (changes.containsKey(eventsParamName)) {
          final Set<HookResource> events =
              assembleSetOfEvents(command.arrayOfParameterNamed(eventsParamName));
          final boolean updated = hook.updateEvents(events);
          if (!updated) {
            changes.remove(eventsParamName);
          }
        }

        if (changes.containsKey(configParamName)) {
          final String configJson = command.jsonFragment(configParamName);
          final Set<HookConfiguration> config =
              assembleConfig(command.mapValueOfParameterNamed(configJson), template);
          final boolean updated = hook.updateConfig(config);
          if (!updated) {
            changes.remove(configParamName);
          }
        }

        this.hookRepository.saveAndFlush(hook);
      }

      return new CommandProcessingResultBuilder() //
          .withCommandId(command.commandId()) //
          .withEntityId(hookId) //
          .with(changes) //
          .build();
    } catch (final DataIntegrityViolationException dve) {
      handleHookDataIntegrityIssues(command, dve);
      return null;
    }
  }
  @Transactional
  @Override
  @Caching(
      evict = {
        @CacheEvict(
            value = "offices",
            key =
                "T(org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat(#root.target.context.authenticatedUser().getOffice().getHierarchy()+'of')"),
        @CacheEvict(
            value = "officesForDropdown",
            key =
                "T(org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat(#root.target.context.authenticatedUser().getOffice().getHierarchy()+'ofd')"),
        @CacheEvict(
            value = "officesById",
            key =
                "T(org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat(#officeId)")
      })
  public CommandProcessingResult updateOffice(final Long officeId, final JsonCommand command) {

    try {
      final AppUser currentUser = context.authenticatedUser();

      this.fromApiJsonDeserializer.validateForUpdate(command.json());

      Long parentId = null;
      if (command.parameterExists("parentId")) {
        parentId = command.longValueOfParameterNamed("parentId");
      }

      final Office office = validateUserPriviledgeOnOfficeAndRetrieve(currentUser, officeId);

      final Map<String, Object> changes = office.update(command);

      if (changes.containsKey("parentId")) {
        final Office parent = validateUserPriviledgeOnOfficeAndRetrieve(currentUser, parentId);
        office.update(parent);
      }

      if (!changes.isEmpty()) {
        this.officeRepository.saveAndFlush(office);
      }

      return new CommandProcessingResultBuilder() //
          .withCommandId(command.commandId()) //
          .withEntityId(office.getId()) //
          .withOfficeId(office.getId()) //
          .with(changes) //
          .build();
    } catch (DataIntegrityViolationException dve) {
      handleOfficeDataIntegrityIssues(command, dve);
      return CommandProcessingResult.empty();
    }
  }
  @Transactional
  @Override
  public CommandProcessingResult deleteOfficeTransaction(
      final Long transactionId, JsonCommand command) {

    context.authenticatedUser();

    this.officeTransactionRepository.delete(transactionId);

    return new CommandProcessingResultBuilder() //
        .withCommandId(command.commandId()) //
        .withEntityId(transactionId) //
        .build();
  }
  @Transactional
  @Override
  public CommandProcessingResult officeTransaction(final JsonCommand command) {

    context.authenticatedUser();

    this.moneyTransferCommandFromApiJsonDeserializer.validateOfficeTransfer(command.json());

    Long officeId = null;
    Office fromOffice = null;
    final Long fromOfficeId = command.longValueOfParameterNamed("fromOfficeId");
    if (fromOfficeId != null) {
      fromOffice = this.officeRepository.findOne(fromOfficeId);
      officeId = fromOffice.getId();
    }
    Office toOffice = null;
    final Long toOfficeId = command.longValueOfParameterNamed("toOfficeId");
    if (toOfficeId != null) {
      toOffice = this.officeRepository.findOne(toOfficeId);
      officeId = toOffice.getId();
    }

    if (fromOffice == null && toOffice == null) {
      throw new OfficeNotFoundException(toOfficeId);
    }

    final String currencyCode = command.stringValueOfParameterNamed("currencyCode");
    final ApplicationCurrency appCurrency =
        this.applicationCurrencyRepository.findOneByCode(currencyCode);
    if (appCurrency == null) {
      throw new CurrencyNotFoundException(currencyCode);
    }

    final MonetaryCurrency currency =
        new MonetaryCurrency(appCurrency.getCode(), appCurrency.getDecimalPlaces());
    final Money amount =
        Money.of(currency, command.bigDecimalValueOfParameterNamed("transactionAmount"));

    final OfficeTransaction entity =
        OfficeTransaction.fromJson(fromOffice, toOffice, amount, command);

    this.officeTransactionRepository.save(entity);

    return new CommandProcessingResultBuilder() //
        .withCommandId(command.commandId()) //
        .withEntityId(entity.getId()) //
        .withOfficeId(officeId) //
        .build();
  }
  @Transactional
  @Override
  @CacheEvict(value = "usersByUsername", allEntries = true)
  public CommandProcessingResult createUser(final JsonCommand command) {

    try {
      this.context.authenticatedUser();

      this.fromApiJsonDeserializer.validateForCreate(command.json());

      final String officeIdParamName = "officeId";
      final Long officeId = command.longValueOfParameterNamed(officeIdParamName);

      final Office userOffice = this.officeRepository.findOne(officeId);
      if (userOffice == null) {
        throw new OfficeNotFoundException(officeId);
      }

      final String[] roles = command.arrayValueOfParameterNamed("roles");
      final Set<Role> allRoles = assembleSetOfRoles(roles);

      final AppUser appUser = AppUser.fromJson(userOffice, allRoles, command);
      final Boolean sendPasswordToEmail =
          command.booleanObjectValueOfParameterNamed("sendPasswordToEmail");
      this.userDomainService.create(appUser, sendPasswordToEmail);

      return new CommandProcessingResultBuilder() //
          .withCommandId(command.commandId()) //
          .withEntityId(appUser.getId()) //
          .withOfficeId(userOffice.getId()) //
          .build();
    } catch (final DataIntegrityViolationException dve) {
      handleDataIntegrityIssues(command, dve);
      return CommandProcessingResult.empty();
    } catch (final PlatformEmailSendException e) {
      final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();

      final String email = command.stringValueOfParameterNamed("email");
      final ApiParameterError error =
          ApiParameterError.parameterError(
              "error.msg.user.email.invalid", "The parameter email is invalid.", "email", email);
      dataValidationErrors.add(error);

      throw new PlatformApiDataValidationException(
          "validation.msg.validation.errors.exist",
          "Validation errors exist.",
          dataValidationErrors);
    }
  }
 public CommandProcessingResult updateEventPricing(JsonCommand command) {
   try {
     this.apiJsonDeserializer.validateForCreate(command.json());
     EventPricing eventPrice = this.eventPricingRepository.findOne(command.entityId());
     EventMaster eventMaster = eventPrice.getEventId();
     EventPricing newEventPricing = EventPricing.fromJson(command, eventMaster);
     eventPrice = (EventPricing) UpdateCompareUtil.compare(eventPrice, newEventPricing);
     this.eventPricingRepository.save(eventPrice);
     return new CommandProcessingResultBuilder()
         .withEntityId(eventPrice.getId())
         .withCommandId(command.commandId())
         .build();
   } catch (DataIntegrityViolationException dev) {
     return CommandProcessingResult.empty();
   }
 }
  @Transactional
  @Override
  public CommandProcessingResult createGLClosure(final JsonCommand command) {
    try {
      final GLClosureCommand closureCommand =
          this.fromApiJsonDeserializer.commandFromApiJson(command.json());
      closureCommand.validateForCreate();

      // check office is valid
      final Long officeId =
          command.longValueOfParameterNamed(GLClosureJsonInputParams.OFFICE_ID.getValue());
      final Office office = this.officeRepository.findOne(officeId);
      if (office == null) {
        throw new OfficeNotFoundException(officeId);
      }

      // TODO: Get Tenant specific date
      // ensure closure date is not in the future
      final Date todaysDate = DateUtils.getDateOfTenant();
      final Date closureDate =
          command.DateValueOfParameterNamed(GLClosureJsonInputParams.CLOSING_DATE.getValue());
      if (closureDate.after(todaysDate)) {
        throw new GLClosureInvalidException(GL_CLOSURE_INVALID_REASON.FUTURE_DATE, closureDate);
      }
      // shouldn't be before an existing accounting closure
      final GLClosure latestGLClosure =
          this.glClosureRepository.getLatestGLClosureByBranch(officeId);
      if (latestGLClosure != null) {
        if (latestGLClosure.getClosingDate().after(closureDate)) {
          throw new GLClosureInvalidException(
              GL_CLOSURE_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate());
        }
      }
      final GLClosure glClosure = GLClosure.fromJson(office, command);

      this.glClosureRepository.saveAndFlush(glClosure);

      return new CommandProcessingResultBuilder()
          .withCommandId(command.commandId())
          .withOfficeId(officeId)
          .withEntityId(glClosure.getId())
          .build();
    } catch (final DataIntegrityViolationException dve) {
      handleGLClosureIntegrityIssues(command, dve);
      return CommandProcessingResult.empty();
    }
  }
  @Transactional
  @Override
  public EntityIdentifier undoLoanDisbursal(final Long loanId, final JsonCommand command) {

    context.authenticatedUser();

    final Loan loan = retrieveLoanBy(loanId);

    final Map<String, Object> changes = loan.undoDisbursal(defaultLoanLifecycleStateMachine());
    this.loanRepository.save(loan);

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

    return EntityIdentifier.resourceResult(loanId, command.commandId(), changes);
  }
  @Transactional
  @Override
  public CommandProcessingResult createCharge(final JsonCommand command) {
    try {
      this.context.authenticatedUser();

      this.fromApiJsonDeserializer.validateForCreate(command.json());

      final Charge charge = Charge.fromJson(command);
      this.chargeRepository.save(charge);

      return new CommandProcessingResultBuilder()
          .withCommandId(command.commandId())
          .withEntityId(charge.getId())
          .build();
    } catch (DataIntegrityViolationException dve) {
      handleDataIntegrityIssues(command, dve);
      return CommandProcessingResult.empty();
    }
  }
  @Transactional
  @Override
  @CacheEvict(value = "hooks", allEntries = true)
  public CommandProcessingResult createHook(final JsonCommand command) {

    try {
      this.context.authenticatedUser();

      this.fromApiJsonDeserializer.validateForCreate(command.json());

      final HookTemplate template =
          retrieveHookTemplateBy(command.stringValueOfParameterNamed(nameParamName));
      final String configJson = command.jsonFragment(configParamName);
      final Set<HookConfiguration> config =
          assembleConfig(command.mapValueOfParameterNamed(configJson), template);
      final JsonArray events = command.arrayOfParameterNamed(eventsParamName);
      final Set<HookResource> allEvents = assembleSetOfEvents(events);
      Template ugdTemplate = null;
      if (command.hasParameter(templateIdParamName)) {
        final Long ugdTemplateId = command.longValueOfParameterNamed(templateIdParamName);
        ugdTemplate = this.ugdTemplateRepository.findOne(ugdTemplateId);
        if (ugdTemplate == null) {
          throw new TemplateNotFoundException(ugdTemplateId);
        }
      }
      final Hook hook = Hook.fromJson(command, template, config, allEvents, ugdTemplate);

      validateHookRules(template, config, allEvents);

      this.hookRepository.save(hook);

      return new CommandProcessingResultBuilder()
          .withCommandId(command.commandId())
          .withEntityId(hook.getId())
          .build();
    } catch (final DataIntegrityViolationException dve) {
      handleHookDataIntegrityIssues(command, dve);
      return CommandProcessingResult.empty();
    }
  }
  @Transactional
  @Override
  public CommandProcessingResult updateOffice(final Long officeId, final JsonCommand command) {

    try {
      final AppUser currentUser = context.authenticatedUser();

      this.fromApiJsonDeserializer.validateForUpdate(command.json());

      Long parentId = null;
      if (command.parameterExists("parentId")) {
        parentId = command.longValueOfParameterNamed("parentId");
      }

      final Office office = validateUserPriviledgeOnOfficeAndRetrieve(currentUser, officeId);

      final Map<String, Object> changes = office.update(command);

      if (changes.containsKey("parentId")) {
        final Office parent = validateUserPriviledgeOnOfficeAndRetrieve(currentUser, parentId);
        office.update(parent);
      }

      if (!changes.isEmpty()) {
        this.officeRepository.saveAndFlush(office);
      }

      return new CommandProcessingResultBuilder() //
          .withCommandId(command.commandId()) //
          .withEntityId(office.getId()) //
          .withOfficeId(office.getId()) //
          .with(changes) //
          .build();
    } catch (DataIntegrityViolationException dve) {
      handleOfficeDataIntegrityIssues(command, dve);
      return CommandProcessingResult.empty();
    }
  }
  @Transactional
  @Override
  public CommandProcessingResult createSavingAccount(final JsonCommand command) {
    try {
      this.context.authenticatedUser();
      this.savingsAccountDataValidator.validateForCreate(command.json());

      final List<Long> existingTransactionIds = new ArrayList<Long>();
      final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
      final SavingsAccount account =
          this.savingAccountAssembler.assembleFrom(
              command, existingTransactionIds, existingReversedTransactionIds);
      this.savingAccountRepository.save(account);

      if (account.isAccountNumberRequiresAutoGeneration()) {
        final AccountNumberGenerator accountNoGenerator =
            this.accountIdentifierGeneratorFactory.determineLoanAccountNoGenerator(account.getId());
        account.updateAccountNo(accountNoGenerator.generate());

        this.savingAccountRepository.save(account);
      }

      postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);

      final Long savingsId = account.getId();
      return new CommandProcessingResultBuilder() //
          .withCommandId(command.commandId()) //
          .withEntityId(savingsId) //
          .withOfficeId(account.officeId()) //
          .withClientId(account.clientId()) //
          .withGroupId(account.groupId()) //
          .withSavingsId(savingsId) //
          .build();
    } catch (DataAccessException dve) {
      handleDataIntegrityIssues(command, dve);
      return CommandProcessingResult.empty();
    }
  }
  @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);
  }
  @Transactional
  @Override
  public CommandProcessingResult updateSavingAccount(
      final Long savingsId, final JsonCommand command) {
    try {
      this.context.authenticatedUser();
      this.savingsAccountDataValidator.validateForUpdate(command.json());

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

      final SavingsAccount account =
          this.savingAccountRepository.findOneWithNotFoundDetection(savingsId);

      account.update(command, changes);

      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);
            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);
            }
            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);
        }

        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 (DataAccessException dve) {
      handleDataIntegrityIssues(command, dve);
      return new CommandProcessingResult(Long.valueOf(-1));
    }
  }
  @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));
    }
  }