Skip to content

Commit

Permalink
created the AccountAppraisalDelegate and refactored the Account#balan…
Browse files Browse the repository at this point in the history
…ce method into the delegate allowing other Account implementations of the Account interface to reuse this method. This makes a way to implement the PersistentAccount I have been thinking about, without so much repetition
  • Loading branch information
ghacupha committed Apr 1, 2018
1 parent 64891fe commit 9768b15
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 83 deletions.
19 changes: 18 additions & 1 deletion src/main/java/io/github/ghacupha/keeper/book/api/Account.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@

import io.github.ghacupha.keeper.book.balance.AccountBalance;
import io.github.ghacupha.keeper.book.balance.AccountSide;
import io.github.ghacupha.keeper.book.base.AccountAppraisalDelegate;
import io.github.ghacupha.keeper.book.unit.time.TimePoint;
import io.github.ghacupha.keeper.book.util.MismatchedCurrencyException;
import io.github.ghacupha.keeper.book.util.UnableToPostException;
import io.github.ghacupha.keeper.book.util.UntimelyBookingDateException;

import java.util.Currency;
import java.util.List;

/**
* A collection of {@link Entry} items.
Expand Down Expand Up @@ -73,4 +74,20 @@ public interface Account {
* @return {@link TimePoint} date when the account was opened
*/
TimePoint getOpeningDate();

/**
* @implSpec As per implementation notes this is for use only by the {@link AccountAppraisalDelegate}
* allowing inexpensive evaluation of the {@link AccountBalance} without causing circular reference. Otherwise anyone else who needs
* to know the {@code AccountSide} of this needs to query the {@link AccountBalance} first, and from it acquire the {@link AccountSide}
*
* @return Shows the side of the balance sheet to which this belongs which could be either
* {@link AccountSide#DEBIT} or {@link AccountSide#CREDIT}
*/
AccountSide getAccountSide();

/**
*
* @return Returns this object's current copy of the {@link Entry} items
*/
List<Entry> getEntries();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package io.github.ghacupha.keeper.book.base;

import io.github.ghacupha.keeper.book.api.Account;
import io.github.ghacupha.keeper.book.api.Entry;
import io.github.ghacupha.keeper.book.balance.AccountBalance;
import io.github.ghacupha.keeper.book.balance.AccountSide;
import io.github.ghacupha.keeper.book.unit.money.Cash;
import io.github.ghacupha.keeper.book.unit.money.HardCash;
import io.github.ghacupha.keeper.book.unit.time.DateRange;

import java.util.List;

import static io.github.ghacupha.keeper.book.balance.AccountSide.CREDIT;
import static io.github.ghacupha.keeper.book.balance.AccountSide.DEBIT;

/**
* Okay so then we had to expose the {@link AccountSide} against better advise since calling the {@link Account#balance}
* method is going to be an expensive method, which could most likely trigger a circular dependency loop. There needs to be a method
* for getting the current {@code AccountSide} without gritting your teeth. So uncle Bob please forgive me for I have sinned,
* but there is just no practical inexpensive way of doing this stuff, and still be able to use this delegate for any
* {@link Account} implementation.
*
* @author edwin.njeru
*/
public class AccountAppraisalDelegate {

private final Account account;

AccountAppraisalDelegate(Account account) {

this.account = account;
}

public AccountBalance balance(DateRange dateRange){

Cash debits = getDebits(dateRange,account.getEntries());

Cash credits = getCredits(dateRange, account.getEntries());

if (debits.isZero() || credits.isZero()) {
if(!debits.isZero() && credits.isZero()){
return new AccountBalance(debits, DEBIT);
} else if(debits.isZero() && !credits.isZero()){
return new AccountBalance(credits, CREDIT);
}
} else if (account.getAccountSide() == DEBIT) {

if (credits.isMoreThan(debits)) {
return new AccountBalance(credits.minus(debits).abs(), CREDIT);
}

if (!credits.isMoreThan(debits)) {
return new AccountBalance(credits.minus(debits).abs(), DEBIT);
}
} else if (account.getAccountSide() == CREDIT) {

if (debits.isMoreThan(credits)) {
return new AccountBalance(debits.minus(credits).abs(), DEBIT);
}

if (!debits.isMoreThan(credits)) {
return new AccountBalance(debits.minus(credits).abs(), CREDIT);
}
}

return new AccountBalance(HardCash.of(0.0,account.getCurrency()),account.getAccountSide());
}

private Cash getCredits(DateRange dateRange,List<Entry> accountEntries) {
return HardCash.of(accountEntries
.parallelStream()
.filter(entry -> dateRange.includes(entry.getBookingDate()))
.filter(entry -> entry.getAccountSide() == CREDIT)
.map(entry -> entry.getAmount().getNumber().doubleValue())
.reduce(0.00,(acc,value) -> acc + value), account.getCurrency());
}

private Cash getDebits(DateRange dateRange,List<Entry> accountEntries) {
return HardCash.of(accountEntries
.parallelStream()
.filter(entry -> dateRange.includes(entry.getBookingDate()))
.filter(entry -> entry.getAccountSide() == DEBIT)
.map(entry -> entry.getAmount().getNumber().doubleValue())
.reduce(0.00,(acc,value) -> acc + value), account.getCurrency());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ public int hashCode() {

@Override
public String toString() {
final StringBuffer sb = new StringBuffer("EntryDetails{");
sb.append("narration='").append(narration).append('\'');
sb.append(", entryMap=").append(entryMap);
final StringBuffer sb = new StringBuffer("{");
sb.append("'").append(narration).append('\'');
sb.append(", otherEntryDetails=").append(entryMap);
sb.append('}');
return sb.toString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
import io.github.ghacupha.keeper.book.api.Entry;
import io.github.ghacupha.keeper.book.balance.AccountBalance;
import io.github.ghacupha.keeper.book.balance.AccountSide;
import io.github.ghacupha.keeper.book.unit.money.Cash;
import io.github.ghacupha.keeper.book.unit.money.HardCash;
import io.github.ghacupha.keeper.book.unit.time.DateRange;
import io.github.ghacupha.keeper.book.unit.time.SimpleDate;
import io.github.ghacupha.keeper.book.unit.time.TimePoint;
Expand Down Expand Up @@ -60,6 +58,8 @@ public final class SimpleAccount implements Account {

private static final Logger log = LoggerFactory.getLogger(SimpleAccount.class);

private AccountAppraisalDelegate evalulationDelegate = new AccountAppraisalDelegate(this);

private final Currency currency;
private final AccountDetails accountDetails;
private volatile AccountSide accountSide;
Expand Down Expand Up @@ -109,7 +109,7 @@ public AccountBalance balance(TimePoint asAt) {

log.debug("Account balance enquiry raised as at {}, for account : {}", asAt, this);

AccountBalance balance = balance(new DateRange(accountDetails.getOpeningDate(), asAt));
AccountBalance balance = evalulationDelegate.balance(new DateRange(accountDetails.getOpeningDate(), asAt));

log.debug("Returning accounting balance for {} as at : {} as : {}", this, asAt, balance);

Expand All @@ -118,7 +118,7 @@ public AccountBalance balance(TimePoint asAt) {

/**
* Similar to the balance query for a given date except the date is provided through a
* simple varags int argument
* simple {@code VarArgs} int argument
*
* @param asAt The date as at when the {@link AccountBalance} we want is effective given
* in the following order
Expand All @@ -137,59 +137,6 @@ public AccountBalance balance(int... asAt) {
return balance;
}

private AccountBalance balance(DateRange dateRange){

Cash debits = getDebits(dateRange);

Cash credits = getCredits(dateRange);

if (debits.isZero() || credits.isZero()) {
if(!debits.isZero() && credits.isZero()){
return new AccountBalance(debits, DEBIT);
} else if(debits.isZero() && !credits.isZero()){
return new AccountBalance(credits, CREDIT);
}
} else if (this.accountSide == DEBIT) {

if (credits.isMoreThan(debits)) {
return new AccountBalance(credits.minus(debits).abs(), CREDIT);
}

if (!credits.isMoreThan(debits)) {
return new AccountBalance(credits.minus(debits).abs(), DEBIT);
}
} else if (this.accountSide == CREDIT) {

if (debits.isMoreThan(credits)) {
return new AccountBalance(debits.minus(credits).abs(), DEBIT);
}

if (!debits.isMoreThan(credits)) {
return new AccountBalance(debits.minus(credits).abs(), CREDIT);
}
}

return new AccountBalance(HardCash.of(0.0,this.currency),this.accountSide);
}

private Cash getCredits(DateRange dateRange) {
return HardCash.of(this.getEntries()
.parallelStream()
.filter(entry -> dateRange.includes(entry.getBookingDate()))
.filter(entry -> entry.getAccountSide() == CREDIT)
.map(entry -> entry.getAmount().getNumber().doubleValue())
.reduce(0.00,(acc,value) -> acc + value), this.getCurrency());
}

private Cash getDebits(DateRange dateRange) {
return HardCash.of(this.getEntries()
.parallelStream()
.filter(entry -> dateRange.includes(entry.getBookingDate()))
.filter(entry -> entry.getAccountSide() == DEBIT)
.map(entry -> entry.getAmount().getNumber().doubleValue())
.reduce(0.00,(acc,value) -> acc + value), this.getCurrency());
}

/**
* @return Currency of the account
*/
Expand All @@ -198,9 +145,10 @@ public Currency getCurrency() {
return currency;
}

private List<Entry> getEntries() {
@Override
public List<Entry> getEntries() {

return entries.stream().collect(ImmutableListCollector.toImmutableList());
return new CopyOnWriteArrayList<>(entries.parallelStream().collect(ImmutableListCollector.toImmutableList()));
}

@Override
Expand All @@ -212,4 +160,19 @@ public TimePoint getOpeningDate() {
public String toString() {
return this.accountDetails.getNumber()+" "+this.accountDetails.getName();
}

/**
* @return Shows the side of the balance sheet to which this belongs which could be either
* {@link AccountSide#DEBIT} or {@link AccountSide#CREDIT}
* @implSpec As per implementation notes this is for use only by the {@link AccountAppraisalDelegate}
* allowing inexpensive evaluation of the {@link AccountBalance} without causing circular reference. Otherwise anyone else who needs
* to know the {@code AccountSide} of this needs to query the {@link AccountBalance} first, and from it acquire the {@link AccountSide}.
* Also note that the object's {@link AccountSide} is never really exposed since this implementation is returning a value based on its
* current status.
*/
@Override
public AccountSide getAccountSide() {

return this.accountSide == DEBIT ? DEBIT : CREDIT;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,17 @@ private static Double mapCashToDouble(Entry entry) {

private static boolean predicateCredits(Entry entry) {
boolean credit;
log.debug("Checking if entry {} is credit ", entry.getEntryDetails());
log.trace("Checking if entry {} is credit ", entry.getEntryDetails());
credit = entry.getAccountSide() == CREDIT;
log.debug("Entry : {} is credit {}", entry.getEntryDetails(), credit);
log.trace("Entry : {} is credit {}", entry.getEntryDetails(), credit);
return credit;
}

private static boolean predicateDebits(Entry entry) {
boolean debit;
log.debug("Checking if entry {} is debit ", entry.getEntryDetails());
log.trace("Checking if entry {} is debit ", entry.getEntryDetails());
debit = entry.getAccountSide() == DEBIT;
log.debug("Entry : {} is credit {}", entry.getEntryDetails(), debit);
log.trace("Entry : {} is credit {}", entry.getEntryDetails(), debit);
return debit;
}

Expand All @@ -104,7 +104,9 @@ public void addEntry(AccountSide accountSide, Cash amount, Account account, Entr
throw new MismatchedCurrencyException("Cannot add entry whose getCurrency differs to that of the transaction");
} else {
log.debug("Adding entry : {} into transaction : {}",details,this);
entries.add(new SimpleEntry(accountSide, account, amount, date, details));
Entry tempEntry = new SimpleEntry(accountSide, account, amount, date, details);
entries.add(tempEntry);
log.debug("Entry {} has been added to {}",tempEntry,this);
}
}

Expand Down Expand Up @@ -171,7 +173,7 @@ public int hashCode() {

public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append(label).append('\'');
sb.append('\'').append(label).append('\'');
sb.append(", date=").append(date);
sb.append(", currency=").append(currency);
sb.append(", entries=").append(entries);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public TimePoint getStart() {
}

public boolean includes(TimePoint arg) {
log.debug("Checking if : {} includes timePoint : {}", this, arg);
log.trace("Checking if : {} includes timePoint : {}", this, arg);
return !arg.before(start) && !arg.after(end);
}

Expand Down
Loading

0 comments on commit 9768b15

Please sign in to comment.