/*
* Copyright (c) 2005-2011 Grameen Foundation USA
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* See also http://www.apache.org/licenses/LICENSE-2.0.html for an
* explanation of the license and how it is applied.
*/
package org.mifos.customers.business.service;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.joda.time.DateMidnight;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.LocalDate;
import org.mifos.accounts.business.AccountBO;
import org.mifos.accounts.business.AccountFeesEntity;
import org.mifos.accounts.exceptions.AccountException;
import org.mifos.accounts.loan.business.LoanBO;
import org.mifos.accounts.productdefinition.business.SavingsOfferingBO;
import org.mifos.accounts.productdefinition.persistence.SavingsProductDao;
import org.mifos.accounts.productdefinition.util.helpers.RecommendedAmountUnit;
import org.mifos.accounts.savings.business.SavingsBO;
import org.mifos.accounts.savings.persistence.SavingsPersistence;
import org.mifos.accounts.util.helpers.AccountState;
import org.mifos.application.admin.servicefacade.InvalidDateException;
import org.mifos.application.holiday.business.Holiday;
import org.mifos.application.holiday.persistence.HolidayDao;
import org.mifos.application.master.MessageLookup;
import org.mifos.application.master.persistence.LegacyMasterDao;
import org.mifos.application.meeting.business.MeetingBO;
import org.mifos.application.meeting.exceptions.MeetingException;
import org.mifos.application.meeting.util.helpers.RankOfDay;
import org.mifos.application.meeting.util.helpers.WeekDay;
import org.mifos.application.servicefacade.ApplicationContextProvider;
import org.mifos.application.servicefacade.CustomerStatusUpdate;
import org.mifos.application.servicefacade.SavingsServiceFacade;
import org.mifos.calendar.CalendarEvent;
import org.mifos.config.FiscalCalendarRules;
import org.mifos.config.persistence.ConfigurationPersistence;
import org.mifos.config.util.helpers.ConfigurationConstants;
import org.mifos.core.MifosRuntimeException;
import org.mifos.customers.api.CustomerLevel;
import org.mifos.customers.business.CustomerAccountBO;
import org.mifos.customers.business.CustomerBO;
import org.mifos.customers.business.CustomerHierarchyEntity;
import org.mifos.customers.business.CustomerMeetingEntity;
import org.mifos.customers.business.CustomerNoteEntity;
import org.mifos.customers.business.CustomerPositionEntity;
import org.mifos.customers.business.CustomerStatusEntity;
import org.mifos.customers.business.CustomerStatusFlagEntity;
import org.mifos.customers.business.PositionEntity;
import org.mifos.customers.center.business.CenterBO;
import org.mifos.customers.client.business.ClientBO;
import org.mifos.customers.client.business.ClientInitialSavingsOfferingEntity;
import org.mifos.customers.client.util.helpers.ClientConstants;
import org.mifos.customers.exceptions.CustomerException;
import org.mifos.customers.group.business.GroupBO;
import org.mifos.customers.group.util.helpers.GroupConstants;
import org.mifos.customers.office.business.OfficeBO;
import org.mifos.customers.office.persistence.OfficeDao;
import org.mifos.customers.persistence.CustomerDao;
import org.mifos.customers.persistence.CustomerPersistence;
import org.mifos.customers.personnel.business.PersonnelBO;
import org.mifos.customers.personnel.persistence.PersonnelDao;
import org.mifos.customers.util.helpers.CustomerConstants;
import org.mifos.customers.util.helpers.CustomerStatus;
import org.mifos.customers.util.helpers.CustomerStatusFlag;
import org.mifos.dto.domain.CenterUpdate;
import org.mifos.dto.domain.ClientFamilyInfoUpdate;
import org.mifos.dto.domain.ClientMfiInfoUpdate;
import org.mifos.dto.domain.ClientPersonalInfoUpdate;
import org.mifos.dto.domain.CustomFieldDto;
import org.mifos.dto.domain.CustomerDto;
import org.mifos.dto.domain.CustomerPositionDto;
import org.mifos.dto.domain.GroupUpdate;
import org.mifos.framework.business.util.Address;
import org.mifos.framework.exceptions.ApplicationException;
import org.mifos.framework.exceptions.PersistenceException;
import org.mifos.framework.hibernate.helper.HibernateTransactionHelper;
import org.mifos.framework.image.service.ClientPhotoService;
import org.mifos.framework.util.DateTimeService;
import org.mifos.framework.util.helpers.DateUtils;
import org.mifos.security.util.UserContext;
import org.mifos.service.BusinessRuleException;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Default implementation of {@link CustomerService}.
*/
public class CustomerServiceImpl implements CustomerService {
private final CustomerDao customerDao;
private final PersonnelDao personnelDao;
private final OfficeDao officeDao;
private final HolidayDao holidayDao;
private final HibernateTransactionHelper hibernateTransactionHelper;
private CustomerAccountFactory customerAccountFactory = DefaultCustomerAccountFactory.createNew();
private MessageLookupHelper messageLookupHelper = DefaultMessageLookupHelper.createNew();
private ConfigurationPersistence configurationPersistence;
@Autowired
private LegacyMasterDao legacyMasterDao;
@Autowired
private SavingsProductDao savingsProductDao;
@Autowired
private ClientPhotoService clientPhotoService;
@Autowired
private SavingsServiceFacade savingsServiceFacade;
private MifosConfigurationHelper configurationHelper = new DefaultMifosConfigurationHelper();
@Autowired
public CustomerServiceImpl(CustomerDao customerDao, PersonnelDao personnelDao, OfficeDao officeDao,
HolidayDao holidayDao, final HibernateTransactionHelper hibernateTransactionHelper) {
this.customerDao = customerDao;
this.personnelDao = personnelDao;
this.officeDao = officeDao;
this.holidayDao = holidayDao;
this.hibernateTransactionHelper = hibernateTransactionHelper;
}
public void setCustomerAccountFactory(CustomerAccountFactory customerAccountFactory) {
this.customerAccountFactory = customerAccountFactory;
}
public void setMessageLookupHelper(MessageLookupHelper messageLookupHelper) {
this.messageLookupHelper = messageLookupHelper;
}
private ConfigurationPersistence getConfigurationPersistence() {
if (configurationPersistence == null) {
configurationPersistence = new ConfigurationPersistence();
}
return configurationPersistence;
}
public void setConfigurationPersistence(ConfigurationPersistence configurationPersistence) {
this.configurationPersistence = configurationPersistence;
}
@Override
public final void createCenter(CenterBO customer, MeetingBO meeting, List<AccountFeesEntity> accountFees) {
try {
customer.validate();
customer.validateMeetingAndFees(accountFees);
customerDao.validateCenterNameIsNotTakenForOffice(customer.getDisplayName(), customer.getOfficeId());
createCustomer(customer, meeting, accountFees);
} catch (CustomerException e) {
throw new BusinessRuleException(e.getKey(), new Object[] { customer.getDisplayName()});
}
}
@Override
public final void createGroup(GroupBO group, MeetingBO meeting, List<AccountFeesEntity> accountFees)
throws CustomerException {
group.validate();
customerDao.validateGroupNameIsNotTakenForOffice(group.getDisplayName(), group.getOffice().getOfficeId());
createCustomer(group, meeting, accountFees);
}
@Override
public final void createClient(ClientBO client, MeetingBO meeting, List<AccountFeesEntity> accountFees,
List<SavingsOfferingBO> savingProducts) throws CustomerException {
client.validate();
client.validateNoDuplicateSavings(savingProducts);
customerDao.validateClientForDuplicateNameOrGovtId(client.getDisplayName(), client.getDateOfBirth(), client.getGovernmentId());
if (client.isActive()) {
client.validateFieldsForActiveClient();
createSavingsAccountsForActiveSavingProducts(client, savingProducts);
generateSavingSchedulesForGroupAndCenterSavingAccounts(client);
}
createCustomer(client, meeting, accountFees);
}
private void generateSavingSchedulesForGroupAndCenterSavingAccounts(ClientBO client) {
try {
List<Days> workingDays = new FiscalCalendarRules().getWorkingDaysAsJodaTimeDays();
List<Holiday> holidays = new ArrayList<Holiday>();
UserContext userContext = client.getUserContext();
CustomerBO group = client.getParentCustomer();
if (group != null) {
List<SavingsBO> groupSavingAccounts = new CustomerPersistence().retrieveSavingsAccountForCustomer(group.getCustomerId());
CustomerBO center = group.getParentCustomer();
if (center != null) {
List<SavingsBO> centerSavingAccounts = new CustomerPersistence().retrieveSavingsAccountForCustomer(center.getCustomerId());
groupSavingAccounts.addAll(centerSavingAccounts);
}
for (SavingsBO savings : groupSavingAccounts) {
savings.setUserContext(userContext);
if (client.getCustomerMeetingValue() != null) {
if (!(savings.getCustomer().getLevel() == CustomerLevel.GROUP && savings.getRecommendedAmntUnit().getId().equals(RecommendedAmountUnit.COMPLETE_GROUP.getValue()))) {
DateTime today = new DateTime().toDateMidnight().toDateTime();
savings.generateDepositAccountActions(client, client.getCustomerMeeting().getMeeting(), workingDays, holidays, today);
}
}
}
}
} catch (PersistenceException pe) {
throw new MifosRuntimeException(pe);
}
}
private void createSavingsAccountsForActiveSavingProducts(ClientBO client, List<SavingsOfferingBO> savingProducts) {
UserContext userContext = client.getUserContext();
List<SavingsBO> savingsAccounts = new ArrayList<SavingsBO>();
for (SavingsOfferingBO clientSavingsProduct : savingProducts) {
try {
if (clientSavingsProduct.isActive()) {
List<CustomFieldDto> savingCustomFieldViews = new ArrayList<CustomFieldDto>();
SavingsBO savingsAccount = new SavingsBO(userContext, clientSavingsProduct, client,
AccountState.SAVINGS_ACTIVE, clientSavingsProduct.getRecommendedAmount(),
savingCustomFieldViews);
savingsAccounts.add(savingsAccount);
}
} catch (AccountException pe) {
throw new MifosRuntimeException(pe);
}
}
client.addSavingsAccounts(savingsAccounts);
}
private void createCustomer(CustomerBO customer, MeetingBO meeting, List<AccountFeesEntity> accountFees) {
try {
this.hibernateTransactionHelper.startTransaction();
this.customerDao.save(customer);
this.hibernateTransactionHelper.flushSession();
// generate globalids for savings accounts, as they require accouont_id the savings accounts
// must first be saved via the customer save.
for (AccountBO account: customer.getAccounts()) {
if (account.isSavingsAccount()) {
SavingsBO savingsAccount = (SavingsBO)account;
savingsAccount.setUserContext(customer.getUserContext());
savingsAccount.generateSystemId(customer.getOffice().getGlobalOfficeNum());
}
}
this.customerDao.save(customer);
this.hibernateTransactionHelper.flushSession();
CalendarEvent applicableCalendarEvents = this.holidayDao.findCalendarEventsForThisYearAndNext(customer.getOfficeId());
CustomerAccountBO customerAccount = this.customerAccountFactory.create(customer, accountFees, meeting, applicableCalendarEvents);
customer.addAccount(customerAccount);
this.customerDao.save(customer);
this.hibernateTransactionHelper.flushSession();
if (customer.getParentCustomer() != null) {
this.customerDao.save(customer.getParentCustomer());
}
customer.generateGlobalCustomerNumber();
customer.generateSearchId();
this.customerDao.save(customer);
if (customer.getParentCustomer() != null) {
this.customerDao.save(customer.getParentCustomer());
}
this.hibernateTransactionHelper.commitTransaction();
} catch (Exception e) {
this.hibernateTransactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
}
}
@Override
public final void updateCenter(UserContext userContext, CenterUpdate centerUpdate) throws ApplicationException {
CustomerBO center = customerDao.findCustomerById(centerUpdate.getCustomerId());
center.validateVersion(centerUpdate.getVersionNum());
center.setUserContext(userContext);
if(!centerUpdate.getDisplayName().equals(center.getDisplayName())) {
customerDao.validateCenterNameIsNotTakenForOffice(centerUpdate.getDisplayName(), center.getOfficeId());
}
assembleCustomerPostionsFromDto(centerUpdate.getCustomerPositions(), center);
try {
hibernateTransactionHelper.startTransaction();
hibernateTransactionHelper.beginAuditLoggingFor(center);
center.setDisplayName(centerUpdate.getDisplayName());
updateLoanOfficerAndValidate(centerUpdate.getLoanOfficerId(), center);
center.updateCenterDetails(userContext, centerUpdate);
customerDao.save(center);
hibernateTransactionHelper.commitTransaction();
} catch (ApplicationException e) {
hibernateTransactionHelper.rollbackTransaction();
throw e;
} catch (Exception e) {
hibernateTransactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} finally {
hibernateTransactionHelper.closeSession();
}
}
@Override
public final void updateGroup(UserContext userContext, GroupUpdate groupUpdate) throws ApplicationException {
GroupBO group = customerDao.findGroupBySystemId(groupUpdate.getGlobalCustNum());
group.validateVersion(groupUpdate.getVersionNo());
group.setUserContext(userContext);
assembleCustomerPostionsFromDto(groupUpdate.getCustomerPositions(), group);
try {
hibernateTransactionHelper.startTransaction();
hibernateTransactionHelper.beginAuditLoggingFor(group);
group.updateTrainedDetails(groupUpdate);
group.setExternalId(groupUpdate.getExternalId());
Address address = null;
if (groupUpdate.getAddress() != null) {
address = new Address(groupUpdate.getAddress().getLine1(), groupUpdate.getAddress().getLine2(), groupUpdate.getAddress().getLine3(),
groupUpdate.getAddress().getCity(), groupUpdate.getAddress().getState(), groupUpdate.getAddress().getCountry(),
groupUpdate.getAddress().getZip(), groupUpdate.getAddress().getPhoneNumber());
}
group.updateAddress(address);
if (group.isNameDifferent(groupUpdate.getDisplayName())) {
customerDao.validateGroupNameIsNotTakenForOffice(groupUpdate.getDisplayName(), group.getOffice().getOfficeId());
group.setDisplayName(groupUpdate.getDisplayName());
}
updateLoanOfficerAndValidate(groupUpdate.getLoanOfficerId(), group);
customerDao.save(group);
hibernateTransactionHelper.commitTransaction();
} catch (ApplicationException e) {
hibernateTransactionHelper.rollbackTransaction();
throw e;
} catch (Exception e) {
hibernateTransactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} finally {
hibernateTransactionHelper.closeSession();
}
}
@Override
public final void updateClientPersonalInfo(UserContext userContext, ClientPersonalInfoUpdate personalInfo) throws CustomerException {
ClientBO client = (ClientBO) this.customerDao.findCustomerById(personalInfo.getCustomerId());
client.validateVersion(personalInfo.getOriginalClientVersionNumber());
client.updateDetails(userContext);
LocalDate currentDOB = new LocalDate(client.getDateOfBirth());
LocalDate newDOB = currentDOB;
try {
// updating Date of birth
// doesn''t sound normal but it can be required in certain cases
// see http://mifosforge.jira.com/browse/MIFOS-4368
newDOB = new LocalDate(DateUtils.getDateAsSentFromBrowser(personalInfo.getDateOfBirth()));
} catch (InvalidDateException e) {
throw new MifosRuntimeException(e);
}
if(!currentDOB.isEqual(newDOB)) {
customerDao.validateClientForDuplicateNameOrGovtId(personalInfo.getClientDisplayName(), newDOB.toDateMidnight().toDate(), null);
}
try {
hibernateTransactionHelper.startTransaction();
hibernateTransactionHelper.beginAuditLoggingFor(client);
client.updatePersonalInfo(personalInfo);
clientPhotoService.update(personalInfo.getCustomerId().longValue(), personalInfo.getPicture());
customerDao.save(client);
hibernateTransactionHelper.commitTransaction();
} catch (ApplicationException e) {
hibernateTransactionHelper.rollbackTransaction();
throw new CustomerException(e.getKey(), e);
} catch (Exception e) {
hibernateTransactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} finally {
hibernateTransactionHelper.commitTransaction();
}
}
public void removeFromBlacklist(UserContext userContext, Integer customerId) {
CustomerBO customer = this.customerDao.findCustomerById(customerId);
customer.updateDetails(userContext);
try{
hibernateTransactionHelper.startTransaction();
hibernateTransactionHelper.beginAuditLoggingFor(customer);
customer.unBlacklist();
customerDao.save(customer);
hibernateTransactionHelper.commitTransaction();
}catch (Exception e) {
hibernateTransactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} finally {
hibernateTransactionHelper.closeSession();
}
}
@Override
public void updateClientFamilyInfo(UserContext userContext, ClientFamilyInfoUpdate clientFamilyInfoUpdate)
throws CustomerException {
ClientBO client = (ClientBO) this.customerDao.findCustomerById(clientFamilyInfoUpdate.getCustomerId());
client.validateVersion(clientFamilyInfoUpdate.getOldVersionNum());
client.updateDetails(userContext);
try {
hibernateTransactionHelper.startTransaction();
hibernateTransactionHelper.beginAuditLoggingFor(client);
client.updateFamilyInfo(clientFamilyInfoUpdate);
customerDao.save(client);
hibernateTransactionHelper.commitTransaction();
} catch (Exception e) {
hibernateTransactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} finally {
hibernateTransactionHelper.closeSession();
}
}
@Override
public final void updateClientMfiInfo(UserContext userContext, ClientMfiInfoUpdate clientMfiInfoUpdate) throws CustomerException {
ClientBO client = (ClientBO) this.customerDao.findCustomerById(clientMfiInfoUpdate.getClientId());
client.validateVersion(clientMfiInfoUpdate.getOrginalClientVersionNumber());
client.updateDetails(userContext);
try {
hibernateTransactionHelper.startTransaction();
hibernateTransactionHelper.beginAuditLoggingFor(client);
PersonnelBO personnel = this.personnelDao.findPersonnelById(clientMfiInfoUpdate.getPersonnelId());
client.updateMfiInfo(personnel, clientMfiInfoUpdate);
customerDao.save(client);
hibernateTransactionHelper.commitTransaction();
} catch (CustomerException e) {
hibernateTransactionHelper.rollbackTransaction();
throw e;
} catch (Exception e) {
hibernateTransactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} finally {
hibernateTransactionHelper.closeSession();
}
}
private void updateLoanOfficerAndValidate(Short loanOfficerId, CustomerBO customer) throws CustomerException {
PersonnelBO loanOfficer = personnelDao.findPersonnelById(loanOfficerId);
if (customer.isLoanOfficerChanged(loanOfficer)) {
customer.setLoanOfficer(loanOfficer);
customer.validate();
customerDao.updateLoanOfficersForAllChildrenAndAccounts(loanOfficerId, customer.getSearchId(), customer.getOffice().getOfficeId());
} else {
customer.validate();
}
}
private void assembleCustomerPostionsFromDto(List<CustomerPositionDto> customerPositions, CustomerBO customer) {
for (CustomerPositionDto positionView : customerPositions) {
boolean isPositionFound = false;
for (CustomerPositionEntity positionEntity : customer.getCustomerPositions()) {
if (positionView.getPositionId().equals(positionEntity.getPosition().getId())) {
CustomerBO customerInPosition = null;
if (positionView.getCustomerId() != null) {
customerInPosition = customerDao.findCustomerById(positionView.getCustomerId());
}
positionEntity.setCustomer(customerInPosition);
isPositionFound = true;
break;
}
}
if (!isPositionFound) {
CustomerBO customerInPosition = null;
if (positionView.getCustomerId() != null) {
customerInPosition = customerDao.findCustomerById(positionView.getCustomerId());
}
CustomerPositionEntity customerPosition = new CustomerPositionEntity(new PositionEntity(positionView.getPositionId()), customerInPosition, customer);
customer.addCustomerPosition(customerPosition);
}
}
}
@Override
public final void updateCustomerStatus(UserContext userContext, CustomerStatusUpdate customerStatusUpdate) throws CustomerException {
CustomerBO customer = this.customerDao.findCustomerById(customerStatusUpdate.getCustomerId());
customer.validateVersion(customerStatusUpdate.getVersionNum());
customer.updateDetails(userContext);
checkPermission(customer, userContext, customerStatusUpdate.getNewStatus(), customerStatusUpdate.getCustomerStatusFlag());
Short oldStatusId = customer.getCustomerStatus().getId();
CustomerStatus oldStatus = CustomerStatus.fromInt(oldStatusId);
PersonnelBO loggedInUser = this.personnelDao.findPersonnelById(userContext.getId());
CustomerNoteEntity customerNote = new CustomerNoteEntity(customerStatusUpdate.getNotes(), new Date(), loggedInUser, customer);
if (customer.isGroup()) {
GroupBO group = (GroupBO) customer;
updateGroupStatus(group, oldStatus, customerStatusUpdate.getNewStatus(), customerStatusUpdate.getCustomerStatusFlag(), customerNote);
} else if (customer.isClient()) {
ClientBO client = (ClientBO) customer;
updateClientStatus(client, oldStatus, customerStatusUpdate.getNewStatus(), customerStatusUpdate.getCustomerStatusFlag(), customerNote);
} else {
CenterBO center = (CenterBO) customer;
updateCenterStatus(center, customerStatusUpdate.getNewStatus(), customerStatusUpdate.getCustomerStatusFlag(), customerNote);
}
}
private void checkPermission(CustomerBO customerBO, UserContext userContext, CustomerStatus newStatus,
CustomerStatusFlag statusFlag) throws CustomerException {
Short statusFlagId = null;
if (statusFlag != null) {
statusFlagId = statusFlag.getValue();
}
if (null != customerBO.getPersonnel()) {
this.customerDao.checkPermissionForStatusChange(newStatus.getValue(), userContext, statusFlagId, customerBO.getOfficeId(), customerBO.getPersonnel().getPersonnelId());
} else {
this.customerDao.checkPermissionForStatusChange(newStatus.getValue(), userContext, statusFlagId, customerBO.getOfficeId(), userContext.getId());
}
}
@Override
public final void updateCenterStatus(CenterBO center, CustomerStatus newStatus, CustomerStatusFlag customerStatusFlag,
CustomerNoteEntity customerNote) throws CustomerException {
if (newStatus.isCenterInActive()) {
center.validateChangeToInActive();
List<CustomerDto> clientsThatAreNotClosedOrCanceled = this.customerDao
.findClientsThatAreNotCancelledOrClosed(center.getSearchId(), center.getOffice().getOfficeId());
List<CustomerDto> groupsThatAreNotClosedOrCancelled = this.customerDao
.findGroupsThatAreNotCancelledOrClosed(center.getSearchId(), center.getOffice().getOfficeId());
if (clientsThatAreNotClosedOrCanceled.size() > 0 || groupsThatAreNotClosedOrCancelled.size() > 0) {
final String errorMessage = messageLookupHelper.lookupLabel(ConfigurationConstants.GROUP);
throw new CustomerException(CustomerConstants.ERROR_STATE_CHANGE_EXCEPTION, new Object[] {errorMessage});
}
} else if (newStatus.isCenterActive()) {
center.validateChangeToActive();
center.validateLoanOfficerIsActive();
}
CustomerStatusFlagEntity customerStatusFlagEntity = populateCustomerStatusFlag(customerStatusFlag);
try {
hibernateTransactionHelper.startTransaction();
hibernateTransactionHelper.beginAuditLoggingFor(center);
center.updateCustomerStatus(newStatus, customerNote, customerStatusFlagEntity);
customerDao.save(center);
hibernateTransactionHelper.commitTransaction();
} catch (Exception e) {
this.hibernateTransactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} finally {
this.hibernateTransactionHelper.closeSession();
}
}
@Override
public final void updateGroupStatus(GroupBO group, CustomerStatus oldStatus, CustomerStatus newStatus,
CustomerStatusFlag customerStatusFlag, CustomerNoteEntity customerNote) throws CustomerException {
validateChangeOfStatusForGroup(group, oldStatus, newStatus);
CustomerStatusFlagEntity customerStatusFlagEntity = populateCustomerStatusFlag(customerStatusFlag);
try {
hibernateTransactionHelper.startTransaction();
hibernateTransactionHelper.beginAuditLoggingFor(group);
if (group.isActiveForFirstTime(oldStatus.getValue(), newStatus.getValue())) {
group.setCustomerActivationDate(new DateTime().toDate());
group.updateCustomerHierarchy();
CalendarEvent applicableCalendarEvents = this.holidayDao.findCalendarEventsForThisYearAndNext(group.getOfficeId());
group.regenerateCustomerFeeSchedule(applicableCalendarEvents);
}
Set<CustomerBO> groupChildren = group.getChildren();
if (oldStatus.isGroupPending() && newStatus.isGroupCancelled() && groupChildren != null) {
for (CustomerBO child : groupChildren) {
ClientBO client = (ClientBO) child;
if (client.isPending()) {
client.setUserContext(group.getUserContext());
hibernateTransactionHelper.beginAuditLoggingFor(client);
client.updateCustomerStatus(CustomerStatus.CLIENT_PARTIAL);
customerDao.save(client);
}
}
}
group.updateCustomerStatus(newStatus, customerNote, customerStatusFlagEntity);
customerDao.save(group);
hibernateTransactionHelper.commitTransaction();
} catch (Exception e) {
this.hibernateTransactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} finally {
this.hibernateTransactionHelper.closeSession();
}
}
private CustomerStatusFlagEntity populateCustomerStatusFlag(CustomerStatusFlag customerStatusFlag)
throws CustomerException {
CustomerStatusFlagEntity customerStatusFlagEntity = null;
if (customerStatusFlag != null) {
try {
customerStatusFlagEntity = legacyMasterDao.getPersistentObject(
CustomerStatusFlagEntity.class, customerStatusFlag.getValue());
} catch (PersistenceException e) {
throw new CustomerException(e);
}
}
return customerStatusFlagEntity;
}
private void validateChangeOfStatusForGroup(GroupBO group, CustomerStatus oldStatus, CustomerStatus newStatus)
throws CustomerException {
if (newStatus.isGroupActive()) {
group.validateGroupCanBeActive();
}
if (newStatus.isGroupClosed()) {
group.validateNoActiveAccountExist();
List<CustomerDto> clientsThatAreNotClosedOrCanceled = this.customerDao
.findClientsThatAreNotCancelledOrClosed(group.getSearchId(), group.getOffice().getOfficeId());
if (clientsThatAreNotClosedOrCanceled.size() > 0) {
throw new CustomerException(CustomerConstants.ERROR_STATE_CHANGE_EXCEPTION,
new Object[] { ApplicationContextProvider.getBean(MessageLookup.class).lookupLabel(ConfigurationConstants.CLIENT) });
}
}
if (oldStatus.isGroupCancelled() && newStatus.isGroupPartial()) {
if (group.getParentCustomer() != null && group.getParentCustomer().getCustomerId() != null) {
group.validateTransitionFromCancelledToPartialIsAllowedBasedOnCenter();
} else {
this.officeDao.validateBranchIsActiveWithNoActivePersonnel(group.getOffice().getOfficeId(), group
.getUserContext());
}
}
}
@Override
public final void updateClientStatus(ClientBO client, CustomerStatus oldStatus, CustomerStatus newStatus,
CustomerStatusFlag customerStatusFlag, CustomerNoteEntity customerNote) throws CustomerException {
PersonnelBO loggedInUser = this.personnelDao.findPersonnelById(client.getUserContext().getId());
handeClientChangeOfStatus(client, newStatus);
CustomerStatusFlagEntity customerStatusFlagEntity = populateCustomerStatusFlag(customerStatusFlag);
try {
hibernateTransactionHelper.startTransaction();
hibernateTransactionHelper.beginAuditLoggingFor(client);
client.clearCustomerFlagsIfApplicable(oldStatus, newStatus);
client.updateCustomerStatus(newStatus);
changeStatus(client, oldStatus, newStatus);
if (customerStatusFlagEntity != null) {
client.addCustomerFlag(customerStatusFlagEntity);
}
client.addCustomerNotes(customerNote);
this.handleChangeOfClientStatusToClosedOrCancelled(client, customerStatusFlag, customerNote, loggedInUser);
customerDao.save(client);
hibernateTransactionHelper.commitTransaction();
} catch (ApplicationException e) {
this.hibernateTransactionHelper.rollbackTransaction();
throw new BusinessRuleException(e.getKey(), e);
} catch (Exception e) {
this.hibernateTransactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} finally {
this.hibernateTransactionHelper.closeSession();
}
}
private void handleChangeOfClientStatusToClosedOrCancelled(ClientBO client, CustomerStatusFlag customerStatusFlag,
CustomerNoteEntity customerNote, PersonnelBO loggedInUser) throws AccountException {
if (client.isClosedOrCancelled()) {
if (client.isClientUnderGroup()) {
CustomerBO parentCustomer = client.getParentCustomer();
client.resetPositions(parentCustomer);
parentCustomer.setUserContext(client.getUserContext());
CustomerBO center = parentCustomer.getParentCustomer();
if (center != null) {
parentCustomer.resetPositions(center);
center.setUserContext(client.getUserContext());
}
}
CustomerAccountBO customerAccount = client.getCustomerAccount();
if (customerAccount.isOpen()) {
customerAccount.setUserContext(client.getUserContext());
customerAccount.changeStatus(AccountState.CUSTOMER_ACCOUNT_INACTIVE, customerStatusFlag.getValue(),
customerNote.getComment(), loggedInUser);
customerAccount.update();
}
}
}
private void changeStatus(CustomerBO customer, CustomerStatus oldStatus, CustomerStatus newStatus) throws CustomerException {
Short oldStatusId = oldStatus.getValue();
Short newStatusId = newStatus.getValue();
if (customer.isClient()) {
ClientBO client = (ClientBO) customer;
if (client.isActiveForFirstTime(oldStatusId, newStatusId)) {
if (client.getParentCustomer() != null) {
CustomerHierarchyEntity hierarchy = new CustomerHierarchyEntity(client, client.getParentCustomer());
client.addCustomerHierarchy(hierarchy);
}
CalendarEvent applicableCalendarEvents = holidayDao.findCalendarEventsForThisYearAndNext(customer.getOfficeId());
List<AccountFeesEntity> accountFees = new ArrayList<AccountFeesEntity>(customer.getCustomerAccount().getAccountFees());
client.getCustomerAccount().createSchedulesAndFeeSchedulesForFirstTimeActiveCustomer(customer, accountFees, customer.getCustomerMeetingValue(), applicableCalendarEvents, new DateMidnight().toDateTime());
client.setCustomerActivationDate(new DateTimeService().getCurrentJavaDateTime());
if (client.getOfferingsAssociatedInCreate() != null) {
for (ClientInitialSavingsOfferingEntity clientOffering : client.getOfferingsAssociatedInCreate()) {
try {
SavingsOfferingBO savingsOffering = savingsProductDao.findById(
clientOffering.getSavingsOffering().getPrdOfferingId().intValue());
if (savingsOffering.isActive()) {
List<CustomFieldDto> customerFieldsForSavings = new ArrayList<CustomFieldDto>();
client.addAccount(new SavingsBO(client.getUserContext(), savingsOffering, client,
AccountState.SAVINGS_ACTIVE, savingsOffering.getRecommendedAmount(),
customerFieldsForSavings));
}
} catch (AccountException pe) {
throw new CustomerException(pe);
}
}
}
new SavingsPersistence().persistSavingAccounts(client);
try {
if (client.getParentCustomer() != null) {
List<SavingsBO> savingsList = new CustomerPersistence()
.retrieveSavingsAccountForCustomer(client.getParentCustomer().getCustomerId());
if (client.getParentCustomer().getParentCustomer() != null) {
savingsList.addAll(new CustomerPersistence().retrieveSavingsAccountForCustomer(client
.getParentCustomer().getParentCustomer().getCustomerId()));
}
for (SavingsBO savings : savingsList) {
savings.setUserContext(client.getUserContext());
if (client.getCustomerMeeting().getMeeting() != null) {
if (!(savings.getCustomer().getLevel() == CustomerLevel.GROUP && savings
.getRecommendedAmntUnit().getId().equals(
RecommendedAmountUnit.COMPLETE_GROUP.getValue()))) {
DateTime today = new DateTime().toDateMidnight().toDateTime();
savings.generateDepositAccountActions(client, client.getCustomerMeeting()
.getMeeting(), applicableCalendarEvents.getWorkingDays(), applicableCalendarEvents.getHolidays(), today);
savings.update();
}
}
}
}
} catch (PersistenceException pe) {
throw new CustomerException(pe);
} catch (AccountException ae) {
throw new CustomerException(ae);
}
}
}
}
private void handeClientChangeOfStatus(ClientBO client, CustomerStatus newStatus) throws CustomerException {
if (client.getParentCustomer() != null) {
CustomerStatus groupStatus = client.getParentCustomer().getStatus();
if ((newStatus.isClientActive() || newStatus.isClientPending()) && client.isClientUnderGroup()) {
if (groupStatus.isGroupCancelled()) {
throw new CustomerException(ClientConstants.ERRORS_GROUP_CANCELLED,
new Object[] { ApplicationContextProvider.getBean(MessageLookup.class).lookupLabel(ConfigurationConstants.GROUP) });
}
if (client.isGroupStatusLower(newStatus.getValue(), groupStatus.getValue())) {
throw new CustomerException(ClientConstants.INVALID_CLIENT_STATUS_EXCEPTION);
}
}
}
if (newStatus.isClientClosed()) {
if (client.isAnyLoanAccountOpen() || client.isAnySavingsAccountOpen()) {
throw new CustomerException(CustomerConstants.CUSTOMER_HAS_ACTIVE_ACCOUNTS_EXCEPTION);
}
}
if (newStatus.isClientActive()) {
if (client.getPersonnel() == null || client.getPersonnel().getPersonnelId() == null) {
throw new CustomerException(ClientConstants.CLIENT_LOANOFFICER_NOT_ASSIGNED);
}
if (client.getCustomerMeeting() == null || client.getCustomerMeeting().getMeeting() == null) {
throw new CustomerException(GroupConstants.MEETING_NOT_ASSIGNED);
}
}
}
@Override
public final String transferGroupTo(GroupBO group, CenterBO receivingCenter) throws CustomerException {
group.validateReceivingCenter(receivingCenter);
group.validateNoActiveAccountsExist();
if (group.isDifferentBranch(receivingCenter.getOffice())) {
customerDao.validateGroupNameIsNotTakenForOffice(group.getDisplayName(), receivingCenter.getOfficeId());
}
CustomerBO oldParent = group.getParentCustomer();
try {
hibernateTransactionHelper.startTransaction();
hibernateTransactionHelper.beginAuditLoggingFor(group);
boolean regenerateSchedules = group.transferTo(receivingCenter);
if (oldParent != null) {
oldParent.updateDetails(group.getUserContext());
customerDao.save(oldParent);
}
receivingCenter.updateDetails(group.getUserContext());
customerDao.save(receivingCenter);
group.updateDetails(group.getUserContext());
customerDao.save(group);
Set<CustomerBO> clients = group.getChildren();
for (CustomerBO client : clients) {
client.setUserContext(group.getUserContext());
((ClientBO) client).handleGroupTransfer();
client.setUpdateDetails();
customerDao.save(client);
}
hibernateTransactionHelper.flushSession();
GroupBO groupInitialised = group;
if (regenerateSchedules) {
CalendarEvent calendarEvents = holidayDao.findCalendarEventsForThisYearAndNext(group.getOfficeId());
groupInitialised = customerDao.findGroupBySystemId(group.getGlobalCustNum());
handleChangeInMeetingSchedule(groupInitialised, calendarEvents.getWorkingDays(), calendarEvents.getHolidays());
}
hibernateTransactionHelper.commitTransaction();
return groupInitialised.getGlobalCustNum();
} catch (ApplicationException e) {
this.hibernateTransactionHelper.rollbackTransaction();
throw new BusinessRuleException(e.getKey(), e);
} catch (Exception e) {
this.hibernateTransactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} finally {
this.hibernateTransactionHelper.closeSession();
}
}
@Override
public ClientBO transferClientTo(UserContext userContext, Integer groupId, String clientGlobalCustNum, Integer previousClientVersionNo) throws CustomerException {
ClientBO client = customerDao.findClientBySystemId(clientGlobalCustNum);
client.validateVersion(previousClientVersionNo);
client.validateIsSameGroup(groupId);
client.updateDetails(userContext);
GroupBO receivingGroup = (GroupBO) customerDao.findCustomerById(groupId);
client.validateReceivingGroup(receivingGroup);
client.validateForActiveAccounts();
client.validateForPeriodicFees();
if(client.getOfficeId() != receivingGroup.getOfficeId()){
customerDao.checkPermissionforEditingClientOfficeMembership(client.getUserContext(), client);
}
CustomerBO oldParent = client.getParentCustomer();
try {
hibernateTransactionHelper.startTransaction();
hibernateTransactionHelper.beginAuditLoggingFor(client);
boolean regenerateSchedules = client.transferTo(receivingGroup);
if (oldParent != null) {
client.resetPositions(oldParent);
oldParent.updateDetails(client.getUserContext());
if (oldParent.getParentCustomer() != null) {
CustomerBO center = oldParent.getParentCustomer();
client.resetPositions(center);
center.setUserContext(client.getUserContext());
}
customerDao.save(oldParent);
}
receivingGroup.updateDetails(client.getUserContext());
customerDao.save(receivingGroup);
client.updateDetails(userContext);
customerDao.save(client);
hibernateTransactionHelper.flushAndClearSession();
if (regenerateSchedules) {
client = customerDao.findClientBySystemId(clientGlobalCustNum);
CalendarEvent calendarEvents = holidayDao.findCalendarEventsForThisYearAndNext(client.getOfficeId());
handleChangeInMeetingSchedule(client, calendarEvents.getWorkingDays(), calendarEvents.getHolidays());
}
hibernateTransactionHelper.commitTransaction();
return client;
} catch (ApplicationException e) {
this.hibernateTransactionHelper.rollbackTransaction();
throw new BusinessRuleException(e.getKey(), e);
} catch (Exception e) {
this.hibernateTransactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} finally {
this.hibernateTransactionHelper.closeSession();
}
}
@Override
public void transferClientTo(ClientBO client, OfficeBO receivingBranch) {
try {
this.hibernateTransactionHelper.startTransaction();
this.hibernateTransactionHelper.beginAuditLoggingFor(client);
client.transferToBranch(receivingBranch);
if (client.getParentCustomer() != null) {
CustomerBO parent = client.getParentCustomer();
parent.incrementChildCount();
}
this.hibernateTransactionHelper.flushSession();
client.generateSearchId();
this.customerDao.save(client);
if (client.getParentCustomer() != null) {
this.customerDao.save(client.getParentCustomer());
}
this.hibernateTransactionHelper.commitTransaction();
} catch (ApplicationException e) {
this.hibernateTransactionHelper.rollbackTransaction();
throw new BusinessRuleException(e.getKey(), e);
} catch (Exception e) {
this.hibernateTransactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} finally {
this.hibernateTransactionHelper.closeSession();
}
}
@Override
public final String transferGroupTo(GroupBO group, OfficeBO transferToOffice) throws CustomerException {
group.validateNewOffice(transferToOffice);
group.validateNoActiveAccountsExist();
customerDao.validateGroupNameIsNotTakenForOffice(group.getDisplayName(), transferToOffice.getOfficeId());
try {
hibernateTransactionHelper.startTransaction();
hibernateTransactionHelper.beginAuditLoggingFor(group);
group.makeCustomerMovementEntries(transferToOffice);
group.setPersonnel(null);
if (group.isActive()) {
group.setCustomerStatus(new CustomerStatusEntity(CustomerStatus.GROUP_HOLD));
}
CustomerBO oldParentOfGroup = group.getParentCustomer();
if (oldParentOfGroup != null) {
oldParentOfGroup.incrementChildCount();
customerDao.save(oldParentOfGroup);
}
this.hibernateTransactionHelper.flushSession();
group.generateSearchId();
group.setUpdateDetails();
customerDao.save(group);
Set<CustomerBO> clients = group.getChildren();
if (clients != null) {
for (CustomerBO client : clients) {
client.setUserContext(group.getUserContext());
((ClientBO) client).handleGroupTransfer();
client.setUpdateDetails();
customerDao.save(client);
}
}
hibernateTransactionHelper.commitTransaction();
return group.getGlobalCustNum();
} catch (Exception e) {
hibernateTransactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} finally {
hibernateTransactionHelper.closeSession();
}
}
@Override
public void updateCustomerMeetingSchedule(MeetingBO updatedMeeting, CustomerBO customer) {
try {
customer.validateIsTopOfHierarchy();
customerDao.checkPermissionForEditMeetingSchedule(updatedMeeting.getUserContext(), customer);
CalendarEvent calendarEvents = holidayDao.findCalendarEventsForThisYearAndNext(customer.getOfficeId());
this.hibernateTransactionHelper.startTransaction();
boolean scheduleUpdateRequired = false;
CustomerMeetingEntity meetingEntity = customer.getCustomerMeeting();
if (meetingEntity != null) {
MeetingBO meeting = customer.getCustomerMeetingValue();
scheduleUpdateRequired = updateMeeting(meeting, updatedMeeting);
} else {
CustomerMeetingEntity newMeetingEntity = customer.createCustomerMeeting(updatedMeeting);
customer.setCustomerMeeting(newMeetingEntity);
}
customerDao.save(customer);
if (scheduleUpdateRequired) {
handleChangeInMeetingSchedule(customer, calendarEvents.getWorkingDays(), calendarEvents.getHolidays());
}
this.hibernateTransactionHelper.commitTransaction();
} catch (CustomerException e) {
this.hibernateTransactionHelper.rollbackTransaction();
throw new BusinessRuleException(e.getKey(), e);
} catch (AccountException e) {
this.hibernateTransactionHelper.rollbackTransaction();
throw new BusinessRuleException(e.getKey(), e);
} finally {
this.hibernateTransactionHelper.closeSession();
}
}
private boolean updateMeeting(final MeetingBO oldMeeting, final MeetingBO updatedDetails) throws CustomerException {
boolean isRegenerationOfSchedulesRequired = false;
try {
if (oldMeeting.isWeekly()) {
oldMeeting.setMeetingStartDate(updatedDetails.getMeetingStartDate());
WeekDay dayOfWeek = updatedDetails.getMeetingDetails().getWeekDay();
isRegenerationOfSchedulesRequired = oldMeeting.isDayOfWeekDifferent(dayOfWeek);
oldMeeting.update(dayOfWeek.getValue(), updatedDetails.getMeetingPlace());
oldMeeting.update(dayOfWeek, updatedDetails.getMeetingPlace());
} else if (oldMeeting.isDaily()) {
isRegenerationOfSchedulesRequired = false;
oldMeeting.update(updatedDetails.getMeetingPlace());
} else if (oldMeeting.isMonthlyOnDate()) {
isRegenerationOfSchedulesRequired = oldMeeting.isDayOfMonthDifferent(updatedDetails.getMeetingDetails().getDayNumber());
oldMeeting.update(updatedDetails.getMeetingDetails().getDayNumber(), updatedDetails.getMeetingPlace());
} else if (oldMeeting.isMonthly()) {
RankOfDay rankOfday = updatedDetails.getMeetingDetails().getWeekRank();
// WeekDay weekOfMonth = WeekDay.getWeekDay(updatedDetails.getMonthWeek());
WeekDay weekOfMonth = updatedDetails.getMeetingDetails().getWeekDay();
isRegenerationOfSchedulesRequired = oldMeeting.isWeekOfMonthDifferent(rankOfday, weekOfMonth);
oldMeeting.update(weekOfMonth, rankOfday, updatedDetails.getMeetingPlace());
}
} catch (MeetingException me) {
throw new CustomerException(me);
}
return isRegenerationOfSchedulesRequired;
}
private void handleChangeInMeetingSchedule(CustomerBO customer, final List<Days> workingDays, final List<Holiday> orderedUpcomingHolidays) throws AccountException {
boolean lsimEnabled = this.configurationHelper.isLoanScheduleRepaymentIndependentOfCustomerMeetingEnabled();
Set<AccountBO> accounts = customer.getAccounts();
for (AccountBO account : accounts) {
if (account instanceof LoanBO && lsimEnabled) {
// do not change schedules when LSIm is on for loan accounts
} else {
account.handleChangeInMeetingSchedule(workingDays, orderedUpcomingHolidays, customer.isTopOfHierarchy());
customerDao.save(account);
}
}
for (CustomerBO child : customer.getChildren()) {
handleChangeInMeetingSchedule(child, workingDays, orderedUpcomingHolidays);
}
}
@Override
public void removeGroupMembership(ClientBO client, PersonnelBO loanOfficer, CustomerNoteEntity accountNotesEntity, Short localeId) {
if (client.hasActiveLoanAccounts()) {
throw new BusinessRuleException(CustomerConstants.CLIENT_HAS_ACTIVE_ACCOUNTS_EXCEPTION);
}
if (client.getParentCustomer() != null) {
boolean glimEnabled = getConfigurationPersistence().isGlimEnabled();
if (glimEnabled) {
if (customerIsMemberOfAnyExistingGlimLoanAccount(client, client.getParentCustomer())) {
throw new BusinessRuleException(CustomerConstants.GROUP_HAS_ACTIVE_ACCOUNTS_EXCEPTION);
}
} else if (client.getParentCustomer().hasActiveLoanAccounts()) {
// not glim - then disallow removing client from group with active account
throw new BusinessRuleException(CustomerConstants.GROUP_HAS_ACTIVE_ACCOUNTS_EXCEPTION);
}
//Recalculate Group Savings schedules
for(SavingsBO savingAccount : client.getParentCustomer().getOpenSavingAccounts()){
if(savingAccount.isMandatory() && savingAccount.isGroupModelWithIndividualAccountability()) {
//Get all schedule accounts and set their deposits to zero
this.savingsServiceFacade.updateCustomerSchedules(savingAccount.getAccountId(), client.getCustomerId());
}
}
}
try {
this.hibernateTransactionHelper.startTransaction();
this.hibernateTransactionHelper.beginAuditLoggingFor(client);
client.addCustomerNotes(accountNotesEntity);
client.resetPositions(client.getParentCustomer());
client.getParentCustomer().updateDetails(client.getUserContext());
this.customerDao.save(client.getParentCustomer());
this.hibernateTransactionHelper.flushSession();
client.setPersonnel(loanOfficer);
client.setParentCustomer(null);
client.removeGroupMembership();
client.generateSearchId();
this.customerDao.save(client);
this.hibernateTransactionHelper.commitTransaction();
} catch (Exception e) {
this.hibernateTransactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} finally {
this.hibernateTransactionHelper.closeSession();
}
}
private boolean customerIsMemberOfAnyExistingGlimLoanAccount(CustomerBO customerToRemoveFromGroup, CustomerBO customerWithActiveAccounts) {
List<AccountBO> activeLoanAccounts = customerDao.findGLIMLoanAccountsApplicableTo(customerToRemoveFromGroup.getCustomerId(), customerWithActiveAccounts.getCustomerId());
for (int i = activeLoanAccounts.size() -1 ; i >=0; i--) {
AccountBO glim = activeLoanAccounts.get(i);
if (glim.getAccountState().isLoanClosedObligationsMet() ||
glim.getAccountState().isLoanClosedWrittenOff() ||
glim.getAccountState().isLoanClosedReschedule() ||
glim.getAccountState().isLoanCanceled()) {
activeLoanAccounts.remove(i);
}
}
return !activeLoanAccounts.isEmpty();
}
public void setConfigurationHelper(MifosConfigurationHelper configurationHelper) {
this.configurationHelper = configurationHelper;
}
}