@Override
  public CustomerChargesDetailsDto retrieveChargesDetails(Integer customerId) {

    MifosUser mifosUser =
        (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    UserContext userContext = new UserContextFactory().create(mifosUser);

    CustomerBO customerBO = this.customerDao.findCustomerById(customerId);
    CustomerAccountBO customerAccount = customerBO.getCustomerAccount();

    List<AccountFeesDto> accountFeesDtos = new ArrayList<AccountFeesDto>();
    if (!customerAccount.getAccountFees().isEmpty()) {
      for (AccountFeesEntity accountFeesEntity : customerAccount.getAccountFees()) {
        AccountFeesDto accountFeesDto =
            new AccountFeesDto(
                accountFeesEntity.getFees().getFeeFrequency().getFeeFrequencyType().getId(),
                (accountFeesEntity.getFees().getFeeFrequency().getFeePayment() != null
                    ? accountFeesEntity.getFees().getFeeFrequency().getFeePayment().getId()
                    : null),
                accountFeesEntity.getFeeStatus(),
                accountFeesEntity.getFees().getFeeName(),
                accountFeesEntity.getAccountFeeAmount().toString(),
                getMeetingRecurrence(
                    accountFeesEntity.getFees().getFeeFrequency().getFeeMeetingFrequency(),
                    userContext),
                accountFeesEntity.getFees().getFeeId());
        accountFeesDtos.add(accountFeesDto);
      }
    }

    CustomerScheduleDto customerSchedule = null;
    CustomerScheduleEntity scheduleEntity =
        (CustomerScheduleEntity) customerAccount.getUpcomingInstallment();
    if (scheduleEntity != null) {
      Set<AccountFeesActionDetailEntity> feeEntities = scheduleEntity.getAccountFeesActionDetails();

      List<AccountFeeScheduleDto> feeDtos = new ArrayList<AccountFeeScheduleDto>();
      for (AccountFeesActionDetailEntity feeEntity : feeEntities) {
        feeDtos.add(convertToDto(feeEntity));
      }

      customerSchedule =
          new CustomerScheduleDto(
              scheduleEntity.getMiscFee().toString(),
              scheduleEntity.getMiscFeePaid().toString(),
              scheduleEntity.getMiscPenalty().toString(),
              scheduleEntity.getMiscPenaltyPaid().toString(),
              feeDtos);
    }

    return new CustomerChargesDetailsDto(
        customerAccount.getNextDueAmount().toString(),
        customerAccount.getTotalAmountInArrears().toString(),
        customerAccount.getTotalAmountDue().toString(),
        customerAccount.getUpcomingChargesDate(),
        customerSchedule,
        accountFeesDtos);
  }
  @Override
  public void waiveChargesDue(Integer accountId, Integer waiveType) {

    MifosUser user =
        (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    UserContext userContext = toUserContext(user);

    try {
      AccountBO account = new AccountBusinessService().getAccount(accountId);
      account.updateDetails(userContext);
      PersonnelBO loggedInUser = this.personnelDao.findPersonnelById(userContext.getId());

      WaiveEnum waiveEnum = WaiveEnum.fromInt(waiveType);
      if (account.getPersonnel() != null) {
        new AccountBusinessService()
            .checkPermissionForWaiveDue(
                waiveEnum,
                account.getType(),
                account.getCustomer().getLevel(),
                userContext,
                account.getOffice().getOfficeId(),
                account.getPersonnel().getPersonnelId());
      } else {
        new AccountBusinessService()
            .checkPermissionForWaiveDue(
                waiveEnum,
                account.getType(),
                account.getCustomer().getLevel(),
                userContext,
                account.getOffice().getOfficeId(),
                userContext.getId());
      }

      if (account.isLoanAccount()) {
        ((LoanBO) account).waiveAmountDue(waiveEnum);
      } else if (account.isSavingsAccount()) {
        ((SavingsBO) account).waiveNextDepositAmountDue(loggedInUser);
      } else {
        try {
          this.transactionHelper.startTransaction();
          ((CustomerAccountBO) account).waiveAmountDue();
          this.customerDao.save(account);
          this.transactionHelper.commitTransaction();
        } catch (Exception e) {
          this.transactionHelper.rollbackTransaction();
          throw new BusinessRuleException(account.getAccountId().toString(), e);
        } finally {
          this.transactionHelper.closeSession();
        }
      }
    } catch (ServiceException e) {
      throw new MifosRuntimeException(e);
    } catch (ApplicationException e) {
      throw new BusinessRuleException(e.getKey(), e);
    }
  }
  @Override
  public ParsedClientsDto save(ParsedClientsDto parsedClientsDto) {
    MifosUser user =
        (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    UserContext userContext = toUserContext(user);

    OfficeBO userOffice = this.officeDao.findOfficeById(userContext.getBranchId());
    userContext.setBranchGlobalNum(userOffice.getGlobalOfficeNum());

    DateTimeService dateTimeService = new DateTimeService();

    /* Construct ClientBO objects */
    List<NewClientDto> newClients = new ArrayList<NewClientDto>();

    for (ImportedClientDetail importedClient : parsedClientsDto.getSuccessfullyParsedRows()) {
      String secondMiddleName = null;
      ClientCreationDetail clientCreationDetail = importedClient.getClientCreationDetail();

      PersonnelBO formedBy = null;

      /* Client name details */
      ClientNameDetailDto clientNameDetails = clientCreationDetail.getClientNameDetailDto();
      ClientNameDetailEntity clientNameDetailEntity =
          new ClientNameDetailEntity(null, secondMiddleName, clientNameDetails);

      ClientDetailEntity clientDetailEntity = new ClientDetailEntity();
      clientDetailEntity.updateClientDetails(clientCreationDetail.getClientPersonalDetailDto());

      String clientFirstName = clientNameDetails.getFirstName();
      String clientLastName = clientNameDetails.getLastName();
      String secondLastName = clientNameDetails.getSecondLastName();

      /* Spouse/father name details */
      ClientNameDetailEntity spouseFatherNameDetailEntity = null;
      if (clientCreationDetail.getSpouseFatherName() != null) {
        spouseFatherNameDetailEntity =
            new ClientNameDetailEntity(
                null, secondMiddleName, clientCreationDetail.getSpouseFatherName());
      }
      /* Data conversion */
      DateTime dateOfBirth = new DateTime(clientCreationDetail.getDateOfBirth());
      DateTime mfiJoiningDate = new DateTime(clientCreationDetail.getMfiJoiningDate());
      DateTime trainedDateTime = null;
      if (clientCreationDetail.getTrainedDate() != null) {
        trainedDateTime = new DateTime(clientCreationDetail.getTrainedDate());
      }
      /* Status */
      CustomerStatus clientStatus = CustomerStatus.fromInt(clientCreationDetail.getClientStatus());
      CustomerStatus finalStatus = clientStatus;
      if (clientStatus == CustomerStatus.CLIENT_ACTIVE
          && clientCreationDetail.getActivationDate() == null) {
        clientStatus = CustomerStatus.CLIENT_PENDING;
      }
      /* Address */
      Address address = null;
      if (clientCreationDetail.getAddress() != null) {
        AddressDto dto = clientCreationDetail.getAddress();
        address =
            new Address(
                dto.getLine1(),
                dto.getLine2(),
                dto.getLine3(),
                dto.getCity(),
                dto.getState(),
                dto.getCountry(),
                dto.getZip(),
                dto.getPhoneNumber());
      }
      // empty list
      List<ClientInitialSavingsOfferingEntity> associatedOfferings =
          new ArrayList<ClientInitialSavingsOfferingEntity>();
      // client object
      ClientBO client;
      if (clientCreationDetail.getGroupFlag() == 1) {
        CustomerBO group =
            customerDao.findCustomerBySystemId(clientCreationDetail.getParentGroupId());

        if (clientCreationDetail.getFormedBy() != null) {
          formedBy = this.personnelDao.findPersonnelById(clientCreationDetail.getFormedBy());
        } else {
          formedBy = group.getPersonnel();
        }

        client =
            ClientBO.createNewInGroupHierarchy(
                userContext,
                clientCreationDetail.getClientName(),
                clientStatus,
                mfiJoiningDate,
                group,
                formedBy,
                clientNameDetailEntity,
                dateOfBirth,
                clientCreationDetail.getGovernmentId(),
                clientCreationDetail.isTrained(),
                trainedDateTime,
                clientCreationDetail.getGroupFlag(),
                clientFirstName,
                clientLastName,
                secondLastName,
                spouseFatherNameDetailEntity,
                clientDetailEntity,
                associatedOfferings,
                clientCreationDetail.getExternalId(),
                address,
                clientCreationDetail.getActivationDate());
      } else {
        Short officeId = clientCreationDetail.getOfficeId();
        Short officerId = clientCreationDetail.getLoanOfficerId();

        PersonnelBO loanOfficer = personnelDao.findPersonnelById(officerId);
        OfficeBO office = this.officeDao.findOfficeById(officeId);

        if (clientCreationDetail.getFormedBy() != null) {
          formedBy = this.personnelDao.findPersonnelById(clientCreationDetail.getFormedBy());
        } else {
          formedBy = loanOfficer;
        }

        int lastSearchIdCustomerValue =
            customerDao.retrieveLastSearchIdValueForNonParentCustomersInOffice(officeId);

        /* meeting */
        final MeetingDto meetingDto = importedClient.getMeeting();
        MeetingBO clientMeeting = null;
        if (meetingDto != null) {
          clientMeeting = new MeetingFactory().create(meetingDto);
          clientMeeting.setUserContext(userContext);
        }

        client =
            ClientBO.createNewOutOfGroupHierarchy(
                userContext,
                clientCreationDetail.getClientName(),
                clientStatus,
                mfiJoiningDate,
                office,
                loanOfficer,
                clientMeeting,
                formedBy,
                clientNameDetailEntity,
                dateOfBirth,
                clientCreationDetail.getGovernmentId(),
                clientCreationDetail.isTrained(),
                trainedDateTime,
                clientCreationDetail.getGroupFlag(),
                clientFirstName,
                clientLastName,
                secondLastName,
                spouseFatherNameDetailEntity,
                clientDetailEntity,
                associatedOfferings,
                clientCreationDetail.getExternalId(),
                address,
                lastSearchIdCustomerValue);

        if (clientCreationDetail.getActivationDate() != null) {
          client.setCustomerActivationDate(
              clientCreationDetail.getActivationDate().toDateMidnight().toDate());
        }
      }
      // global id
      if (importedClient.getClientGlobalNum() != null) {
        client.setGlobalCustNum(importedClient.getClientGlobalNum());
      }

      NewClientDto newClient = new NewClientDto(client, finalStatus);

      newClients.add(newClient);
    }

    /* Validate client data */
    for (NewClientDto newClient : newClients) {
      ClientBO client = newClient.getClientBO();
      try {
        client.validate();
        customerDao.validateClientForDuplicateNameOrGovtId(
            client.getDisplayName(), client.getDateOfBirth(), client.getGovernmentId());
      } catch (CustomerException ex) {
        throw new MifosRuntimeException(ex);
      }
    }

    /* Save clients */
    List<AccountFeesEntity> accountFees = new ArrayList<AccountFeesEntity>(); // empty list
    try {
      hibernateTransactionHelper.startTransaction();
      for (NewClientDto newClient : newClients) {

        ClientBO client = newClient.getClientBO();
        CustomerStatus finalStatus = newClient.getCustomerStatus();
        // status to pending approval if active

        MeetingBO meeting = client.getCustomerMeetingValue();

        customerDao.save(client);
        hibernateTransactionHelper.flushSession();

        CalendarEvent applicableCalendarEvents =
            holidayDao.findCalendarEventsForThisYearAndNext(client.getOfficeId());
        CustomerAccountBO customerAccount =
            customerAccountFactory.create(client, accountFees, meeting, applicableCalendarEvents);
        client.addAccount(customerAccount);
        customerDao.save(client);
        hibernateTransactionHelper.flushSession();

        if (client.getParentCustomer() != null) {
          customerDao.save(client.getParentCustomer());
        }

        if (client.getGlobalCustNum() == null) {
          client.generateGlobalCustomerNumber();
        }
        client.generateSearchId();
        customerDao.save(client);
        hibernateTransactionHelper.flushSession();

        if (client.getParentCustomer() != null) {
          customerDao.save(client.getParentCustomer());
        }

        /* activate client */
        if (finalStatus == CustomerStatus.CLIENT_ACTIVE) {
          hibernateTransactionHelper.flushSession();
          hibernateTransactionHelper.beginAuditLoggingFor(client);
          client.clearCustomerFlagsIfApplicable(client.getStatus(), finalStatus);

          client.updateCustomerStatus(finalStatus);
          // changeStatus(client, oldStatus, newStatus);
          if (client.getParentCustomer() != null) {
            CustomerHierarchyEntity hierarchy =
                new CustomerHierarchyEntity(client, client.getParentCustomer());
            client.addCustomerHierarchy(hierarchy);
          }

          if (client.getCustomerActivationDate() != null) {
            client.setCustomerActivationDate(client.getCustomerActivationDate());
          } else {
            client.setCustomerActivationDate(dateTimeService.getCurrentJavaDateTime());
          }
          customerAccount.createSchedulesAndFeeSchedulesForFirstTimeActiveCustomer(
              client,
              accountFees,
              meeting,
              applicableCalendarEvents,
              new DateTime(client.getCustomerActivationDate()));

          customerDao.save(client);
        }
      }
      hibernateTransactionHelper.commitTransaction();
    } catch (Exception ex) {
      hibernateTransactionHelper.rollbackTransaction();
      throw new MifosRuntimeException(ex);
    }

    return parsedClientsDto;
  }