-
Notifications
You must be signed in to change notification settings - Fork 92
Will Baxter #55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Will Baxter #55
Changes from all commits
be546ce
1777ac3
81e33b4
da325d3
7060564
a174c81
431ebef
244b7f7
3f93ef0
40ba7ae
b903d44
4ea4df4
25c2f9a
4fc7762
0a7204d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| 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" | ||
| } |
| 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); | ||
| }); | ||
|
|
||
| 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') | ||
|
|
||
| }) | ||
|
|
||
| }); | ||
|
|
||
| ; | ||
|
|
||
| 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 | ||
| } | ||
| } |
| 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
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 }; | ||
There was a problem hiding this comment.
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