diff --git a/src/main/java/honours/ing/banq/access/AccessServiceImpl.java b/src/main/java/honours/ing/banq/access/AccessServiceImpl.java index 8038b61..d63dc7b 100644 --- a/src/main/java/honours/ing/banq/access/AccessServiceImpl.java +++ b/src/main/java/honours/ing/banq/access/AccessServiceImpl.java @@ -74,6 +74,7 @@ public NewCardBean provideAccess(String authToken, String iBAN, String username) account.addHolder(holder); accountRepository.save(account); + Card card = new Card(holder, account, CardUtil.generateCardNumber(cardRepository)); cardRepository.save(card); diff --git a/src/main/java/honours/ing/banq/account/Account.java b/src/main/java/honours/ing/banq/account/Account.java new file mode 100644 index 0000000..c388010 --- /dev/null +++ b/src/main/java/honours/ing/banq/account/Account.java @@ -0,0 +1,79 @@ +package honours.ing.banq.account; + +import javax.persistence.*; +import java.math.BigDecimal; + +/** + * @author Jeffrey Bakker + * @since 7-8-17 + */ +@Entity +@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) +public abstract class Account { + + @Id + @GeneratedValue(strategy = GenerationType.TABLE) + private Integer id; + + @OneToOne(targetEntity = BankAccount.class, cascade = CascadeType.REFRESH, fetch = FetchType.LAZY) + protected BankAccount account; + + protected BigDecimal balance; + + /** + * @deprecated empty constructor for the Hibernate ORM + */ + @Deprecated + public Account() { } + + /** + * Creates a new {@link Account}. + * @param account the bank account to which this account belongs + */ + public Account(BankAccount account) { + this.account = account; + this.balance = BigDecimal.ZERO; + } + + /** + * Returns whether the given amount can be payed from the balance on this account. + * @param amount the amount the user wishes to pay + * @return {@code true} if the user can pay the amount + */ + public boolean canPayAmount(BigDecimal amount) { + return balance.subtract(amount).compareTo(BigDecimal.ZERO) >= 0; + } + + /** + * Returns the {@link BankAccount} to which this account belongs. + * @return a {@link BankAccount} + */ + public BankAccount getAccount() { + return account; + } + + /** + * Adds balance to the balance of the account. + * @param delta the difference in balance + */ + public void addBalance(BigDecimal delta) { + balance = balance.add(delta); + } + + /** + * Returns the balance on the account. + * @return the balance + */ + public BigDecimal getBalance() { + return balance; + } + + /** + * Returns the ID of the account. + * @return the ID + */ + public Integer getId() { + return id; + } + +} diff --git a/src/main/java/honours/ing/banq/account/BankAccount.java b/src/main/java/honours/ing/banq/account/BankAccount.java index 6015177..847aa6c 100644 --- a/src/main/java/honours/ing/banq/account/BankAccount.java +++ b/src/main/java/honours/ing/banq/account/BankAccount.java @@ -18,17 +18,18 @@ public class BankAccount { @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; - private BigDecimal balance; - private BigDecimal interest; - private BigDecimal lowestBalance; - private BigDecimal overdraftLimit; - @ManyToOne(targetEntity = Customer.class) private Customer primaryHolder; @ManyToMany(targetEntity = Customer.class) private List holders; + @OneToOne(targetEntity = CheckingAccount.class, cascade = CascadeType.ALL, orphanRemoval = true) + private CheckingAccount checkingAccount; + + @OneToOne(targetEntity = SavingAccount.class, cascade = CascadeType.ALL, orphanRemoval = true) + private SavingAccount savingAccount; + /** * @deprecated empty constructor for spring */ @@ -37,105 +38,51 @@ public BankAccount() { } /** * Creates a new {@link BankAccount} with the given primary holder and 0.0 for balance. - * @param primaryHolder + * @param primaryHolder the primary holder of this account */ public BankAccount(Customer primaryHolder) { this.primaryHolder = primaryHolder; - balance = new BigDecimal(0.0); - interest = new BigDecimal(0.0); - lowestBalance = new BigDecimal(0.0); - overdraftLimit = new BigDecimal(0.0); holders = new ArrayList<>(); - } - - /** - * Returns the ID of the account. - * @return the ID - */ - public Integer getId() { - return id; - } - - /** - * Returns the balance of the account. - * @return the balance - */ - public BigDecimal getBalance() { - return balance; - } - - /** - * Adds balance on the account. - * @param delta the difference in balance - */ - public void addBalance(BigDecimal delta) { - balance = balance.add(delta); - if (balance.compareTo(lowestBalance) < 0) { - lowestBalance = balance; - } - } - /** - * Returns whether the amount can be payed from this bank account with respect to the overdraft limit. - * @param amount the amount the user wishes to pay - * @return {@code true} if the account has enough balance - */ - public boolean canPayAmount(BigDecimal amount) { - return balance.subtract(amount).compareTo(overdraftLimit) >= 0; - } - - /** - * Adds interest to the interest that will have to be payed at the end of the month. - * @param delta the amount of interest to add - */ - public void addInterest(BigDecimal delta) { - interest = interest.add(delta); + checkingAccount = new CheckingAccount(this); } /** - * Returns the amount of interest that will have to be payed at the end of the month. - * @return the amount of interest + * Returns this {@link BankAccount}'s {@link CheckingAccount}. + * @return a {@link CheckingAccount} */ - public BigDecimal getInterest() { - return interest; + public CheckingAccount getCheckingAccount() { + return checkingAccount; } /** - * Resets the amount of interest that still has to be cashed in. + * Adds a {@link SavingAccount} to this {@link BankAccount}. */ - public void resetInterest() { - interest = new BigDecimal(0.0); + public void addSavingAccount() { + savingAccount = new SavingAccount(this); } /** - * Returns the lowest balance on this account since the last reset. - * @return the lowest balance + * Returns this {@link BankAccount}'s {@link SavingAccount}. + * @return a {@link SavingAccount} */ - public BigDecimal getLowestBalance() { - return lowestBalance; + public SavingAccount getSavingAccount() { + return savingAccount; } /** - * Resets the lowest balance to the current balance. + * Removes this {@link BankAccount}'s {@link SavingAccount}. */ - public void resetLowestBalance() { - lowestBalance = balance; + public void removeSavingAccount() { + savingAccount = null; } /** - * Returns the maximum amount of negative balance that this account may have. - * @return the overdraft limit - */ - public BigDecimal getOverdraftLimit() { - return overdraftLimit.multiply(new BigDecimal(-1.0)); - } - - /** - * Sets the maximum amount of negative balance that this account may have. - * @param overdraftLimit the overdraft limit + * Returns the ID of the account. + * @return the ID */ - public void setOverdraftLimit(BigDecimal overdraftLimit) { - this.overdraftLimit = overdraftLimit.multiply(new BigDecimal(-1.0)); + public Integer getId() { + return id; } /** diff --git a/src/main/java/honours/ing/banq/account/BankAccountService.java b/src/main/java/honours/ing/banq/account/BankAccountService.java index 35452f4..4b21972 100644 --- a/src/main/java/honours/ing/banq/account/BankAccountService.java +++ b/src/main/java/honours/ing/banq/account/BankAccountService.java @@ -84,4 +84,10 @@ Object setOverdraftLimit( @JsonRpcParam("overdraftLimit") double overdraftLimit) throws NotAuthorizedError, InvalidParamValueError; + Object openSavingsAccount(@JsonRpcParam("authToken") String authToken, @JsonRpcParam("iBAN") String iBAN) + throws InvalidParamValueError, NotAuthorizedError; + + Object closeSavingsAccount(@JsonRpcParam("authToken") String authToken, @JsonRpcParam("iBAN") String iBAN) + throws InvalidParamValueError, NotAuthorizedError; + } \ No newline at end of file diff --git a/src/main/java/honours/ing/banq/account/BankAccountServiceImpl.java b/src/main/java/honours/ing/banq/account/BankAccountServiceImpl.java index 51e70ab..011fdfb 100644 --- a/src/main/java/honours/ing/banq/account/BankAccountServiceImpl.java +++ b/src/main/java/honours/ing/banq/account/BankAccountServiceImpl.java @@ -93,17 +93,21 @@ public NewAccountBean openAdditionalAccount(String authToken) throws NotAuthoriz @Transactional @Override public Object closeAccount(String authToken, String iBAN) throws NotAuthorizedError, InvalidParamValueError { + BankAccount account = auth.getAuthorizedBankAccount(authToken, iBAN); Customer customer = auth.getAuthorizedCustomer(authToken); - long accountNumber = IBANUtil.getAccountNumber(iBAN); - BankAccount account = repository.findOne((int) accountNumber); + if (!account.getPrimaryHolder().equals(customer)) { + throw new NotAuthorizedError(); + } - if (account == null) { - throw new InvalidParamValueError("Bank account does not exist"); + Account savingsAccount = account.getSavingAccount(); + if (savingsAccount != null && savingsAccount.getBalance().compareTo(BigDecimal.ZERO) != 0) { + throw new InvalidParamValueError("Account balance needs to be cleared"); } - if (!account.getPrimaryHolder().equals(customer)) { - throw new NotAuthorizedError(); + Account checkingAccount = account.getCheckingAccount(); + if (checkingAccount != null && checkingAccount.getBalance().compareTo(BigDecimal.ZERO) != 0) { + throw new InvalidParamValueError("Account balance needs to be cleared"); } List cards = cardRepository.findByAccount(account); @@ -125,46 +129,62 @@ public Object closeAccount(String authToken, String iBAN) throws NotAuthorizedEr @Override public OverdraftBean getOverdraftLimit(String authToken, String iBAN) throws NotAuthorizedError, InvalidParamValueError { - Customer customer = auth.getAuthorizedCustomer(authToken); - - long accountNumber = IBANUtil.getAccountNumber(iBAN); - BankAccount account = repository.findOne((int) accountNumber); + return new OverdraftBean( + auth.getAuthorizedBankAccount(authToken, iBAN).getCheckingAccount().getOverdraftLimit()); + } - if (account == null) { - throw new InvalidParamValueError("Bank account does not exist"); - } + @Transactional + @Override + public Object setOverdraftLimit(String authToken, String iBAN, double overdraftLimit) throws NotAuthorizedError, InvalidParamValueError { + Customer customer = auth.getAuthorizedCustomer(authToken); + BankAccount account = auth.getAuthorizedBankAccount(authToken, iBAN); if (!account.getPrimaryHolder().equals(customer)) { throw new NotAuthorizedError(); } - return new OverdraftBean(account.getOverdraftLimit()); + if (overdraftLimit < 0.0d) { + throw new InvalidParamValueError("The overdraft limit must be a positive value"); + } + + account.getCheckingAccount().setOverdraftLimit(new BigDecimal(overdraftLimit)); + repository.save(account); + + return new Object(); } @Transactional @Override - public Object setOverdraftLimit(String authToken, String iBAN, double overdraftLimit) throws NotAuthorizedError, InvalidParamValueError { + public Object openSavingsAccount(String authToken, String iBAN) throws InvalidParamValueError, NotAuthorizedError { Customer customer = auth.getAuthorizedCustomer(authToken); + BankAccount account = auth.getAuthorizedBankAccount(authToken, iBAN); - long accountNumber = IBANUtil.getAccountNumber(iBAN); - BankAccount account = repository.findOne((int) accountNumber); - - if (account == null) { - throw new InvalidParamValueError("Bank account does not exist"); + if (!account.getPrimaryHolder().equals(customer)) { + throw new NotAuthorizedError(); } + account.addSavingAccount(); + repository.save(account); + + return new Object(); + } + + @Override + public Object closeSavingsAccount(String authToken, String iBAN) throws InvalidParamValueError, NotAuthorizedError { + Customer customer = auth.getAuthorizedCustomer(authToken); + BankAccount account = auth.getAuthorizedBankAccount(authToken, iBAN); + if (!account.getPrimaryHolder().equals(customer)) { throw new NotAuthorizedError(); } - if (overdraftLimit < 0.0d) { - throw new InvalidParamValueError("The overdraft limit must be a positive value"); + if (!account.getSavingAccount().getBalance().equals(BigDecimal.ZERO)) { + throw new InvalidParamValueError("Account balance needs to be cleared"); } - account.setOverdraftLimit(new BigDecimal(overdraftLimit)); + account.removeSavingAccount(); repository.save(account); return new Object(); } - } \ No newline at end of file diff --git a/src/main/java/honours/ing/banq/account/CheckingAccount.java b/src/main/java/honours/ing/banq/account/CheckingAccount.java new file mode 100644 index 0000000..0c3f9af --- /dev/null +++ b/src/main/java/honours/ing/banq/account/CheckingAccount.java @@ -0,0 +1,57 @@ +package honours.ing.banq.account; + +import javax.persistence.Entity; +import java.math.BigDecimal; + +/** + * An regular checking account that can be used for paying. + * @author Jeffrey Bakker + * @since 7-8-17 + */ +@Entity +public class CheckingAccount extends InterestAccount { + + private static final BigDecimal MINUS_ONE = new BigDecimal("-1.0"); + + private BigDecimal overdraftLimit; + + /** + * @deprecated empty constructor for the Hibernate ORM. + */ + @Deprecated + public CheckingAccount() { + super(); + } + + /** + * Creates a new {@link CheckingAccount}. + * @param account the bank account to which this account belongs + */ + public CheckingAccount(BankAccount account) { + super(account); + + overdraftLimit = BigDecimal.ZERO; + } + + @Override + public boolean canPayAmount(BigDecimal amount) { + return balance.subtract(amount).compareTo(overdraftLimit) >= 0; + } + + /** + * Returns the maximum amount of negative balance that this account may have. + * @return the overdraft limit + */ + public BigDecimal getOverdraftLimit() { + return overdraftLimit.multiply(MINUS_ONE); + } + + /** + * Sets the maximum amount of negative balance that this account may have. + * @param overdraftLimit the overdraft limit + */ + public void setOverdraftLimit(BigDecimal overdraftLimit) { + this.overdraftLimit = overdraftLimit.multiply(MINUS_ONE); + } + +} diff --git a/src/main/java/honours/ing/banq/account/InterestAccount.java b/src/main/java/honours/ing/banq/account/InterestAccount.java new file mode 100644 index 0000000..cfcd5c8 --- /dev/null +++ b/src/main/java/honours/ing/banq/account/InterestAccount.java @@ -0,0 +1,85 @@ +package honours.ing.banq.account; + +import javax.persistence.Entity; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.MappedSuperclass; +import java.math.BigDecimal; + +/** + * @author Jeffrey Bakker + * @since 7-8-17 + */ +@MappedSuperclass +public abstract class InterestAccount extends Account { + + private BigDecimal interest; + private BigDecimal lowestBalance; + + /** + * @deprecated empty constructor for the Hibernate ORM + */ + @Deprecated + public InterestAccount() { + super(); + } + + /** + * Creates a new {@link InterestAccount}. + * @param account the bank account to which this account belongs + */ + public InterestAccount(BankAccount account) { + super(account); + + interest = BigDecimal.ZERO; + lowestBalance = balance; + } + + @Override + public void addBalance(BigDecimal delta) { + super.addBalance(delta); + + if (balance.compareTo(lowestBalance) < 0) { + lowestBalance = balance; + } + } + + /** + * Adds interest to the interest that will have to be payed at the end of the month. + * @param delta the amount of interest to add + */ + public void addInterest(BigDecimal delta) { + interest = interest.add(delta); + } + + /** + * Returns the amount of interest that will have to be payed at the end of the month. + * @return the amount of interest + */ + public BigDecimal getInterest() { + return interest; + } + + /** + * Resets the amount of interest that still has to be cashed in. + */ + public void resetInterest() { + interest = new BigDecimal(0.0); + } + + /** + * Returns the lowest balance on this account since the last reset. + * @return the lowest balance + */ + public BigDecimal getLowestBalance() { + return lowestBalance; + } + + /** + * Resets the lowest balance to the current balance. + */ + public void resetLowestBalance() { + lowestBalance = balance; + } + +} diff --git a/src/main/java/honours/ing/banq/account/SavingAccount.java b/src/main/java/honours/ing/banq/account/SavingAccount.java new file mode 100644 index 0000000..45dd045 --- /dev/null +++ b/src/main/java/honours/ing/banq/account/SavingAccount.java @@ -0,0 +1,28 @@ +package honours.ing.banq.account; + +import javax.persistence.Entity; + +/** + * @author jeffrey + * @since 7-8-17 + */ +@Entity +public class SavingAccount extends InterestAccount { + + /** + * @deprecated empty constructor for the Hibernate ORM. + */ + @Deprecated + public SavingAccount() { + super(); + } + + /** + * Creates a new {@link SavingAccount} + * @param account the bank account to which this account belongs + */ + public SavingAccount(BankAccount account) { + super(account); + } + +} diff --git a/src/main/java/honours/ing/banq/auth/AuthService.java b/src/main/java/honours/ing/banq/auth/AuthService.java index af2e4b8..362ffca 100644 --- a/src/main/java/honours/ing/banq/auth/AuthService.java +++ b/src/main/java/honours/ing/banq/auth/AuthService.java @@ -2,6 +2,8 @@ import com.googlecode.jsonrpc4j.JsonRpcParam; import com.googlecode.jsonrpc4j.JsonRpcService; +import honours.ing.banq.InvalidParamValueError; +import honours.ing.banq.account.Account; import honours.ing.banq.account.BankAccount; import honours.ing.banq.auth.bean.AuthToken; import honours.ing.banq.card.Card; @@ -20,6 +22,10 @@ public interface AuthService { Card getAuthorizedCard(String token, String iBan, String pinCard) throws NotAuthorizedError, InvalidPINError; Customer getAuthorizedCustomer(String token) throws NotAuthorizedError; - BankAccount getAuthorizedAccount(String iBAN, String pinCard, String pinCode) throws InvalidPINError, CardBlockedError; + + Account getAuthorizedAccount(String token, String iBAN) throws NotAuthorizedError, InvalidParamValueError; + + BankAccount getAuthorizedBankAccount(String iBAN, String pinCard, String pinCode) throws InvalidPINError, CardBlockedError; + BankAccount getAuthorizedBankAccount(String token, String iBAN) throws NotAuthorizedError, InvalidParamValueError; } diff --git a/src/main/java/honours/ing/banq/auth/AuthServiceImpl.java b/src/main/java/honours/ing/banq/auth/AuthServiceImpl.java index 92f6181..72cb6bd 100644 --- a/src/main/java/honours/ing/banq/auth/AuthServiceImpl.java +++ b/src/main/java/honours/ing/banq/auth/AuthServiceImpl.java @@ -2,6 +2,7 @@ import com.googlecode.jsonrpc4j.spring.AutoJsonRpcServiceImpl; import honours.ing.banq.InvalidParamValueError; +import honours.ing.banq.account.Account; import honours.ing.banq.account.BankAccount; import honours.ing.banq.account.BankAccountRepository; import honours.ing.banq.auth.bean.AuthToken; @@ -124,9 +125,16 @@ public Customer getAuthorizedCustomer(String token) throws NotAuthorizedError { return auth.getCustomer(); } + @Override + public Account getAuthorizedAccount(String token, String iBAN) throws NotAuthorizedError, InvalidParamValueError { + BankAccount account = getAuthorizedBankAccount(token, iBAN); + + return iBAN.endsWith("S") ? account.getSavingAccount() : account.getCheckingAccount(); + } + @Transactional @Override - public BankAccount getAuthorizedAccount(String iBAN, String pinCard, String pinCode) throws InvalidPINError, CardBlockedError { + public BankAccount getAuthorizedBankAccount(String iBAN, String pinCard, String pinCode) throws InvalidPINError, CardBlockedError { if (iBAN == null || iBAN.length() <= 8 || pinCard == null || pinCode == null) { throw new InvalidParamValueError("One of the parameters is null or the IBAN is not long enough"); } @@ -161,6 +169,24 @@ public BankAccount getAuthorizedAccount(String iBAN, String pinCard, String pinC return account; } + @Override + public BankAccount getAuthorizedBankAccount(String token, String iBAN) throws NotAuthorizedError, InvalidParamValueError { + Customer customer = getAuthorizedCustomer(token); + + long accountNumber = IBANUtil.getAccountNumber(iBAN); + BankAccount account = accountRepository.findOne((int) accountNumber); + + if (account == null) { + throw new InvalidParamValueError("Bank account does not exist"); + } + + if (!account.getPrimaryHolder().equals(customer) && !account.getHolders().contains(customer)) { + throw new NotAuthorizedError(); + } + + return account; + } + private String genToken() { char[] res = new char[TOKEN_LENGTH]; diff --git a/src/main/java/honours/ing/banq/event/InterestEvent.java b/src/main/java/honours/ing/banq/event/InterestEvent.java index 7a4db68..3eb47cb 100644 --- a/src/main/java/honours/ing/banq/event/InterestEvent.java +++ b/src/main/java/honours/ing/banq/event/InterestEvent.java @@ -1,76 +1,16 @@ package honours.ing.banq.event; -import honours.ing.banq.account.BankAccount; -import honours.ing.banq.account.BankAccountRepository; -import honours.ing.banq.transaction.TransactionService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.math.BigDecimal; -import java.math.RoundingMode; import java.util.Calendar; import java.util.Date; -import java.util.List; import static java.util.Calendar.*; +import static java.util.Calendar.DAY_OF_MONTH; /** - * @author Jeffrey Bakker - * @since 5-8-17 + * @author jeffrey + * @since 12-8-17 */ -@Component -public class InterestEvent implements Event { - - private static final BigDecimal INTEREST = new BigDecimal("0.10"); - private static final BigDecimal MONTHLY_RATE = new BigDecimal(Math.pow(INTEREST.doubleValue() + 1.0, 1.0 / 12.0)) - .subtract(new BigDecimal(1.0)); - - private static final BigDecimal ZERO = new BigDecimal(0.0d); - - // Services - private TransactionService transactionService; - - // Repositories - private BankAccountRepository accountRepository; - - @Autowired - public InterestEvent(TransactionService transactionService, BankAccountRepository accountRepository) { - this.transactionService = transactionService; - this.accountRepository = accountRepository; - } - - @Override - public void execute(long time) { - final Calendar c = Calendar.getInstance(); - c.setTime(new Date(time)); - - //noinspection MagicConstant - final boolean firstOfMonth = c.get(DAY_OF_MONTH) == 1; - - final int daysInMonth = c.getActualMaximum(DAY_OF_MONTH); - final BigDecimal interest = MONTHLY_RATE.divide( - new BigDecimal((double) daysInMonth), - 7, RoundingMode.HALF_UP); - - List accounts = accountRepository.findAll(); - - for (BankAccount account : accounts) { - if (account.getLowestBalance().compareTo(ZERO) >= 0) { - continue; - } - - account.addInterest(account.getLowestBalance().multiply(interest).multiply(new BigDecimal(-1.0))); - account.resetLowestBalance(); - - if (firstOfMonth) { - transactionService.forcePayFromAccount( - account, account.getInterest().setScale(2, BigDecimal.ROUND_HALF_UP), "Interest"); - account.resetInterest(); - } - - accountRepository.save(account); - } - } +public abstract class InterestEvent implements Event { @Override public long nextIteration(long lastIteration) { diff --git a/src/main/java/honours/ing/banq/event/OverdraftInterestEvent.java b/src/main/java/honours/ing/banq/event/OverdraftInterestEvent.java new file mode 100644 index 0000000..3b203c5 --- /dev/null +++ b/src/main/java/honours/ing/banq/event/OverdraftInterestEvent.java @@ -0,0 +1,80 @@ +package honours.ing.banq.event; + +import honours.ing.banq.account.BankAccount; +import honours.ing.banq.account.BankAccountRepository; +import honours.ing.banq.account.CheckingAccount; +import honours.ing.banq.transaction.TransactionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import static java.util.Calendar.*; + +/** + * @author Jeffrey Bakker + * @since 5-8-17 + */ +@Component +public class OverdraftInterestEvent extends InterestEvent { + + private static final BigDecimal INTEREST = new BigDecimal("0.10"); + private static final BigDecimal MONTHLY_RATE = new BigDecimal(Math.pow(INTEREST.doubleValue() + 1.0, 1.0 / 12.0)) + .subtract(new BigDecimal(1.0)); + + private static final BigDecimal ZERO = new BigDecimal(0.0d); + + // Services + private TransactionService transactionService; + + // Repositories + private BankAccountRepository accountRepository; + + @Autowired + public OverdraftInterestEvent(TransactionService transactionService, BankAccountRepository accountRepository) { + this.transactionService = transactionService; + this.accountRepository = accountRepository; + } + + @Override + public void execute(long time) { + final Calendar c = Calendar.getInstance(); + c.setTime(new Date(time)); + + //noinspection MagicConstant + final boolean firstOfMonth = c.get(DAY_OF_MONTH) == 1; + + final int daysInMonth = c.getActualMaximum(DAY_OF_MONTH); + final BigDecimal interest = MONTHLY_RATE.divide( + new BigDecimal((double) daysInMonth), + 7, RoundingMode.HALF_UP); + + List accounts = accountRepository.findAll(); + + for (BankAccount account : accounts) { + if (account.getCheckingAccount().getLowestBalance().compareTo(ZERO) >= 0) { + continue; + } + + CheckingAccount ca = account.getCheckingAccount(); + + ca.addInterest(ca.getLowestBalance().multiply(interest).multiply(new BigDecimal(-1.0))); + ca.resetLowestBalance(); + + if (firstOfMonth) { + transactionService.forceTransactionAccount( + ca, ca.getInterest() + .multiply(new BigDecimal("-1.0")) + .setScale(2, BigDecimal.ROUND_HALF_UP), "Interest"); + ca.resetInterest(); + } + + accountRepository.save(account); + } + } + +} diff --git a/src/main/java/honours/ing/banq/event/SavingsInterestEvent.java b/src/main/java/honours/ing/banq/event/SavingsInterestEvent.java new file mode 100644 index 0000000..93ec93b --- /dev/null +++ b/src/main/java/honours/ing/banq/event/SavingsInterestEvent.java @@ -0,0 +1,117 @@ +package honours.ing.banq.event; + +import honours.ing.banq.account.BankAccount; +import honours.ing.banq.account.BankAccountRepository; +import honours.ing.banq.account.SavingAccount; +import honours.ing.banq.transaction.TransactionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import static java.util.Calendar.*; + +/** + * @author Jeffrey Bakker + * @since 5-8-17 + */ +@Component +public class SavingsInterestEvent extends InterestEvent { + + private static final InterestRate[] RATES = { + new InterestRate(new BigDecimal("0.0015"), new BigDecimal("25000")), + new InterestRate(new BigDecimal("0.0015"), new BigDecimal("75000")), + new InterestRate(new BigDecimal("0.0020"), new BigDecimal("1000000")), + }; + + // Services + private TransactionService transactionService; + + // Repositories + private BankAccountRepository accountRepository; + + @Autowired + public SavingsInterestEvent(TransactionService transactionService, BankAccountRepository accountRepository) { + this.transactionService = transactionService; + this.accountRepository = accountRepository; + } + + @Override + public void execute(long time) { + final Calendar c = Calendar.getInstance(); + c.setTime(new Date(time)); + + //noinspection MagicConstant + final boolean firstOfYear = c.get(DAY_OF_MONTH) == 1 && c.get(MONTH) == 0; + final InterestRate[] rates = calcDailyRates(c, RATES); + + List accounts = accountRepository.findAll(); + + for (BankAccount account : accounts) { + SavingAccount sa = account.getSavingAccount(); + if (sa == null) { + continue; + } + + BigDecimal balance = sa.getLowestBalance(); + BigDecimal lastMax = new BigDecimal("0.00"); + BigDecimal interest = new BigDecimal("0.00"); + + for (InterestRate rate : rates) { + balance = balance.subtract(lastMax); + if (balance.compareTo(BigDecimal.ZERO) <= 0) { + break; + } + + BigDecimal delta = rate.maxBalance.subtract(lastMax); + lastMax = rate.maxBalance; + + BigDecimal rateAmt = balance.min(delta); + interest = interest.add(rateAmt.multiply(rate.interest)); + } + + sa.addInterest(interest); + sa.resetLowestBalance(); + + if (firstOfYear) { + transactionService.forceTransactionAccount( + sa, sa.getInterest() + .setScale(2, BigDecimal.ROUND_HALF_UP), "Interest"); + sa.resetInterest(); + } + + accountRepository.save(account); + } + } + + private static InterestRate[] calcDailyRates(Calendar now, InterestRate[] yearlyRates) { + InterestRate[] res = new InterestRate[yearlyRates.length]; + + final int daysInYear = now.getActualMaximum(DAY_OF_YEAR); + + for (int i = 0; i < res.length; i++) { + res[i] = new InterestRate( + yearlyRates[i].interest.divide(new BigDecimal((double) daysInYear), 7, RoundingMode.HALF_UP), + yearlyRates[i].maxBalance + ); + } + + return res; + } + + private static class InterestRate { + + public BigDecimal interest; + public BigDecimal maxBalance; + + public InterestRate(BigDecimal interest, BigDecimal maxBalance) { + this.interest = interest; + this.maxBalance = maxBalance; + } + } + +} diff --git a/src/main/java/honours/ing/banq/info/bean/BalanceBean.java b/src/main/java/honours/ing/banq/info/bean/BalanceBean.java index 0ceedb5..3fc2af7 100644 --- a/src/main/java/honours/ing/banq/info/bean/BalanceBean.java +++ b/src/main/java/honours/ing/banq/info/bean/BalanceBean.java @@ -10,12 +10,19 @@ public class BalanceBean { private Double balance; + private Double savingsAccountBalance; public BalanceBean(BankAccount bankAccount) { - balance = bankAccount.getBalance().doubleValue(); + balance = bankAccount.getCheckingAccount().getBalance().doubleValue(); + savingsAccountBalance = bankAccount.getSavingAccount() == null + ? 0.0d : bankAccount.getSavingAccount().getBalance().doubleValue(); } public Double getBalance() { return balance; } + + public Double getSavingsAccountBalance() { + return savingsAccountBalance; + } } diff --git a/src/main/java/honours/ing/banq/time/TimeServiceImpl.java b/src/main/java/honours/ing/banq/time/TimeServiceImpl.java index e884a4d..6edc672 100644 --- a/src/main/java/honours/ing/banq/time/TimeServiceImpl.java +++ b/src/main/java/honours/ing/banq/time/TimeServiceImpl.java @@ -77,7 +77,15 @@ public Object reset() { entityManager.flush(); entityManager.createNativeQuery("SET FOREIGN_KEY_CHECKS=0;").executeUpdate(); - tableNames.forEach((name) -> entityManager.createNativeQuery("TRUNCATE TABLE " + name).executeUpdate()); + + tableNames.forEach((name) -> { + // This is to test for some weird glitch where there is also a table name containing a select query for some + // table, last time I checked it was for the "checking_account" table + if (!name.startsWith("(")) { + entityManager.createNativeQuery("TRUNCATE TABLE " + name).executeUpdate(); + } + }); + entityManager.createNativeQuery("SET FOREIGN_KEY_CHECKS=1;").executeUpdate(); eventInterceptor.calcTimers(); diff --git a/src/main/java/honours/ing/banq/transaction/TransactionService.java b/src/main/java/honours/ing/banq/transaction/TransactionService.java index d37ddfb..b0d84f8 100644 --- a/src/main/java/honours/ing/banq/transaction/TransactionService.java +++ b/src/main/java/honours/ing/banq/transaction/TransactionService.java @@ -3,6 +3,7 @@ import com.googlecode.jsonrpc4j.JsonRpcParam; import com.googlecode.jsonrpc4j.JsonRpcService; import honours.ing.banq.InvalidParamValueError; +import honours.ing.banq.account.Account; import honours.ing.banq.account.BankAccount; import honours.ing.banq.auth.CardBlockedError; import honours.ing.banq.auth.InvalidPINError; @@ -49,7 +50,7 @@ void payFromAccount( @JsonRpcParam("pinCard") String pinCard, @JsonRpcParam("pinCode") String pinCode, @JsonRpcParam("amount") Double amount) throws InvalidParamValueError, InvalidPINError, CardBlockedError; - void forcePayFromAccount(BankAccount account, BigDecimal amount, String description); + void forceTransactionAccount(Account account, BigDecimal amount, String description); /** * Transer money from one {@link BankAccount} to another {@link BankAccount}. diff --git a/src/main/java/honours/ing/banq/transaction/TransactionServiceImpl.java b/src/main/java/honours/ing/banq/transaction/TransactionServiceImpl.java index e47b8ff..a664543 100644 --- a/src/main/java/honours/ing/banq/transaction/TransactionServiceImpl.java +++ b/src/main/java/honours/ing/banq/transaction/TransactionServiceImpl.java @@ -2,8 +2,10 @@ import com.googlecode.jsonrpc4j.spring.AutoJsonRpcServiceImpl; import honours.ing.banq.InvalidParamValueError; +import honours.ing.banq.account.Account; import honours.ing.banq.account.BankAccount; import honours.ing.banq.account.BankAccountRepository; +import honours.ing.banq.account.CheckingAccount; import honours.ing.banq.auth.AuthService; import honours.ing.banq.auth.CardBlockedError; import honours.ing.banq.auth.InvalidPINError; @@ -33,19 +35,15 @@ public class TransactionServiceImpl implements TransactionService { @Autowired private AuthService auth; + @Autowired + private TimeService timeService; + // Repositories @Autowired private BankAccountRepository bankAccountRepository; @Autowired private TransactionRepository transactionRepository; - - @Autowired - private CardRepository cardRepository; - - @Autowired - private TimeService timeService; - @Override public void depositIntoAccount(String iBAN, String pinCard, String pinCode, Double amount) throws InvalidParamValueError, InvalidPINError, CardBlockedError { @@ -53,15 +51,21 @@ public void depositIntoAccount(String iBAN, String pinCard, String pinCode, Doub throw new InvalidParamValueError("The given IBAN is not valid."); } - BankAccount bankAccount = auth.getAuthorizedAccount(iBAN, pinCard, pinCode); + BankAccount bankAccount = auth.getAuthorizedBankAccount(iBAN, pinCard, pinCode); // Check balance if (amount <= 0d) { throw new InvalidParamValueError("Amount should be greater than 0."); } + Account account = iBAN.endsWith("S") + ? bankAccount.getSavingAccount() : bankAccount.getCheckingAccount(); + if (account == null) { + throw new InvalidParamValueError("The account does not exist"); + } + // Update balance - bankAccount.addBalance(new BigDecimal(amount)); + account.addBalance(new BigDecimal(amount).setScale(2, BigDecimal.ROUND_HALF_UP)); bankAccountRepository.save(bankAccount); // Save transaction @@ -82,42 +86,68 @@ public void payFromAccount(String sourceIBAN, String targetIBAN, String pinCard, throw new InvalidParamValueError("The given target IBAN is not valid."); } - BankAccount fromBankAccount = auth.getAuthorizedAccount(sourceIBAN, pinCard, pinCode); - BankAccount toBankAccount = bankAccountRepository.findOne((int) IBANUtil.getAccountNumber(targetIBAN)); + BankAccount sourceBankAccount = auth.getAuthorizedBankAccount(sourceIBAN, pinCard, pinCode); + Account sourceAccount = sourceIBAN.endsWith("S") ? + sourceBankAccount.getSavingAccount() : sourceBankAccount.getCheckingAccount(); + + BankAccount destBankAccount = bankAccountRepository.findOne((int) IBANUtil.getAccountNumber(targetIBAN)); + Account destAccount = null; + String destName = ""; - BigDecimal amt = new BigDecimal(amount); + if (destBankAccount != null) { + destAccount = targetIBAN.endsWith("S") ? + destBankAccount.getSavingAccount() : destBankAccount.getCheckingAccount(); + destName = destBankAccount.getPrimaryHolder().getName() + " " + + destBankAccount.getPrimaryHolder().getSurname(); + } + + BigDecimal amt = new BigDecimal(amount).setScale(2, BigDecimal.ROUND_HALF_UP); // Check balance if (amount <= 0.0d) { throw new InvalidParamValueError("Amount should be greater than 0."); } - if (!fromBankAccount.canPayAmount(amt)) { + if (!sourceAccount.canPayAmount(amt)) { throw new InvalidParamValueError("Not enough balance on account."); } // Update balance - fromBankAccount.addBalance(amt.multiply(new BigDecimal(-1.0))); - toBankAccount.addBalance(amt); - bankAccountRepository.save(fromBankAccount); - bankAccountRepository.save(toBankAccount); + sourceAccount.addBalance(amt.multiply(new BigDecimal(-1.0))); + bankAccountRepository.save(sourceBankAccount); + + if (destBankAccount != null) { + destAccount.addBalance(amt); + bankAccountRepository.save(destBankAccount); + } // Save Transaction Transaction transaction = new Transaction( - sourceIBAN, targetIBAN, toBankAccount.getPrimaryHolder().getName(), + sourceIBAN, targetIBAN, destName, timeService.getDate().getDate(), amount, "Payment with debit card."); transactionRepository.save(transaction); } @Override - public void forcePayFromAccount(BankAccount account, BigDecimal amount, String description) { - account.addBalance(amount.multiply(new BigDecimal(-1.0))); - bankAccountRepository.save(account); + public void forceTransactionAccount(Account account, BigDecimal amount, String description) { + account.addBalance(amount.setScale(2, BigDecimal.ROUND_HALF_UP)); + bankAccountRepository.save(account.getAccount()); + + String iBAN = IBANUtil.generateIBAN(account.getAccount()) + (account instanceof CheckingAccount ? "S" : ""); // Save Transaction - Transaction transaction = new Transaction( - IBANUtil.generateIBAN(account), "", "Banq", - timeService.getDate().getDate(), amount.doubleValue(), description); + Transaction transaction; + if (amount.compareTo(BigDecimal.ZERO) <= 0) { + transaction = new Transaction( + iBAN, "", "Banq", + timeService.getDate().getDate(), amount.multiply(new BigDecimal("-1.0")).doubleValue(), description); + } else { + transaction = new Transaction( + "", iBAN, + account.getAccount().getPrimaryHolder().getName() + + " " + account.getAccount().getPrimaryHolder().getSurname(), + timeService.getDate().getDate(), amount.doubleValue(), description); + } transactionRepository.save(transaction); } @@ -132,31 +162,28 @@ public void transferMoney(String authToken, String sourceIBAN, String targetIBAN throw new InvalidParamValueError("The given target IBAN is not valid."); } - BankAccount fromBankAccount = bankAccountRepository.findOne((int) IBANUtil.getAccountNumber(sourceIBAN)); - BankAccount toBankAccount = bankAccountRepository.findOne((int) IBANUtil.getAccountNumber(targetIBAN)); - Customer customer = auth.getAuthorizedCustomer(authToken); + BankAccount sourceBankAccount = auth.getAuthorizedBankAccount(authToken, sourceIBAN); + Account sourceAccount = sourceBankAccount.getCheckingAccount(); - // Check if bank account is held by customer - if (!fromBankAccount.getHolders().contains(customer) && !fromBankAccount.getPrimaryHolder().equals(customer)) { - throw new NotAuthorizedError(); - } + BankAccount destBankAccount = bankAccountRepository.findOne((int) IBANUtil.getAccountNumber(targetIBAN)); + Account destAccount = destBankAccount.getCheckingAccount(); - BigDecimal amt = new BigDecimal(amount); + BigDecimal amt = new BigDecimal(amount).setScale(2, BigDecimal.ROUND_HALF_UP); // Check balance if (amount <= 0.0d) { throw new InvalidParamValueError("Amount should be greater than 0."); } - if (!fromBankAccount.canPayAmount(amt)) { + if (!sourceBankAccount.getCheckingAccount().canPayAmount(amt)) { throw new InvalidParamValueError("Not enough balance on account."); } // Update balance - fromBankAccount.addBalance(amt.multiply(new BigDecimal(-1.0))); - toBankAccount.addBalance(amt); - bankAccountRepository.save(fromBankAccount); - bankAccountRepository.save(toBankAccount); + sourceAccount.addBalance(amt.multiply(new BigDecimal(-1.0))); + destAccount.addBalance(amt); + bankAccountRepository.save(sourceBankAccount); + bankAccountRepository.save(destBankAccount); // Save Transaction Transaction transaction = new Transaction( diff --git a/src/main/java/honours/ing/banq/util/IBANUtil.java b/src/main/java/honours/ing/banq/util/IBANUtil.java index 5c25d40..1aa978c 100644 --- a/src/main/java/honours/ing/banq/util/IBANUtil.java +++ b/src/main/java/honours/ing/banq/util/IBANUtil.java @@ -15,12 +15,18 @@ public static boolean isValidIBAN(String iBAN) { } String check = iBAN.substring(2, 4); + int offset = iBAN.endsWith("S") ? 1 : 0; - return calculateChecksum(iBAN.substring(4, iBAN.length())).equals(check); + return calculateChecksum(iBAN.substring(4, iBAN.length() - offset)).equals(check); } public static long getAccountNumber(String iBAN) { - return Long.parseLong(iBAN.substring(8, iBAN.length())); + String nr = iBAN; + if (nr.endsWith("S")) { + nr = nr.substring(0, nr.length() - 1); + } + + return Long.parseLong(nr.substring(8, nr.length())); } public static String generateIBAN(BankAccount account) { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f9e13ec..1c588f0 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,6 +1,13 @@ -spring.jpa.hibernate.ddl-auto=update -spring.jpa.hibernate.use-new-id-generator-mappings=true +# Specify the data source spring.datasource.url=jdbc:mysql://localhost:3306/ing_db spring.datasource.username=ing_project spring.datasource.password=localhost1234 + +# Debugging +spring.jpa.show-sql=false + +# Hibernate properties +spring.jpa.hibernate.ddl-auto=update +spring.jpa.hibernate.use-new-id-generator-mappings=true + server.port=8080 \ No newline at end of file diff --git a/src/test/java/honours/ing/banq/BoilerplateTest.java b/src/test/java/honours/ing/banq/BoilerplateTest.java index 4dd5243..833dbd9 100644 --- a/src/test/java/honours/ing/banq/BoilerplateTest.java +++ b/src/test/java/honours/ing/banq/BoilerplateTest.java @@ -1,12 +1,16 @@ package honours.ing.banq; +import honours.ing.banq.access.NoEffectError; import honours.ing.banq.account.BankAccountService; import honours.ing.banq.auth.AuthService; import honours.ing.banq.bean.AccountInfo; +import honours.ing.banq.card.CardService; import honours.ing.banq.config.TestConfiguration; import honours.ing.banq.info.InfoService; +import honours.ing.banq.info.bean.BalanceBean; import honours.ing.banq.time.TimeService; import honours.ing.banq.transaction.TransactionService; +import honours.ing.banq.util.IBANUtil; import org.junit.After; import org.junit.Before; import org.junit.Ignore; @@ -44,6 +48,9 @@ public class BoilerplateTest { @Autowired protected AuthService authService; + @Autowired + protected CardService cardService; + @Autowired protected InfoService infoService; @@ -86,6 +93,51 @@ public void tearDown() throws Exception { account1.token = authService.getAuthToken("jantje96", "1234").getAuthToken(); account2.token = authService.getAuthToken("piet1", "1234").getAuthToken(); + // Unblock pin cards + try { + cardService.unblockCard(account1.token, account1.iBan, account1.cardNumber); + } catch (NoEffectError ignored) { } + + try { + cardService.unblockCard(account2.token, account2.iBan, account2.cardNumber); + } catch (NoEffectError ignored) { } + + // Clear balance from account 1 + BalanceBean acc1 = infoService.getBalance(account1.token, account1.iBan); + if (acc1.getBalance() <= -0.01d) { + transactionService.depositIntoAccount( + account1.iBan, account1.cardNumber, account1.pin, acc1.getBalance() * -1.0d); + } else if (acc1.getBalance() >= 0.01d) { + transactionService.payFromAccount(account1.iBan, IBANUtil.generateIBAN(12345678), + account1.cardNumber, account1.pin, acc1.getBalance()); + } + + if (acc1.getSavingsAccountBalance() <= -0.01d) { + transactionService.depositIntoAccount( + account1.iBan + "S", account1.cardNumber, account1.pin, acc1.getSavingsAccountBalance() * -1.0d); + } else if (acc1.getSavingsAccountBalance() >= 0.01d) { + transactionService.payFromAccount(account1.iBan + "S", IBANUtil.generateIBAN(12345678), + account1.cardNumber, account1.pin, acc1.getSavingsAccountBalance()); + } + + // Clear balance from account 2 + BalanceBean acc2 = infoService.getBalance(account2.token, account2.iBan); + if (acc2.getBalance() <= -0.01d) { + transactionService.depositIntoAccount( + account2.iBan, account2.cardNumber, account2.pin, acc2.getBalance() * -1.0d); + } else if (acc2.getBalance() >= 0.01d) { + transactionService.payFromAccount(account2.iBan, IBANUtil.generateIBAN(12345678), + account2.cardNumber, account2.pin, acc2.getBalance()); + } + + if (acc2.getSavingsAccountBalance() <= -0.01d) { + transactionService.depositIntoAccount( + account2.iBan + "S", account2.cardNumber, account2.pin, acc2.getSavingsAccountBalance() * -1.0d); + } else if (acc2.getSavingsAccountBalance() >= 0.01d) { + transactionService.payFromAccount(account2.iBan + "S", IBANUtil.generateIBAN(12345678), + account2.cardNumber, account2.pin, acc2.getSavingsAccountBalance()); + } + accountService.closeAccount(account1.token, account1.iBan); accountService.closeAccount(account2.token, account2.iBan); @@ -97,4 +149,9 @@ protected void testBalance(AccountInfo account, double expected, double accuracy assertEquals(expected, infoService.getBalance(account.token, account.iBan).getBalance(), accuracy); } + protected void testSavingsBalance(AccountInfo account, double expected, double accuracy) throws Exception { + account.token = authService.getAuthToken(account.username, account.password).getAuthToken(); + assertEquals(expected, infoService.getBalance(account.token, account.iBan).getSavingsAccountBalance(), accuracy); + } + } diff --git a/src/test/java/honours/ing/banq/auth/AuthServiceTest.java b/src/test/java/honours/ing/banq/auth/AuthServiceTest.java index 87422fb..65b0840 100644 --- a/src/test/java/honours/ing/banq/auth/AuthServiceTest.java +++ b/src/test/java/honours/ing/banq/auth/AuthServiceTest.java @@ -20,8 +20,6 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import java.util.Calendar; - import static org.junit.Assert.*; /** @@ -157,7 +155,7 @@ public void getAuthorizedCustomerInvalidToken() throws Exception { @Test public void getAuthorizedAccount() throws Exception { - BankAccount account = service.getAuthorizedAccount(accountInfo.iBan, accountInfo.cardNumber, accountInfo.pin); + BankAccount account = service.getAuthorizedBankAccount(accountInfo.iBan, accountInfo.cardNumber, accountInfo.pin); assertNotNull(account); assertEquals(accountInfo.iBan, IBANUtil.generateIBAN(account)); @@ -166,19 +164,19 @@ public void getAuthorizedAccount() throws Exception { @Test public void getAuthorizedAccountInvalidIBan() throws Exception { try { - service.getAuthorizedAccount(null, accountInfo.cardNumber, accountInfo.pin); + service.getAuthorizedBankAccount(null, accountInfo.cardNumber, accountInfo.pin); fail(); } catch (InvalidParamValueError ignored) { } try { - service.getAuthorizedAccount("", accountInfo.cardNumber, accountInfo.pin); + service.getAuthorizedBankAccount("", accountInfo.cardNumber, accountInfo.pin); fail(); } catch (InvalidParamValueError ignored) { } try { - service.getAuthorizedAccount("NL45INGB0705001903", accountInfo.cardNumber, accountInfo.pin); + service.getAuthorizedBankAccount("NL45INGB0705001903", accountInfo.cardNumber, accountInfo.pin); fail(); } catch (InvalidParamValueError ignored) { } @@ -187,19 +185,19 @@ public void getAuthorizedAccountInvalidIBan() throws Exception { @Test public void getAuthorizedAccountInvalidCardNumber() throws Exception { try { - service.getAuthorizedAccount(accountInfo.iBan, null, accountInfo.pin); + service.getAuthorizedBankAccount(accountInfo.iBan, null, accountInfo.pin); fail(); } catch (InvalidParamValueError ignored) { } try { - service.getAuthorizedAccount(accountInfo.iBan, "", accountInfo.pin); + service.getAuthorizedBankAccount(accountInfo.iBan, "", accountInfo.pin); fail(); } catch (InvalidParamValueError ignored) { } try { - service.getAuthorizedAccount(accountInfo.iBan, "-1", accountInfo.pin); + service.getAuthorizedBankAccount(accountInfo.iBan, "-1", accountInfo.pin); fail(); } catch (InvalidParamValueError ignored) { } @@ -208,13 +206,13 @@ public void getAuthorizedAccountInvalidCardNumber() throws Exception { @Test public void getAuthorizedAccountInvalidPin() throws Exception { try { - service.getAuthorizedAccount(accountInfo.iBan, accountInfo.cardNumber, null); + service.getAuthorizedBankAccount(accountInfo.iBan, accountInfo.cardNumber, null); fail(); } catch (InvalidParamValueError ignored) { } try { - service.getAuthorizedAccount(accountInfo.iBan, accountInfo.cardNumber, ""); + service.getAuthorizedBankAccount(accountInfo.iBan, accountInfo.cardNumber, ""); fail(); } catch (InvalidPINError ignored) { } diff --git a/src/test/java/honours/ing/banq/event/InterestEventTest.java b/src/test/java/honours/ing/banq/event/OverdraftInterestEventTest.java similarity index 95% rename from src/test/java/honours/ing/banq/event/InterestEventTest.java rename to src/test/java/honours/ing/banq/event/OverdraftInterestEventTest.java index 97a663d..d726c8e 100644 --- a/src/test/java/honours/ing/banq/event/InterestEventTest.java +++ b/src/test/java/honours/ing/banq/event/OverdraftInterestEventTest.java @@ -11,7 +11,7 @@ * @author jeffrey * @since 5-8-17 */ -public class InterestEventTest extends BoilerplateTest { +public class OverdraftInterestEventTest extends BoilerplateTest { @Test public void testInterest() throws Exception { diff --git a/src/test/java/honours/ing/banq/event/SavingsInterestEventTest.java b/src/test/java/honours/ing/banq/event/SavingsInterestEventTest.java new file mode 100644 index 0000000..011fa33 --- /dev/null +++ b/src/test/java/honours/ing/banq/event/SavingsInterestEventTest.java @@ -0,0 +1,49 @@ +package honours.ing.banq.event; + +import honours.ing.banq.BoilerplateTest; +import honours.ing.banq.time.TimeServiceImpl; +import org.junit.Test; + +import java.util.Calendar; +import java.util.Date; + +import static org.junit.Assert.*; + +/** + * @author jeffrey + * @since 12-8-17 + */ +public class SavingsInterestEventTest extends BoilerplateTest { + + @Test + public void testInterest() throws Exception { + long now = TimeServiceImpl.currentTimeMillis(); + + Calendar c = Calendar.getInstance(); + c.setTime(new Date(now)); + c.add(Calendar.YEAR, 1); + c.set(Calendar.MONTH, Calendar.JANUARY); + c.set(Calendar.DAY_OF_MONTH, 1); + c.set(Calendar.HOUR_OF_DAY, 0); + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + + long nextJan = c.getTimeInMillis(); + long diff = (nextJan - now) / (1000 * 60 * 60 * 24) + 1; + + // Shift the time to the first of january so that all the math is correct + timeService.simulateTime((int) diff); + + account1.token = authService.getAuthToken(account1.username, account1.password).getAuthToken(); + accountService.openSavingsAccount(account1.token, account1.iBan); + transactionService.depositIntoAccount(account1.iBan + "S", account1.cardNumber, account1.pin, 1000.0); + + testSavingsBalance(account1, 1000.0, 0.01); + + timeService.simulateTime(365); + + testSavingsBalance(account1, 1001.50, 0.01); + } + +} \ No newline at end of file diff --git a/src/test/java/honours/ing/banq/time/ResetTest.java b/src/test/java/honours/ing/banq/time/ResetTest.java new file mode 100644 index 0000000..4e10603 --- /dev/null +++ b/src/test/java/honours/ing/banq/time/ResetTest.java @@ -0,0 +1,34 @@ +package honours.ing.banq.time; + +import honours.ing.banq.config.TestConfiguration; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import javax.transaction.Transactional; + +/** + * @author jeffrey + * @since 7-8-17 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest +@Import(TestConfiguration.class) +@Transactional +@ActiveProfiles("test") +public class ResetTest { + + @Autowired + private TimeService timeService; + + @Test + public void reset() { + timeService.reset(); + } + +} diff --git a/src/test/java/honours/ing/banq/util/IBANUtilTest.java b/src/test/java/honours/ing/banq/util/IBANUtilTest.java index f3df745..1137809 100644 --- a/src/test/java/honours/ing/banq/util/IBANUtilTest.java +++ b/src/test/java/honours/ing/banq/util/IBANUtilTest.java @@ -20,7 +20,9 @@ public void testGenerateIBAN() { String iban = generateIBAN(accountNumber); assertTrue(isValidIBAN(iban)); + assertTrue(isValidIBAN(iban + "S")); assertEquals(accountNumber, getAccountNumber(iban)); + assertEquals(accountNumber, getAccountNumber(iban + "S")); } }