@dhyasama/totem-models 9.4.2 → 9.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/lib/CapTable.js +82 -133
- package/package.json +1 -1
package/lib/CapTable.js
CHANGED
|
@@ -34,41 +34,21 @@ module.exports = function(mongoose, config) {
|
|
|
34
34
|
// optional reference to download the cap table file
|
|
35
35
|
document: { type: Schema.ObjectId, ref: 'Document', required: false },
|
|
36
36
|
|
|
37
|
-
// computed on save (via pre-save hook); don't set directly;
|
|
38
|
-
shares: { type: Number, default: 0 },
|
|
39
|
-
|
|
40
37
|
stakeholders: [{
|
|
38
|
+
|
|
39
|
+
_id: false,
|
|
41
40
|
|
|
42
|
-
//
|
|
41
|
+
// the raw stakeholder name on the cap table
|
|
43
42
|
name: { type: String, trim: true, required: true },
|
|
44
43
|
|
|
44
|
+
// the stakeholders shares broken down by round
|
|
45
45
|
rounds: [{
|
|
46
|
-
|
|
46
|
+
_id: false,
|
|
47
|
+
name: { type: String, trim: true, required: true },
|
|
47
48
|
shares: { type: Number, default: 0 },
|
|
48
49
|
}],
|
|
49
50
|
|
|
50
|
-
//
|
|
51
|
-
shares: { type: Number, default: 0 },
|
|
52
|
-
|
|
53
|
-
// computed on save (via pre-save hook); don't set directly;
|
|
54
|
-
ownership: { type: Number, default: 0 },
|
|
55
|
-
|
|
56
|
-
// link stakeholder to a fund in our system
|
|
57
|
-
fund: {
|
|
58
|
-
type: Schema.ObjectId,
|
|
59
|
-
ref: 'Fund',
|
|
60
|
-
default: null,
|
|
61
|
-
required: false,
|
|
62
|
-
validate: {
|
|
63
|
-
validator: function(v) {
|
|
64
|
-
if (v && (this.lp || this.org || this.person)) return false;
|
|
65
|
-
else return true;
|
|
66
|
-
},
|
|
67
|
-
message: 'A stakeholder can only be linked to one of the following: fund, lp, org, person'
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
// link stakeholder to a lp in our system
|
|
51
|
+
// stakeholder reference to an lp
|
|
72
52
|
lp: {
|
|
73
53
|
type: Schema.ObjectId,
|
|
74
54
|
ref: 'LimitedPartner',
|
|
@@ -76,14 +56,14 @@ module.exports = function(mongoose, config) {
|
|
|
76
56
|
required: false,
|
|
77
57
|
validate: {
|
|
78
58
|
validator: function(v) {
|
|
79
|
-
if (v && (this.
|
|
59
|
+
if (v && (this.org || this.person)) return false;
|
|
80
60
|
else return true;
|
|
81
61
|
},
|
|
82
|
-
message: 'A stakeholder can only be linked to one of the following:
|
|
62
|
+
message: 'A stakeholder can only be linked to one of the following: lp, org, person'
|
|
83
63
|
}
|
|
84
64
|
},
|
|
85
65
|
|
|
86
|
-
//
|
|
66
|
+
// stakeholder reference to an org
|
|
87
67
|
org: {
|
|
88
68
|
type: Schema.ObjectId,
|
|
89
69
|
ref: 'Organization',
|
|
@@ -91,14 +71,14 @@ module.exports = function(mongoose, config) {
|
|
|
91
71
|
required: false,
|
|
92
72
|
validate: {
|
|
93
73
|
validator: function(v) {
|
|
94
|
-
if (v && (this.
|
|
74
|
+
if (v && (this.lp || this.person)) return false;
|
|
95
75
|
else return true;
|
|
96
76
|
},
|
|
97
|
-
message: 'A stakeholder can only be linked to one of the following:
|
|
77
|
+
message: 'A stakeholder can only be linked to one of the following: lp, org, person'
|
|
98
78
|
}
|
|
99
79
|
},
|
|
100
80
|
|
|
101
|
-
//
|
|
81
|
+
// stakeholder reference to an person
|
|
102
82
|
person: {
|
|
103
83
|
type: Schema.ObjectId,
|
|
104
84
|
ref: 'Person',
|
|
@@ -106,12 +86,12 @@ module.exports = function(mongoose, config) {
|
|
|
106
86
|
required: false,
|
|
107
87
|
validate: {
|
|
108
88
|
validator: function(v) {
|
|
109
|
-
if (v && (this.
|
|
89
|
+
if (v && (this.lp || this.org)) return false;
|
|
110
90
|
else return true;
|
|
111
91
|
},
|
|
112
|
-
message: 'A stakeholder can only be linked to one of the following:
|
|
92
|
+
message: 'A stakeholder can only be linked to one of the following: lp, org, person'
|
|
113
93
|
}
|
|
114
|
-
}
|
|
94
|
+
}
|
|
115
95
|
|
|
116
96
|
}],
|
|
117
97
|
|
|
@@ -127,30 +107,19 @@ module.exports = function(mongoose, config) {
|
|
|
127
107
|
// Properties that are not persisted to the database
|
|
128
108
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
129
109
|
|
|
130
|
-
|
|
110
|
+
CapTable.virtual('shares').get(function() {
|
|
131
111
|
|
|
132
112
|
var self = this;
|
|
133
113
|
|
|
134
|
-
var
|
|
114
|
+
var shares = 0;
|
|
135
115
|
|
|
136
116
|
_.each(self.stakeholders, function(stakeholder) {
|
|
137
117
|
_.each(stakeholder.rounds, function(round) {
|
|
138
|
-
|
|
139
|
-
var roundMatch = _.find(stakeholdersByRound, function(r) { return r.round == round.round });
|
|
140
|
-
|
|
141
|
-
if(roundMatch) {
|
|
142
|
-
roundMatch.stakeholders.push(stakeholder);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
else {
|
|
146
|
-
stakeholdersByRound.push({round: round.round, stakeholders: []});
|
|
147
|
-
_.last(stakeholdersByRound).stakeholders.push(stakeholder);
|
|
148
|
-
}
|
|
149
|
-
|
|
118
|
+
shares += round.shares;
|
|
150
119
|
});
|
|
151
120
|
});
|
|
152
121
|
|
|
153
|
-
return
|
|
122
|
+
return shares;
|
|
154
123
|
|
|
155
124
|
});
|
|
156
125
|
|
|
@@ -165,21 +134,7 @@ module.exports = function(mongoose, config) {
|
|
|
165
134
|
// Statics operate on the entire collection
|
|
166
135
|
//////////////////////////////////////////////////////
|
|
167
136
|
|
|
168
|
-
CapTable.statics.getForCustomer = function getForCustomer(
|
|
169
|
-
|
|
170
|
-
var self = this;
|
|
171
|
-
|
|
172
|
-
self
|
|
173
|
-
.findOne({customer: customerId, organization: orgId })
|
|
174
|
-
.populate('stakeholders.fund', 'name shortName')
|
|
175
|
-
.populate('stakeholders.lp', 'name')
|
|
176
|
-
.populate('stakeholders.org', 'name logoUrl')
|
|
177
|
-
.populate('stakeholders.person', 'name avatarUrl title')
|
|
178
|
-
.exec(cb);
|
|
179
|
-
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
CapTable.statics.getAllForCustomer = function getAllForCustomer(options, cb) {
|
|
137
|
+
CapTable.statics.getForCustomer = function getForCustomer(options, cb) {
|
|
183
138
|
|
|
184
139
|
var self = this;
|
|
185
140
|
|
|
@@ -197,7 +152,6 @@ module.exports = function(mongoose, config) {
|
|
|
197
152
|
query.populate('organization', 'name logoUrl');
|
|
198
153
|
|
|
199
154
|
if(options.populateStakeholders) {
|
|
200
|
-
query.populate('stakeholders.fund', 'name shortName')
|
|
201
155
|
query.populate('stakeholders.lp', 'name')
|
|
202
156
|
query.populate('stakeholders.org', 'name logoUrl')
|
|
203
157
|
query.populate('stakeholders.person', 'name avatarUrl title')
|
|
@@ -207,61 +161,100 @@ module.exports = function(mongoose, config) {
|
|
|
207
161
|
|
|
208
162
|
};
|
|
209
163
|
|
|
210
|
-
CapTable.statics.
|
|
164
|
+
CapTable.statics.deleteForCustomer = function deleteForCustomer(options, cb) {
|
|
165
|
+
|
|
211
166
|
var self = this;
|
|
212
|
-
|
|
167
|
+
|
|
168
|
+
if (!cb) { throw new Error('cb is required'); }
|
|
169
|
+
if (!options) { return cb(new Error('options is required'), null); }
|
|
170
|
+
if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
|
|
171
|
+
if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
|
|
172
|
+
|
|
173
|
+
self.remove({ customer: options.CUSTOMER_ID, 'entered.by': { $ne: 'carta-parser' } }, cb);
|
|
174
|
+
|
|
213
175
|
};
|
|
214
176
|
|
|
215
|
-
CapTable.statics.
|
|
177
|
+
CapTable.statics.getForOrg = function getForOrg(options, cb) {
|
|
216
178
|
|
|
217
179
|
var self = this;
|
|
218
180
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
181
|
+
if (!cb) { throw new Error('cb is required'); }
|
|
182
|
+
if (!options) { return cb(new Error('options is required'), null); }
|
|
183
|
+
if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
|
|
184
|
+
if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
|
|
223
185
|
|
|
224
|
-
|
|
186
|
+
var query;
|
|
225
187
|
|
|
226
|
-
|
|
188
|
+
query = self.find({
|
|
189
|
+
customer: options.CUSTOMER_ID,
|
|
190
|
+
organization: options.orgId
|
|
191
|
+
});
|
|
227
192
|
|
|
228
|
-
|
|
193
|
+
query.populate('organization', 'name logoUrl');
|
|
229
194
|
|
|
230
|
-
|
|
231
|
-
.
|
|
232
|
-
.populate('
|
|
233
|
-
.
|
|
195
|
+
if(options.populateStakeholders) {
|
|
196
|
+
query.populate('stakeholders.lp', 'name')
|
|
197
|
+
query.populate('stakeholders.org', 'name logoUrl')
|
|
198
|
+
query.populate('stakeholders.person', 'name avatarUrl title')
|
|
199
|
+
}
|
|
234
200
|
|
|
235
201
|
};
|
|
236
202
|
|
|
237
|
-
CapTable.statics.getByLP = function getByLP(
|
|
203
|
+
CapTable.statics.getByLP = function getByLP(options, cb) {
|
|
238
204
|
|
|
239
205
|
var self = this;
|
|
240
206
|
|
|
207
|
+
if (!cb) { throw new Error('cb is required'); }
|
|
208
|
+
if (!options) { return cb(new Error('options is required'), null); }
|
|
209
|
+
if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
|
|
210
|
+
if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
|
|
211
|
+
|
|
212
|
+
query = self.find({
|
|
213
|
+
customer: options.CUSTOMER_ID,
|
|
214
|
+
'stakeholders.lp': options.lpId
|
|
215
|
+
});
|
|
216
|
+
|
|
241
217
|
self
|
|
242
|
-
.find({ 'stakeholders.lp': lpId })
|
|
243
218
|
.populate('organization', 'name logoUrl')
|
|
244
219
|
.exec(cb);
|
|
245
220
|
|
|
246
221
|
};
|
|
247
222
|
|
|
248
|
-
CapTable.statics.getByOrg = function getByOrg(
|
|
223
|
+
CapTable.statics.getByOrg = function getByOrg(options, cb) {
|
|
249
224
|
|
|
250
225
|
var self = this;
|
|
251
226
|
|
|
227
|
+
if (!cb) { throw new Error('cb is required'); }
|
|
228
|
+
if (!options) { return cb(new Error('options is required'), null); }
|
|
229
|
+
if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
|
|
230
|
+
if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
|
|
231
|
+
|
|
232
|
+
query = self.find({
|
|
233
|
+
customer: options.CUSTOMER_ID,
|
|
234
|
+
'stakeholders.org': options.orgId
|
|
235
|
+
});
|
|
236
|
+
|
|
252
237
|
self
|
|
253
|
-
.find({ 'stakeholders.org': orgId })
|
|
254
238
|
.populate('organization', 'name logoUrl')
|
|
255
239
|
.exec(cb);
|
|
256
240
|
|
|
257
241
|
};
|
|
258
242
|
|
|
259
|
-
CapTable.statics.getByPerson = function getByPerson(
|
|
243
|
+
CapTable.statics.getByPerson = function getByPerson(options, cb) {
|
|
260
244
|
|
|
261
245
|
var self = this;
|
|
262
246
|
|
|
247
|
+
if (!cb) { throw new Error('cb is required'); }
|
|
248
|
+
if (!options) { return cb(new Error('options is required'), null); }
|
|
249
|
+
if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
|
|
250
|
+
if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
|
|
251
|
+
|
|
252
|
+
query = self.find({
|
|
253
|
+
customer: options.CUSTOMER_ID,
|
|
254
|
+
'stakeholders.person': options.personId
|
|
255
|
+
});
|
|
256
|
+
|
|
263
257
|
self
|
|
264
|
-
.find({ 'stakeholders.person': personId })
|
|
265
258
|
.populate('organization', 'name logoUrl')
|
|
266
259
|
.exec(cb);
|
|
267
260
|
|
|
@@ -298,12 +291,7 @@ module.exports = function(mongoose, config) {
|
|
|
298
291
|
capTable.entered.by = username;
|
|
299
292
|
capTable.entered.on = new Date();
|
|
300
293
|
|
|
301
|
-
self.update(
|
|
302
|
-
{ '_id': capTable._id },
|
|
303
|
-
capTable,
|
|
304
|
-
{ upsert: false, multi: false, overwrite: true },
|
|
305
|
-
cb
|
|
306
|
-
);
|
|
294
|
+
self.update({ '_id': capTable._id }, capTable, { upsert: false, multi: false, overwrite: true }, cb);
|
|
307
295
|
|
|
308
296
|
};
|
|
309
297
|
|
|
@@ -324,8 +312,7 @@ module.exports = function(mongoose, config) {
|
|
|
324
312
|
query = self.find({
|
|
325
313
|
customer: options.CUSTOMER_ID,
|
|
326
314
|
'stakeholders.name': new RegExp(terms, 'i'),
|
|
327
|
-
'stakeholders.person': null
|
|
328
|
-
'stakeholders.fund': null
|
|
315
|
+
'stakeholders.person': null
|
|
329
316
|
});
|
|
330
317
|
|
|
331
318
|
query.populate('organization', 'name logoUrl');
|
|
@@ -352,44 +339,6 @@ module.exports = function(mongoose, config) {
|
|
|
352
339
|
// Pre-save: fired on every document before it is saved
|
|
353
340
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
354
341
|
|
|
355
|
-
CapTable.pre('save', function(next) {
|
|
356
|
-
|
|
357
|
-
var self = this;
|
|
358
|
-
|
|
359
|
-
// Sum each stakeholders shares
|
|
360
|
-
_.each(self.stakeholders, function(stakeholder) {
|
|
361
|
-
|
|
362
|
-
var shares = _.reduce(stakeholder.rounds, function(memo, i) {
|
|
363
|
-
return memo + i.shares;
|
|
364
|
-
}, 0);
|
|
365
|
-
|
|
366
|
-
stakeholder.shares = shares;
|
|
367
|
-
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
// Sum total shares
|
|
371
|
-
self.shares = _.reduce(self.stakeholders, function(memo, stakeholder) {
|
|
372
|
-
return memo + stakeholder.shares;
|
|
373
|
-
}, 0);
|
|
374
|
-
|
|
375
|
-
// Calculate each stakeholders ownership
|
|
376
|
-
_.each(self.stakeholders, function(stakeholder) {
|
|
377
|
-
if (self.shares == 0) stakeholder.shares = 0;
|
|
378
|
-
else stakeholder.ownership = ((stakeholder.shares / self.shares) * 100).toFixed(2);
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
return next();
|
|
382
|
-
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
CapTable.post('init', function(doc, next) {
|
|
386
|
-
|
|
387
|
-
doc.stakeholders = _.sortBy(doc.stakeholders, 'ownership').reverse();
|
|
388
|
-
|
|
389
|
-
return next();
|
|
390
|
-
|
|
391
|
-
});
|
|
392
|
-
|
|
393
342
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
394
343
|
// CONFIG
|
|
395
344
|
///////////////////////////////////////////////////////////////////////////////////////
|