Skip to content
Permalink
Browse files
fixed ATEN-416, ATEN-419
  • Loading branch information
mgeiss committed Sep 14, 2017
1 parent 050391c commit 127a5ae380fb37cb10a08056a131e6fc7fbd1865
Showing 5 changed files with 143 additions and 62 deletions.
@@ -165,7 +165,8 @@ TellerTransactionCosts post(@PathVariable("tellerCode") final String tellerCode,
produces = MediaType.APPLICATION_JSON_VALUE
)
@ThrowsExceptions({
@ThrowsException(status = HttpStatus.NOT_FOUND, exception = TellerNotFoundException.class)
@ThrowsException(status = HttpStatus.NOT_FOUND, exception = TellerNotFoundException.class),
@ThrowsException(status = HttpStatus.CONFLICT, exception = TransactionProcessingException.class)
})
void confirm(@PathVariable("tellerCode") final String tellerCode,
@PathVariable("identifier") final String tellerTransactionIdentifier,
@@ -241,7 +241,9 @@ public void shouldNotCloseAccountRemainingBalance() throws Exception {
Mockito.doAnswer(invocation -> Collections.emptyList())
.when(super.depositAccountManagementServiceSpy).fetchProductInstances(tellerTransaction.getCustomerIdentifier());

super.testSubject.post(teller.getCode(), tellerTransaction);
final TellerTransactionCosts tellerTransactionCosts = super.testSubject.post(teller.getCode(), tellerTransaction);

super.testSubject.confirm(teller.getCode(), tellerTransactionCosts.getTellerTransactionIdentifier(), "CONFIRM", null);
}

@Test
@@ -266,13 +268,18 @@ public void shouldTransferAccountToAccount() throws Exception {
tellerTransaction.setClerk(AbstractTellerTest.TEST_USER);
tellerTransaction.setAmount(commonAmount);

final Account account = new Account();
account.setBalance(2000.00D);
account.setState(Account.State.OPEN.name());
Mockito.doAnswer(invocation -> Optional.of(account))
final Account customerAccount = new Account();
customerAccount.setBalance(2000.00D);
customerAccount.setState(Account.State.OPEN.name());
Mockito.doAnswer(invocation -> Optional.of(customerAccount))
.when(super.accountingServiceSpy).findAccount(tellerTransaction.getCustomerAccountIdentifier());
Mockito.doAnswer(invocation -> Optional.of(new Account()))

final Account targetAccount = new Account();
targetAccount.setBalance(2000.00D);
targetAccount.setState(Account.State.OPEN.name());
Mockito.doAnswer(invocation -> Optional.of(targetAccount))
.when(super.accountingServiceSpy).findAccount(tellerTransaction.getTargetAccountIdentifier());

Mockito.doAnswer(invocation -> Collections.emptyList())
.when(super.depositAccountManagementServiceSpy).getCharges(Matchers.eq(tellerTransaction));
Mockito.doAnswer(invocation -> Collections.emptyList())
@@ -379,7 +386,9 @@ public void shouldNotWithdrawLackingBalance() throws Exception {
Mockito.doAnswer(invocation -> Collections.emptyList())
.when(super.depositAccountManagementServiceSpy).fetchProductInstances(tellerTransaction.getCustomerIdentifier());

super.testSubject.post(teller.getCode(), tellerTransaction);
final TellerTransactionCosts tellerTransactionCosts = super.testSubject.post(teller.getCode(), tellerTransaction);

super.testSubject.confirm(teller.getCode(), tellerTransactionCosts.getTellerTransactionIdentifier(), "CONFIRM", null);
}

@Test(expected = TransactionProcessingException.class)
@@ -609,6 +618,14 @@ public void shouldProcessCheque() throws Exception {
micr.setBranchSortCode("08154711");
micr.setAccountNumber("4711");

Mockito
.doAnswer(invocation -> {
final Account mockedAccount = new Account();
mockedAccount.setState(Account.State.OPEN.name());
return Optional.of(mockedAccount);
})
.when(super.accountingServiceSpy).findAccount(Matchers.eq(micr.getAccountNumber()));

final Cheque cheque = new Cheque();
cheque.setMicr(micr);
cheque.setDrawee("whatever Bank");
@@ -665,6 +682,14 @@ public void shouldNotProcessChequeAlreadyUsed() throws Exception {
micr.setBranchSortCode("08154711");
micr.setAccountNumber("4711");

Mockito
.doAnswer(invocation -> {
final Account mockedAccount = new Account();
mockedAccount.setState(Account.State.OPEN.name());
return Optional.of(mockedAccount);
})
.when(super.accountingServiceSpy).findAccount(Matchers.eq(micr.getAccountNumber()));

final Cheque cheque = new Cheque();
cheque.setMicr(micr);
cheque.setDrawee("whatever Bank");
@@ -167,13 +167,24 @@ public void processCashWithdrawal(final String tellerCode, final TellerTransacti
}

final JournalEntry journalEntry = this.prepareJournalEntry(tellerTransaction);

final HashSet<Debtor> debtors = new HashSet<>();
journalEntry.setDebtors(debtors);

final Debtor customerDebtor = new Debtor();
customerDebtor.setAccountNumber(tellerTransaction.getCustomerAccountIdentifier());
customerDebtor.setAmount(tellerTransaction.getAmount().toString());
debtors.add(customerDebtor);
final HashSet<Creditor> creditors = new HashSet<>();
journalEntry.setCreditors(creditors);

if (tellerTransaction.getAmount().compareTo(BigDecimal.ZERO) > 0) {
final Debtor customerDebtor = new Debtor();
customerDebtor.setAccountNumber(tellerTransaction.getCustomerAccountIdentifier());
customerDebtor.setAmount(tellerTransaction.getAmount().toString());
debtors.add(customerDebtor);

final Creditor tellerCreditor = new Creditor();
tellerCreditor.setAccountNumber(tellerEntity.getTellerAccountIdentifier());
tellerCreditor.setAmount(tellerTransaction.getAmount().toString());
creditors.add(tellerCreditor);
}

if (!tellerTransactionCosts.getCharges().isEmpty()) {
if (chargesIncluded) {
@@ -183,14 +194,6 @@ public void processCashWithdrawal(final String tellerCode, final TellerTransacti
}
}

final HashSet<Creditor> creditors = new HashSet<>();
journalEntry.setCreditors(creditors);

final Creditor tellerCreditor = new Creditor();
tellerCreditor.setAccountNumber(tellerEntity.getTellerAccountIdentifier());
tellerCreditor.setAmount(tellerTransaction.getAmount().toString());
creditors.add(tellerCreditor);

creditors.addAll(this.createChargeCreditors(tellerTransactionCosts));

this.accountingService.postJournalEntry(journalEntry);
@@ -24,6 +24,7 @@
import io.mifos.teller.service.internal.repository.ChequeRepository;
import io.mifos.teller.service.internal.repository.TellerEntity;
import io.mifos.teller.service.internal.repository.TellerRepository;
import io.mifos.teller.service.internal.repository.TellerTransactionEntity;
import io.mifos.teller.service.internal.repository.TellerTransactionRepository;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
@@ -55,8 +56,21 @@ public TellerOperationService(@Qualifier(ServiceConstants.LOGGER_NAME) final Log
this.chequeRepository = chequeRepository;
}

public boolean tellerTransactionExists(final String tellerTransactionIdentifier) {
return this.tellerTransactionRepository.findByIdentifier(tellerTransactionIdentifier).isPresent();
public Optional<TellerTransaction> getTellerTransaction(final String tellerTransactionIdentifier) {

final Optional<TellerTransactionEntity> optionalTellerTransaction =
this.tellerTransactionRepository.findByIdentifier(tellerTransactionIdentifier);

return optionalTellerTransaction.map(tellerTransactionEntity -> {
final TellerTransaction tellerTransaction = TellerTransactionMapper.map(tellerTransactionEntity);
if (tellerTransaction.getTransactionType().equals(ServiceConstants.TX_CHEQUE)) {
final Optional<ChequeEntity> optionalCheque =
this.chequeRepository.findByTellerTransactionId(tellerTransactionEntity.getId());

optionalCheque.ifPresent(chequeEntity -> tellerTransaction.setCheque(ChequeMapper.map(chequeEntity)));
}
return tellerTransaction;
});
}

public List<TellerTransaction> fetchTellerTransactions(final String tellerCode, final String state) {
@@ -24,6 +24,7 @@
import io.mifos.core.lang.ServiceException;
import io.mifos.teller.ServiceConstants;
import io.mifos.teller.api.v1.PermittableGroupIds;
import io.mifos.teller.api.v1.domain.MICR;
import io.mifos.teller.api.v1.domain.Teller;
import io.mifos.teller.api.v1.domain.TellerTransaction;
import io.mifos.teller.api.v1.domain.TellerTransactionCosts;
@@ -33,6 +34,7 @@
import io.mifos.teller.service.internal.command.DrawerUnlockCommand;
import io.mifos.teller.service.internal.command.InitializeTellerTransactionCommand;
import io.mifos.teller.service.internal.command.PauseTellerCommand;
import io.mifos.teller.service.internal.processor.TellerTransactionProcessor;
import io.mifos.teller.service.internal.service.TellerManagementService;
import io.mifos.teller.service.internal.service.TellerOperationService;
import io.mifos.teller.service.internal.service.helper.AccountingService;
@@ -68,20 +70,23 @@ public class TellerOperationRestController {
private final TellerManagementService tellerManagementService;
private final AccountingService accountingService;
private final ChequeService chequeService;
private final TellerTransactionProcessor tellerTransactionProcessor;

@Autowired
public TellerOperationRestController(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
final CommandGateway commandGateway,
final TellerOperationService tellerOperationService,
final TellerManagementService tellerManagementService,
final AccountingService accountingService,
final ChequeService chequeService) {
final ChequeService chequeService,
final TellerTransactionProcessor tellerTransactionProcessor) {
this.logger = logger;
this.commandGateway = commandGateway;
this.tellerOperationService = tellerOperationService;
this.tellerManagementService = tellerManagementService;
this.accountingService = accountingService;
this.chequeService = chequeService;
this.tellerTransactionProcessor = tellerTransactionProcessor;
}

@Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.TELLER_OPERATION)
@@ -174,52 +179,22 @@ ResponseEntity<TellerTransactionCosts> post(@PathVariable("tellerCode") final St
}
}

this.verifyAccounts(tellerTransaction);

if (transactionType.equals(ServiceConstants.TX_CHEQUE)) {
final LocalDate dateIssued = DateConverter.dateFromIsoString(tellerTransaction.getCheque().getDateIssued());
final LocalDate sixMonth = LocalDate.now(Clock.systemUTC()).minusMonths(6);
if (dateIssued.isBefore(sixMonth)) {
throw ServiceException.conflict("Cheque is older than 6 month.");
}

final String chequeIdentifier = MICRParser.toIdentifier(tellerTransaction.getCheque().getMicr());
final MICR micr = tellerTransaction.getCheque().getMicr();
final String chequeIdentifier = MICRParser.toIdentifier(micr);
if (this.chequeService.chequeExists(chequeIdentifier)) {
throw ServiceException.conflict("Cheque {0} already used.", chequeIdentifier);
}
}

final Optional<Account> optionalCustomerAccount =
this.accountingService.findAccount(tellerTransaction.getCustomerAccountIdentifier());
if (!optionalCustomerAccount.isPresent()) {
throw ServiceException.badRequest("Customer account {0} not found.");
} else {
final Account customerAccount = optionalCustomerAccount.get();

if (!customerAccount.getState().equals(Account.State.OPEN.name())) {
throw ServiceException.conflict("Account {0} is not open.", customerAccount.getIdentifier());
}

if (transactionType.equals(ServiceConstants.TX_ACCOUNT_TRANSFER)
|| transactionType.equals(ServiceConstants.TX_CASH_WITHDRAWAL)
|| transactionType.equals(ServiceConstants.TX_CLOSE_ACCOUNT)) {
if (tellerTransaction.getAmount().compareTo(BigDecimal.valueOf(customerAccount.getBalance())) > 0 ) {
throw ServiceException.conflict("Not enough balance.");
}
}

if (transactionType.equals(ServiceConstants.TX_CLOSE_ACCOUNT)) {
final BigDecimal newBalance =
BigDecimal.valueOf(customerAccount.getBalance()).subtract(tellerTransaction.getAmount());
if (newBalance.compareTo(BigDecimal.ZERO) > 0) {
throw ServiceException.conflict("Account has remaining balance");
}
}
}

if (tellerTransaction.getTargetAccountIdentifier() != null &&
!this.accountingService.findAccount(tellerTransaction.getTargetAccountIdentifier()).isPresent()) {
throw ServiceException.badRequest("Target account {0} not found.");
}

try {
return ResponseEntity.ok(
this.commandGateway.process(
@@ -246,13 +221,19 @@ ResponseEntity<Void> confirm(@PathVariable("tellerCode") final String tellerCode

this.verifyEmployee(teller);

if (!this.tellerOperationService.tellerTransactionExists(tellerTransactionIdentifier)) {
throw ServiceException.notFound("Transaction {0} not found.", tellerTransactionIdentifier);
}

switch (command.toUpperCase()) {
case "CONFIRM" :
this.commandGateway.process(new ConfirmTellerTransactionCommand(tellerTransactionIdentifier, charges));
final ConfirmTellerTransactionCommand confirmTellerTransactionCommand =
new ConfirmTellerTransactionCommand(tellerTransactionIdentifier, charges);

final TellerTransaction tellerTransaction =
this.tellerOperationService.getTellerTransaction(tellerTransactionIdentifier)
.orElseThrow(() -> ServiceException.notFound("Transaction {0} not found.", tellerTransactionIdentifier));

this.verifyAccounts(tellerTransaction);
this.verifyWithdrawalTransaction(tellerTransactionIdentifier, confirmTellerTransactionCommand);

this.commandGateway.process(confirmTellerTransactionCommand);
break;
case "CANCEL" :
this.commandGateway.process(new CancelTellerTransactionCommand(tellerTransactionIdentifier));
@@ -295,4 +276,61 @@ private void verifyEmployee(final Teller teller) {
throw ServiceException.badRequest("User {0} is not assigned to teller {1}", currentUser, teller.getCode());
}
}

private void verifyAccounts(final TellerTransaction tellerTransaction) {
this.verifyAccount(tellerTransaction.getCustomerAccountIdentifier());

if (tellerTransaction.getTargetAccountIdentifier() != null) {
this.verifyAccount(tellerTransaction.getTargetAccountIdentifier());
}

if (tellerTransaction.getTransactionType().equals(ServiceConstants.TX_CHEQUE)) {
final MICR micr = tellerTransaction.getCheque().getMicr();
this.verifyAccount(micr.getAccountNumber());
}
}

private void verifyAccount(final String accountIdentifier) {
final Account account = this.accountingService.findAccount(accountIdentifier).orElseThrow(
() -> ServiceException.conflict("Account {0} not found."));

if (!account.getState().equals(Account.State.OPEN.name())) {
throw ServiceException.conflict("Account {0} is not open.", account.getIdentifier());
}
}

private void verifyWithdrawalTransaction(final String tellerTransactionIdentifier,
final ConfirmTellerTransactionCommand confirmTellerTransactionCommand) {

final TellerTransaction tellerTransaction =
this.tellerOperationService.getTellerTransaction(tellerTransactionIdentifier)
.orElseThrow(() -> ServiceException.notFound("Transaction {0} not found.", tellerTransactionIdentifier));

final String transactionType = tellerTransaction.getTransactionType();

if (transactionType.equals(ServiceConstants.TX_ACCOUNT_TRANSFER)
|| transactionType.equals(ServiceConstants.TX_CASH_WITHDRAWAL)
|| transactionType.equals(ServiceConstants.TX_CLOSE_ACCOUNT)) {

final Account account = this.accountingService.findAccount(tellerTransaction.getCustomerAccountIdentifier()).orElseThrow(
() -> ServiceException.notFound("Customer account {0} not found.", tellerTransaction.getCustomerAccountIdentifier()));
final BigDecimal currentBalance = BigDecimal.valueOf(account.getBalance());

final TellerTransactionCosts tellerTransactionCosts =
this.tellerTransactionProcessor.getCosts(tellerTransaction);
final BigDecimal transactionAmount = confirmTellerTransactionCommand.chargesIncluded()
? tellerTransactionCosts.getTotalAmount()
: tellerTransaction.getAmount();

if (transactionAmount.compareTo(currentBalance) > 0) {
throw ServiceException.conflict("Account has not enough balance.");
}

if (transactionType.equals(ServiceConstants.TX_CLOSE_ACCOUNT)) {
if (currentBalance.compareTo(transactionAmount) > 0) {
throw ServiceException.conflict("Account has remaining balance.");
}
}
}
}
}

0 comments on commit 127a5ae

Please sign in to comment.