@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 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
- quarter: { type: String, enum: ['all', 'q1', 'q2', 'q3', 'q4'] },
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.find({
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
- financials.save(function(err, result) {
169
- return cb(null, result);
170
- });
382
+
383
+ financials.save(cb);
384
+
171
385
  };
172
386
 
173
387
 
@@ -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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dhyasama/totem-models",
3
- "version": "6.36.0",
3
+ "version": "7.0.1",
4
4
  "author": "Jason Reynolds",
5
5
  "license": "UNLICENSED",
6
6
  "description": "Models for Totem platform",