Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
1,754 changes: 1,754 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"type": "module",
"devDependencies": {
"jasmine": "^5.1.0"
},
"dependencies": {
"date-and-time": "^3.3.0",
"numeral": "^2.0.6",
"puppeteer-html-pdf": "^4.0.8",
"uuid": "^10.0.0"
},
"name": "ood-bank-challenge",
"description": "This challenge asks you to put into practice everything you've learned about object oriented programming. Use the concepts we've covered to help organise your code, keep things modular and easy to change.",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
131 changes: 131 additions & 0 deletions spec/Accounts.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { Account, CheckingAccount, SavingsAccount, InvestmentAccount } from "../src/Accounts.js";

describe("Accounts", () => {
let testAccount;

beforeEach(() => {
testAccount = new Account("Will Baxter", "12345678");
});

it("should accepts credits into a list of transactions", () => {
testAccount.credit(10);

expect(testAccount.getTransactions().length).toEqual(1);
expect(Number(testAccount.getTransactions()[0].amount)).toEqual(10);
expect(testAccount.getTransactions()[0].constructor.name).toEqual("Credit");
});

it("should accepts debits into a list of transactions", () => {
testAccount.credit(10);
testAccount.debit(10);

expect(testAccount.getTransactions().length).toEqual(2);
});

it("should have a method to return all credits to account", () => {
testAccount.credit(10);
testAccount.debit(5);
testAccount.credit(3);

expect(testAccount.credits.length).toEqual(2);
});

it("should have a method to return all debits to account", () => {
testAccount.credit(10);
testAccount.debit(5);
testAccount.debit(3);

expect(testAccount.debits.length).toEqual(2);
});

it("should have a method to return current balance", () => {
testAccount.credit(23.32);
testAccount.debit(12.01);

expect(testAccount.balance).toEqual(11.31);
});

it("should have a method to return transactions between certain dates", () => {
testAccount.credit(23.32);
testAccount.debit(12.01);

expect(testAccount.balance).toEqual(11.31);
});

it("should return transactions for a set period", () => {
testAccount.credit(10, '2023-9-3');
testAccount.debit(5, '2023-9-5');
testAccount.credit(3, '2023-9-7');
testAccount.credit(23.32, '2023-9-9');
testAccount.debit(12.01, '2023-9-11');
testAccount.credit(10, '2023-9-13');
testAccount.debit(5, '2023-9-15');
testAccount.credit(3, '2023-9-17');

expect(
testAccount.getTransactions("2023-9-4", "2023-9-10").length
).toEqual(3);
Comment on lines +65 to +67

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this, but I think you should also have some expects for each of the transaction dates to ensure the correct transactions are being returned

});

it("should prevent debits that take balance below zero", () => {
testAccount.credit(10);
expect(() => testAccount.debit(12)).toThrowError("Insufficient funds");
});
});

describe("Checking Accounts", () => {
let testCheckingAccount;

beforeEach(() => {
testCheckingAccount = new CheckingAccount("Will Baxter", 12345678);
});
it("should allow debits up to the overdraft", () => {
testCheckingAccount.overdraft = 10
testCheckingAccount.credit(5);
testCheckingAccount.debit(15);

expect(testCheckingAccount.balance).toEqual(-10)
});

});

describe("Savings Accounts", () => {
let testSavingsAccount;

beforeEach(() => {
testSavingsAccount = new SavingsAccount("Will Baxter", 12345678);
});
it("should only allow 20,000 worth of deposits per year", () => {
testSavingsAccount.credit(10001, '2024-1-1')

expect(()=> {testSavingsAccount.credit(10001, '2024-13-06')}).toThrowError('You are only able to deposit £20,000 per year')



});

});


describe("Accounts", () => {
let testAccount;

it('should make a PDF', () => {
testAccount = new Account("Will Baxter", "12345678");
testAccount.credit(10, '2023-9-3');
testAccount.debit(5, '2023-9-5');
testAccount.credit(3, '2023-9-7');
testAccount.credit(23.32, '2023-9-9');
testAccount.debit(12.01, '2023-9-11');
testAccount.credit(10, '2023-9-13');
testAccount.debit(5, '2023-9-15');
testAccount.credit(3, '2023-9-17');

testAccount.getStatement('pdf')

})

});

;

13 changes: 13 additions & 0 deletions spec/support/jasmine.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"spec_dir": "spec",
"spec_files": [
"**/*[sS]pec.?(m)js"
],
"helpers": [
"helpers/**/*.?(m)js"
],
"env": {
"stopSpecOnExpectationFailure": false,
"random": true
}
}
Binary file added src/.DS_Store
Binary file not shown.
188 changes: 188 additions & 0 deletions src/Accounts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import numeral from "numeral";
import date from "date-and-time";
import { Credit, Debit } from "./Transactions.js";
import { Statement } from "./Statement.js";

class Account {
#transactions;
#overdraft;

constructor(accountHolder, accountNumber) {
this.accountHolder = accountHolder;
this.accountNumber = accountNumber;
this.#overdraft = 0;
this.#transactions = [];
}

credit(amount, testDate) {
if (this.constructor.name === "SavingsAccount") {
const today = new Date();
const oneYearAgo = today - 365 * 24 * 60 * 60 * 1000;
const totalCreditsThisYear = this.getTransactions(oneYearAgo, today)
.filter((transaction) => transaction.constructor.name === "Credit")
.reduce((a, b) => a + Number(b.amount), 0);
if (totalCreditsThisYear + amount > 20000) {
throw new Error("You are only able to deposit £20,000 per year");
}
}
Comment on lines +17 to +27

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice solution!


const newTransaction = new Credit(
numeral(amount).format("0.00"),
this.getDate(testDate)
);
this.#transactions.push(newTransaction);
}

debit(amount, testDate) {
if (this.balance - amount < 0 - this.#overdraft) {
throw new Error("Insufficient funds");
}
const newTransaction = new Debit(
numeral(amount).format("0.00"),
this.getDate(testDate)
);
this.#transactions.push(newTransaction);
}

getDate(testDate) {
if (testDate) {
const date = new Date(testDate);
return date;
}
const date = new Date();
return date;
}
Comment on lines +47 to +54

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (testDate) { // why is it called testDate?
  return new Date(testDate)
}

return new Date()


getStatement(type, startDate, endDate) {
let thisStatement;

if (startDate && endDate) {
const statementStartDate = new Date(startDate);
const statementEndDate = new Date(endDate);

if (
isNaN(statementStartDate.getTime()) ||
isNaN(statementEndDate.getTime())
) {
throw new Error("Date format must be YYYY-MM-DD");
}

thisStatement = new Statement(this, statementStartDate, statementEndDate);
} else {
thisStatement = new Statement(this);
}

if (type === "JSON") {
return thisStatement.json;
}
if (type === "pdf") {
return thisStatement.getPDF();
}
if (type === "Console") {
thisStatement.console;
return;
}
return thisStatement;
}

getTransactions(startDate, endDate) {
const transactionsToSort = [...this.#transactions];

const transactionsSortedByDate = transactionsToSort.sort(
(a, b) => a.date.getTime() - b.date.getTime()
);
let ongoingBalance = 0;



transactionsSortedByDate.forEach((transaction) => {
if (transaction.constructor.name === "Debit") {
transaction.balanceAfterTransaction = numeral(
ongoingBalance - Number(transaction.amount)
).format("0.00");
ongoingBalance -= Number(transaction.amount);
}
if (transaction.constructor.name === "Credit") {
transaction.balanceAfterTransaction = numeral(
ongoingBalance + Number(transaction.amount)
).format("0.00");
ongoingBalance += Number(transaction.amount);
}
});

if (startDate && endDate) {
const transactionsWithinPeriod = transactionsSortedByDate.filter(
(transaction) =>
transaction.date > new Date(startDate) &&
transaction.date < new Date(endDate)
);
return transactionsWithinPeriod;
}

return transactionsSortedByDate;
}

get balance() {
const totalCredit = this.credits.reduce((a, b) => a + Number(b.amount), 0);
const totalDebit = this.debits.reduce((a, b) => a + Number(b.amount), 0);

return totalCredit - totalDebit;
}

get credits() {
const filteredTransactions = this.#transactions.filter(
(transaction) => transaction.constructor.name === "Credit"
);
return [...filteredTransactions];
Comment on lines +133 to +136

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filter will return a new array containing the filtered items so you're safe to return it directly

return this.#transactions.filter(
      (transaction) => transaction.constructor.name === "Credit"
    )

}

get debits() {
const filteredTransactions = this.#transactions.filter(
(transaction) => transaction.constructor.name === "Debit"
);
return [...filteredTransactions];
}

get overdraft() {
return this.#overdraft;
}

set overdraft(number) {
if (this.constructor.name !== "CheckingAccount") {
throw new Error("Overdrafts are only allowed on checking accounts");
}
this.#overdraft = number;
}
}

class CheckingAccount extends Account {
constructor(accountHolder, accountNumber) {
super(accountHolder, accountNumber);
}
}

class SavingsAccount extends Account {
constructor(accountHolder, accountNumber) {
super(accountHolder, accountNumber);
}
}

class InvestmentAccount extends Account {
constructor(accountHolder, accountNumber) {
super(accountHolder, accountNumber);
}
}

let testAccount = new Account("Will Baxter", "12345678");
testAccount.credit(10, '2023-9-3');
testAccount.debit(5, '2023-9-5');
testAccount.credit(3, '2023-9-7');
testAccount.credit(23.32, '2023-9-9');
testAccount.debit(12.01, '2023-9-11');
testAccount.credit(10, '2023-9-13');
testAccount.debit(5, '2023-9-15');
testAccount.credit(3, '2023-9-17');

testAccount.getStatement('pdf')

export { Account, CheckingAccount, SavingsAccount, InvestmentAccount };
Loading