@dhyasama/totem-models 8.46.0 → 8.47.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/lib/Investment.js +10 -61
- package/lib/Round.js +29 -117
- 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,11 +405,9 @@ module.exports = function(mongoose, config) {
|
|
|
453
405
|
// reverse chronological
|
|
454
406
|
rounds = _.sortBy(rounds, function(r) {
|
|
455
407
|
|
|
456
|
-
let date = r.closingDate;
|
|
457
|
-
|
|
458
408
|
// use investment date in absence of closing date
|
|
459
409
|
if (!date && r.vehicles.length && r.vehicles[0].investments.length) {
|
|
460
|
-
date = r.vehicles[0].investments[0].
|
|
410
|
+
date = r.vehicles[0].investments[0].date;
|
|
461
411
|
}
|
|
462
412
|
|
|
463
413
|
return date;
|
|
@@ -515,11 +465,9 @@ module.exports = function(mongoose, config) {
|
|
|
515
465
|
// reverse chronological
|
|
516
466
|
rounds = _.sortBy(rounds, function(r) {
|
|
517
467
|
|
|
518
|
-
let date = r.closingDate;
|
|
519
|
-
|
|
520
468
|
// use investment date in absence of closing date
|
|
521
469
|
if (!date && r.vehicles.length && r.vehicles[0].investments.length) {
|
|
522
|
-
date = r.vehicles[0].investments[0].
|
|
470
|
+
date = r.vehicles[0].investments[0].date;
|
|
523
471
|
}
|
|
524
472
|
|
|
525
473
|
return date;
|
|
@@ -607,27 +555,27 @@ module.exports = function(mongoose, config) {
|
|
|
607
555
|
|
|
608
556
|
// we now have all the rounds the fund has participated in with other fund information removed from each round
|
|
609
557
|
|
|
610
|
-
let
|
|
611
|
-
let
|
|
558
|
+
let totalInvestment = _.reduce(rounds, function(memo, round) {
|
|
559
|
+
let roundInvestment = _.reduce(round.vehicles, function(memo, vehicle) { return memo + vehicle.investment; }, 0);
|
|
612
560
|
return memo + roundCost;
|
|
613
561
|
}, 0);
|
|
614
562
|
|
|
615
563
|
let totalUnrealized = _.reduce(rounds, function(memo, round) {
|
|
616
|
-
let
|
|
617
|
-
return memo +
|
|
564
|
+
let roundUnrealized = _.reduce(round.vehicles, function(memo, vehicle) { return memo + vehicle.unrealized; }, 0);
|
|
565
|
+
return memo + roundUnrealized;
|
|
618
566
|
}, 0);
|
|
619
567
|
|
|
620
568
|
let totalRealized = _.reduce(rounds, function(memo, round) {
|
|
621
|
-
let
|
|
622
|
-
return memo +
|
|
569
|
+
let roundRealized = _.reduce(round.vehicles, function(memo, vehicle) { return memo + vehicle.realized; }, 0);
|
|
570
|
+
return memo + roundRealized;
|
|
623
571
|
}, 0);
|
|
624
572
|
|
|
625
573
|
return cb(null, {
|
|
626
|
-
|
|
574
|
+
investment: totalInvestment,
|
|
627
575
|
unrealized: totalUnrealized,
|
|
628
576
|
realized: totalRealized,
|
|
629
577
|
value: totalUnrealized + totalRealized,
|
|
630
|
-
multiple:
|
|
578
|
+
multiple: totalInvestment > 0 ? (totalUnrealized + totalRealized) / totalInvestment : 0
|
|
631
579
|
});
|
|
632
580
|
|
|
633
581
|
});
|
|
@@ -652,7 +600,7 @@ module.exports = function(mongoose, config) {
|
|
|
652
600
|
|
|
653
601
|
let query = self.find({ 'vehicles.fund': fundId });
|
|
654
602
|
|
|
655
|
-
query.select('organization vehicles
|
|
603
|
+
query.select('organization vehicles');
|
|
656
604
|
query.populate('organization', 'name slug logoUrl description contact filters status ipo closed acquired operating website websiteAliases');
|
|
657
605
|
query.populate('vehicles.fund');
|
|
658
606
|
|
|
@@ -706,7 +654,7 @@ module.exports = function(mongoose, config) {
|
|
|
706
654
|
|
|
707
655
|
let query = self.find({ 'vehicles.fund': { $in: fundIds } });
|
|
708
656
|
|
|
709
|
-
query.select('organization vehicles
|
|
657
|
+
query.select('organization vehicles');
|
|
710
658
|
query.populate('organization', 'name slug logoUrl description contact chairs filters status ipo closed acquired operating website websiteAliases');
|
|
711
659
|
query.deepPopulate([
|
|
712
660
|
'organization.chairs.first',
|
|
@@ -879,26 +827,10 @@ module.exports = function(mongoose, config) {
|
|
|
879
827
|
var investments = vehicle.investments;
|
|
880
828
|
|
|
881
829
|
if (investments.length >= 1) {
|
|
882
|
-
|
|
883
|
-
vehicle.cost = _.reduce(investments, function(memo, investment){ return memo + investment.cost; }, 0);
|
|
830
|
+
vehicle.investment = _.reduce(investments, function(memo, investment) { return memo + investment.investment; }, 0);
|
|
884
831
|
}
|
|
885
832
|
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;
|
|
833
|
+
vehicle.investment = 0;
|
|
902
834
|
}
|
|
903
835
|
|
|
904
836
|
});
|
|
@@ -911,7 +843,7 @@ module.exports = function(mongoose, config) {
|
|
|
911
843
|
|
|
912
844
|
result.populate({
|
|
913
845
|
path: 'vehicles.investments',
|
|
914
|
-
select: '
|
|
846
|
+
select: 'investment'
|
|
915
847
|
}, function(err, doc) {
|
|
916
848
|
|
|
917
849
|
if (err) return cb(err, null);
|
|
@@ -940,13 +872,6 @@ module.exports = function(mongoose, config) {
|
|
|
940
872
|
|
|
941
873
|
// note participating people are public as there is no investment, i.e., private, data attached to them
|
|
942
874
|
|
|
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
875
|
// participating vehicles are public
|
|
951
876
|
// merge private data that was matched for customer
|
|
952
877
|
|
|
@@ -954,17 +879,6 @@ module.exports = function(mongoose, config) {
|
|
|
954
879
|
return vehicle.investments.length > 0;
|
|
955
880
|
});
|
|
956
881
|
|
|
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
882
|
next();
|
|
969
883
|
|
|
970
884
|
});
|
|
@@ -985,6 +899,4 @@ module.exports = function(mongoose, config) {
|
|
|
985
899
|
|
|
986
900
|
mongoose.model('Round', Round);
|
|
987
901
|
|
|
988
|
-
};
|
|
989
|
-
|
|
990
|
-
|
|
902
|
+
};
|