teller cashier issues #334 #432 #433 #434 #435

This commit is contained in:
nazeer1100126
2017-04-20 19:26:49 +05:30
parent 8baf9f4cd0
commit 755d4d2f92
9 changed files with 297 additions and 16 deletions
@@ -0,0 +1,153 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.fineract.organisation.teller.data;
import java.math.BigDecimal;
import java.util.Date;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
import org.apache.fineract.infrastructure.core.service.SearchParameters;
import org.apache.fineract.organisation.teller.domain.Cashier;
import org.apache.fineract.organisation.teller.domain.Teller;
import org.apache.fineract.organisation.teller.exception.CashierAlreadyAlloacated;
import org.apache.fineract.organisation.teller.exception.CashierDateRangeOutOfTellerDateRangeException;
import org.apache.fineract.organisation.teller.exception.CashierInsufficientAmountException;
import org.apache.fineract.organisation.teller.service.TellerManagementReadPlatformService;
import org.apache.fineract.useradministration.domain.AppUser;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
@Component
public class CashierTransactionDataValidator {
private final TellerManagementReadPlatformService tellerManagementReadPlatformService;
private final JdbcTemplate jdbcTemplate;
@Autowired
public CashierTransactionDataValidator(
final TellerManagementReadPlatformService tellerManagementReadPlatformService,
final RoutingDataSource dataSource) {
this.tellerManagementReadPlatformService = tellerManagementReadPlatformService;
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public void validateSettleCashAndCashOutTransactions(final Long cashierId,
String currencyCode, final BigDecimal transactionAmount) {
final Integer offset = null;
final Integer limit = null;
final String orderBy = null;
final String sortOrder = null;
final Date fromDate = null;
final Date toDate = null;
final SearchParameters searchParameters = SearchParameters
.forPagination(offset, limit, orderBy, sortOrder);
final CashierTransactionsWithSummaryData cashierTxnWithSummary = this.tellerManagementReadPlatformService
.retrieveCashierTransactionsWithSummary(cashierId, false,
fromDate, toDate, currencyCode, searchParameters);
if (cashierTxnWithSummary.getNetCash().subtract(transactionAmount)
.compareTo(BigDecimal.ZERO) < 0) {
throw new CashierInsufficientAmountException();
}
}
public void validateSettleCashAndCashOutTransactions(final Long cashierId,
JsonCommand command) {
String currencyCode = command
.stringValueOfParameterNamed("currencyCode");
BigDecimal transactionAmount = command
.bigDecimalValueOfParameterNamed("txnAmount");
validateSettleCashAndCashOutTransactions(cashierId, currencyCode,
transactionAmount);
}
public void validateCashierAllowedDateAndTime(final Cashier cashier,
final Teller teller) {
Long staffId = cashier.getStaff().getId();
final LocalDate fromDate = new LocalDate(cashier.getStartDate());
final LocalDate endDate = new LocalDate(cashier.getEndDate());
final LocalDate tellerFromDate = teller.getStartLocalDate();
final LocalDate tellerEndDate = teller.getEndLocalDate();
/**
* to validate cashier date range in range of teller date range
*/
if (fromDate.isBefore(tellerFromDate)
|| endDate.isBefore(tellerFromDate)
|| (tellerEndDate != null && fromDate.isAfter(tellerEndDate) || endDate
.isAfter(tellerEndDate))) {
throw new CashierDateRangeOutOfTellerDateRangeException();
}
/**
* to validate cashier has not been assigned for same duration
*/
String sql = "select count(*) from m_cashiers c where c.staff_id = "
+ staffId + " AND " + "(('" + fromDate
+ "' BETWEEN c.start_date AND c.end_date OR '" + endDate
+ "' BETWEEN c.start_date AND c.end_date )"
+ " OR ( c.start_date BETWEEN '" + fromDate + "' AND '"
+ endDate + "' OR c.end_date BETWEEN '" + fromDate + "' AND '"
+ endDate + "'))";
if (!cashier.isFullDay()) {
String startTime = cashier.getStartTime();
String endTime = cashier.getEndTime();
sql = sql + " AND ( Time(c.start_time) BETWEEN TIME('" + startTime
+ "') and TIME('" + endTime
+ "') or Time(c.end_time) BETWEEN TIME('" + startTime
+ "') and TIME('" + endTime + "')) ";
}
int count = this.jdbcTemplate.queryForObject(sql, Integer.class);
if (count > 0) {
throw new CashierAlreadyAlloacated();
}
}
public void validateOnLoanDisbursal(AppUser user, String currencyCode,
BigDecimal transactionAmount) {
LocalDateTime localDateTime = DateUtils.getLocalDateTimeOfTenant();
if (user.getStaff() != null) {
String sql = "select c.id from m_cashiers c where c.staff_id = "
+ user.getStaff().getId()
+ " AND "
+ " (case when c.full_day then '"
+ localDateTime.toLocalDate()
+ "' BETWEEN c.start_date AND c.end_date "
+ " else ('"
+ localDateTime.toLocalDate()
+ "' BETWEEN c.start_date AND c.end_date and "
+ " TIME('"
+ localDateTime.toDateTime()
+ "') BETWEEN TIME(c.start_time) AND TIME(c.end_time) ) end)";
try {
Long cashierId = this.jdbcTemplate.queryForObject(sql,
Long.class);
validateSettleCashAndCashOutTransactions(cashierId,
currencyCode, transactionAmount);
} catch (EmptyResultDataAccessException e) {
}
}
}
}
@@ -0,0 +1,32 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.fineract.organisation.teller.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
@SuppressWarnings("serial")
public class CashierAlreadyAlloacated extends
AbstractPlatformDomainRuleException {
public CashierAlreadyAlloacated() {
super("cashier.already.allocated.for.given.data.and.time.exception",
"Cashier already allocated for given date and time range.");
}
}
@@ -0,0 +1,32 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.fineract.organisation.teller.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
@SuppressWarnings("serial")
public class CashierDateRangeOutOfTellerDateRangeException extends
AbstractPlatformDomainRuleException {
public CashierDateRangeOutOfTellerDateRangeException() {
super("cashier.date.range.out.of.teller.date.range.exception",
"Cashier date range should be in teller date range.");
}
}
@@ -0,0 +1,32 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.fineract.organisation.teller.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
@SuppressWarnings("serial")
public class CashierInsufficientAmountException extends
AbstractPlatformDomainRuleException {
public CashierInsufficientAmountException() {
super("cashier.insufficient.amount.exception",
"Cashier do not have sufficient amount for this transaction.");
}
}
@@ -98,7 +98,7 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
}
@Override
public TellerData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
public TellerData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final Long officeId = rs.getLong("office_id");
@@ -140,7 +140,7 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
}
@Override
public TellerData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
public TellerData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String tellerName = rs.getString("teller_name");
@@ -182,7 +182,7 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
}
@Override
public TellerData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
public TellerData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String tellerName = rs.getString("teller_name");
@@ -510,9 +510,14 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
}
final CashierTransactionMapper ctm = new CashierTransactionMapper();
String sql = "select * from (select " + ctm.cashierTxnSchema()
+ " where txn.cashier_id = ? and txn.currency_code = ? and o.hierarchy like ? ) cashier_txns " + " union (select "
+ " where txn.cashier_id = ? and txn.currency_code = ? and o.hierarchy like ? "
+ "AND ((case when c.full_day then Date(txn.created_date) between c.start_date AND c.end_date else ( Date(txn.created_date) between c.start_date AND c.end_date"
+ " ) and ( TIME(txn.created_date) between TIME(c.start_time) AND TIME(c.end_time)) end) or txn.txn_type = 101)) cashier_txns "
+ " union (select "
+ ctm.savingsTxnSchema()
+ " where sav_txn.is_reversed = 0 and c.id = ? and sav.currency_code = ? and o.hierarchy like ? and "
+ " sav_txn.transaction_date between c.start_date and date_add(c.end_date, interval 1 day) "
@@ -566,7 +571,7 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
}
@Override
public CashierData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
public CashierData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final Long tellerId = rs.getLong("teller_id");
@@ -719,7 +724,7 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
}
@Override
public CashierTransactionData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
public CashierTransactionData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
final Long id = rs.getLong("txn_id");
final Long cashierId = rs.getLong("cashier_id");
@@ -772,6 +777,10 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
sqlBuilder.append(" left join m_office o on o.id = t.office_id ");
sqlBuilder.append(" left join m_staff s on s.id = c.staff_id ");
sqlBuilder.append(" where txn.cashier_id = ? ");
sqlBuilder
.append(" AND (( case when c.full_day then Date(txn.created_date) between c.start_date AND c.end_date ");
sqlBuilder
.append(" else ( Date(txn.created_date) between c.start_date AND c.end_date) and ( TIME(txn.created_date) between TIME(c.start_time) AND TIME(c.end_time)) end) or txn.txn_type = 101) ");
sqlBuilder.append(" and txn.currency_code = ? ");
sqlBuilder.append(" and o.hierarchy like ? ) cashier_txns ");
sqlBuilder.append(" UNION ");
@@ -889,7 +898,7 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
}
@Override
public CashierTransactionTypeTotalsData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum)
public CashierTransactionTypeTotalsData mapRow(final ResultSet rs, final int rowNum)
throws SQLException {
final Integer cashierTxnType = rs.getInt("cash_txn_type");
@@ -20,7 +20,6 @@ package org.apache.fineract.organisation.teller.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.useradministration.domain.AppUser;
/**
* Provides the local service for adding, modifying and deleting tellers.
@@ -18,6 +18,7 @@
*/
package org.apache.fineract.organisation.teller.service;
import java.util.Date;
import java.util.Map;
import java.util.Set;
@@ -42,6 +43,7 @@ import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
import org.apache.fineract.organisation.staff.domain.Staff;
import org.apache.fineract.organisation.staff.domain.StaffRepository;
import org.apache.fineract.organisation.staff.exception.StaffNotFoundException;
import org.apache.fineract.organisation.teller.data.CashierTransactionDataValidator;
import org.apache.fineract.organisation.teller.domain.Cashier;
import org.apache.fineract.organisation.teller.domain.CashierRepository;
import org.apache.fineract.organisation.teller.domain.CashierTransaction;
@@ -54,6 +56,7 @@ import org.apache.fineract.organisation.teller.exception.CashierNotFoundExceptio
import org.apache.fineract.organisation.teller.serialization.TellerCommandFromApiJsonDeserializer;
import org.apache.fineract.portfolio.client.domain.ClientTransaction;
import org.apache.fineract.useradministration.domain.AppUser;
import org.joda.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -75,6 +78,7 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
private final CashierTransactionRepository cashierTxnRepository;
private final JournalEntryRepository glJournalEntryRepository;
private final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper;
private final CashierTransactionDataValidator cashierTransactionDataValidator;
@Autowired
public TellerWritePlatformServiceJpaImpl(final PlatformSecurityContext context,
@@ -82,7 +86,8 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
final TellerRepositoryWrapper tellerRepositoryWrapper, final OfficeRepositoryWrapper officeRepositoryWrapper,
final StaffRepository staffRepository, CashierRepository cashierRepository, CashierTransactionRepository cashierTxnRepository,
JournalEntryRepository glJournalEntryRepository,
FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper) {
FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper,
final CashierTransactionDataValidator cashierTransactionDataValidator) {
this.context = context;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.tellerRepositoryWrapper = tellerRepositoryWrapper;
@@ -92,6 +97,7 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
this.cashierTxnRepository = cashierTxnRepository;
this.glJournalEntryRepository = glJournalEntryRepository;
this.financialActivityAccountRepositoryWrapper = financialActivityAccountRepositoryWrapper;
this.cashierTransactionDataValidator = cashierTransactionDataValidator;
}
@Override
@@ -251,9 +257,9 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
endTime = hourEndTime.toString() + ":" + minEndTime.toString();
}
final Cashier cashier = Cashier.fromJson(tellerOffice, teller, staff, startTime, endTime, command);
this.cashierTransactionDataValidator.validateCashierAllowedDateAndTime(cashier, teller);
this.cashierRepository.save(cashier);
return new CommandProcessingResultBuilder() //
@@ -351,7 +357,7 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
@Override
public CommandProcessingResult allocateCashToCashier(final Long cashierId, JsonCommand command) {
return doTransactionForCashier(cashierId, CashierTxnType.ALLOCATE, command); // For
return doTransactionForCashier(cashierId, CashierTxnType.ALLOCATE, command); // For
// fund
// allocation
// to
@@ -360,7 +366,10 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
@Override
public CommandProcessingResult settleCashFromCashier(final Long cashierId, JsonCommand command) {
return doTransactionForCashier(cashierId, CashierTxnType.SETTLE, command); // For
this.cashierTransactionDataValidator.validateSettleCashAndCashOutTransactions(cashierId, command);
return doTransactionForCashier(cashierId, CashierTxnType.SETTLE, command); // For
// fund
// settlement
// from
@@ -51,6 +51,7 @@ import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.organisation.staff.domain.Staff;
import org.apache.fineract.organisation.teller.data.CashierTransactionDataValidator;
import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
import org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
import org.apache.fineract.portfolio.account.PortfolioAccountType;
@@ -170,6 +171,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService;
private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessingStrategy;
private final CodeValueRepositoryWrapper codeValueRepository;
private final CashierTransactionDataValidator cashierTransactionDataValidator;
@Autowired
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -200,7 +202,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService,
final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessingStrategy,
final CodeValueRepositoryWrapper codeValueRepository,
final LoanRepositoryWrapper loanRepositoryWrapper) {
final LoanRepositoryWrapper loanRepositoryWrapper,
final CashierTransactionDataValidator cashierTransactionDataValidator) {
this.context = context;
this.loanEventApiJsonValidator = loanEventApiJsonValidator;
this.loanAssembler = loanAssembler;
@@ -239,6 +242,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.transactionProcessingStrategy = transactionProcessingStrategy;
this.entityDatatableChecksWritePlatformService = entityDatatableChecksWritePlatformService;
this.codeValueRepository = codeValueRepository;
this.cashierTransactionDataValidator = cashierTransactionDataValidator;
}
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
@@ -293,7 +297,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Map<String, Object> changes = new LinkedHashMap<>();
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
if (paymentDetail != null && paymentDetail.getPaymentType() != null
&& paymentDetail.getPaymentType().isCashPayment()) {
BigDecimal transactionAmount = command
.bigDecimalValueOfParameterNamed("transactionAmount");
this.cashierTransactionDataValidator.validateOnLoanDisbursal(
currentUser, loan.getCurrencyCode(), transactionAmount);
}
final Boolean isPaymnetypeApplicableforDisbursementCharge = configurationDomainService
.isPaymnetypeApplicableforDisbursementCharge();
@@ -406,7 +416,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
isExceptionForBalanceCheck);
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
}
updateRecurringCalendarDatesForInterestRecalculation(loan);
this.loanAccountDomainService.recalculateAccruals(loan);
this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_DISBURSAL,
@@ -94,4 +94,9 @@ public class PaymentType extends AbstractPersistableCustom<Long> {
public PaymentTypeData toData() {
return PaymentTypeData.instance(getId(), this.name, this.description, this.isCashPayment, this.position);
}
public Boolean isCashPayment() {
return isCashPayment;
}
}