Skip to content

Commit 5a02949

Browse files
committed
feat: exposed ledgercollection; fixed dates
1 parent d7d00ac commit 5a02949

19 files changed

+264
-91
lines changed

.circleci/config.yml

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,38 @@ jobs:
7373
path: ./reports/junit/
7474
- store_artifacts:
7575
path: ./reports/junit/
76+
unit-test-badge:
77+
executor: my-custom-executor
78+
working_directory: ~/tmp
79+
steps:
80+
- checkout
81+
- restore_cache:
82+
keys:
83+
# when lock file changes, use increasingly general patterns to restore cache
84+
- node-{{ .Branch }}-{{ checksum "package-lock.json" }}-<< pipeline.id >>
85+
- restore_cache:
86+
keys:
87+
# when lock file changes, use increasingly general patterns to restore cache
88+
- node-{{ .Branch }}-build-<< pipeline.id >>
89+
- run:
90+
name: Run tests with JUnit as reporter
91+
command: npm run test:cov-and-badge
92+
int-test:
93+
executor: my-custom-executor
94+
working_directory: ~/tmp
95+
steps:
96+
- checkout
97+
- restore_cache:
98+
keys:
99+
# when lock file changes, use increasingly general patterns to restore cache
100+
- node-{{ .Branch }}-{{ checksum "package-lock.json" }}-<< pipeline.id >>
101+
- restore_cache:
102+
keys:
103+
# when lock file changes, use increasingly general patterns to restore cache
104+
- node-{{ .Branch }}-build-<< pipeline.id >>
105+
- run:
106+
name: Run tests with JUnit as reporter
107+
command: npm run test:ci:int
76108
publish:
77109
executor: my-custom-executor
78110
working_directory: ~/tmp
@@ -106,9 +138,20 @@ workflows:
106138
- unit-test:
107139
requires:
108140
- lint
109-
- publish:
141+
- build
142+
- int-test:
110143
requires:
111144
- unit-test
145+
- unit-test-badge:
146+
requires:
147+
- unit-test
148+
filters:
149+
branches:
150+
only: main
151+
- publish:
152+
requires:
153+
- int-test
154+
- unit-test-badge
112155
filters:
113156
branches:
114157
only: main

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# realty-investor-timeline
22

3-
[![CircleCI](https://circleci.com/gh/kvernon/realty-investor-timeline.svg?style=shield)](https://circleci.com/gh/kvernon/realty-investor-timeline) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
4-
[![Wallaby.js](https://img.shields.io/badge/wallaby.js-configured-green.svg)](https://wallabyjs.com) ![](./badges/badge.svg)
3+
[![CircleCI](https://circleci.com/gh/kvernon/realty-investor-timeline.svg?style=shield)](https://circleci.com/gh/kvernon/realty-investor-timeline)
4+
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
5+
[![Wallaby.js](https://img.shields.io/badge/wallaby.js-configured-green.svg)](https://wallabyjs.com)
6+
![](./badges/badge.svg)
57

68
A way to determine if and when your expenses would be covered through rental properties
79

badges/badge.svg

Lines changed: 1 addition & 1 deletion
Loading

jest.integation.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ delete uConfig.testMatch;
1313

1414
export default {
1515
...uConfig,
16-
testMatch: ['<rootDir>/tests_int/*.+(int-spec|int-test).[jt]s?(x)'],
16+
verbose: false,
17+
testMatch: ['<rootDir>/tests_int/*.+(spec|test).[jt]s?(x)'],
1718
};

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
"test": "jest",
1313
"pretest": "rimraf ./.jestcache",
1414
"test:cov": "jest --coverage",
15+
"test:cov:badge": "jest --coverage --ci --runInBand",
1516
"test:ci": "jest --config ./jest.ci.config.ts --ci --runInBand",
1617
"test:int": "jest --config ./jest.integation.config.ts",
18+
"test:ci:int": "jest --config ./jest.integation.config.ts --ci --runInBand",
1719
"test:related": "jest --findRelatedTests",
1820
"test:gen-badges": "make-coverage-badge --output-path \"./badges/badge.svg\"",
19-
"test:cov-and-badge": "npm run test:cov && npm run test:gen-badges",
21+
"test:cov-and-badge": "npm run test:cov:badge && npm run test:gen-badges",
2022
"eslint": "eslint . --ext .js,.jsx,.ts,.tsx --config .eslintrc.json",
2123
"eslint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --config .eslintrc.json --fix",
2224
"prepare": "husky install"

release.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module.exports = {
88
}],
99
['@semantic-release/npm'],
1010
['@semantic-release/git', {
11-
'assets': ['docs', 'package.json', 'package-lock.json'],
11+
'assets': ['docs', 'package.json', 'package-lock.json', 'badges'],
1212
'message': 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}'
1313
}]
1414
]

src/account/user.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export interface IUser extends IUserInvestorCheck {
2020

2121
addLedgerItem(item: LedgerItem | Iterable<LedgerItem>): void;
2222

23+
getCashFlowMonth(date: Date): number;
24+
2325
getSummaryMonth(date: Date): ILedgerSummary;
2426

2527
getSummaryAnnual(year: number): ILedgerSummary;
@@ -73,6 +75,10 @@ export class User implements IUser {
7375
return this.ledgerCollection.getBalance(date);
7476
}
7577

78+
getCashFlowMonth(date: Date): number {
79+
return this.ledgerCollection.getCashFlowMonth(date);
80+
}
81+
7682
addLedgerItem(item: LedgerItem | Iterable<LedgerItem>): void {
7783
this.ledgerCollection.add(item);
7884
}

src/ledger/ledger-collection.ts

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { ILedgerSummary } from './i-ledger-summary';
44
import { LedgerItemType } from './ledger-item-type';
55
import { IRentalSavings, RentalSingleFamily } from '../properties';
66
import currency from '../formatters/currency';
7+
import { cloneDateUtc } from '../utils/data-clone-date';
8+
import { differenceInMonths } from 'date-fns';
79

810
export interface ILedgerCollection {
911
getBalance(date: Date): number;
@@ -25,20 +27,27 @@ export interface ILedgerCollection {
2527
clone(): ILedgerCollection;
2628
}
2729

30+
export type LedgerItemPredicate = (x: LedgerItem, index: number) => boolean;
31+
2832
export class LedgerCollection implements ILedgerCollection {
2933
private collection: IterableQuery<LedgerItem>;
3034

3135
private getSummaryByType(collection: IterableQuery<LedgerItem>, type: LedgerItemType): number {
3236
if (!collection) {
3337
return 0;
3438
}
39+
3540
return collection.filter((x) => x.typeMatches(type)).sum((x) => x.amount) || 0;
3641
}
3742

3843
constructor() {
3944
this.collection = itiriri([]);
4045
}
4146

47+
filter(pred: LedgerItemPredicate): LedgerItem[] {
48+
return this.collection.filter(pred).toArray();
49+
}
50+
4251
getBalance(date: Date): number {
4352
return this.isEmpty()
4453
? 0
@@ -113,7 +122,7 @@ export class LedgerCollection implements ILedgerCollection {
113122
}
114123

115124
const result: ILedgerSummary = {
116-
date: new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1)),
125+
date: cloneDateUtc(date),
117126
balance: 0,
118127
cashFlow: 0,
119128
averageCashFlow: 0,
@@ -131,14 +140,14 @@ export class LedgerCollection implements ILedgerCollection {
131140
return result;
132141
}
133142

134-
const salary = this.getSummaryByType(boundary, LedgerItemType.Salary);
135143
result.cashFlow = this.getSummaryByType(boundary, LedgerItemType.CashFlow);
136144
result.averageCashFlow = currency(
137145
boundary.filter((x) => x.type === LedgerItemType.CashFlow).average((x) => x.amount) || 0
138146
);
139147
result.equity = this.getSummaryByType(boundary, LedgerItemType.Equity);
140148
result.purchases = this.getSummaryByType(boundary, LedgerItemType.Purchase);
141-
result.balance = result.cashFlow + salary + result.equity - result.purchases || 0;
149+
result.purchases = this.getSummaryByType(boundary, LedgerItemType.Purchase);
150+
result.balance = this.collection.filter((li) => li.dateNotGreaterThan(date)).sum((x) => x.amount) || 0;
142151

143152
return result;
144153
}
@@ -157,12 +166,14 @@ export class LedgerCollection implements ILedgerCollection {
157166
};
158167
}
159168

169+
const cashFlowSum = summaries.sum((x) => x.cashFlow || 0);
170+
160171
return {
161172
date: summaries.first().date,
162-
balance: summaries.sum((x) => x.balance || 0),
173+
balance: summaries.last().balance,
163174
equity: summaries.sum((x) => x.equity || 0),
164-
cashFlow: summaries.sum((x) => x.cashFlow || 0),
165-
averageCashFlow: currency(summaries.average((x) => x.cashFlow || 0)),
175+
cashFlow: cashFlowSum,
176+
averageCashFlow: currency(cashFlowSum / summaries.length()),
166177
purchases: summaries.sum((x) => x.purchases || 0),
167178
};
168179
}
@@ -183,11 +194,19 @@ export class LedgerCollection implements ILedgerCollection {
183194
}
184195

185196
const collection = [];
186-
for (let month = boundary.first().created.getUTCMonth(); month < 12; month++) {
187-
collection.push(this.getSummaryMonth(new Date(Date.UTC(year, month, 1))));
197+
198+
//need to determine monthDiff between boundary
199+
const totalMonths = differenceInMonths(boundary.first().created, boundary.last().created);
200+
201+
if (totalMonths === 0) {
202+
collection.push(this.getSummaryMonth(boundary.first().created));
203+
} else {
204+
for (let month = boundary.first().created.getUTCMonth(); month < 12; month++) {
205+
collection.push(this.getSummaryMonth(new Date(Date.UTC(year, month, 1))));
206+
}
188207
}
189208

190-
return collection;
209+
return collection; //?
191210
}
192211

193212
clone(): ILedgerCollection {

src/ledger/ledger-item.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { LedgerItemType } from './ledger-item-type';
2+
import compareDates from '../utils/data-compare-date';
23

34
/**
45
* this is an entry into the account. Think of it as a checking account, and it's simply a transaction line.
@@ -29,6 +30,22 @@ export class LedgerItem {
2930
return today.getUTCMonth() === this.created.getUTCMonth();
3031
}
3132

33+
dateLessThanOrEqualTo(today: Date): boolean {
34+
if (!today || !this.created) {
35+
return false;
36+
}
37+
38+
return compareDates(this.created, today) >= 0;
39+
}
40+
41+
dateNotGreaterThan(today: Date): boolean {
42+
if (!today || !this.created) {
43+
return false;
44+
}
45+
46+
return compareDates(this.created, today) !== 1;
47+
}
48+
3249
dateMatchesYear(year: number): boolean {
3350
if (!this.created) {
3451
return false;

src/properties/rental-single-family.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ export class RentalSingleFamily implements IEntityExistence, IRentalSavings, IRe
177177
return false;
178178
}
179179

180-
return compareDates(this.minSellDate, today) !== -1 || areSameDate(this.minSellDate, today);
180+
return compareDates(this.minSellDate, today) <= 0 || areSameDate(this.minSellDate, today);
181181
}
182182

183183
get minSellDate(): Date {
@@ -212,7 +212,7 @@ export class RentalSingleFamily implements IEntityExistence, IRentalSavings, IRe
212212
return 0;
213213
}
214214

215-
if (!this.soldDate && compareDates(this.purchaseDate, today) === -1) {
215+
if (!this.soldDate || compareDates(this.purchaseDate, today) === -1) {
216216
return this.monthlyPrincipalInterestTaxInterest;
217217
}
218218

0 commit comments

Comments
 (0)