/*
* 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.application.servicefacade;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifos.accounts.business.AccountBO;
import org.mifos.accounts.loan.business.LoanBO;
import org.mifos.accounts.persistence.LegacyAccountDao;
import org.mifos.accounts.savings.business.SavingsBO;
import org.mifos.accounts.util.helpers.AccountState;
import org.mifos.config.AccountingRules;
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.client.business.AttendanceType;
import org.mifos.customers.persistence.CustomerPersistence;
import org.mifos.customers.util.helpers.CustomerStatus;
import org.mifos.framework.exceptions.PersistenceException;
import org.mifos.framework.util.helpers.DateUtils;
import org.mifos.framework.util.helpers.Money;
/**
* Validates customer and account structure for SaveCollectionSheetDto.
*
* Note: Accounts with only Zero amount(s) are ignored and not validated (or processed by the save collection sheet
* functionality)
*/
public class SaveCollectionSheetStructureValidator {
private CustomerPersistence customerPersistence;
private LegacyAccountDao legacyAccountDao;
private final Short mifosCurrencyId;
private List<InvalidSaveCollectionSheetReason> validationErrors = new ArrayList<InvalidSaveCollectionSheetReason>();
private List<String> validationErrorsExtended = new ArrayList<String>();
private final List<CustomerStatus> validCustomerStatuses = new ArrayList<CustomerStatus>();
private final List<AccountState> validLoanAccountStates = new ArrayList<AccountState>();
private final List<AccountState> validSavingsAccountStates = new ArrayList<AccountState>();
private CustomerBO topCustomer;
private List<SaveCollectionSheetCustomerDto> saveCollectionSheetCustomers;
private enum ValidationAccountTypes {
CUSTOMER, LOAN, SAVINGS, INDIVIDUAL_SAVINGS;
}
public SaveCollectionSheetStructureValidator() {
this.customerPersistence = new CustomerPersistence();
this.legacyAccountDao = ApplicationContextProvider.getBean(LegacyAccountDao.class);
this.mifosCurrencyId = Money.getDefaultCurrency().getCurrencyId();
validCustomerStatuses.add(CustomerStatus.CLIENT_ACTIVE);
validCustomerStatuses.add(CustomerStatus.CLIENT_HOLD);
validCustomerStatuses.add(CustomerStatus.GROUP_ACTIVE);
validCustomerStatuses.add(CustomerStatus.GROUP_HOLD);
validCustomerStatuses.add(CustomerStatus.CENTER_ACTIVE);
validLoanAccountStates.add(AccountState.LOAN_APPROVED);
validLoanAccountStates.add(AccountState.LOAN_DISBURSED_TO_LOAN_OFFICER);
validLoanAccountStates.add(AccountState.LOAN_ACTIVE_IN_GOOD_STANDING);
validLoanAccountStates.add(AccountState.LOAN_ACTIVE_IN_BAD_STANDING);
validSavingsAccountStates.add(AccountState.SAVINGS_INACTIVE);
validSavingsAccountStates.add(AccountState.SAVINGS_ACTIVE);
}
public void execute(SaveCollectionSheetDto saveCollectionSheet) throws SaveCollectionSheetException {
saveCollectionSheetCustomers = saveCollectionSheet.getSaveCollectionSheetCustomers();
Boolean isTopCustomer = true;
for (SaveCollectionSheetCustomerDto saveCollectionSheetCustomer : saveCollectionSheetCustomers) {
CustomerBO customer;
Integer currentErrorCount = validationErrors.size();
try {
customer = customerPersistence.getCustomer(saveCollectionSheetCustomer.getCustomerId());
} catch (PersistenceException e) {
throw new MifosRuntimeException(e);
}
if (customer == null) {
if (isTopCustomer) {
addValidationError(InvalidSaveCollectionSheetReason.INVALID_TOP_CUSTOMER, "Customer Id: "
+ saveCollectionSheetCustomer.getCustomerId());
throw new SaveCollectionSheetException(validationErrors, validationErrorsExtended);
}
addValidationError(InvalidSaveCollectionSheetReason.CUSTOMER_NOT_FOUND, "Customer Id: "
+ saveCollectionSheetCustomer.getCustomerId());
} else {
if (isTopCustomer) {
topCustomer = customer;
}
validateCustomer(customer, saveCollectionSheetCustomer);
}
if (currentErrorCount.compareTo(validationErrors.size()) == 0) {
validateAttendanceType(customer, saveCollectionSheetCustomer.getAttendanceId());
validateSaveCollectionSheetCustomerAccount(saveCollectionSheetCustomer.getCustomerId(),
saveCollectionSheetCustomer.getSaveCollectionSheetCustomerAccount());
validateSaveCollectionSheetCustomerLoans(saveCollectionSheetCustomer.getCustomerId(),
saveCollectionSheetCustomer.getSaveCollectionSheetCustomerLoans());
validateSaveCollectionSheetCustomerSavings(saveCollectionSheetCustomer.getCustomerId(),
saveCollectionSheetCustomer.getSaveCollectionSheetCustomerSavings(),
ValidationAccountTypes.SAVINGS);
validateSaveCollectionSheetCustomerSavings(saveCollectionSheetCustomer.getCustomerId(),
saveCollectionSheetCustomer.getSaveCollectionSheetCustomerIndividualSavings(),
ValidationAccountTypes.INDIVIDUAL_SAVINGS);
}
isTopCustomer = false;
}
LocalDate validMeetingDate = getValidMeetingDateForTopCustomer(topCustomer.getCustomerId());
if (saveCollectionSheet.getTransactionDate().compareTo(validMeetingDate) != 0) {
addValidationError(InvalidSaveCollectionSheetReason.INVALID_DATE, "Transaction Date: "
+ saveCollectionSheet.getTransactionDate());
throw new SaveCollectionSheetException(validationErrors, validationErrorsExtended);
}
if (validationErrors.size() > 0) {
throw new SaveCollectionSheetException(validationErrors, validationErrorsExtended);
}
}
private void validateCustomer(CustomerBO customer, SaveCollectionSheetCustomerDto saveCollectionSheetCustomer) {
if (!(validCustomerStatuses.contains(customer.getStatus()))) {
addValidationError(InvalidSaveCollectionSheetReason.INVALID_CUSTOMER_STATUS, "Customer Id: "
+ saveCollectionSheetCustomer.getCustomerId() + " Customer Status: "
+ customer.getStatus().toString());
}
if (customer.getLevel().compareTo(CustomerLevel.CLIENT) != 0
&& saveCollectionSheetCustomer.getSaveCollectionSheetCustomerIndividualSavings() != null
&& saveCollectionSheetCustomer.getSaveCollectionSheetCustomerIndividualSavings().size() > 0) {
addValidationError(InvalidSaveCollectionSheetReason.INDIVIDUAL_SAVINGS_ACCOUNTS_ONLY_VALID_FOR_CLIENTS,
"Customer Id: " + saveCollectionSheetCustomer.getCustomerId() + " Customer Level: "
+ customer.getLevel().toString());
}
Integer customerParentId = null;
if (customer.getParentCustomer() != null) {
customerParentId = customer.getParentCustomer().getCustomerId();
}
if (customerParentInvalid(customerParentId, saveCollectionSheetCustomer.getParentCustomerId())) {
addValidationError(InvalidSaveCollectionSheetReason.INVALID_CUSTOMER_PARENT, "Customer Id: "
+ saveCollectionSheetCustomer.getCustomerId() + " Customer Parent Id Should be: "
+ customerParentId);
}
if (customerNotPartOfTopCustomerHierarchy(customer.getOffice().getOfficeId(), customer.getSearchId())) {
addValidationError(InvalidSaveCollectionSheetReason.CUSTOMER_IS_NOT_PART_OF_TOPCUSTOMER_HIERARCHY,
"Customer Id: " + saveCollectionSheetCustomer.getCustomerId());
}
}
private boolean customerParentInvalid(Integer parentId, Integer parentIdDto) {
if (parentId == null && parentIdDto == null) {
return false;
}
if (parentId != null && parentIdDto == null) {
return true;
}
if (parentId == null && parentIdDto != null) {
return true;
}
if (parentId.compareTo(parentIdDto) == 0) {
return false;
}
return true;
}
private boolean customerNotPartOfTopCustomerHierarchy(Short branchId, String searchId) {
// must have the same branchId and either the same searchId or the 'top
// searchid' plus a dot must match
if (branchId == null || searchId == null) {
return true;
}
if (branchId.compareTo(topCustomer.getOffice().getOfficeId()) != 0) {
return true;
}
if ((searchId.compareTo(topCustomer.getSearchId()) != 0)
&& (!(searchId.startsWith(topCustomer.getSearchId() + ".")))) {
return true;
}
return false;
}
private void validateSaveCollectionSheetCustomerAccount(Integer customerId,
SaveCollectionSheetCustomerAccountDto saveCollectionSheetCustomerAccount) {
if (null != saveCollectionSheetCustomerAccount) {
if (saveCollectionSheetCustomerAccount.getTotalCustomerAccountCollectionFee().compareTo(BigDecimal.ZERO) > 0) {
validateAccount(customerId, saveCollectionSheetCustomerAccount.getAccountId(),
saveCollectionSheetCustomerAccount.getCurrencyId(), ValidationAccountTypes.CUSTOMER);
}
}
}
private void validateSaveCollectionSheetCustomerLoans(Integer customerId,
List<SaveCollectionSheetCustomerLoanDto> saveCollectionSheetCustomerLoans) {
if (null != saveCollectionSheetCustomerLoans && saveCollectionSheetCustomerLoans.size() > 0) {
for (SaveCollectionSheetCustomerLoanDto saveCollectionSheetCustomerLoan : saveCollectionSheetCustomerLoans) {
if ((saveCollectionSheetCustomerLoan.getTotalDisbursement().compareTo(BigDecimal.ZERO) > 0)
|| (saveCollectionSheetCustomerLoan.getTotalLoanPayment().compareTo(BigDecimal.ZERO) > 0)) {
validateAccount(customerId, saveCollectionSheetCustomerLoan.getAccountId(),
saveCollectionSheetCustomerLoan.getCurrencyId(), ValidationAccountTypes.LOAN);
}
}
}
}
private void validateSaveCollectionSheetCustomerSavings(Integer customerId,
List<SaveCollectionSheetCustomerSavingDto> saveCollectionSheetCustomerSavings,
ValidationAccountTypes accountType) {
if (null != saveCollectionSheetCustomerSavings && saveCollectionSheetCustomerSavings.size() > 0) {
for (SaveCollectionSheetCustomerSavingDto saveCollectionSheetCustomerSaving : saveCollectionSheetCustomerSavings) {
if ((saveCollectionSheetCustomerSaving.getTotalWithdrawal().compareTo(BigDecimal.ZERO) > 0)
|| (saveCollectionSheetCustomerSaving.getTotalDeposit().compareTo(BigDecimal.ZERO) > 0)) {
validateAccount(customerId, saveCollectionSheetCustomerSaving.getAccountId(),
saveCollectionSheetCustomerSaving.getCurrencyId(), accountType);
}
}
}
}
private void validateAccount(Integer customerId, Integer accountId, Short currencyId,
ValidationAccountTypes accountType) {
AccountBO account;
try {
account = legacyAccountDao.getAccount(accountId);
} catch (PersistenceException e) {
throw new MifosRuntimeException(e);
}
if (account == null) {
addValidationError(InvalidSaveCollectionSheetReason.ACCOUNT_NOT_FOUND, "Customer Id: " + customerId
+ " Account Id: " + accountId);
return;
}
if (!validAccountOwnership(account, customerId, accountType)) {
addValidationError(InvalidSaveCollectionSheetReason.ACCOUNT_DOESNT_BELONG_TO_CUSTOMER, "Customer Id: "
+ customerId + " Account Id: " + accountId);
return;
}
validateCurrency(currencyId);
if (accountType.compareTo(ValidationAccountTypes.CUSTOMER) == 0) {
if (!(account instanceof CustomerAccountBO)) {
addValidationError(InvalidSaveCollectionSheetReason.ACCOUNT_NOT_A_CUSTOMER_ACCOUNT, "Customer Id: "
+ customerId + " Account Id: " + accountId);
}
return;
}
if (accountType.compareTo(ValidationAccountTypes.LOAN) == 0) {
if (!(account instanceof LoanBO)) {
addValidationError(InvalidSaveCollectionSheetReason.ACCOUNT_NOT_A_LOAN_ACCOUNT, "Customer Id: "
+ customerId + " Account Id: " + accountId);
} else {
if (!(validLoanAccountStates.contains(account.getState()))) {
addValidationError(InvalidSaveCollectionSheetReason.INVALID_LOAN_ACCOUNT_STATUS, "Customer Id: "
+ customerId + " Account Id: " + accountId + " Account Status: "
+ account.getState().toString());
}
}
return;
}
if ((accountType.compareTo(ValidationAccountTypes.SAVINGS) == 0)
|| (accountType.compareTo(ValidationAccountTypes.INDIVIDUAL_SAVINGS) == 0)) {
if (!(account instanceof SavingsBO)) {
addValidationError(InvalidSaveCollectionSheetReason.ACCOUNT_NOT_A_SAVINGS_ACCOUNT, "Customer Id: "
+ customerId + " Account Id: " + accountId);
} else {
if (!(validSavingsAccountStates.contains(account.getState()))) {
addValidationError(InvalidSaveCollectionSheetReason.INVALID_SAVINGS_ACCOUNT_STATUS, "Customer Id: "
+ customerId + " Account Id: " + accountId + " Account Status: "
+ account.getState().toString());
}
}
return;
}
throw new MifosRuntimeException("Account Type: " + accountType.toString() + " not recognised");
}
private boolean validAccountOwnership(AccountBO account, Integer customerIdDto, ValidationAccountTypes accountType) {
Integer accountCustomerId = account.getCustomer().getCustomerId();
if (accountType.compareTo(ValidationAccountTypes.INDIVIDUAL_SAVINGS) != 0) {
if (accountCustomerId.compareTo(customerIdDto) == 0) {
return true;
}
return false;
}
// For individual savings accounts, the persisted account id should
// never match with
// the customer id in the dto
if (accountCustomerId.compareTo(customerIdDto) == 0) {
return false;
}
// For individual savings accounts, the account id should appear in the
// normal savings accounts of a higher level customer (GROUP or CENTER)
if (!onHigherLevelCustomer(customerIdDto, account.getAccountId())) {
return false;
}
return true;
}
private boolean onHigherLevelCustomer(Integer customerIdDto, Integer accountId) {
SaveCollectionSheetCustomerDto currentCustomerDto = findSaveCollectionSheetCustomerDto(customerIdDto);
if (currentCustomerDto == null) {
throw new MifosRuntimeException("Couldn't find the SaveCollectionSheetCustomerDto for " + customerIdDto);
}
SaveCollectionSheetCustomerDto parentCustomerDto = findSaveCollectionSheetCustomerDto(currentCustomerDto
.getParentCustomerId());
if (parentCustomerDto == null) {
return false;
}
if (accountIdOnCustomerDto(parentCustomerDto, accountId)) {
return true;
}
SaveCollectionSheetCustomerDto granparentCustomerDto = findSaveCollectionSheetCustomerDto(parentCustomerDto
.getParentCustomerId());
if (granparentCustomerDto == null) {
return false;
}
if (accountIdOnCustomerDto(granparentCustomerDto, accountId)) {
return true;
}
return false;
}
private boolean accountIdOnCustomerDto(SaveCollectionSheetCustomerDto customerDto, Integer accountId) {
if (customerDto.getSaveCollectionSheetCustomerSavings() != null
&& customerDto.getSaveCollectionSheetCustomerSavings().size() > 0) {
for (SaveCollectionSheetCustomerSavingDto CustomerSaving : customerDto
.getSaveCollectionSheetCustomerSavings()) {
if (CustomerSaving.getAccountId().compareTo(accountId) == 0) {
return true;
}
}
}
return false;
}
private SaveCollectionSheetCustomerDto findSaveCollectionSheetCustomerDto(Integer customerIdDto) {
if (customerIdDto == null) {
return null;
}
for (SaveCollectionSheetCustomerDto saveCollectionSheetCustomer : saveCollectionSheetCustomers) {
if (saveCollectionSheetCustomer.getCustomerId().compareTo(customerIdDto) == 0) {
return saveCollectionSheetCustomer;
}
}
return null;
}
private void validateAttendanceType(CustomerBO customer, Short attendanceType) {
if (customer.getLevel().compareTo(CustomerLevel.CLIENT) == 0) {
if (attendanceType == null) {
addValidationError(InvalidSaveCollectionSheetReason.ATTENDANCE_TYPE_NULL, "Client Id: "
+ customer.getCustomerId());
} else {
for (AttendanceType at : AttendanceType.values()) {
if (at.getValue() == attendanceType) {
return;
}
}
addValidationError(InvalidSaveCollectionSheetReason.UNSUPPORTED_ATTENDANCE_TYPE, "Client Id: "
+ customer.getCustomerId() + " Attendance Type Id: " + attendanceType);
}
} else {
if (attendanceType != null) {
addValidationError(InvalidSaveCollectionSheetReason.ATTENDANCE_TYPE_ONLY_VALID_FOR_CLIENTS,
"Customer Id: " + customer.getCustomerId() + " Attendance Type Id: " + attendanceType);
}
}
}
private void validateCurrency(Short currencyId) {
// TODO This needs to change if multi-currency is implemented in
// mifos
if (currencyId.compareTo(mifosCurrencyId) != 0) {
addValidationError(InvalidSaveCollectionSheetReason.INVALID_CURRENCY, "Currency Id: " + currencyId);
}
}
private LocalDate getValidMeetingDateForTopCustomer(Integer customerId) {
if (AccountingRules.isBackDatedTxnAllowed()) {
try {
Date meetingDate = customerPersistence.getLastMeetingDateForCustomer(customerId);
if (meetingDate == null) {
// shouldn't occur but returning current date is okay here
return new LocalDate();
}
return DateUtils.getLocalDateFromDate(meetingDate);
} catch (PersistenceException e) {
throw new MifosRuntimeException(e);
}
}
return new LocalDate();
}
private void addValidationError(InvalidSaveCollectionSheetReason invalidSaveCollectionSheetReason,
String extendedMessage) {
validationErrors.add(invalidSaveCollectionSheetReason);
validationErrorsExtended.add(invalidSaveCollectionSheetReason.toString() + ": " + extendedMessage);
}
public void setCustomerPersistence(CustomerPersistence customerPersistence) {
this.customerPersistence = customerPersistence;
}
public void setlegacyAccountDao(LegacyAccountDao legacyAccountDao) {
this.legacyAccountDao = legacyAccountDao;
}
}