/* * 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; import static org.apache.commons.lang.math.NumberUtils.SHORT_ZERO; import static org.mifos.framework.util.helpers.MoneyUtils.zero; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.joda.time.DateTime; import org.mifos.accounts.business.AccountBO; import org.mifos.accounts.exceptions.AccountException; import org.mifos.accounts.fees.business.FeeDto; import org.mifos.accounts.loan.business.LoanBO; import org.mifos.accounts.productdefinition.business.LoanOfferingBO; import org.mifos.accounts.productdefinition.business.PrdOfferingBO; import org.mifos.accounts.savings.business.SavingsBO; import org.mifos.accounts.util.helpers.AccountState; import org.mifos.accounts.util.helpers.AccountTypes; import org.mifos.application.admin.servicefacade.InvalidDateException; import org.mifos.application.master.business.MifosCurrency; import org.mifos.application.meeting.business.MeetingBO; import org.mifos.application.meeting.exceptions.MeetingException; import org.mifos.application.servicefacade.ApplicationContextProvider; import org.mifos.application.util.helpers.YesNoFlag; import org.mifos.calendar.CalendarUtils; import org.mifos.config.AccountingRules; import org.mifos.customers.api.CustomerLevel; import org.mifos.customers.client.business.ClientBO; import org.mifos.customers.client.business.ClientDetailEntity; import org.mifos.customers.client.business.ClientNameDetailEntity; import org.mifos.customers.client.business.ClientPerformanceHistoryEntity; 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.business.GroupPerformanceHistoryEntity; import org.mifos.customers.group.util.helpers.GroupConstants; import org.mifos.customers.office.business.OfficeBO; 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.LegacyPersonnelDao; import org.mifos.customers.util.helpers.ChildrenStateType; import org.mifos.customers.util.helpers.CustomerConstants; import org.mifos.customers.util.helpers.CustomerStatus; import org.mifos.dto.domain.AddressDto; import org.mifos.dto.domain.CenterUpdate; import org.mifos.dto.domain.CustomFieldDto; import org.mifos.dto.domain.CustomerDetailDto; import org.mifos.dto.domain.CustomerDto; import org.mifos.framework.business.AbstractBusinessObject; import org.mifos.framework.business.util.Address; import org.mifos.framework.exceptions.ApplicationException; import org.mifos.framework.exceptions.PersistenceException; import org.mifos.framework.exceptions.SystemException; import org.mifos.framework.util.DateTimeService; import org.mifos.framework.util.helpers.ChapterNum; import org.mifos.framework.util.helpers.Constants; import org.mifos.framework.util.helpers.Money; import org.mifos.security.util.UserContext; /** * A class that represents a customer entity after being created. */ public abstract class CustomerBO extends AbstractBusinessObject { private Integer customerId; private String globalCustNum; private String externalId; private String searchId; //business meta data private final Set<ClientNameDetailEntity> nameDetailSet; private String displayName; private String displayAddress; private Short trained = 0; private Date trainedDate; private Date mfiJoiningDate; private Set<CustomerCustomFieldEntity> customFields; private Set<CustomerFlagDetailEntity> customerFlags; private Integer maxChildCount = 0; private Date customerActivationDate; private CustomerStatusEntity customerStatus; private Set<CustomerPositionEntity> customerPositions; private CustomerAddressDetailEntity customerAddressDetail; private Set<CustomerNoteEntity> customerNotes; private ClientDetailEntity customerDetail; //business attributes private CustomerBO parentCustomer; private Set<AccountBO> accounts; private final CustomerLevelEntity customerLevel; private CustomerMeetingEntity customerMeeting; private Set<CustomerHierarchyEntity> customerHierarchies; private Set<CustomerMovementEntity> customerMovements; private CustomerHistoricalDataEntity historicalData; public Short blackListed = YesNoFlag.NO.getValue(); private Set<CustomerBO> children; private CustomerAccountBO customerAccount; private CustomerPersistence customerPersistence = null; private LegacyPersonnelDao personnelPersistence = null; private CustomerDao customerDao = null; //associations private PersonnelBO personnel; private final PersonnelBO formedByPersonnel; private OfficeBO office; /** * default constructor for hibernate */ protected CustomerBO() { this(null, null, null, null, null); } /** * @deprecated - use minimal legal constructor or static factory methods of subclasses in builder */ @Deprecated protected CustomerBO(final Integer customerId, final CustomerLevelEntity customerLevel, final PersonnelBO formedByPersonnel, final PersonnelBO personnel, final String displayName) { super(); this.nameDetailSet = new HashSet<ClientNameDetailEntity>(); this.customerId = customerId; this.customerLevel = customerLevel; this.formedByPersonnel = formedByPersonnel; this.personnel = personnel; this.displayName = displayName; } /** * minimal legal constructor for center, groups and clients. */ public CustomerBO(UserContext userContext, String customerName, CustomerLevel customerLevel, CustomerStatus customerStatus, DateTime mfiJoiningDate, OfficeBO office, MeetingBO meeting, PersonnelBO loanOfficer, PersonnelBO formedBy) { super(userContext); this.nameDetailSet = new HashSet<ClientNameDetailEntity>(); this.customerId = null; this.customerHierarchies = new HashSet<CustomerHierarchyEntity>(); this.customerMovements = new HashSet<CustomerMovementEntity>(); this.customerPositions = new HashSet<CustomerPositionEntity>(); this.customFields = new HashSet<CustomerCustomFieldEntity>(); this.accounts = new HashSet<AccountBO>(); this.children = new HashSet<CustomerBO>(); this.customerFlags = new HashSet<CustomerFlagDetailEntity>(); this.customerNotes = new HashSet<CustomerNoteEntity>(); if (mfiJoiningDate != null) { this.mfiJoiningDate = mfiJoiningDate.toDate(); } this.displayName = customerName; this.office = office; this.personnel = loanOfficer; this.formedByPersonnel = formedBy; this.customerLevel = new CustomerLevelEntity(customerLevel); if (meeting != null) { this.customerMeeting = new CustomerMeetingEntity(this, meeting); } this.customerStatus = new CustomerStatusEntity(customerStatus); this.setCreateDetails(); } /** * @deprecated use constructor above */ @Deprecated protected CustomerBO(final UserContext userContext, final String displayName, final CustomerLevel customerLevel, final CustomerStatus customerStatus, final String externalId, final Date mfiJoiningDate, final Address address, final List<CustomFieldDto> customFields, final List<FeeDto> fees, final PersonnelBO formedBy, final OfficeBO office, final CustomerBO parentCustomer, final MeetingBO meeting, final PersonnelBO loanOfficer) throws CustomerException { super(userContext); this.nameDetailSet = new HashSet<ClientNameDetailEntity>(); customerHierarchies = new HashSet<CustomerHierarchyEntity>(); customerMovements = new HashSet<CustomerMovementEntity>(); customerPositions = new HashSet<CustomerPositionEntity>(); validateFields(displayName, customerStatus); this.customFields = new HashSet<CustomerCustomFieldEntity>(); this.accounts = new HashSet<AccountBO>(); this.customerNotes = new HashSet<CustomerNoteEntity>(); this.customerPositions = new HashSet<CustomerPositionEntity>(); this.externalId = externalId; this.mfiJoiningDate = mfiJoiningDate; this.displayName = displayName; this.customerLevel = new CustomerLevelEntity(customerLevel); createAddress(address); if (parentCustomer != null) { inheritDetailsFromParent(parentCustomer); } else { personnel = loanOfficer; customerMeeting = createCustomerMeeting(meeting); this.office = office; } formedByPersonnel = formedBy; setParentCustomer(parentCustomer); createCustomFields(customFields); this.customerStatus = new CustomerStatusEntity(customerStatus); this.maxChildCount = 0; this.blackListed = YesNoFlag.NO.getValue(); this.customerId = null; this.historicalData = null; this.customerFlags = new HashSet<CustomerFlagDetailEntity>(); this.customerAccount = createCustomerAccount(fees); this.addAccount(createCustomerAccount(fees)); this.setCreateDetails(); } private CustomerDao getCustomerDao() { if (customerDao == null) { customerDao = ApplicationContextProvider.getBean(CustomerDao.class); } return customerDao; } public void setCustomerDao(CustomerDao customerDao) { this.customerDao = customerDao; } public Integer getCustomerId() { return customerId; } public Set<ClientNameDetailEntity> getNameDetailSet() { return nameDetailSet; } /** * Most callers will want to call {@link #getLevel()} instead. */ public CustomerLevelEntity getCustomerLevel() { return this.customerLevel; } public String getGlobalCustNum() { return this.globalCustNum; } public void setGlobalCustNum(String globalCustNum) { this.globalCustNum = globalCustNum; } public PersonnelBO getPersonnel() { return this.personnel; } public void setPersonnel(final PersonnelBO personnel) { this.personnel = personnel; } public String getDisplayName() { return this.displayName; } public void setDisplayName(final String displayName) { this.displayName = displayName; } /** * Most callers will instead want an enum - call {@link #getStatus()} for that. */ public CustomerStatusEntity getCustomerStatus() { return customerStatus; } public void setCustomerStatus(final CustomerStatusEntity customerStatus) { this.customerStatus = customerStatus; } public void updateCustomerStatus(CustomerStatus newCustomerStatus) { this.customerStatus = new CustomerStatusEntity(newCustomerStatus); } public void updateCustomerStatus(CustomerStatus newStatus, CustomerNoteEntity customerNote, CustomerStatusFlagEntity customerStatusFlagEntity) { this.customerStatus = new CustomerStatusEntity(newStatus); addCustomerNotes(customerNote); if (customerStatusFlagEntity != null) { addCustomerFlag(customerStatusFlagEntity); } } public String getDisplayAddress() { return displayAddress; } public void setDisplayAddress(final String displayAddress) { this.displayAddress = displayAddress; } public String getExternalId() { return this.externalId; } public void setExternalId(final String externalId) { this.externalId = externalId; } protected void setTrained(final Short trained) { this.trained = trained; } public Date getTrainedDate() { return this.trainedDate; } public void setTrainedDate(final Date trainedDate) { this.trainedDate = trainedDate; } public String getSearchId() { return this.searchId; } public void setSearchId(final String searchId) { this.searchId = searchId; } public Integer getMaxChildCount() { return this.maxChildCount; } public Date getMfiJoiningDate() { return mfiJoiningDate; } public void setMfiJoiningDate(final Date mfiJoiningDate) { this.mfiJoiningDate = mfiJoiningDate; } public Date getCustomerActivationDate() { return customerActivationDate; } public void setCustomerActivationDate(final Date customerActivationDate) { this.customerActivationDate = customerActivationDate; } public void setParentCustomer(final CustomerBO parentCustomer) { this.parentCustomer = parentCustomer; if (parentCustomer != null) { parentCustomer.incrementChildCount(); } } public CustomerBO getParentCustomer() { return parentCustomer; } /* * This only appears to be used in tests */ public final void addChild(final CustomerBO existingClient) { this.children.add(existingClient); } public Set<CustomerBO> getChildren() { if (children == null) { return new HashSet<CustomerBO>(); } return children; } public CustomerAddressDetailEntity getCustomerAddressDetail() { return customerAddressDetail; } public void setCustomerAddressDetail(final CustomerAddressDetailEntity customerAddressDetail) { this.customerAddressDetail = customerAddressDetail; } public OfficeBO getOffice() { return office; } protected void setOffice(final OfficeBO office) { this.office = office; } public CustomerMeetingEntity getCustomerMeeting() { return customerMeeting; } public MeetingBO getCustomerMeetingValue() { if (customerMeeting == null) { return null; } return customerMeeting.getMeeting(); } public void setMeeting(final MeetingBO meeting) { this.customerMeeting = new CustomerMeetingEntity(this, meeting); } public void setCustomerMeeting(final CustomerMeetingEntity customerMeeting) { this.customerMeeting = customerMeeting; } public Set<AccountBO> getAccounts() { if (accounts == null) { return new HashSet<AccountBO>(); } return accounts; } public Set<CustomerCustomFieldEntity> getCustomFields() { return customFields; } public Set<CustomerPositionEntity> getCustomerPositions() { return customerPositions; } public Set<CustomerFlagDetailEntity> getCustomerFlags() { return customerFlags; } public Set<CustomerNoteEntity> getCustomerNotes() { return customerNotes; } public ClientDetailEntity getCustomerDetail() { return customerDetail; } public void setCustomerDetail(ClientDetailEntity customerDetail) { this.customerDetail = customerDetail; } public PersonnelBO getCustomerFormedByPersonnel() { return formedByPersonnel; } public void setTrained(final boolean trained) { this.trained = (short) (trained ? 1 : 0); } public void addCustomerHierarchy(final CustomerHierarchyEntity hierarchy) { if (hierarchy != null) { this.customerHierarchies.add(hierarchy); } } public void addCustomerPosition(final CustomerPositionEntity customerPosition) { this.customerPositions.add(customerPosition); } protected void addCustomerMovement(final CustomerMovementEntity customerMovement) { if (customerMovement != null) { this.customerMovements.add(customerMovement); } } public void addCustomField(final CustomerCustomFieldEntity customField) { if (customField != null) { this.customFields.add(customField); } } public void addCustomerNotes(final CustomerNoteEntity customerNote) { this.customerNotes.add(customerNote); } public void addCustomerFlag(final CustomerStatusFlagEntity customerStatusFlagEntity) { CustomerFlagDetailEntity customerFlag = new CustomerFlagDetailEntity(this, customerStatusFlagEntity, this .getUserContext().getId(), new DateTimeService().getCurrentJavaDateTime()); this.customerFlags.add(customerFlag); if (customerStatusFlagEntity.isBlackListed()) { this.blacklist(); } } public boolean isTrained() { return trained.equals(YesNoFlag.YES.getValue()); } public boolean isBlackListed() { return blackListed != null && blackListed.equals(YesNoFlag.YES.getValue()); } public Address getAddress() { return customerAddressDetail != null ? customerAddressDetail.getAddress() : null; } public CustomerStatus getStatus() { return CustomerStatus.fromInt(customerStatus.getId()); } public void generateGlobalCustomerNumber() throws CustomerException { globalCustNum = generateSystemId(); CustomerAccountBO customerAccount = getCustomerAccount(); if (customerAccount != null) { customerAccount.generateCustomerAccountSystemId(); } } /** * @deprecated - use {@link CustomerDao}. */ @Deprecated public void update() throws CustomerException { try { setUpdateDetails(); getCustomerPersistence().createOrUpdate(this); } catch (PersistenceException e) { throw new CustomerException(CustomerConstants.UPDATE_FAILED_EXCEPTION, e); } } public void updateAddress(final Address address) { if (address == null) { if (this.customerAddressDetail != null) { this.customerAddressDetail.setAddress(null); this.customerAddressDetail = null; } } else if (getCustomerAddressDetail() == null) { setCustomerAddressDetail(new CustomerAddressDetailEntity(this, address)); } else { getCustomerAddressDetail().setAddress(address); } } public CustomerAccountBO getCustomerAccount() { CustomerAccountBO customerAccount = null; for (AccountBO account : accounts) { if (account.getType() == AccountTypes.CUSTOMER_ACCOUNT) { customerAccount = (CustomerAccountBO) account; } } return customerAccount; } public List<CustomerNoteEntity> getRecentCustomerNotes() { List<CustomerNoteEntity> notes = new ArrayList<CustomerNoteEntity>(); int count = 0; for (CustomerNoteEntity customerNote : getCustomerNotes()) { if (count > 2) { break; } notes.add(customerNote); count++; } return notes; } public CustomerHierarchyEntity getActiveCustomerHierarchy() { CustomerHierarchyEntity hierarchy = null; for (CustomerHierarchyEntity customerHierarchyEntity : customerHierarchies) { if (customerHierarchyEntity.isActive()) { hierarchy = customerHierarchyEntity; break; } } return hierarchy; } public CustomerMovementEntity getActiveCustomerMovement() { CustomerMovementEntity movement = null; for (CustomerMovementEntity customerMovementEntity : customerMovements) { if (customerMovementEntity.isActive()) { movement = customerMovementEntity; break; } } return movement; } public void updateHistoricalData(final CustomerHistoricalDataEntity historicalData) { if (historicalData != null) { mfiJoiningDate = historicalData.getMfiJoiningDate(); } this.historicalData = historicalData; } public CustomerHistoricalDataEntity getHistoricalData() { if (historicalData != null) { historicalData.setMfiJoiningDate(mfiJoiningDate); } return historicalData; } /** * @deprecated - use {@link CustomerDao} */ @Deprecated public List<CustomerBO> getChildren(final CustomerLevel customerLevel, final ChildrenStateType stateType) throws CustomerException { try { return getCustomerPersistence().getChildren(getSearchId(), getOffice().getOfficeId(), customerLevel, stateType); } catch (PersistenceException pe) { throw new CustomerException(pe); } } public void adjustPmnt(final String adjustmentComment, final PersonnelBO loggedInUser) throws ApplicationException, SystemException { getCustomerAccount().adjustPmnt(adjustmentComment, loggedInUser); } public abstract boolean isActive(); /** * Is this customer active (but based on level rather than discriminator columm)? (Is there any way this will ever * be different from {@link #isActive()}? I suspect not, but I'm not sure). */ public boolean isActiveViaLevel() { return getCustomerLevel().isGroup() && getStatus() == CustomerStatus.GROUP_ACTIVE || getCustomerLevel().isClient() && getStatus() == CustomerStatus.CLIENT_ACTIVE || getCustomerLevel().isCenter() && getStatus() == CustomerStatus.CENTER_ACTIVE; } public Money getBalanceForAccountsAtRiskForTask(MifosCurrency currency) { Money amount = new Money(currency); for (AccountBO account : getAccounts()) { if (account.getType() == AccountTypes.LOAN_ACCOUNT && ((LoanBO) account).isAccountActive()) { LoanBO loan = (LoanBO) account; if (loan.getAccountState().getId().equals(AccountState.LOAN_ACTIVE_IN_BAD_STANDING.getValue())) { amount = amount.add(loan.getRemainingPrincipalAmount()); } } } return amount; } public Money getOutstandingLoanAmount(MifosCurrency currency) { Money amount = new Money(currency); Set<AccountBO> accounts = getAccounts(); if (accounts != null) { for (AccountBO account : getAccounts()) { if (account.getType() == AccountTypes.LOAN_ACCOUNT && ((LoanBO) account).isAccountActive()) { Money remainingPrincipalAmount = ((LoanBO) account).getRemainingPrincipalAmount(); if (amount.getAmount().equals(BigDecimal.ZERO)) { amount = remainingPrincipalAmount; } else { amount = amount.add(remainingPrincipalAmount); } } } } return amount; } public Integer getActiveLoanCounts() { Integer countOfActiveLoans = 0; for (AccountBO account : getAccounts()) { if (account.getType() == AccountTypes.LOAN_ACCOUNT && ((LoanBO) account).isAccountActive()) { countOfActiveLoans++; } } return countOfActiveLoans; } /* * This method is unused and is a candidate for removal. */ @Deprecated public BigDecimal getDelinquentPortfolioAmount(MifosCurrency currency) { Money amountOverDue = new Money(currency); Money totalOutStandingAmount = new Money(currency); for (AccountBO accountBO : getAccounts()) { if (accountBO.getType() == AccountTypes.LOAN_ACCOUNT && ((LoanBO) accountBO).isAccountActive()) { amountOverDue = amountOverDue.add(((LoanBO) accountBO).getTotalPrincipalAmountInArrears()); totalOutStandingAmount = totalOutStandingAmount.add(((LoanBO) accountBO).getLoanSummary() .getOriginalPrincipal()); } } if (totalOutStandingAmount.isNonZero()) { return amountOverDue.divide(totalOutStandingAmount); } return BigDecimal.ZERO; } public CustomerPerformanceHistory getPerformanceHistory() { if (this instanceof ClientBO) { return ((ClientBO) this).getClientPerformanceHistory(); } else if (this instanceof GroupBO) { return ((GroupBO) this).getGroupPerformanceHistory(); } else { return null; } } public Money getSavingsBalance(MifosCurrency currency) { Money amount = new Money(currency); for (AccountBO account : getAccounts()) { if (account.getType() == AccountTypes.SAVINGS_ACCOUNT) { SavingsBO savingsBO = (SavingsBO) account; amount = amount.add(savingsBO.getSavingsBalance()); } } return amount; } public Money getLoanBalance(MifosCurrency currency){ Money amount = new Money(currency); for (AccountBO account : getAccounts()) { if (account.isActiveLoanAccount()) { LoanBO loanBO = (LoanBO) account; amount = amount.add(loanBO.getLoanSummary().getTotalAmntDue()); } } return amount; } public List<LoanBO> getOpenLoanAccounts() { List<LoanBO> loanAccounts = new ArrayList<LoanBO>(); for (AccountBO account : getAccounts()) { if (account.isLoanAccount() && account.isOpen()) { loanAccounts.add((LoanBO) account); } } return loanAccounts; } //method returns also new kind of group loan account //for group details public List<LoanBO> getOpenLoanAccountsAndGroupLoans() { List<LoanBO> loanAccounts = new ArrayList<LoanBO>(); for (AccountBO account : getAccounts()) { if ((account.isLoanAccount() || account.isGroupLoanAccount()) && account.isOpen()) { loanAccounts.add((LoanBO) account); } } return loanAccounts; } public List<LoanBO> getOpenIndividualLoanAccounts() { List<LoanBO> loanAccounts = new ArrayList<LoanBO>(); for (AccountBO account : getAccounts()) { if (account.isOfType(AccountTypes.INDIVIDUAL_LOAN_ACCOUNT) && account.isOpen()) { loanAccounts.add((LoanBO) account); } } return loanAccounts; } public List<LoanBO> getOpenGroupLoanAccounts() { List<LoanBO> loanAccounts = new ArrayList<LoanBO>(); for (AccountBO account : getAccounts()) { if (account.isOfType(AccountTypes.GROUP_LOAN_ACCOUNT) && account.isOpen()) { loanAccounts.add((LoanBO) account); } } return loanAccounts; } public List<SavingsBO> getOpenSavingAccounts() { List<SavingsBO> savingAccounts = new ArrayList<SavingsBO>(); for (AccountBO account : getAccounts()) { if (account.isSavingsAccount() && account.isOpen()) { savingAccounts.add((SavingsBO) account); } } return savingAccounts; } public boolean isAnyLoanAccountOpen() { for (AccountBO account : getAccounts()) { if ((account.isLoanAccount() || account.isGroupLoanAccount()) && account.isOpen()) { return true; } } return false; } public boolean isAnySavingsAccountOpen() { for (AccountBO account : getAccounts()) { if (account.isSavingsAccount() && account.isOpen()) { return true; } } return false; } void resetPositionsAssignedToClient(final Integer clientId) { if (getCustomerPositions() != null) { for (CustomerPositionEntity position : getCustomerPositions()) { if (position.getCustomer() != null && position.getCustomer().getCustomerId().equals(clientId)) { position.setCustomer(null); } } } } public void incrementChildCount() { this.maxChildCount = this.getMaxChildCount().intValue() + 1; } public CustomerLevel getLevel() { return CustomerLevel.getLevel(getCustomerLevel().getId()); } protected void validateMeetingEntity(final CustomerMeetingEntity meeting) throws CustomerException { if (meeting == null) { throw new CustomerException(CustomerConstants.INVALID_MEETING); } validateMeeting(meeting.getMeeting()); } protected void validateMeeting(final MeetingBO meeting) throws CustomerException { if (meeting == null) { throw new CustomerException(CustomerConstants.INVALID_MEETING); } } public final void validateOffice() throws CustomerException { if (office == null) { throw new CustomerException(CustomerConstants.INVALID_OFFICE); } } @Deprecated protected void validateOffice(final OfficeBO office) throws CustomerException { if (office == null) { throw new CustomerException(CustomerConstants.INVALID_OFFICE); } } public final void validateLoanOfficer() throws CustomerException { if (this.personnel == null) { throw new CustomerException(CustomerConstants.ERRORS_SELECT_LOAN_OFFICER); } } @Deprecated public final void validateLO(final PersonnelBO loanOfficer) throws CustomerException { if (loanOfficer == null || loanOfficer.getPersonnelId() == null) { throw new CustomerException(CustomerConstants.INVALID_LOAN_OFFICER); } } @Deprecated public final void validateLO(final Short loanOfficerId) throws CustomerException { if (loanOfficerId == null) { throw new CustomerException(CustomerConstants.INVALID_LOAN_OFFICER); } } public CustomerMeetingEntity createCustomerMeeting(final MeetingBO meeting) { return meeting != null ? new CustomerMeetingEntity(this, meeting) : null; } public abstract boolean isActiveForFirstTime(Short oldStatus, Short newStatusId); public boolean checkStatusChangeCancelToPartial(final CustomerStatus oldStatus, final CustomerStatus newStatus) { if ((oldStatus.equals(CustomerStatus.GROUP_CANCELLED) || oldStatus.equals(CustomerStatus.CLIENT_CANCELLED)) && (newStatus.equals(CustomerStatus.GROUP_PARTIAL) || newStatus.equals(CustomerStatus.CLIENT_PARTIAL))) { return true; } return false; } public final boolean isSameBranch(final OfficeBO officeObj) { return this.office.getGlobalOfficeNum().equals(officeObj.getGlobalOfficeNum()); } public boolean isDifferentBranch(final OfficeBO otherOffice) { return !isSameBranch(otherOffice); } public void checkIfClientIsATitleHolder() throws CustomerException { if (getParentCustomer() != null) { for (CustomerPositionEntity position : getParentCustomer().getCustomerPositions()) { if (position.getCustomer() != null && position.getCustomer().getCustomerId().intValue() == this.getCustomerId().intValue()) { // position.getPosition().getId().shortValue()==new // Short("1").shortValue()) throw new CustomerException(CustomerConstants.CLIENT_IS_A_TITLE_HOLDER_EXCEPTION); } } } } public void setLoanOfficer(PersonnelBO loanOfficer) { this.personnel = loanOfficer; } public boolean isLoanOfficerChanged(PersonnelBO oldLoanOfficer) { if (oldLoanOfficer == null && this.personnel == null) { return false; } if (oldLoanOfficer != null && this.personnel == null) { return true; } if (oldLoanOfficer == null && this.personnel != null) { return true; } return oldLoanOfficer.isDifferentIdentityTo(this.personnel); } @Deprecated public boolean isLOChanged(final Short loanOfficerId) { return getPersonnel() == null && loanOfficerId != null || getPersonnel() != null && loanOfficerId == null || getPersonnel() != null && loanOfficerId != null && !getPersonnel().getPersonnelId().equals(loanOfficerId); } public final int countOfCustomerMovements() { return this.customerMovements.size(); } public void makeCustomerMovementEntries(final OfficeBO officeToTransfer) { CustomerMovementEntity currentCustomerMovement = getActiveCustomerMovement(); if (currentCustomerMovement == null) { currentCustomerMovement = new CustomerMovementEntity(this, getCreatedDate()); this.addCustomerMovement(currentCustomerMovement); } currentCustomerMovement.makeInactive(userContext.getId()); this.setOffice(officeToTransfer); CustomerMovementEntity newCustomerMovement = new CustomerMovementEntity(this, new DateTimeService().getCurrentJavaDateTime()); this.addCustomerMovement(newCustomerMovement); } protected String generateSystemId() { String systemId = ""; int numberOfZeros = CustomerConstants.SYSTEM_ID_LENGTH - String.valueOf(getCustomerId()).length(); for (int i = 0; i < numberOfZeros; i++) { systemId = systemId + "0"; } return getOffice().getGlobalOfficeNum() + "-" + systemId + getCustomerId(); } /** * @deprecated - move methods that call persistence out of domain model. */ @Deprecated protected void deleteCustomerMeeting() { try { getCustomerPersistence().deleteCustomerMeeting(this); setCustomerMeeting(null); } catch (PersistenceException pe) { new CustomerException(pe); } } private void createAddress(final Address address) { if (address != null) { this.customerAddressDetail = new CustomerAddressDetailEntity(this, address); this.displayAddress = this.customerAddressDetail.getDisplayAddress(); } } /** * @deprecated - using deprecated {@link CustomerAccountBO} constructor */ @Deprecated private CustomerAccountBO createCustomerAccount(final List<FeeDto> fees) throws CustomerException { try { return new CustomerAccountBO(userContext, this, fees); } catch (AccountException ae) { throw new CustomerException(ae); } } private void createCustomFields(final List<CustomFieldDto> customFields) { if (customFields != null) { for (CustomFieldDto customField : customFields) { addCustomField(new CustomerCustomFieldEntity(customField.getFieldId(), customField.getFieldValue(), customField.getFieldType(), this)); } } } private void inheritDetailsFromParent(final CustomerBO parentCustomer) { personnel = parentCustomer.getPersonnel(); office = parentCustomer.getOffice(); if (parentCustomer.getCustomerMeeting() != null) { customerMeeting = createCustomerMeeting(parentCustomer.getCustomerMeeting().getMeeting()); } this.addCustomerHierarchy(new CustomerHierarchyEntity(this, parentCustomer)); } public void addAccount(final AccountBO account) { this.accounts.add(account); } public void validate() throws CustomerException { validateName(); validateOffice(); if (isActive()) { validateLoanOfficer(); } } private void validateName() throws CustomerException { if (StringUtils.isBlank(displayName)) { throw new CustomerException(CustomerConstants.ERRORS_SPECIFY_NAME); } } public final void validateTrained() throws CustomerException { if (this.isTrained() && this.trainedDate == null || !this.isTrained() && this.trainedDate != null) { throw new CustomerException(CustomerConstants.INVALID_TRAINED_OR_TRAINEDDATE); } } public final void validateFormedBy() throws CustomerException { if (this.formedByPersonnel == null) { throw new CustomerException(CustomerConstants.INVALID_FORMED_BY); } } @Deprecated public void validateFields(final String displayName, final CustomerStatus customerStatus) throws CustomerException { if (StringUtils.isBlank(displayName)) { throw new CustomerException(CustomerConstants.INVALID_NAME); } if (customerStatus == null) { throw new CustomerException(CustomerConstants.INVALID_STATUS); } } public void resetPositions(final CustomerBO newParent) { newParent.resetPositionsAssignedToClient(this.getCustomerId()); } protected void validateMeetingRecurrenceForTransfer(final MeetingBO meetingFrom, final MeetingBO meetingTo) throws CustomerException { if (meetingFrom.isWeekly() && meetingTo.isMonthly() || meetingFrom.isMonthly() && meetingTo.isWeekly()) { throw new CustomerException(CustomerConstants.ERRORS_MEETING_FREQUENCY_MISMATCH); } } public boolean hasActiveLoanAccounts() { for (AccountBO account : getAccounts()) { if (account.isActiveLoanAccount()) { return true; } } return false; } public boolean isDisbursalPreventedDueToAnyExistingActiveLoansForTheSameProduct(final LoanOfferingBO loanOffering) { if (definedAsSameForAllLoan(loanOffering)) { return false; } for (AccountBO account : getAccounts()) { if (account.isActiveLoanAccount()) { LoanOfferingBO compareLoanOffering = ((LoanBO) account).getLoanOffering(); if (compareLoanOffering.isOfSameOffering(loanOffering)) { return true; } } } return false; } private boolean definedAsSameForAllLoan(LoanOfferingBO loanOffering) { if (loanOffering.getLoanAmountSameForAllLoan() == null || loanOffering.getLoanAmountSameForAllLoan().size() == 0) { return false; } if (loanOffering.getNoOfInstallSameForAllLoan() == null || loanOffering.getNoOfInstallSameForAllLoan().size() == 0) { return false; } return true; } public void generateSearchId() { if (getParentCustomer() != null) { this.setSearchId(getParentCustomer().getSearchId() + "." + getParentCustomer().getMaxChildCount()); } else { String searchId = GroupConstants.PREFIX_SEARCH_STRING + getCustomerId(); this.setSearchId(searchId); } } protected void handleAddClientToGroup() { setPersonnel(getParentCustomer().getPersonnel()); if (getCustomerMeeting() != null) { deleteCustomerMeeting(); setCustomerMeeting(createCustomerMeeting(getParentCustomer().getCustomerMeeting().getMeeting())); } else { setCustomerMeeting(createCustomerMeeting(getParentCustomer().getCustomerMeeting().getMeeting())); } } @Override public String toString() { return "{" + customerId + ", " + displayName + "}"; } public boolean isCenter() { return getCustomerLevel().isCenter(); } public boolean isGroup() { return getCustomerLevel().isGroup(); } public boolean isClient() { return getCustomerLevel().isClient(); } /** * Returns the amount which this customer * **/ public Money getMaxLoanAmount(final LoanOfferingBO loanOffering) { ArrayList<Money> loanAmounts = new ArrayList<Money>(); Set<AccountBO> accounts = getAccounts(); for (AccountBO accountBO : accounts) { // If account not in loan obligations met, continue to next loan // account if (!accountBO.isInState(AccountState.LOAN_CLOSED_OBLIGATIONS_MET)) { continue; } if (accountBO.isLoanAccount() && ((LoanBO) accountBO).isOfProductOffering(loanOffering)) { loanAmounts.add(((LoanBO) accountBO).getLoanAmount()); } } if (loanAmounts.isEmpty()) { loanAmounts.add(zero()); } return Collections.max(loanAmounts); } public Short getMaxLoanCycleForProduct(final PrdOfferingBO prdOffering) { // implement strategy and delegate logic to PerformanceHistory instead // of being here // only checking for clients if (getPerformanceHistory() instanceof ClientPerformanceHistoryEntity) { return ((ClientPerformanceHistoryEntity) getPerformanceHistory()).getMaxLoanCycleForProduct(prdOffering); } else if (getPerformanceHistory() instanceof GroupPerformanceHistoryEntity) { return ((GroupPerformanceHistoryEntity) getPerformanceHistory()).getMaxLoanCycleForProduct(prdOffering); } return SHORT_ZERO; } /** * <code>searchId</code> should indicate the order in which clients became part of a group. This method was * originally created for use in fixing <a href="https://mifos.dev.java.net/issues/show_bug.cgi?id=1417">bug * #1417</a>. * * @return A {@link java.util.Comparator} useful for comparing customers by searchId. */ public static Comparator<CustomerBO> searchIdComparator() { return new Comparator<CustomerBO>() { @Override public int compare(final CustomerBO o1, final CustomerBO o2) { return ChapterNum.compare(o1.getSearchId(), o2.getSearchId()); } }; } public void updatePerformanceHistoryOnDisbursement(final LoanBO loan, final Money disburseAmount) throws CustomerException { } public void updatePerformanceHistoryOnWriteOff(final LoanBO loan) throws CustomerException { } public void updatePerformanceHistoryOnReversal(final LoanBO loan, final Money lastLoanAmount) throws CustomerException { } public void updatePerformanceHistoryOnRepayment(final LoanBO loan, final Money totalAmount) throws CustomerException { } public void updatePerformanceHistoryOnLastInstlPayment(final LoanBO loan, final Money totalAmount) throws CustomerException { } public void addCustomerAccount(final CustomerAccountBO customerAccount) { this.accounts.add(customerAccount); } public void setCustomerAccount(CustomerAccountBO customerAccount) { this.customerAccount = customerAccount; this.accounts.add(customerAccount); } public CustomerPersistence getCustomerPersistence() { if (null == customerPersistence) { customerPersistence = new CustomerPersistence(); } return this.customerPersistence; } public void setCustomerPersistence(final CustomerPersistence customerPersistence) { this.customerPersistence = customerPersistence; } public LegacyPersonnelDao getPersonnelPersistence() { if (null == personnelPersistence) { personnelPersistence = ApplicationContextProvider.getBean(LegacyPersonnelDao.class); } return personnelPersistence; } public void setPersonnelPersistence(final LegacyPersonnelDao personnelPersistence) { this.personnelPersistence = personnelPersistence; } public void validateLoanOfficerIsActive() throws CustomerException { PersonnelBO loanOfficer = this.personnel; if (loanOfficer != null) { if (!loanOfficer.isActive() || !(loanOfficer.getOffice().getOfficeId().equals(this.office.getOfficeId()) || !loanOfficer .isLoanOfficer())) { throw new CustomerException(CustomerConstants.CUSTOMER_LOAN_OFFICER_INACTIVE_EXCEPTION); } } } public void validateChangeToActive() throws CustomerException { if (this.personnel == null || this.personnel.getPersonnelId() == null) { throw new CustomerException(ClientConstants.CLIENT_LOANOFFICER_NOT_ASSIGNED); } } public void validateNoActiveAccountExist() throws CustomerException { if (this.isAnyLoanAccountOpen() || this.isAnySavingsAccountOpen()) { throw new CustomerException(CustomerConstants.CUSTOMER_HAS_ACTIVE_ACCOUNTS_EXCEPTION); } } public void clearCustomerFlagsIfApplicable(CustomerStatus oldStatus, CustomerStatus newStatus) { if (checkStatusChangeCancelToPartial(oldStatus, newStatus)) { this.customerFlags.clear(); } if (checkStatusChangeClosedToActiveOrPartial(oldStatus, newStatus)) { this.customerFlags.clear(); } } public boolean checkStatusChangeClosedToActiveOrPartial(final CustomerStatus oldStatus, final CustomerStatus newStatus) { if ((oldStatus.equals(CustomerStatus.CLIENT_CLOSED) && newStatus.equals(CustomerStatus.CLIENT_ACTIVE)) || (oldStatus.equals(CustomerStatus.CLIENT_CLOSED) && newStatus.equals(CustomerStatus.CLIENT_PARTIAL))) { return true; } return false; } public void blacklist() { this.blackListed = YesNoFlag.YES.getValue(); } public void unBlacklist(){ this.blackListed = YesNoFlag.NO.getValue(); } public CustomerDetailDto toCustomerDetailDto() { Short loanOfficerId = null; if (this.personnel != null) { loanOfficerId = this.personnel.getPersonnelId(); } Address address = new Address(); if (this.customerAddressDetail != null) { address = this.customerAddressDetail.getAddress(); } AddressDto addressDto = null; if (address != null) { addressDto = Address.toDto(address); } return new CustomerDetailDto(this.customerId, this.displayName, this.searchId, this.globalCustNum, loanOfficerId, this.externalId, addressDto); } public boolean hasSameIdentityAs(CustomerBO customer) { if ((this.customerId == null && customer.getCustomerId() == null) && (this.globalCustNum == null && customer.getGlobalCustNum() == null)) { return this.displayName.equals(customer.displayName); } return this.globalCustNum.equals(customer.getGlobalCustNum()); } public void validateVersion(Integer newVersionNum) throws CustomerException { if (!this.versionNo.equals(newVersionNum)) { throw new CustomerException(Constants.ERROR_VERSION_MISMATCH); } } public boolean isNameDifferent(final String nameToCheck) { return !this.displayName.equalsIgnoreCase(nameToCheck); } public void updateCenterDetails(UserContext userContext, CenterUpdate centerUpdate) throws CustomerException { this.setUserContext(userContext); this.setUpdateDetails(); this.setExternalId(centerUpdate.getExternalId()); AddressDto dto = centerUpdate.getAddress(); if ( dto != null) { Address address = new Address(dto.getLine1(), dto.getLine2(), dto.getLine3(), dto.getCity(), dto.getState(), dto.getCountry(), dto.getZip(), dto.getPhoneNumber()); this.updateAddress(address); } try { if (centerUpdate.getMfiJoiningDate() != null) { DateTime mfiJoiningDate = CalendarUtils.getDateFromString(centerUpdate.getMfiJoiningDate(), userContext .getPreferredLocale()); this.setMfiJoiningDate(mfiJoiningDate.toDate()); } } catch (InvalidDateException e) { throw new CustomerException(CustomerConstants.MFI_JOINING_DATE_MANDATORY, e); } } public Short getOfficeId() { return office.getOfficeId(); } public void validateIsTopOfHierarchy() throws CustomerException { if (this.parentCustomer != null) { throw new CustomerException(CustomerConstants.INVALID_PARENT); } } public boolean isTopOfHierarchy() { if (this.parentCustomer == null) { return true; } return false; } public boolean hasMeetingDifferentTo(MeetingBO groupMeeting) { MeetingBO customerMeeting = getCustomerMeetingValue(); if ((groupMeeting.getMeetingId() == null) && (customerMeeting.getMeetingId() == null)) { return false; } return !groupMeeting.getMeetingId().equals(customerMeeting.getMeetingId()); } public Short getLoanOfficerId() { Short loanOfficerId = null; if (this.personnel != null) { loanOfficerId = this.personnel.getPersonnelId(); } return loanOfficerId; } // To be used strictly from test code @Deprecated public void setCustomerId(Integer customerId) { this.customerId = customerId; } public boolean isValidMeetingDate(Date transactionDate) throws MeetingException { return customerMeeting.getMeeting().isValidMeetingDateUntilNextYear(transactionDate); } public CustomerDto toCustomerDto() { Short statusId = (getStatus() == null) ? null : getStatus().getValue(); Short customerLevelId = (getCustomerLevel() == null) ? null : getCustomerLevel().getId(); Short personnelId = (getPersonnel() == null) ? null : getPersonnel().getPersonnelId(); return new CustomerDto(getCustomerId(), getDisplayName(), getGlobalCustNum(), statusId, customerLevelId, getVersionNo(), getOfficeId(), personnelId); } /** * Checks if any account (either savings or loan) is in 'active' state * @return true if any of customer's accounts is in active state */ public boolean isAnyAccountActive(){ boolean activeAccountExits=false; for (AccountBO account : getAccounts()) { activeAccountExits=account.isActiveLoanAccount() || account.isActiveSavingsAccount(); if(activeAccountExits){ break; } } return activeAccountExits; } /** * Checks if there are any active periodic fees for customer. * @return true if at least one active and periodic fee is found, otherwise false */ public boolean isAnyPeriodicFeeActive() { for (AccountBO account : getAccounts()) { if(!account.isAnyPeriodicFeeActive()) { return true; } } return false; } }