@dhyasama/totem-models 1.0.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@dhyasama/totem-models",
3
- "version": "1.0.1",
3
+ "version": "1.6.0",
4
4
  "author": "Jason Reynolds",
5
- "license": "none",
5
+ "license": "UNLICENSED",
6
6
  "description": "Models for Totem platform",
7
7
  "main": "index.js",
8
8
  "scripts": {
@@ -16,7 +16,7 @@
16
16
  "@dhyasama/ffvc-geoip": "^1.0.0",
17
17
  "@dhyasama/ffvc-s3": "^1.0.0",
18
18
  "@dhyasama/ffvc-utilities": "^2.0.0",
19
- "async": "^0.9.0",
19
+ "async": "^2.6.0",
20
20
  "awesome-phonenumber": "^1.0.14",
21
21
  "bcrypt": "^0.8.0",
22
22
  "bluebird": "^3.5.0",
@@ -32,6 +32,7 @@
32
32
  "mocha-mongoose": "^1.2.0",
33
33
  "mongoose": "~4.5.7",
34
34
  "nock": "^7.2.2",
35
+ "rewire": "^3.0.2",
35
36
  "should": "^11.1.1"
36
37
  }
37
38
  }
@@ -0,0 +1,359 @@
1
+ "use strict";
2
+
3
+ var
4
+
5
+ should = require("should"),
6
+ config = require('./config')['test'],
7
+ mongoose = require('mongoose'),
8
+ clearDB = require('mocha-mongoose')(config.db.uri, {noClear: true}),
9
+ CapTable = mongoose.model('CapTable'),
10
+ Fund = mongoose.model('Fund'),
11
+ Organization = mongoose.model('Organization');
12
+
13
+ var customerid = new mongoose.Types.ObjectId();
14
+ var orgid = new mongoose.Types.ObjectId();
15
+ var org = new Organization();
16
+ var fund = new Fund();
17
+
18
+
19
+ describe('Cap Tables', function() {
20
+
21
+ before(function(done) {
22
+ if (mongoose.connection.db) return done();
23
+ mongoose.connect(config.db.uri, done);
24
+ });
25
+
26
+ before(function(done) {
27
+ clearDB(done);
28
+ });
29
+
30
+ before(function(done) {
31
+ CapTable.collection.dropIndexes(function (err, result) {
32
+ should.not.exist(err);
33
+ should.exist(result);
34
+ result.should.equal(true);
35
+ return done();
36
+ });
37
+ });
38
+
39
+ before(function(done) {
40
+
41
+ fund.name = 'My Huge Fund';
42
+ fund.slug = 'my-huge-fund';
43
+ fund.shortName = 'Huge';
44
+
45
+ Fund.upsert(fund, 'test-user', function(err, result) {
46
+ should.not.exist(err);
47
+ should.exist(result);
48
+ done();
49
+ });
50
+
51
+ });
52
+
53
+ before(function(done) {
54
+
55
+ org.name = 'No Thanks 2';
56
+ org.slug = 'org2-slug';
57
+ org.website = 'nothanks2.org';
58
+ org.funds.push(fund);
59
+
60
+ Organization.upsert(org, 'test', function(err, result) {
61
+ should.not.exist(err);
62
+ should.exist(result);
63
+ org = result;
64
+ done();
65
+ });
66
+
67
+ });
68
+
69
+ it('creates a cap table', function(done) {
70
+
71
+ var ct = new CapTable({
72
+ customer: customerid,
73
+ organization: orgid
74
+ });
75
+
76
+ ct.stakeholders.push({
77
+ name: 'Test Fund IV, LLC',
78
+ common:[{
79
+ round: 'Series A',
80
+ shares: 100
81
+ }, {
82
+ round: 'Series Seed',
83
+ shares: 200
84
+ }],
85
+ warrants:[{
86
+ round: 'Series A',
87
+ shares: 15
88
+ }],
89
+ fund: fund
90
+ });
91
+
92
+ ct.stakeholders.push({
93
+ name: 'My Little Trust',
94
+ common:[{
95
+ round: 'Series Seed',
96
+ shares: 50
97
+ }]
98
+ });
99
+
100
+ ct.stakeholders.push({
101
+ name: 'John Appleseed',
102
+ options:[{
103
+ round: 'Series Seed',
104
+ shares: 5
105
+ }]
106
+ });
107
+
108
+ CapTable.insert(ct, 'test', function(err, result) {
109
+ should.not.exist(err);
110
+ should.exist(result);
111
+ done();
112
+ });
113
+
114
+ });
115
+
116
+ it('prevents cap table from being created with both a person and a fund link', function(done) {
117
+
118
+ var ct = new CapTable({
119
+ customer: customerid,
120
+ organization: orgid
121
+ });
122
+
123
+ ct.stakeholders.push({
124
+ name: 'Test Fund IV, LLC',
125
+ person: new mongoose.Types.ObjectId(),
126
+ fund: new mongoose.Types.ObjectId()
127
+ });
128
+
129
+ CapTable.insert(ct, 'test', function(err, result) {
130
+ should.exist(err);
131
+ return done();
132
+ });
133
+
134
+ });
135
+
136
+ it('prevents cap table from being created with duplicate org/customer combo', function(done) {
137
+
138
+ var ct = new CapTable({
139
+ customer: customerid,
140
+ organization: orgid
141
+ });
142
+
143
+ CapTable.insert(ct, 'test', function(err, result) {
144
+ should.exist(err);
145
+ return done();
146
+ });
147
+
148
+ });
149
+
150
+ it('finds a cap table', function(done) {
151
+
152
+ CapTable.getForCustomer(orgid, customerid, function(err, result) {
153
+ should.not.exist(err);
154
+ should.exist(result);
155
+ done();
156
+ });
157
+
158
+ });
159
+
160
+ it('get a fund to org map', function(done) {
161
+
162
+ CapTable.getForCustomer(orgid, customerid, function(err, result) {
163
+
164
+ should.not.exist(err);
165
+ should.exist(result);
166
+
167
+ result.mapFundsToOrgs(function(err, result) {
168
+ should.not.exist(err);
169
+ should.exist(result);
170
+ result.length.should.equal(1);
171
+ result[0].fund.toString().should.equal(fund._id.toString());
172
+ result[0].org._id.toString().should.equal(org._id.toString());
173
+ return done();
174
+ });
175
+
176
+ });
177
+
178
+ });
179
+
180
+ it('auto-calculates dilution', function(done) {
181
+
182
+ CapTable.getForCustomer(orgid, customerid, function(err, result) {
183
+
184
+ should.not.exist(err);
185
+ should.exist(result);
186
+
187
+ result.fullyDiluted.should.be.above(0);
188
+
189
+ result.stakeholders.forEach(function(s) {
190
+ s.fullyDiluted.should.be.above(0);
191
+ s.fullyDilutedPercentage.should.be.above(0);
192
+ });
193
+
194
+ return done();
195
+
196
+ });
197
+
198
+ });
199
+
200
+ it('gets stakeholders by round', function(done) {
201
+
202
+ CapTable.getForCustomer(orgid, customerid, function(err, result) {
203
+
204
+ should.not.exist(err);
205
+ should.exist(result);
206
+
207
+ var stakeholders = result.stakeholdersByRound;
208
+ should.exist(stakeholders);
209
+ stakeholders.length.should.equal(2);
210
+ should.exist(stakeholders[0].round);
211
+ should.exist(stakeholders[0].participants);
212
+ stakeholders[0].participants.length.should.be.above(0);
213
+ should.exist(stakeholders[1].round);
214
+ should.exist(stakeholders[1].participants);
215
+ stakeholders[1].participants.length.should.be.above(0);
216
+
217
+ return done();
218
+
219
+ });
220
+
221
+ });
222
+
223
+ it('gets stakeholders by percentage', function(done) {
224
+
225
+ CapTable.getForCustomer(orgid, customerid, function(err, result) {
226
+
227
+ should.not.exist(err);
228
+ should.exist(result);
229
+
230
+ var stakeholders = result.stakeholders;
231
+ should.exist(stakeholders);
232
+
233
+ return done();
234
+
235
+ });
236
+
237
+ });
238
+
239
+ it('updates a document url', function(done) {
240
+
241
+ var url = 'https://s3.aws.amazon.com/bucket/key';
242
+
243
+ // get a table
244
+ CapTable.find({}, function (err, result1) {
245
+
246
+ should.not.exist(err);
247
+ should.exist(result1);
248
+ result1.length.should.be.above(0);
249
+
250
+ // update url
251
+ CapTable.updateOriginalDocumentUrl(result1[0]._id, url, function(err, result2) {
252
+
253
+ should.not.exist(err);
254
+ should.exist(result2);
255
+ result2.originalDocumentUrl.should.equal(url);
256
+
257
+ return done();
258
+
259
+ });
260
+
261
+ });
262
+
263
+ });
264
+
265
+ it('replaces a cap table', function(done) {
266
+
267
+ // get a table
268
+ CapTable.find({}, function (err, result1) {
269
+
270
+ should.not.exist(err);
271
+ should.exist(result1);
272
+ result1.length.should.be.above(0);
273
+
274
+ CapTable.replace(result1[0], 'test', function(err, result2) {
275
+
276
+ should.not.exist(err);
277
+ should.exist(result2);
278
+
279
+ return done();
280
+
281
+ });
282
+
283
+ });
284
+
285
+ });
286
+
287
+ it('deletes all cap tables for a customer', function(done) {
288
+
289
+ var cid = new mongoose.Types.ObjectId();
290
+ var ct1 = new CapTable({ customer: cid, organization: new mongoose.Types.ObjectId() });
291
+ var ct2 = new CapTable({ customer: cid, organization: new mongoose.Types.ObjectId() });
292
+ var ct3 = new CapTable({ customer: new mongoose.Types.ObjectId(), organization: new mongoose.Types.ObjectId() });
293
+
294
+ CapTable.find({}, function(err, result) {
295
+
296
+ should.not.exist(err);
297
+ should.exist(result);
298
+
299
+ var count = result.length;
300
+
301
+ CapTable.insert(ct1, 'test', function (err, result) {
302
+ should.not.exist(err);
303
+ should.exist(result);
304
+ CapTable.insert(ct2, 'test', function (err, result) {
305
+ should.not.exist(err);
306
+ should.exist(result);
307
+ CapTable.insert(ct3, 'test', function (err, result) {
308
+ should.not.exist(err);
309
+ should.exist(result);
310
+ CapTable.deleteAllForCustomer(cid, function (err, result) {
311
+
312
+ should.not.exist(err);
313
+ should.exist(result);
314
+
315
+ CapTable.find({}, function (err, result) {
316
+ should.not.exist(err);
317
+ should.exist(result);
318
+ result.length.should.equal(count + 1);
319
+ done();
320
+ });
321
+
322
+ });
323
+ });
324
+ });
325
+ });
326
+
327
+ });
328
+
329
+ });
330
+
331
+ it('deletes a specific cap table', function(done) {
332
+
333
+ // Get a table to delete
334
+ CapTable.find({}, function (err, result1) {
335
+
336
+ should.not.exist(err);
337
+ should.exist(result1);
338
+ result1.length.should.be.above(0);
339
+
340
+ // Delete it
341
+ CapTable.delete(result1[0]._id, function(err, result2) {
342
+
343
+ should.not.exist(err);
344
+ should.exist(result2);
345
+
346
+ // Make sure it's really gone
347
+ CapTable.findById(result2._id, function(err, result3) {
348
+ should.not.exist(err);
349
+ should.not.exist(result3);
350
+ return done();
351
+ });
352
+
353
+ });
354
+
355
+ });
356
+
357
+ });
358
+
359
+ });
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+
3
+ var
4
+
5
+ should = require("should"),
6
+ config = require('./config')['test'],
7
+ mongoose = require('mongoose'),
8
+ clearDB = require('mocha-mongoose')(config.db.uri, {noClear: true}),
9
+ Investment = mongoose.model('Investment'),
10
+ Round = mongoose.model('Round');
11
+
12
+ var customerid = new mongoose.Types.ObjectId();
13
+
14
+ describe('Investments', function() {
15
+
16
+ before(function(done) {
17
+ if (mongoose.connection.db) return done();
18
+ mongoose.connect(config.db.uri, done);
19
+ });
20
+
21
+ before(function(done) {
22
+ clearDB(done);
23
+ });
24
+
25
+ before(function (done) {
26
+ Investment.collection.dropIndexes(function (err, result) {
27
+ should.not.exist(err);
28
+ should.exist(result);
29
+ result.should.equal(true);
30
+ return done();
31
+ });
32
+ });
33
+
34
+ it('creates an investment', function(done) {
35
+
36
+ var inv = new Investment({
37
+ customer: customerid,
38
+ cost: 200000,
39
+ costPlusInterest: 200000,
40
+ investmentDate: Date.now(),
41
+ pricePerShare: 0.002,
42
+ preMoneyValuation: 1000000,
43
+ dollarsRaised: 200000,
44
+ closingDate: Date.now()
45
+ });
46
+
47
+ Investment.upsert(inv, function(err, result) {
48
+ should.not.exist(err);
49
+ should.exist(result);
50
+ result.customer.should.equal(customerid);
51
+ result.cost.should.equal(200000);
52
+ done();
53
+ });
54
+
55
+ });
56
+
57
+ it('removes customer investments', function(done) {
58
+
59
+ var inv1 = new Investment({
60
+ customer: customerid,
61
+ amount: 1,
62
+ investmentDate: Date.now()
63
+ });
64
+
65
+ var inv2 = new Investment({
66
+ customer: new mongoose.Types.ObjectId(),
67
+ amount: 2,
68
+ investmentDate: Date.now()
69
+ });
70
+
71
+ Investment.upsert(inv1, function(err, result) {
72
+
73
+ should.not.exist(err);
74
+ should.exist(result);
75
+
76
+ Investment.upsert(inv2, function(err, result) {
77
+
78
+ should.not.exist(err);
79
+ should.exist(result);
80
+
81
+ Investment.removeCustomerInvestments(customerid, function(err) {
82
+
83
+ should.not.exist(err);
84
+
85
+ Investment.find({}, function(err, result) {
86
+ should.not.exist(err);
87
+ should.exist(result);
88
+ result.length.should.equal(1);
89
+ done();
90
+ });
91
+
92
+ });
93
+
94
+ });
95
+ });
96
+
97
+ });
98
+
99
+ it('removes investment reference from round when deleted', function(done) {
100
+
101
+ var inv1 = new Investment({
102
+ customer: customerid,
103
+ amount: 1,
104
+ investmentDate: Date.now()
105
+ });
106
+
107
+ var round1 = new Round({
108
+ organization: new mongoose.Types.ObjectId(),
109
+ roundName: 'Series Test'
110
+ });
111
+
112
+ Investment.upsert(inv1, function(err, result) {
113
+
114
+ should.not.exist(err);
115
+ should.exist(result);
116
+ inv1 = result;
117
+
118
+ Round.upsert(round1, function(err, result) {
119
+
120
+ should.not.exist(err);
121
+ should.exist(result);
122
+ round1 = result;
123
+
124
+ round1.addInvestment(inv1, new mongoose.Types.ObjectId());
125
+ Round.upsert(round1, function(err, result) {
126
+
127
+ should.not.exist(err);
128
+ should.exist(result);
129
+ round1.vehicles[0].investments.length.should.equal(1);
130
+
131
+ Investment.removeCustomerInvestments(customerid, function(err, results) {
132
+
133
+ should.not.exist(err);
134
+
135
+ setTimeout(function() {
136
+ Round.findById(round1._id, function(err, toot) {
137
+
138
+ should.not.exist(err);
139
+ should.exist(toot);
140
+
141
+ toot.vehicles[0].investments.length.should.equal(0);
142
+
143
+ done();
144
+
145
+ })
146
+ }, 1000);
147
+
148
+ });
149
+
150
+ });
151
+
152
+ });
153
+
154
+ });
155
+
156
+ });
157
+
158
+ it('cannot create a non-debt investment with a debt type', function(done) {
159
+
160
+ var inv = new Investment({
161
+ customer: customerid,
162
+ cost: 200000,
163
+ costPlusInterest: 200000,
164
+ investmentDate: Date.now(),
165
+ pricePerShare: 0.002,
166
+ preMoneyValuation: 1000000,
167
+ dollarsRaised: 200000,
168
+ closingDate: Date.now(),
169
+ debt: false,
170
+ debtType: 'SAFE'
171
+ });
172
+
173
+ Investment.upsert(inv, function(err, result) {
174
+ should.exist(err);
175
+ done();
176
+ });
177
+
178
+ });
179
+
180
+ });
@@ -28,6 +28,15 @@ describe('Organization', function() {
28
28
  clearDB(done);
29
29
  });
30
30
 
31
+ before(function (done) {
32
+ Organization.collection.dropIndexes(function (err, result) {
33
+ should.not.exist(err);
34
+ should.exist(result);
35
+ result.should.equal(true);
36
+ return done();
37
+ });
38
+ });
39
+
31
40
  before(function (done) {
32
41
 
33
42
  org2 = new Organization();
@@ -36,7 +45,6 @@ describe('Organization', function() {
36
45
  org2.website = 'nothanks2.org';
37
46
  org2.websiteAliases = [ 'myolddomain.tld' ];
38
47
  org2.related = [];
39
- //org2.investedIn = ['123', '456'];
40
48
  org2.aliases.push('NT2');
41
49
  org2.crunchbase.uuid = 'abc';
42
50
  org2.crunchbase.url = 'abc';
@@ -212,6 +220,47 @@ describe('Organization', function() {
212
220
 
213
221
  });
214
222
 
223
+ before(function(done) {
224
+
225
+ org3.people.push({
226
+ person: person2.id,
227
+ current: true,
228
+ board: 'director'
229
+ });
230
+
231
+ org3.people.push({
232
+ person: person1.id,
233
+ current: true,
234
+ board: 'chairman'
235
+ });
236
+
237
+ Organization.upsert(org3, 'test-user', function(err, result) {
238
+ should.not.exist(err);
239
+ should.exist(result);
240
+ result.people.length.should.equal(2);
241
+ org3 = result;
242
+ done();
243
+ });
244
+
245
+ });
246
+
247
+ before(function(done) {
248
+
249
+ org2.chairs.push({
250
+ first: person2.id,
251
+ customer: org1
252
+ });
253
+
254
+ Organization.upsert(org2, 'test-user', function(err, result) {
255
+ should.not.exist(err);
256
+ should.exist(result);
257
+ result.chairs.length.should.equal(1);
258
+ org2 = result;
259
+ done();
260
+ });
261
+
262
+ });
263
+
215
264
  it('it soft deletes an org', function(done) {
216
265
 
217
266
  org4.deleteOrg();
@@ -445,7 +494,6 @@ describe('Organization', function() {
445
494
  should.exist(org);
446
495
  org1 = org;
447
496
  org1.id.should.equal(org.id);
448
- org1.sources.length.should.equal(1);
449
497
  return done();
450
498
  });
451
499
 
@@ -492,6 +540,40 @@ describe('Organization', function() {
492
540
 
493
541
  });
494
542
 
543
+ it('finds boards of a person', function(done) {
544
+
545
+ Organization.getBoardsOfPerson(person2.id, function(err, result) {
546
+ should.not.exist(err);
547
+ should.exist(result);
548
+ result.length.should.equal(1);
549
+ done();
550
+ });
551
+
552
+ });
553
+
554
+ it('finds chairs of a person', function(done) {
555
+
556
+ Organization.getChairsOfPerson(person2.id, function(err, result) {
557
+ should.not.exist(err);
558
+ should.exist(result);
559
+ result.length.should.equal(1);
560
+ done();
561
+ });
562
+
563
+ });
564
+
565
+ it('gets the board of a company', function(done) {
566
+
567
+ Organization.getById(org3.id, function(err, result) {
568
+ should.not.exist(err);
569
+ should.exist(result);
570
+ var board = result.board;
571
+ board.all.length.should.equal(2);
572
+ done();
573
+ });
574
+
575
+ });
576
+
495
577
  it('removes a person from people (unpopulated)', function(done) {
496
578
 
497
579
  // remove person
@@ -590,6 +672,15 @@ describe('Organization', function() {
590
672
  });
591
673
  });
592
674
 
675
+ it('finds orgs by ids', function(done) {
676
+ Organization.findByIds([org4._id, org5._id], function(err, result) {
677
+ should.not.exist(err);
678
+ should.exist(result);
679
+ result.length.should.equal(2);
680
+ return done();
681
+ });
682
+ });
683
+
593
684
  it('finds org by domain alias', function(done) {
594
685
  Organization.findByDomains(['doesnotexist.io', 'myolddomain.tld'], function(err, result) {
595
686
  should.not.exist(err);
@@ -689,6 +780,12 @@ describe('Organization', function() {
689
780
 
690
781
  });
691
782
 
783
+ // not yet tested:
784
+ // listAllFlagged
785
+ // listPage
786
+ // removeById
787
+ // stats
788
+
692
789
  // it('does not duplicate website domains with investors', function(done) {
693
790
  //
694
791
  // var investor = new Investor();