@RequiredArgsConstructor
final class CreateStatementService implements CreateStatementUseCase {
private final Customers customers;
private final Accounts accounts;
private final TransactionHistory transactionHistory;
private final AccountBalances accountBalances;
@Override
public Either<Failure, Statement> create(CreateStatement command) {
Optional<Account> account = accounts.find(command.accountId());
if (account.isEmpty()) {
return Either.left(new NotFound("Account"));
}
Optional<Customer> customer = customers.find(command.customerId());
if (customer.isEmpty()) {
return Either.left(new NotFound("Customer"));
}
List<Transaction> sortedTransactionList = transactionHistory
.findAll(account.get().getAccountNumber(), command.interval())
.stream()
.sorted(Comparator.comparing(Transaction::getOccurredAt))
.collect(Collectors.toList());
Either<Failure, Tuple2<MonetaryAmount, MonetaryAmount>> initialAndFinalBalance = getInitialAndFinalBalance(
command.accountId(), command.interval(), sortedTransactionList
);
if (initialAndFinalBalance.isLeft()) {
return Either.left(initialAndFinalBalance.getLeft());
}
return Either.right(new Statement(
customer.get(),
account.get(),
command.interval(),
sortedTransactionList,
initialAndFinalBalance.get()._1(),
initialAndFinalBalance.get()._2()
));
}
private Either<Failure, Tuple2<MonetaryAmount, MonetaryAmount>> getInitialAndFinalBalance(
String accountId, Interval interval, List<Transaction> transactionList
) {
if (!transactionList.isEmpty()) {
return Either.right(getBalanceBasedOnTransactions(transactionList));
}
Optional<MonetaryAmount> initialBalance = accountBalances.find(accountId, interval.getStart());
if (initialBalance.isEmpty()) {
return Either.left(new NotFound("Initial Balance"));
}
Optional<MonetaryAmount> finalBalance;
if (interval.getEnd() == null) {
finalBalance = accountBalances.find(accountId);
} else {
finalBalance = accountBalances.find(accountId, interval.getEnd());
}
if (finalBalance.isEmpty()) {
return Either.left(new NotFound("Final Balance"));
}
return Either.right(new Tuple2<>(initialBalance.get(), finalBalance.get()));
}
private Tuple2<MonetaryAmount, MonetaryAmount> getBalanceBasedOnTransactions(List<Transaction> transactionList) {
Transaction first = transactionList.get(0);
Transaction last = transactionList.get(transactionList.size() - 1);
MonetaryAmount initialBalance = first
.getBalanceAfterTransaction()
.add(first.getTotalTransactionAmountInAccountCurrency());
MonetaryAmount finalBalance = last.getBalanceAfterTransaction();
return new Tuple2<>(initialBalance, finalBalance);
}
}