@dhyasama/totem-models 8.46.0 → 8.47.1
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/lib/Investment.js +10 -61
- package/lib/Round.js +37 -123
- package/package.json +1 -1
package/lib/Investment.js
CHANGED
|
@@ -25,40 +25,14 @@ module.exports = function(mongoose, config) {
|
|
|
25
25
|
// Used by Round to selectively populate
|
|
26
26
|
customer: { type: Schema.ObjectId, ref: 'Organization', required: true, index: true },
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
enum: [ 'Convertible Note', 'Convertible Promissory Note', 'Demand Note', 'Demand Promissory Note', 'SAFE', 'SAFT', 'SAFTE', 'KISS', null ],
|
|
37
|
-
default: null,
|
|
38
|
-
required: false,
|
|
39
|
-
validate: {
|
|
40
|
-
validator: function(v) {
|
|
41
|
-
var self = this;
|
|
42
|
-
if (v && v.length && self.debt == false) return false;
|
|
43
|
-
else return true;
|
|
44
|
-
},
|
|
45
|
-
message: 'There is a debt type ({VALUE}) but debt is set to false! Either set debt to true or remove the debt type.'
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
converted: { type: Boolean, default: false },
|
|
50
|
-
convertedInto: {
|
|
51
|
-
type: String,
|
|
52
|
-
trim: true,
|
|
53
|
-
validate: {
|
|
54
|
-
validator: function(v) {
|
|
55
|
-
var self = this;
|
|
56
|
-
if (self.converted && (!v || v.length == 0)) return false;
|
|
57
|
-
else return true;
|
|
58
|
-
},
|
|
59
|
-
message: 'The investments is marked as converted but a converted into value was not provided! Either set converted to false or provide a value for converted into.'
|
|
60
|
-
}
|
|
61
|
-
},
|
|
28
|
+
date: { type: Date, required: true },
|
|
29
|
+
investment: { type: Number, default: 0 },
|
|
30
|
+
unrealized: { type: Number, default: 0 },
|
|
31
|
+
realized: { type: Number, default: 0 },
|
|
32
|
+
raised: { type: Number, required: false },
|
|
33
|
+
premoney: { type: Number, required: false },
|
|
34
|
+
postmoney: { type: Number, required: false },
|
|
35
|
+
pricePerShare: { type: Number, required: false },
|
|
62
36
|
|
|
63
37
|
// Catch-all for customer specific key-value pairs from sheet that aren't supported by our schema
|
|
64
38
|
details: [{
|
|
@@ -66,38 +40,15 @@ module.exports = function(mongoose, config) {
|
|
|
66
40
|
value: { type: Schema.Types.Mixed, required: true },
|
|
67
41
|
format: { type: String, enum: [null, 'number', 'decimal', 'money', 'percentage', 'date']},
|
|
68
42
|
_id: false
|
|
69
|
-
}]
|
|
70
|
-
|
|
71
|
-
// note these fields are duplicative at the round level
|
|
72
|
-
// this is private data and will override the round level numbers where appropriate
|
|
73
|
-
preMoneyValuation: { type: Number, default: 0 },
|
|
74
|
-
postMoneyValuation: { type: Number, default: 0 },
|
|
75
|
-
dollarsRaised: { type: Number, default: 0 },
|
|
76
|
-
closingDate: { type: Date, default: null }
|
|
43
|
+
}]
|
|
77
44
|
|
|
78
45
|
});
|
|
79
46
|
|
|
80
|
-
|
|
81
|
-
|
|
82
47
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
83
48
|
// VIRTUALS
|
|
84
49
|
// Properties that are not persisted to the database
|
|
85
50
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
86
51
|
|
|
87
|
-
Investment.virtual('month').get(function () {
|
|
88
|
-
var self = this;
|
|
89
|
-
var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
|
90
|
-
if (!self.investmentDate) return 'Unknown';
|
|
91
|
-
else return monthNames[self.investmentDate.getMonth()];
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
Investment.virtual('year').get(function () {
|
|
95
|
-
var self = this;
|
|
96
|
-
if (!self.investmentDate) return 'Unknown';
|
|
97
|
-
else return self.investmentDate.getFullYear();
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
|
|
101
52
|
|
|
102
53
|
//////////////////////////////////////////////////////
|
|
103
54
|
// STATICS
|
|
@@ -179,6 +130,4 @@ module.exports = function(mongoose, config) {
|
|
|
179
130
|
|
|
180
131
|
mongoose.model('Investment', Investment);
|
|
181
132
|
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
|
|
133
|
+
};
|
package/lib/Round.js
CHANGED
|
@@ -29,16 +29,6 @@ module.exports = function(mongoose, config) {
|
|
|
29
29
|
|
|
30
30
|
roundName: { type: String, trim: true, required: true },
|
|
31
31
|
|
|
32
|
-
// used when cap table round names are changed and need to be matched to investment data
|
|
33
|
-
oldRoundName: { type: String, trim: true, default: null },
|
|
34
|
-
|
|
35
|
-
// these three values are public and will be used in the absence of overriding private data
|
|
36
|
-
// if private data exists (in the form of investments), the most recent investment data will be used
|
|
37
|
-
preMoneyValuation: { type: Number, default: 0 },
|
|
38
|
-
postMoneyValuation: { type: Number, default: 0 },
|
|
39
|
-
dollarsRaised: { type: Number, default: 0 },
|
|
40
|
-
closingDate: { type: Date, default: null },
|
|
41
|
-
|
|
42
32
|
// People that participated in this round
|
|
43
33
|
// This is a public list of people that invested
|
|
44
34
|
// Use the addPerson method and removePerson static
|
|
@@ -65,11 +55,7 @@ module.exports = function(mongoose, config) {
|
|
|
65
55
|
investments: [{ type: Schema.ObjectId, ref: 'Investment', required: false }],
|
|
66
56
|
|
|
67
57
|
// computed from investments
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// set at the vehicle level during investment pull
|
|
71
|
-
fairValue: { type: Number, default: 0 },
|
|
72
|
-
valuationDate: { type: Date, default: null },
|
|
58
|
+
investment: { type: Number, default: 0 },
|
|
73
59
|
unrealized: { type: Number, default: 0 },
|
|
74
60
|
realized: { type: Number, default: 0 },
|
|
75
61
|
|
|
@@ -79,8 +65,6 @@ module.exports = function(mongoose, config) {
|
|
|
79
65
|
|
|
80
66
|
});
|
|
81
67
|
|
|
82
|
-
|
|
83
|
-
|
|
84
68
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
85
69
|
// HELPERS
|
|
86
70
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
@@ -93,15 +77,15 @@ module.exports = function(mongoose, config) {
|
|
|
93
77
|
|
|
94
78
|
var calculateOrgPerformance = function calculateOrgPerformance(org, rounds) {
|
|
95
79
|
|
|
96
|
-
// sum the
|
|
97
|
-
org.
|
|
98
|
-
var sum = _.reduce(round.vehicles, function(memo, vehicle) { return memo + vehicle.
|
|
80
|
+
// sum the investment of each vehicle
|
|
81
|
+
org.investment = _.reduce(rounds, function(memo, round) {
|
|
82
|
+
var sum = _.reduce(round.vehicles, function(memo, vehicle) { return memo + vehicle.investment; }, 0);
|
|
99
83
|
return memo + sum;
|
|
100
84
|
}, 0);
|
|
101
85
|
|
|
102
|
-
// sum the
|
|
86
|
+
// sum the unrealized of each vehicle
|
|
103
87
|
org.unrealized = _.reduce(rounds, function(memo, round) {
|
|
104
|
-
var sum = _.reduce(round.vehicles, function(memo, vehicle) { return memo + vehicle.
|
|
88
|
+
var sum = _.reduce(round.vehicles, function(memo, vehicle) { return memo + vehicle.unrealized; }, 0);
|
|
105
89
|
return memo + sum;
|
|
106
90
|
}, 0);
|
|
107
91
|
|
|
@@ -112,7 +96,7 @@ module.exports = function(mongoose, config) {
|
|
|
112
96
|
}, 0);
|
|
113
97
|
|
|
114
98
|
// finally, calc the multiple
|
|
115
|
-
org.multiple = org.
|
|
99
|
+
org.multiple = org.investment > 0 ? (org.unrealized + org.realized) / org.investment : 0;
|
|
116
100
|
|
|
117
101
|
return org;
|
|
118
102
|
|
|
@@ -158,8 +142,6 @@ module.exports = function(mongoose, config) {
|
|
|
158
142
|
contact: rounds[0].organization.contact,
|
|
159
143
|
filters: rounds[0].organization.filters,
|
|
160
144
|
status: rounds[0].organization.status,
|
|
161
|
-
preMoneyValuation: rounds[0].preMoneyValuation,
|
|
162
|
-
postMoneyValuation: rounds[0].postMoneyValuation,
|
|
163
145
|
chairs: rounds[0].organization.chairs,
|
|
164
146
|
funds: funds
|
|
165
147
|
};
|
|
@@ -220,43 +202,19 @@ module.exports = function(mongoose, config) {
|
|
|
220
202
|
|
|
221
203
|
};
|
|
222
204
|
|
|
223
|
-
|
|
224
|
-
|
|
225
205
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
226
206
|
// VIRTUALS
|
|
227
207
|
// Properties that are not persisted to the database
|
|
228
208
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
229
209
|
|
|
230
|
-
Round.virtual('cost').get(function () {
|
|
231
|
-
// sums the cost of all vehicles in the round
|
|
232
|
-
var self = this;
|
|
233
|
-
return _.reduce(self.vehicles, function(memo, vehicle) { return memo + vehicle.cost; }, 0);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
Round.virtual('month').get(function () {
|
|
237
|
-
var self = this;
|
|
238
|
-
var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
|
239
|
-
if (!self.closingDate) return 'Unknown';
|
|
240
|
-
else return monthNames[self.closingDate.getMonth()];
|
|
241
|
-
});
|
|
242
|
-
|
|
243
210
|
Round.virtual('value').get(function () {
|
|
244
|
-
// sums the value (
|
|
211
|
+
// sums the value (realized and unrealized) of all vehicles in the round
|
|
245
212
|
var self = this;
|
|
246
213
|
return _.reduce(self.vehicles, function(memo, vehicle) {
|
|
247
|
-
|
|
248
|
-
else return memo + vehicle.fairValue;
|
|
214
|
+
return memo + vehicle.unrealized + vehicle.realized;
|
|
249
215
|
}, 0);
|
|
250
216
|
});
|
|
251
217
|
|
|
252
|
-
Round.virtual('year').get(function () {
|
|
253
|
-
var self = this;
|
|
254
|
-
if (!self.closingDate) return 'Unknown';
|
|
255
|
-
else return self.closingDate.getFullYear();
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
218
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
261
219
|
// METHODS
|
|
262
220
|
//
|
|
@@ -315,7 +273,6 @@ module.exports = function(mongoose, config) {
|
|
|
315
273
|
|
|
316
274
|
// If not, add it
|
|
317
275
|
if (!match) vehicle.investments.push(investment);
|
|
318
|
-
// note there is no need to compute vehicle cost as it is done during upsert
|
|
319
276
|
|
|
320
277
|
};
|
|
321
278
|
|
|
@@ -366,9 +323,8 @@ module.exports = function(mongoose, config) {
|
|
|
366
323
|
}
|
|
367
324
|
|
|
368
325
|
data = data || {};
|
|
369
|
-
data.
|
|
326
|
+
data.unrealized = data.unrealized || 0;
|
|
370
327
|
data.realized = data.realized || 0;
|
|
371
|
-
data.valuationDate = data.valuationDate || null;
|
|
372
328
|
|
|
373
329
|
// Check if vehicle is already added
|
|
374
330
|
let vehicle = _.find(self.vehicles, function(v) {
|
|
@@ -378,18 +334,16 @@ module.exports = function(mongoose, config) {
|
|
|
378
334
|
|
|
379
335
|
// update data on existing vehicle
|
|
380
336
|
if (vehicle) {
|
|
381
|
-
vehicle.
|
|
337
|
+
vehicle.unrealized = data.unrealized;
|
|
382
338
|
vehicle.realized = data.realized;
|
|
383
|
-
vehicle.valuationDate = data.valuationDate;
|
|
384
339
|
}
|
|
385
340
|
// create vehicle
|
|
386
341
|
else {
|
|
387
342
|
vehicle = {
|
|
388
343
|
fund: fund,
|
|
389
344
|
investments: [],
|
|
390
|
-
|
|
391
|
-
realized: data.realized
|
|
392
|
-
valuationDate: data.valuationDate
|
|
345
|
+
unrealized: data.unrealized,
|
|
346
|
+
realized: data.realized
|
|
393
347
|
};
|
|
394
348
|
self.vehicles.push(vehicle);
|
|
395
349
|
}
|
|
@@ -398,8 +352,6 @@ module.exports = function(mongoose, config) {
|
|
|
398
352
|
|
|
399
353
|
};
|
|
400
354
|
|
|
401
|
-
|
|
402
|
-
|
|
403
355
|
//////////////////////////////////////////////////////
|
|
404
356
|
// STATICS
|
|
405
357
|
// Statics operate on the entire collection
|
|
@@ -453,14 +405,13 @@ module.exports = function(mongoose, config) {
|
|
|
453
405
|
// reverse chronological
|
|
454
406
|
rounds = _.sortBy(rounds, function(r) {
|
|
455
407
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
// use investment date in absence of closing date
|
|
459
|
-
if (!date && r.vehicles.length && r.vehicles[0].investments.length) {
|
|
460
|
-
date = r.vehicles[0].investments[0].investmentDate;
|
|
408
|
+
if (r.vehicles.length && r.vehicles[0].investments.length) {
|
|
409
|
+
return r.vehicles[0].investments[0].date;
|
|
461
410
|
}
|
|
462
411
|
|
|
463
|
-
|
|
412
|
+
else {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
464
415
|
|
|
465
416
|
}).reverse();
|
|
466
417
|
|
|
@@ -515,14 +466,13 @@ module.exports = function(mongoose, config) {
|
|
|
515
466
|
// reverse chronological
|
|
516
467
|
rounds = _.sortBy(rounds, function(r) {
|
|
517
468
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
// use investment date in absence of closing date
|
|
521
|
-
if (!date && r.vehicles.length && r.vehicles[0].investments.length) {
|
|
522
|
-
date = r.vehicles[0].investments[0].investmentDate;
|
|
469
|
+
if (r.vehicles.length && r.vehicles[0].investments.length) {
|
|
470
|
+
return r.vehicles[0].investments[0].date;
|
|
523
471
|
}
|
|
524
472
|
|
|
525
|
-
|
|
473
|
+
else {
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
526
476
|
|
|
527
477
|
}).reverse();
|
|
528
478
|
|
|
@@ -607,27 +557,27 @@ module.exports = function(mongoose, config) {
|
|
|
607
557
|
|
|
608
558
|
// we now have all the rounds the fund has participated in with other fund information removed from each round
|
|
609
559
|
|
|
610
|
-
let
|
|
611
|
-
let
|
|
560
|
+
let totalInvestment = _.reduce(rounds, function(memo, round) {
|
|
561
|
+
let roundInvestment = _.reduce(round.vehicles, function(memo, vehicle) { return memo + vehicle.investment; }, 0);
|
|
612
562
|
return memo + roundCost;
|
|
613
563
|
}, 0);
|
|
614
564
|
|
|
615
565
|
let totalUnrealized = _.reduce(rounds, function(memo, round) {
|
|
616
|
-
let
|
|
617
|
-
return memo +
|
|
566
|
+
let roundUnrealized = _.reduce(round.vehicles, function(memo, vehicle) { return memo + vehicle.unrealized; }, 0);
|
|
567
|
+
return memo + roundUnrealized;
|
|
618
568
|
}, 0);
|
|
619
569
|
|
|
620
570
|
let totalRealized = _.reduce(rounds, function(memo, round) {
|
|
621
|
-
let
|
|
622
|
-
return memo +
|
|
571
|
+
let roundRealized = _.reduce(round.vehicles, function(memo, vehicle) { return memo + vehicle.realized; }, 0);
|
|
572
|
+
return memo + roundRealized;
|
|
623
573
|
}, 0);
|
|
624
574
|
|
|
625
575
|
return cb(null, {
|
|
626
|
-
|
|
576
|
+
investment: totalInvestment,
|
|
627
577
|
unrealized: totalUnrealized,
|
|
628
578
|
realized: totalRealized,
|
|
629
579
|
value: totalUnrealized + totalRealized,
|
|
630
|
-
multiple:
|
|
580
|
+
multiple: totalInvestment > 0 ? (totalUnrealized + totalRealized) / totalInvestment : 0
|
|
631
581
|
});
|
|
632
582
|
|
|
633
583
|
});
|
|
@@ -652,7 +602,7 @@ module.exports = function(mongoose, config) {
|
|
|
652
602
|
|
|
653
603
|
let query = self.find({ 'vehicles.fund': fundId });
|
|
654
604
|
|
|
655
|
-
query.select('organization vehicles
|
|
605
|
+
query.select('organization vehicles');
|
|
656
606
|
query.populate('organization', 'name slug logoUrl description contact filters status ipo closed acquired operating website websiteAliases');
|
|
657
607
|
query.populate('vehicles.fund');
|
|
658
608
|
|
|
@@ -706,7 +656,7 @@ module.exports = function(mongoose, config) {
|
|
|
706
656
|
|
|
707
657
|
let query = self.find({ 'vehicles.fund': { $in: fundIds } });
|
|
708
658
|
|
|
709
|
-
query.select('organization vehicles
|
|
659
|
+
query.select('organization vehicles');
|
|
710
660
|
query.populate('organization', 'name slug logoUrl description contact chairs filters status ipo closed acquired operating website websiteAliases');
|
|
711
661
|
query.deepPopulate([
|
|
712
662
|
'organization.chairs.first',
|
|
@@ -879,26 +829,10 @@ module.exports = function(mongoose, config) {
|
|
|
879
829
|
var investments = vehicle.investments;
|
|
880
830
|
|
|
881
831
|
if (investments.length >= 1) {
|
|
882
|
-
|
|
883
|
-
vehicle.cost = _.reduce(investments, function(memo, investment){ return memo + investment.cost; }, 0);
|
|
832
|
+
vehicle.investment = _.reduce(investments, function(memo, investment) { return memo + investment.investment; }, 0);
|
|
884
833
|
}
|
|
885
834
|
else {
|
|
886
|
-
|
|
887
|
-
vehicle.cost = 0;
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
// proceeds are special in that the cost is actually a realized gain
|
|
891
|
-
if (doc.roundName == 'Proceeds') {
|
|
892
|
-
vehicle.realized = vehicle.cost;
|
|
893
|
-
vehicle.cost = 0;
|
|
894
|
-
vehicle.unrealized = 0;
|
|
895
|
-
vehicle.fairValue = 0;
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
// escrow is also special, except the gains are unrealized
|
|
899
|
-
else if (doc.roundName === 'Escrow') {
|
|
900
|
-
vehicle.unrealized = vehicle.fairValue - vehicle.cost;
|
|
901
|
-
vehicle.realized = 0;
|
|
835
|
+
vehicle.investment = 0;
|
|
902
836
|
}
|
|
903
837
|
|
|
904
838
|
});
|
|
@@ -911,7 +845,7 @@ module.exports = function(mongoose, config) {
|
|
|
911
845
|
|
|
912
846
|
result.populate({
|
|
913
847
|
path: 'vehicles.investments',
|
|
914
|
-
select: '
|
|
848
|
+
select: 'investment'
|
|
915
849
|
}, function(err, doc) {
|
|
916
850
|
|
|
917
851
|
if (err) return cb(err, null);
|
|
@@ -940,13 +874,6 @@ module.exports = function(mongoose, config) {
|
|
|
940
874
|
|
|
941
875
|
// note participating people are public as there is no investment, i.e., private, data attached to them
|
|
942
876
|
|
|
943
|
-
// note that public versions of the following data exist at this level:
|
|
944
|
-
// preMoneyValuation
|
|
945
|
-
// postMoneyValuation
|
|
946
|
-
// amountRaised
|
|
947
|
-
// closingDate
|
|
948
|
-
// this data will be overwritten if private data belonging to the current customer is present
|
|
949
|
-
|
|
950
877
|
// participating vehicles are public
|
|
951
878
|
// merge private data that was matched for customer
|
|
952
879
|
|
|
@@ -954,17 +881,6 @@ module.exports = function(mongoose, config) {
|
|
|
954
881
|
return vehicle.investments.length > 0;
|
|
955
882
|
});
|
|
956
883
|
|
|
957
|
-
let allInvestments = _.flatten(_.pluck(doc.vehicles, 'investments'));
|
|
958
|
-
let mostRecentInvestment = _.max(allInvestments, function(investment) { return investment.investmentDate; });
|
|
959
|
-
|
|
960
|
-
// if we have private data, use it rather than the public data
|
|
961
|
-
if (mostRecentInvestment) {
|
|
962
|
-
if (mostRecentInvestment.preMoneyValuation) { doc.preMoneyValuation = mostRecentInvestment.preMoneyValuation; }
|
|
963
|
-
if (mostRecentInvestment.postMoneyValuation) { doc.postMoneyValuation = mostRecentInvestment.postMoneyValuation; }
|
|
964
|
-
if (mostRecentInvestment.dollarsRaised) { doc.dollarsRaised = mostRecentInvestment.dollarsRaised; }
|
|
965
|
-
if (mostRecentInvestment.closingDate) { doc.closingDate = mostRecentInvestment.closingDate; }
|
|
966
|
-
}
|
|
967
|
-
|
|
968
884
|
next();
|
|
969
885
|
|
|
970
886
|
});
|
|
@@ -985,6 +901,4 @@ module.exports = function(mongoose, config) {
|
|
|
985
901
|
|
|
986
902
|
mongoose.model('Round', Round);
|
|
987
903
|
|
|
988
|
-
};
|
|
989
|
-
|
|
990
|
-
|
|
904
|
+
};
|