@dhyasama/totem-models 1.0.0 → 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/index.js +18 -6
- package/lib/Account.js +13 -58
- package/lib/CapTable.js +397 -0
- package/lib/Fund.js +56 -12
- package/lib/Investment.js +143 -0
- package/lib/LimitedPartner.js +0 -50
- package/lib/List.js +1 -2
- package/lib/News.js +8 -61
- package/lib/Organization.js +520 -504
- package/lib/Person.js +49 -94
- package/lib/Round.js +805 -0
- package/package.json +4 -3
- package/test/CapTable.js +359 -0
- package/test/Investment.js +180 -0
- package/test/News.js +20 -46
- package/test/Organization.js +100 -30
- package/test/Person.js +0 -48
- package/test/Round.js +684 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
///////////////////////////////////////////////////////////////////////////////////////
|
|
4
|
+
// NOTES
|
|
5
|
+
//
|
|
6
|
+
// This is private data
|
|
7
|
+
//
|
|
8
|
+
// If you're looking for investments into an org, go in via Rounds.
|
|
9
|
+
//
|
|
10
|
+
// If you're looking for investments made by an org, you could go in this way
|
|
11
|
+
// but note there isn't round information here so you're probably better off
|
|
12
|
+
// going through Rounds as well.
|
|
13
|
+
//
|
|
14
|
+
///////////////////////////////////////////////////////////////////////////////////////
|
|
15
|
+
|
|
16
|
+
module.exports = function(mongoose, config) {
|
|
17
|
+
|
|
18
|
+
var Schema = mongoose.Schema;
|
|
19
|
+
var Round = mongoose.model('Round');
|
|
20
|
+
var async = require('async');
|
|
21
|
+
var _ = require('underscore');
|
|
22
|
+
|
|
23
|
+
var Investment = new Schema({
|
|
24
|
+
|
|
25
|
+
// Used by Round to selectively populate
|
|
26
|
+
customer: { type: Schema.ObjectId, ref: 'Organization', required: true, index: true },
|
|
27
|
+
|
|
28
|
+
investmentDate: { type: Date, required: true },
|
|
29
|
+
cost: { type: Number, default: 0 },
|
|
30
|
+
costPlusInterest: { type: Number, default: 0 },
|
|
31
|
+
pricePerShare: { type: Number, default: 0 },
|
|
32
|
+
|
|
33
|
+
debt: { type: Boolean, default: false },
|
|
34
|
+
debtType: {
|
|
35
|
+
type: String,
|
|
36
|
+
enum: [ 'Convertible Note', 'SAFE', 'SAFT', '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
|
+
// Catch-all for customer specific key-value pairs from sheet that aren't supported by our schema
|
|
50
|
+
details: [{
|
|
51
|
+
key: { type: String, trim: true, required: true },
|
|
52
|
+
value: { type: String, trim: true, required: true },
|
|
53
|
+
_id: false
|
|
54
|
+
}],
|
|
55
|
+
|
|
56
|
+
// note these fields are duplicative at the round level
|
|
57
|
+
// this is private data and will override the round level numbers where appropriate
|
|
58
|
+
preMoneyValuation: { type: Number, default: 0 },
|
|
59
|
+
dollarsRaised: { type: Number, default: 0 },
|
|
60
|
+
closingDate: { type: Date, default: null }
|
|
61
|
+
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
//////////////////////////////////////////////////////
|
|
67
|
+
// STATICS
|
|
68
|
+
// Statics operate on the entire collection
|
|
69
|
+
//////////////////////////////////////////////////////
|
|
70
|
+
|
|
71
|
+
Investment.statics.removeCustomerInvestments = function removeCustomerInvestments(customerId, cb) {
|
|
72
|
+
|
|
73
|
+
// Note that rounds hold references to investments, so when an investment is deleted we need
|
|
74
|
+
// to also remove any references to it. Investment has a post-remove hook which does that, but
|
|
75
|
+
// post-remove hooks only fire on document.remove not schema.remove, hence the loop below to call
|
|
76
|
+
// remove on each matching investment.
|
|
77
|
+
|
|
78
|
+
var self = this;
|
|
79
|
+
|
|
80
|
+
self
|
|
81
|
+
.find({customer: customerId})
|
|
82
|
+
.select('_id')
|
|
83
|
+
.exec(function(err, investments) {
|
|
84
|
+
|
|
85
|
+
if (err) return cb(err, null);
|
|
86
|
+
|
|
87
|
+
async.each(investments, function(investment, callback) {
|
|
88
|
+
investment.remove(callback);
|
|
89
|
+
}, function(err) {
|
|
90
|
+
return cb(err, null);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
Investment.statics.upsert = function upsert(investment, cb) {
|
|
98
|
+
if (!investment) { return cb(new Error('investment is required'), null); }
|
|
99
|
+
investment.save(cb);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
///////////////////////////////////////////////////////////////////////////////////////
|
|
105
|
+
// HOOKS
|
|
106
|
+
// Pre-save: fired on every document before it is saved
|
|
107
|
+
// Post-init: fired on every document when it's returned from a query
|
|
108
|
+
// Post-remove: fired on every document after it's deleted
|
|
109
|
+
///////////////////////////////////////////////////////////////////////////////////////
|
|
110
|
+
|
|
111
|
+
Investment.pre('save', function(next) {
|
|
112
|
+
|
|
113
|
+
var self = this;
|
|
114
|
+
|
|
115
|
+
if (self.debtType) self.debt = true;
|
|
116
|
+
|
|
117
|
+
return next();
|
|
118
|
+
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
Investment.post('remove', function(doc) {
|
|
122
|
+
// Clean up reference
|
|
123
|
+
Round.removeInvestment(doc._id, function(err, result) {
|
|
124
|
+
if (err) console.log(err);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
///////////////////////////////////////////////////////////////////////////////////////
|
|
131
|
+
// CONFIG
|
|
132
|
+
///////////////////////////////////////////////////////////////////////////////////////
|
|
133
|
+
|
|
134
|
+
Investment.set('toJSON', { virtuals: true });
|
|
135
|
+
Investment.set('autoIndex', true);
|
|
136
|
+
|
|
137
|
+
Investment.on('index', function(err) { console.log('error building Investment indexes: ' + err); });
|
|
138
|
+
|
|
139
|
+
mongoose.model('Investment', Investment);
|
|
140
|
+
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
|
package/lib/LimitedPartner.js
CHANGED
|
@@ -494,16 +494,6 @@ module.exports = function(mongoose, config) {
|
|
|
494
494
|
|
|
495
495
|
if (err) return cb(err, null);
|
|
496
496
|
|
|
497
|
-
lps = _.filter(lps, function(lp) {
|
|
498
|
-
if (CUSTOMER_ID == 'GLOBAL_PROCESS') { return lp; }
|
|
499
|
-
else {
|
|
500
|
-
var match = _.find(lp.fundsInvested, function(fund) {
|
|
501
|
-
return fund.fund.investor.toString() == CUSTOMER_ID.toString();
|
|
502
|
-
});
|
|
503
|
-
return typeof match != 'undefined';
|
|
504
|
-
}
|
|
505
|
-
});
|
|
506
|
-
|
|
507
497
|
lps = _.sortBy(lps, function(lp) {
|
|
508
498
|
return lp.name ? lp.name.toLowerCase() : '';
|
|
509
499
|
});
|
|
@@ -584,28 +574,6 @@ module.exports = function(mongoose, config) {
|
|
|
584
574
|
|
|
585
575
|
};
|
|
586
576
|
|
|
587
|
-
//TODO: this is same as Person & Portfolio Company Function
|
|
588
|
-
//Helper function that multiple models can use...
|
|
589
|
-
LimitedPartner.statics.verify = function(lpId, verified, username, role, cb) {
|
|
590
|
-
|
|
591
|
-
this
|
|
592
|
-
.findById(lpId, this.getReadFilterKeys(role))
|
|
593
|
-
.exec(function(err, lp) {
|
|
594
|
-
|
|
595
|
-
lp.status.verified = verified;
|
|
596
|
-
lp.status.verifiedBy = username;
|
|
597
|
-
lp.status.verifiedOn = Date.now();
|
|
598
|
-
|
|
599
|
-
if(verified) {
|
|
600
|
-
lp.flagged.resolved = true;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
return this.upsert(lp, username, role, cb);
|
|
604
|
-
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
};
|
|
608
|
-
|
|
609
577
|
LimitedPartner.statics.flag = function(lpId, resolved, text, username, role, cb) {
|
|
610
578
|
|
|
611
579
|
var context = this;
|
|
@@ -685,24 +653,6 @@ module.exports = function(mongoose, config) {
|
|
|
685
653
|
return next();
|
|
686
654
|
});
|
|
687
655
|
|
|
688
|
-
// var takeSnapshot = function(cb) {
|
|
689
|
-
//
|
|
690
|
-
// var LimitedPartnerHistory = mongoose.model('LimitedPartnerHistory');
|
|
691
|
-
// var snapshot = new LimitedPartnerHistory();
|
|
692
|
-
// snapshot.create(self, 'jason', function(err, result) {
|
|
693
|
-
// if (err) {} // todo: alert admin to fix; ok to proceed;
|
|
694
|
-
// return cb();
|
|
695
|
-
// });
|
|
696
|
-
//
|
|
697
|
-
// };
|
|
698
|
-
//
|
|
699
|
-
// encryptSensitiveData(function() {
|
|
700
|
-
// takeSnapshot(function() {
|
|
701
|
-
// return next();
|
|
702
|
-
// })
|
|
703
|
-
// });
|
|
704
|
-
|
|
705
|
-
|
|
706
656
|
});
|
|
707
657
|
|
|
708
658
|
LimitedPartner.pre('init', function(next) {
|
package/lib/List.js
CHANGED
|
@@ -24,13 +24,12 @@ module.exports = function(mongoose, config) {
|
|
|
24
24
|
|
|
25
25
|
// Keeping these separate to facilitate population
|
|
26
26
|
|
|
27
|
-
// todo - how to update these with company consolidation?
|
|
28
|
-
|
|
29
27
|
limitedPartners: [{ type: Schema.ObjectId, ref: 'LimitedPartner', index: false, required: false }],
|
|
30
28
|
|
|
31
29
|
organizations: [{ type: Schema.ObjectId, ref: 'Organization', index: false, required: false }],
|
|
32
30
|
|
|
33
31
|
people: [{ type: Schema.ObjectId, ref: 'Person', index: false, required: false }]
|
|
32
|
+
|
|
34
33
|
});
|
|
35
34
|
|
|
36
35
|
// Items are kept separate to facilitate population
|
package/lib/News.js
CHANGED
|
@@ -22,24 +22,6 @@ module.exports = function(mongoose, config) {
|
|
|
22
22
|
|
|
23
23
|
publication: { type: String },
|
|
24
24
|
|
|
25
|
-
fundsInvolved: [{
|
|
26
|
-
type: Schema.ObjectId,
|
|
27
|
-
ref: 'Fund'
|
|
28
|
-
}],
|
|
29
|
-
|
|
30
|
-
// TODO - make all news about port cos
|
|
31
|
-
// required: true
|
|
32
|
-
// remove internal
|
|
33
|
-
// update admin to reflect these changes
|
|
34
|
-
|
|
35
|
-
// todo - put new org id in org field
|
|
36
|
-
portfolioCompany: {
|
|
37
|
-
type: Schema.ObjectId,
|
|
38
|
-
ref: 'PortfolioCompany',
|
|
39
|
-
required: false,
|
|
40
|
-
default: null
|
|
41
|
-
},
|
|
42
|
-
|
|
43
25
|
org: {
|
|
44
26
|
type: Schema.ObjectId,
|
|
45
27
|
ref: 'Organization',
|
|
@@ -49,13 +31,6 @@ module.exports = function(mongoose, config) {
|
|
|
49
31
|
|
|
50
32
|
internal: { type: Boolean, default: false },
|
|
51
33
|
|
|
52
|
-
// TODO - this will be going away
|
|
53
|
-
discourse: {
|
|
54
|
-
id: { type: String },
|
|
55
|
-
firstPostId: { type: String },
|
|
56
|
-
slug: { type: String }
|
|
57
|
-
},
|
|
58
|
-
|
|
59
34
|
extracted: { type: Schema.Types.Mixed }
|
|
60
35
|
|
|
61
36
|
});
|
|
@@ -64,7 +39,7 @@ module.exports = function(mongoose, config) {
|
|
|
64
39
|
|
|
65
40
|
this
|
|
66
41
|
.findById(id)
|
|
67
|
-
.populate('org
|
|
42
|
+
.populate('org')
|
|
68
43
|
.exec(cb);
|
|
69
44
|
|
|
70
45
|
};
|
|
@@ -73,17 +48,21 @@ module.exports = function(mongoose, config) {
|
|
|
73
48
|
|
|
74
49
|
this
|
|
75
50
|
.find()
|
|
76
|
-
.populate('org
|
|
51
|
+
.populate('org')
|
|
77
52
|
.sort('-createdOn')
|
|
78
53
|
.exec(cb);
|
|
79
54
|
|
|
80
55
|
};
|
|
81
56
|
|
|
57
|
+
News.statics.listForCompanies = function listForCompanies(orgids, cb) {
|
|
58
|
+
this.find({ 'org': { $in : orgids }, 'deleted': {$ne: true} }).exec(cb);
|
|
59
|
+
};
|
|
60
|
+
|
|
82
61
|
News.statics.listForCompany = function (id, cb) {
|
|
83
62
|
|
|
84
63
|
this
|
|
85
64
|
.find({ org: id })
|
|
86
|
-
.populate('org
|
|
65
|
+
.populate('org')
|
|
87
66
|
.sort('-createdOn')
|
|
88
67
|
.exec(cb);
|
|
89
68
|
|
|
@@ -93,7 +72,7 @@ module.exports = function(mongoose, config) {
|
|
|
93
72
|
|
|
94
73
|
this
|
|
95
74
|
.find()
|
|
96
|
-
.populate('org
|
|
75
|
+
.populate('org')
|
|
97
76
|
.sort('-createdOn')
|
|
98
77
|
.exec(function (err, news) {
|
|
99
78
|
|
|
@@ -109,38 +88,6 @@ module.exports = function(mongoose, config) {
|
|
|
109
88
|
|
|
110
89
|
};
|
|
111
90
|
|
|
112
|
-
News.statics.listInternal = function (cb) {
|
|
113
|
-
|
|
114
|
-
this
|
|
115
|
-
.find({ internal: true })
|
|
116
|
-
.populate('org fundsInvolved')
|
|
117
|
-
.sort('-createdOn')
|
|
118
|
-
.exec(cb);
|
|
119
|
-
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
News.statics.listForFunds = function (fundIds, cb) {
|
|
123
|
-
|
|
124
|
-
if (!fundIds || fundIds.length == 0) return cb(null, null);
|
|
125
|
-
|
|
126
|
-
this
|
|
127
|
-
.find({ fundsInvolved: { $in : fundIds } })
|
|
128
|
-
.populate('org fundsInvolved')
|
|
129
|
-
.sort('-createdOn')
|
|
130
|
-
.exec(function(err, result) {
|
|
131
|
-
|
|
132
|
-
if (err) return cb(err, null);
|
|
133
|
-
|
|
134
|
-
_.each(result, function(newsItem) {
|
|
135
|
-
newsItem.fundsInvolved = _.filter(newsItem.fundsInvolved, function(fund) { return fundIds.indexOf(fund._id.toString()) >= 0; });
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
return cb(null, result);
|
|
139
|
-
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
};
|
|
143
|
-
|
|
144
91
|
News.statics.upsert = function (news, cb) {
|
|
145
92
|
news.markModified('extracted');
|
|
146
93
|
news.save(cb);
|