@dhyasama/totem-models 6.35.0 → 7.0.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/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,36 @@ 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
+ return snapshot.year + ' ' + snapshot.period;
180
+ });
181
+ });
182
+
183
+
184
+
185
+ ///////////////////////////////////////////////////////////////////////////////////////
186
+ // METHODS
187
+ // Methods operate on a single document
188
+ ///////////////////////////////////////////////////////////////////////////////////////
189
+
190
+ Financials.methods.snapshotStatus = function(uuid) {
191
+
192
+ if (!uuid) { return null; }
193
+
194
+ var snapshot = _.find(this.snapshots, function(s) {
195
+ if (!s || !s.uuid) { return; }
196
+ return s.uuid.toLowerCase() === uuid.toLowerCase();
197
+ });
198
+
199
+ if (!snapshot) { return null; }
200
+ else if (snapshot.submittedOn) { return "Submitted"; }
201
+ else if (snapshot.notifications.company.reminder.status) { return snapshot.notifications.company.reminder.status; }
202
+ else if (snapshot.notifications.company.initial.status) { return snapshot.notifications.company.initial.status; }
203
+ else { return null; }
204
+
205
+ };
206
+
113
207
 
114
208
 
115
209
  //////////////////////////////////////////////////////
@@ -121,7 +215,7 @@ module.exports = function(mongoose, config) {
121
215
 
122
216
  var self = this;
123
217
 
124
- var query = self.find({
218
+ var query = self.findOne({
125
219
  'customer': customerId,
126
220
  'organization': orgId
127
221
  }).populate({
@@ -133,20 +227,138 @@ module.exports = function(mongoose, config) {
133
227
 
134
228
  };
135
229
 
230
+ Financials.statics.getByPostmarkMessageId = function getByPostmarkMessageId(postmarkMessageId, cb) {
231
+
232
+ var self = this;
233
+
234
+ var query = self.findOne({
235
+ $or: [
236
+ { "snapshots.notifications.company.initial.postmarkMessageId": postmarkMessageId },
237
+ { "snapshots.notifications.company.reminder.postmarkMessageId": postmarkMessageId }
238
+ ]
239
+ });
240
+
241
+ query.exec(cb);
242
+
243
+ };
244
+
136
245
  Financials.statics.getByUUID = function getByUUID(customerId, uuid, cb) {
137
246
 
138
247
  var self = this;
139
248
 
249
+ var query = self.findOne({
250
+ 'customer': customerId,
251
+ 'snapshots.uuid': uuid
252
+ });
253
+
254
+ query.populate('organization', 'name logoUrl');
255
+ query.populate('customer', 'name logoUrl ');
256
+ query.populate({
257
+ path: 'snapshots.documents',
258
+ match: { customer: customerId }
259
+ });
260
+
261
+ query.exec(cb);
262
+
263
+ };
264
+
265
+ Financials.statics.getForCampaign = function getForCampaign(customerId, cb) {
266
+
267
+ var self = this;
268
+
140
269
  var query = self.find({
141
270
  'customer': customerId,
271
+ 'notification.contact.exclude': { $ne: true }, // null, false, or non-existing field
272
+ 'notification.contact.email': { $ne: null } // field exists and has a value
273
+ });
274
+
275
+ query.exec(cb);
276
+
277
+ };
278
+
279
+ Financials.statics.getForCustomer = function getForCustomer(customerId, cb) {
280
+
281
+ var self = this;
282
+
283
+ var query = self.find({
284
+ 'customer': customerId
285
+ });
286
+
287
+ query.exec(cb);
288
+
289
+ };
290
+
291
+ Financials.statics.getToSend = function getToSend(uuid, cb) {
292
+
293
+ var self = this;
294
+
295
+ var query = self.findOne({
142
296
  'snapshots.uuid': uuid
143
297
  });
144
298
 
145
- query.populate('organization', 'name logoUrl')
299
+ query.populate('organization', 'name logoUrl');
300
+ query.populate('customer', 'name logoUrl customer.totemUrl');
301
+
146
302
  query.exec(cb);
147
303
 
148
304
  };
149
305
 
306
+ Financials.statics.getUUIDsToSendInitial = function getUUIDsToSendInitial(cb) {
307
+
308
+ var self = this;
309
+ var now = new Date();
310
+
311
+ var query = self.find({
312
+ "snapshots.notifications.company.initial.sendOn": { $lte: now },
313
+ "snapshots.notifications.company.initial.sentOn": null
314
+ });
315
+
316
+ query.exec(function(err, result) {
317
+
318
+ if (err) { return cb(err, null); }
319
+
320
+ var snapshots = _.pluck(result || [], 'snapshots');
321
+
322
+ snapshots = _.flatten(snapshots);
323
+
324
+ snapshots = _.filter(snapshots, function(snapshot) {
325
+ return snapshot.notifications.company.initial.sendOn < now && !snapshot.notifications.company.initial.sentOn;
326
+ });
327
+
328
+ return cb(null, _.pluck(snapshots, 'uuid'));
329
+
330
+ });
331
+
332
+ };
333
+
334
+ Financials.statics.getUUIDsToSendReminder = function getUUIDsToSendReminder(cb) {
335
+
336
+ var self = this;
337
+ var now = new Date();
338
+
339
+ var query = self.find({
340
+ "snapshots.notifications.company.reminder.sendOn": { $lte: now },
341
+ "snapshots.notifications.company.reminder.sentOn": null
342
+ });
343
+
344
+ query.exec(function(err, result) {
345
+
346
+ if (err) { return cb(err, null); }
347
+
348
+ var snapshots = _.pluck(result || [], 'snapshots');
349
+
350
+ snapshots = _.flatten(snapshots);
351
+
352
+ snapshots = _.filter(snapshots, function(snapshot) {
353
+ return snapshot.notifications.company.reminder.sendOn < now && !snapshot.notifications.company.reminder.sentOn;
354
+ });
355
+
356
+ return cb(null, _.pluck(snapshots, 'uuid'));
357
+
358
+ });
359
+
360
+ };
361
+
150
362
  Financials.statics.removeCustomerFinancials = function removeCustomerFinancials(customerId, cb) {
151
363
 
152
364
  // this is only used to wipe out financials created by the google sheet import process
@@ -164,10 +376,11 @@ module.exports = function(mongoose, config) {
164
376
  };
165
377
 
166
378
  Financials.statics.upsert = function upsert(financials, cb) {
379
+
167
380
  if (!financials) { return cb(new Error('financials is required'), null); }
168
- financials.save(function(err, result) {
169
- return cb(null, result);
170
- });
381
+
382
+ financials.save(cb);
383
+
171
384
  };
172
385
 
173
386
 
@@ -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.35.0",
3
+ "version": "7.0.0",
4
4
  "author": "Jason Reynolds",
5
5
  "license": "UNLICENSED",
6
6
  "description": "Models for Totem platform",