@dhyasama/totem-models 6.36.0 → 7.0.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/CapTable.js +0 -12
- package/lib/Financials.js +231 -17
- package/lib/Organization.js +13 -1
- package/package.json +1 -1
package/lib/CapTable.js
CHANGED
|
@@ -254,18 +254,6 @@ module.exports = function(mongoose, config) {
|
|
|
254
254
|
|
|
255
255
|
};
|
|
256
256
|
|
|
257
|
-
CapTable.statics.getAllForCustomer2 = function getAllForCustomer2(customerId, cb) {
|
|
258
|
-
|
|
259
|
-
var self = this;
|
|
260
|
-
|
|
261
|
-
self
|
|
262
|
-
.find({customer: customerId })
|
|
263
|
-
.populate('stakeholders.person', 'name avatarUrl title')
|
|
264
|
-
.populate('stakeholders.fund', 'name shortName')
|
|
265
|
-
.exec(cb);
|
|
266
|
-
|
|
267
|
-
};
|
|
268
|
-
|
|
269
257
|
CapTable.statics.getByFunds = function getByFunds(fundIds, cb) {
|
|
270
258
|
|
|
271
259
|
var self = this;
|
package/lib/Financials.js
CHANGED
|
@@ -23,13 +23,29 @@ module.exports = function(mongoose, config) {
|
|
|
23
23
|
|
|
24
24
|
currency: { type: String, required: true, default: 'USD' },
|
|
25
25
|
|
|
26
|
+
notifications: {
|
|
27
|
+
|
|
28
|
+
company: {
|
|
29
|
+
|
|
30
|
+
contact: {
|
|
31
|
+
|
|
32
|
+
exclude: { type: Boolean, default: false, required: true },
|
|
33
|
+
email: { type: String, trim: true },
|
|
34
|
+
name: { type: String, trim: true }
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
},
|
|
41
|
+
|
|
26
42
|
snapshots: [{
|
|
27
|
-
|
|
43
|
+
|
|
28
44
|
uuid: { type: String },
|
|
29
|
-
|
|
45
|
+
|
|
30
46
|
year: { type: Number, default: 0 },
|
|
31
|
-
|
|
32
|
-
|
|
47
|
+
period: { type: String, enum: ['Annual', 'Q1', 'Q2', 'Q3', 'Q4', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] },
|
|
48
|
+
|
|
33
49
|
cash: {
|
|
34
50
|
amount: { type: Number, default: 0 },
|
|
35
51
|
},
|
|
@@ -42,11 +58,11 @@ module.exports = function(mongoose, config) {
|
|
|
42
58
|
},
|
|
43
59
|
runway: {
|
|
44
60
|
amount: { type: Number, default: 0 },
|
|
45
|
-
period: { type: String, enum: [null, 'monthly', 'quarterly', 'yearly']},
|
|
61
|
+
period: { type: String, enum: [null, 'monthly', 'quarterly', 'yearly']},
|
|
46
62
|
},
|
|
47
63
|
|
|
48
64
|
// income statement
|
|
49
|
-
revenue: {
|
|
65
|
+
revenue: {
|
|
50
66
|
amount: { type: Number, default: 0 },
|
|
51
67
|
projected: { type: Number, default: 0 },
|
|
52
68
|
breakdown: [{
|
|
@@ -55,7 +71,7 @@ module.exports = function(mongoose, config) {
|
|
|
55
71
|
_id: false
|
|
56
72
|
}]
|
|
57
73
|
},
|
|
58
|
-
operatingExpenses: {
|
|
74
|
+
operatingExpenses: {
|
|
59
75
|
amount: { type: Number, default: 0 },
|
|
60
76
|
projected: { type: Number, default: 0 },
|
|
61
77
|
breakdown: [{
|
|
@@ -64,7 +80,7 @@ module.exports = function(mongoose, config) {
|
|
|
64
80
|
_id: false
|
|
65
81
|
}]
|
|
66
82
|
},
|
|
67
|
-
grossProfit: {
|
|
83
|
+
grossProfit: {
|
|
68
84
|
amount: { type: Number, default: 0 },
|
|
69
85
|
projected: { type: Number, default: 0 },
|
|
70
86
|
breakdown: [{
|
|
@@ -73,7 +89,7 @@ module.exports = function(mongoose, config) {
|
|
|
73
89
|
_id: false
|
|
74
90
|
}]
|
|
75
91
|
},
|
|
76
|
-
operatingIncome: {
|
|
92
|
+
operatingIncome: {
|
|
77
93
|
amount: { type: Number, default: 0 },
|
|
78
94
|
projected: { type: Number, default: 0 },
|
|
79
95
|
breakdown: [{
|
|
@@ -95,13 +111,61 @@ module.exports = function(mongoose, config) {
|
|
|
95
111
|
_id: false
|
|
96
112
|
}],
|
|
97
113
|
|
|
114
|
+
// Catch-all for customer specific key-value pairs from sheet that aren't supported by our schema
|
|
115
|
+
details: [{
|
|
116
|
+
key: { type: String, trim: true },
|
|
117
|
+
value: { type: String, trim: true },
|
|
118
|
+
_id: false
|
|
119
|
+
}],
|
|
120
|
+
|
|
98
121
|
comment: { type: String, trim: true },
|
|
99
122
|
documents: [ { type: Schema.ObjectId, ref: 'Document' } ],
|
|
100
123
|
|
|
101
124
|
submittedOn: { type: Date, index: false },
|
|
102
|
-
submittedBy: { type: String, trim: true }
|
|
103
|
-
|
|
104
|
-
|
|
125
|
+
submittedBy: { type: String, trim: true },
|
|
126
|
+
|
|
127
|
+
notifications: {
|
|
128
|
+
|
|
129
|
+
company: {
|
|
130
|
+
|
|
131
|
+
sendTo: {
|
|
132
|
+
email: { type: String, trim: true },
|
|
133
|
+
name: { type: String, trim: true }
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
initial: {
|
|
137
|
+
sendOn: { type: Date, required: false },
|
|
138
|
+
sentOn: { type: Date, required: false },
|
|
139
|
+
postmarkMessageId: { type: String, trim: true },
|
|
140
|
+
status: { type: String, enum: [null, 'Sent', 'Bounced', 'Delivered', 'Opened', 'Clicked']}
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
reminder: {
|
|
144
|
+
sendOn: { type: Date, required: false },
|
|
145
|
+
sentOn: { type: Date, required: false },
|
|
146
|
+
postmarkMessageId: { type: String, trim: true },
|
|
147
|
+
status: { type: String, enum: [null, 'Sent', 'Bounced', 'Delivered', 'Opened', 'Clicked']}
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
history: [{
|
|
151
|
+
email: { type: String, trim: true },
|
|
152
|
+
postmarkMessageId: { type: String, trim: true },
|
|
153
|
+
status: { type: String, enum: [null, 'Sent', 'Bounced', 'Delivered', 'Opened', 'Clicked']}
|
|
154
|
+
}]
|
|
155
|
+
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
share: {
|
|
159
|
+
recipients: [{
|
|
160
|
+
email: { type: String, trim: true },
|
|
161
|
+
postmarkMessageId: { type: String, trim: true },
|
|
162
|
+
status: { type: String, enum: [null, 'Sent', 'Bounced', 'Delivered', 'Opened', 'Clicked']}
|
|
163
|
+
}]
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
}]
|
|
105
169
|
|
|
106
170
|
});
|
|
107
171
|
|
|
@@ -110,6 +174,37 @@ module.exports = function(mongoose, config) {
|
|
|
110
174
|
// Properties that are not persisted to the database
|
|
111
175
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
112
176
|
|
|
177
|
+
Financials.virtual('snapshotNames').get(function () {
|
|
178
|
+
return _.map(this.snapshots, function(snapshot) {
|
|
179
|
+
if (snapshot.year) { return snapshot.year.toString() + ' ' + snapshot.period; }
|
|
180
|
+
else { return snapshot.period; }
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
///////////////////////////////////////////////////////////////////////////////////////
|
|
187
|
+
// METHODS
|
|
188
|
+
// Methods operate on a single document
|
|
189
|
+
///////////////////////////////////////////////////////////////////////////////////////
|
|
190
|
+
|
|
191
|
+
Financials.methods.snapshotStatus = function(uuid) {
|
|
192
|
+
|
|
193
|
+
if (!uuid) { return null; }
|
|
194
|
+
|
|
195
|
+
var snapshot = _.find(this.snapshots, function(s) {
|
|
196
|
+
if (!s || !s.uuid) { return; }
|
|
197
|
+
return s.uuid.toLowerCase() === uuid.toLowerCase();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
if (!snapshot) { return null; }
|
|
201
|
+
else if (snapshot.submittedOn) { return "Submitted"; }
|
|
202
|
+
else if (snapshot.notifications.company.reminder.status) { return snapshot.notifications.company.reminder.status; }
|
|
203
|
+
else if (snapshot.notifications.company.initial.status) { return snapshot.notifications.company.initial.status; }
|
|
204
|
+
else { return null; }
|
|
205
|
+
|
|
206
|
+
};
|
|
207
|
+
|
|
113
208
|
|
|
114
209
|
|
|
115
210
|
//////////////////////////////////////////////////////
|
|
@@ -121,7 +216,7 @@ module.exports = function(mongoose, config) {
|
|
|
121
216
|
|
|
122
217
|
var self = this;
|
|
123
218
|
|
|
124
|
-
var query = self.
|
|
219
|
+
var query = self.findOne({
|
|
125
220
|
'customer': customerId,
|
|
126
221
|
'organization': orgId
|
|
127
222
|
}).populate({
|
|
@@ -133,20 +228,138 @@ module.exports = function(mongoose, config) {
|
|
|
133
228
|
|
|
134
229
|
};
|
|
135
230
|
|
|
231
|
+
Financials.statics.getByPostmarkMessageId = function getByPostmarkMessageId(postmarkMessageId, cb) {
|
|
232
|
+
|
|
233
|
+
var self = this;
|
|
234
|
+
|
|
235
|
+
var query = self.findOne({
|
|
236
|
+
$or: [
|
|
237
|
+
{ "snapshots.notifications.company.initial.postmarkMessageId": postmarkMessageId },
|
|
238
|
+
{ "snapshots.notifications.company.reminder.postmarkMessageId": postmarkMessageId }
|
|
239
|
+
]
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
query.exec(cb);
|
|
243
|
+
|
|
244
|
+
};
|
|
245
|
+
|
|
136
246
|
Financials.statics.getByUUID = function getByUUID(customerId, uuid, cb) {
|
|
137
247
|
|
|
138
248
|
var self = this;
|
|
139
249
|
|
|
250
|
+
var query = self.findOne({
|
|
251
|
+
'customer': customerId,
|
|
252
|
+
'snapshots.uuid': uuid
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
query.populate('organization', 'name logoUrl');
|
|
256
|
+
query.populate('customer', 'name logoUrl ');
|
|
257
|
+
query.populate({
|
|
258
|
+
path: 'snapshots.documents',
|
|
259
|
+
match: { customer: customerId }
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
query.exec(cb);
|
|
263
|
+
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
Financials.statics.getForCampaign = function getForCampaign(customerId, cb) {
|
|
267
|
+
|
|
268
|
+
var self = this;
|
|
269
|
+
|
|
140
270
|
var query = self.find({
|
|
141
271
|
'customer': customerId,
|
|
272
|
+
'notification.contact.exclude': { $ne: true }, // null, false, or non-existing field
|
|
273
|
+
'notification.contact.email': { $ne: null } // field exists and has a value
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
query.exec(cb);
|
|
277
|
+
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
Financials.statics.getForCustomer = function getForCustomer(customerId, cb) {
|
|
281
|
+
|
|
282
|
+
var self = this;
|
|
283
|
+
|
|
284
|
+
var query = self.find({
|
|
285
|
+
'customer': customerId
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
query.exec(cb);
|
|
289
|
+
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
Financials.statics.getToSend = function getToSend(uuid, cb) {
|
|
293
|
+
|
|
294
|
+
var self = this;
|
|
295
|
+
|
|
296
|
+
var query = self.findOne({
|
|
142
297
|
'snapshots.uuid': uuid
|
|
143
298
|
});
|
|
144
299
|
|
|
145
|
-
query.populate('organization', 'name logoUrl')
|
|
300
|
+
query.populate('organization', 'name logoUrl');
|
|
301
|
+
query.populate('customer', 'name logoUrl customer.totemUrl');
|
|
302
|
+
|
|
146
303
|
query.exec(cb);
|
|
147
304
|
|
|
148
305
|
};
|
|
149
306
|
|
|
307
|
+
Financials.statics.getUUIDsToSendInitial = function getUUIDsToSendInitial(cb) {
|
|
308
|
+
|
|
309
|
+
var self = this;
|
|
310
|
+
var now = new Date();
|
|
311
|
+
|
|
312
|
+
var query = self.find({
|
|
313
|
+
"snapshots.notifications.company.initial.sendOn": { $lte: now },
|
|
314
|
+
"snapshots.notifications.company.initial.sentOn": null
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
query.exec(function(err, result) {
|
|
318
|
+
|
|
319
|
+
if (err) { return cb(err, null); }
|
|
320
|
+
|
|
321
|
+
var snapshots = _.pluck(result || [], 'snapshots');
|
|
322
|
+
|
|
323
|
+
snapshots = _.flatten(snapshots);
|
|
324
|
+
|
|
325
|
+
snapshots = _.filter(snapshots, function(snapshot) {
|
|
326
|
+
return snapshot.notifications.company.initial.sendOn < now && !snapshot.notifications.company.initial.sentOn;
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
return cb(null, _.pluck(snapshots, 'uuid'));
|
|
330
|
+
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
Financials.statics.getUUIDsToSendReminder = function getUUIDsToSendReminder(cb) {
|
|
336
|
+
|
|
337
|
+
var self = this;
|
|
338
|
+
var now = new Date();
|
|
339
|
+
|
|
340
|
+
var query = self.find({
|
|
341
|
+
"snapshots.notifications.company.reminder.sendOn": { $lte: now },
|
|
342
|
+
"snapshots.notifications.company.reminder.sentOn": null
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
query.exec(function(err, result) {
|
|
346
|
+
|
|
347
|
+
if (err) { return cb(err, null); }
|
|
348
|
+
|
|
349
|
+
var snapshots = _.pluck(result || [], 'snapshots');
|
|
350
|
+
|
|
351
|
+
snapshots = _.flatten(snapshots);
|
|
352
|
+
|
|
353
|
+
snapshots = _.filter(snapshots, function(snapshot) {
|
|
354
|
+
return snapshot.notifications.company.reminder.sendOn < now && !snapshot.notifications.company.reminder.sentOn;
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
return cb(null, _.pluck(snapshots, 'uuid'));
|
|
358
|
+
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
};
|
|
362
|
+
|
|
150
363
|
Financials.statics.removeCustomerFinancials = function removeCustomerFinancials(customerId, cb) {
|
|
151
364
|
|
|
152
365
|
// this is only used to wipe out financials created by the google sheet import process
|
|
@@ -164,10 +377,11 @@ module.exports = function(mongoose, config) {
|
|
|
164
377
|
};
|
|
165
378
|
|
|
166
379
|
Financials.statics.upsert = function upsert(financials, cb) {
|
|
380
|
+
|
|
167
381
|
if (!financials) { return cb(new Error('financials is required'), null); }
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
382
|
+
|
|
383
|
+
financials.save(cb);
|
|
384
|
+
|
|
171
385
|
};
|
|
172
386
|
|
|
173
387
|
|
package/lib/Organization.js
CHANGED
|
@@ -401,7 +401,19 @@ module.exports = function(mongoose, config) {
|
|
|
401
401
|
// Funds belonging to this organization (which is typically, but not necessarily, what we'd call an investor)
|
|
402
402
|
// An example of a non-"investor" with funds would be Slack with it's Slack Fund
|
|
403
403
|
// Another way to put it, these are funds from which this organization makes investments
|
|
404
|
-
funds: [{ type: Schema.ObjectId, ref: 'Fund' }]
|
|
404
|
+
funds: [{ type: Schema.ObjectId, ref: 'Fund' }],
|
|
405
|
+
|
|
406
|
+
financials: [{
|
|
407
|
+
|
|
408
|
+
customer: { type: Schema.ObjectId, ref: 'Organization' },
|
|
409
|
+
|
|
410
|
+
// ok to send emails to collect financials
|
|
411
|
+
allowed: { type: Boolean, default: true },
|
|
412
|
+
|
|
413
|
+
// email to send link to
|
|
414
|
+
email: { type: String, trim: true }
|
|
415
|
+
|
|
416
|
+
}]
|
|
405
417
|
|
|
406
418
|
});
|
|
407
419
|
|