@dhyasama/totem-models 7.58.0 → 8.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/Deal.js CHANGED
@@ -2,15 +2,14 @@
2
2
 
3
3
  module.exports = function(mongoose, config) {
4
4
 
5
- var
5
+ let
6
6
 
7
7
  Schema = mongoose.Schema,
8
- postFind = require('mongoose-post-find'),
9
8
  async = require('async'),
10
9
  _ = require('underscore'),
11
- moment = require('moment');
10
+ utilities = require('@dhyasama/ffvc-utilities');
12
11
 
13
- var Deal = new Schema({
12
+ let Deal = new Schema({
14
13
 
15
14
  customer: { type: Schema.ObjectId, ref: 'Organization', required: true },
16
15
 
@@ -65,29 +64,23 @@ module.exports = function(mongoose, config) {
65
64
 
66
65
  });
67
66
 
68
- var customExample = {
69
- revenue: 1000000,
70
- note: 'love it',
71
- international: false,
72
- region: 'Northeast',
73
- industries: ['AI', 'Logistics']
74
- };
75
67
 
76
68
 
77
69
  ///////////////////////////////////////////////////////////////////////////////////////
78
70
  // VIRTUALS
79
71
  // Properties that are not persisted to the database
80
72
  ///////////////////////////////////////////////////////////////////////////////////////
73
+
81
74
  Deal.virtual('deck').get(function () {
82
75
 
83
- var self = this;
76
+ const self = this;
84
77
  return _.find(self.documents, function(document) { return document.type == 'deck'; })
85
78
 
86
79
  });
87
80
 
88
81
  Deal.virtual('added').get(function () {
89
82
 
90
- var self = this;
83
+ const self = this;
91
84
 
92
85
  if(self.history && self.history.length > 0) return self.history[0].timestamp;
93
86
  else return null;
@@ -96,7 +89,7 @@ module.exports = function(mongoose, config) {
96
89
 
97
90
  Deal.virtual('updated').get(function () {
98
91
 
99
- var self = this;
92
+ const self = this;
100
93
 
101
94
  if(self.history && self.history.length > 0) return self.history[self.history.length - 1].timestamp;
102
95
  else return null;
@@ -116,58 +109,104 @@ module.exports = function(mongoose, config) {
116
109
  // Statics operate on the entire collection
117
110
  //////////////////////////////////////////////////////
118
111
 
119
- Deal.statics.delete = function(id, cb) {
112
+ Deal.statics.delete = function(id, options, cb) {
120
113
 
121
- var self = this;
114
+ let self = this;
115
+
116
+ if (!cb) { throw new Error('cb is required'); }
117
+ if (!id) { return cb(new Error('id is required'), null); }
118
+ if (!mongoose.Types.ObjectId.isValid(id)) { return cb(new Error('id is not a valid ObjectId'), null); }
119
+ if (!options) { return cb(new Error('options is required'), null); }
120
+ if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
121
+ if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
122
122
 
123
123
  // Not strictly necessary but provides verification that the document being deleted belongs to the customer doing the deleting
124
- self.findOne({
124
+ let query = self.findOne({
125
125
  '_id': id,
126
- 'customer': config.CUSTOMER_ID
127
- }, function(err, deal) {
126
+ 'customer': options.CUSTOMER_ID
127
+ });
128
+
129
+ query.exec(function(err, deal) {
128
130
 
129
131
  if (err) return cb(err, null);
130
132
  else if (!deal) return cb(null, null);
131
133
 
134
+ // No population so no need to scrub
135
+
132
136
  deal.remove(cb);
133
137
 
134
138
  });
135
139
 
136
140
  };
137
141
 
138
- Deal.statics.getAll = function (cb) {
139
- this.find({}).exec(cb);
140
- };
142
+ Deal.statics.getById = function (id, options, cb) {
141
143
 
142
- Deal.statics.getById = function (id, cb) {
144
+ let self = this;
143
145
 
144
- var self = this;
146
+ if (!cb) { throw new Error('cb is required'); }
147
+ if (!id) { return cb(new Error('id is required'), null); }
148
+ if (!mongoose.Types.ObjectId.isValid(id)) { return cb(new Error('id is not a valid ObjectId'), null); }
149
+ if (!options) { return cb(new Error('options is required'), null); }
150
+ if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
151
+ if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
152
+
153
+ let query = self.findOne({
154
+ '_id': id,
155
+ 'customer': options.CUSTOMER_ID
156
+ });
157
+
158
+ query.populate({ path: 'documents', match: { customer: options.CUSTOMER_ID } });
159
+ query.populate({ path: 'messages', match: { customer: options.CUSTOMER_ID } });
160
+ query.populate('organization', 'name description logoUrl website websiteAliases chairs contact');
161
+ query.populate('history.account', 'person');
162
+ query.populate('referrer.person', 'name avatarUrl title');
163
+ query.populate('source.person', 'name avatarUrl title');
164
+
165
+ query.exec(cb);
145
166
 
146
- self.findOne({
147
- '_id': id,
148
- 'customer': config.CUSTOMER_ID
149
- })
150
- .populate('organization', 'name description logoUrl website websiteAliases chairs contact')
151
- .populate('messages')
152
- .populate('referrer.person', 'name avatarUrl title')
153
- .populate({
154
- path: 'documents',
155
- match: { customer: config.CUSTOMER_ID }
156
- })
157
- .exec(cb);
158
167
  };
159
168
 
160
- Deal.statics.getByIds = function (ids, cb) {
161
- this
162
- .find({customer: config.CUSTOMER_ID, '_id': { $in : ids } })
163
- .populate('organization', 'name description logoUrl website websiteAliases chairs contact')
164
- .populate('messages')
165
- .populate('referrer.person', 'name avatarUrl title')
166
- .populate({
167
- path: 'documents',
168
- match: { customer: config.CUSTOMER_ID }
169
- })
170
- .exec(cb);
169
+ Deal.statics.getByIds = function (ids, options, cb) {
170
+
171
+ let self = this;
172
+
173
+ if (!cb) { throw new Error('cb is required'); }
174
+ if (!ids) { return cb(new Error('ids is required'), null); }
175
+ if (!options) { return cb(new Error('options is required'), null); }
176
+ if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
177
+ if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
178
+
179
+ let query = self.find({
180
+ customer: options.CUSTOMER_ID,
181
+ '_id': { $in : ids }
182
+ });
183
+
184
+ query.populate({ path: 'documents', match: { customer: options.CUSTOMER_ID } });
185
+ query.populate({ path: 'messages', match: { customer: options.CUSTOMER_ID } });
186
+ query.populate('organization', 'name description logoUrl website websiteAliases chairs contact');
187
+ query.populate('referrer.person', 'name avatarUrl title');
188
+ query.populate('source.person', 'name avatarUrl title');
189
+
190
+ query.exec(function(err, deals) {
191
+
192
+ if (err) return cb(err, null);
193
+ else if (!deals || deals.length === 0) return cb(null, []);
194
+
195
+ let calls = [];
196
+
197
+ _.each(deals, function(deal) {
198
+ let func = function(callback) {
199
+ deal.deepPopulate('organization.chairs.first', {
200
+ populate: { 'organization.chairs.first': { select: 'name avatarUrl title doNotDisplay' } }
201
+ }, callback);
202
+ };
203
+ calls.push(func);
204
+ });
205
+
206
+ async.parallel(calls, cb);
207
+
208
+ });
209
+
171
210
  };
172
211
 
173
212
  // Get all deals belonging to a customer
@@ -177,132 +216,189 @@ module.exports = function(mongoose, config) {
177
216
  // not sure why chained deepPopulate isn't working (it throws an error)
178
217
  // it works to deepPopulate each result instance in parallel
179
218
 
180
- var self = this;
219
+ let self = this;
220
+
221
+ if (!cb) { throw new Error('cb is required'); }
222
+ if (!customerId) { return cb(new Error('customerId is required'), null); }
223
+ if (!mongoose.Types.ObjectId.isValid(customerId)) { return cb(new Error('customerId is not a valid ObjectId'), null); }
181
224
 
182
225
  options = options || {};
183
226
 
184
227
  // { history: { $elemMatch: { timestamp: { $gte: ISODate("2019-01-03T20:45:14.406+0000") } } } }
185
228
 
186
- var terms = { 'customer': customerId };
229
+ let terms = { 'customer': customerId };
187
230
  if (options.stage) { terms['stage'] = { $in: options.stage }; }
188
231
  if (options.since) { terms['history'] = { $elemMatch: { timestamp: { $gte: options.since } } }; }
189
232
 
190
233
  self
191
234
  .find(terms)
235
+ .populate({ path: 'documents', match: { customer: customerId } })
236
+ .populate({ path: 'messages', match: { customer: customerId } })
192
237
  .populate('organization', 'name description logoUrl website websiteAliases chairs contact')
193
- .populate('messages')
194
238
  .populate('referrer.person', 'name avatarUrl title')
195
- .populate({
196
- path: 'documents',
197
- match: { customer: customerId }
198
- }).exec(cb);
239
+
240
+ .exec(function(err, deals) {
241
+
242
+ if (err) { return cb(err, null); }
243
+ else if (!deals || deals.length === 0) { return cb(null, []); }
244
+
245
+ let calls = [];
246
+
247
+ _.each(deals, function(deal) {
248
+ let func = function(callback) {
249
+ deal.deepPopulate('organization.chairs.first', {
250
+ populate: { 'organization.chairs.first': { select: 'name avatarUrl title doNotDisplay' } }
251
+ }, callback);
252
+ };
253
+ calls.push(func);
254
+ });
255
+
256
+ async.parallel(calls, cb);
257
+
258
+ });
199
259
 
200
260
  };
201
261
 
202
- Deal.statics.getByReferrer = function (personId, cb) {
262
+ Deal.statics.getByReferrer = function (personId, options, cb) {
203
263
 
204
- var self = this;
264
+ let self = this;
205
265
 
206
- self
207
- .find({
208
- 'referrer.person': personId,
209
- 'customer': config.CUSTOMER_ID
210
- })
211
- .populate('organization', 'name description logoUrl website websiteAliases')
212
- .exec(cb);
266
+ if (!cb) { throw new Error('cb is required'); }
267
+ if (!personId) { return cb(new Error('personId is required'), null); }
268
+ if (!mongoose.Types.ObjectId.isValid(personId)) { return cb(new Error('personId is not a valid ObjectId'), null); }
269
+ if (!options) { return cb(new Error('options is required'), null); }
270
+ if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
271
+ if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
272
+
273
+ let query = self.find({
274
+ 'referrer.person': personId,
275
+ 'customer': options.CUSTOMER_ID
276
+ });
277
+
278
+ query.populate('organization', 'name description logoUrl website websiteAliases');
279
+
280
+ query.exec(cb);
213
281
 
214
282
  };
215
283
 
216
284
  // Get a deal for a org, belonging to current customer
217
- Deal.statics.getForOrg = function (orgId, cb) {
285
+ Deal.statics.getForOrg = function (orgId, options, cb) {
218
286
 
219
- var self = this;
287
+ const self = this;
288
+
289
+ if (!cb) { throw new Error('cb is required'); }
290
+ if (!orgId) { return cb(new Error('orgId is required'), null); }
291
+ if (!mongoose.Types.ObjectId.isValid(orgId)) { return cb(new Error('orgId is not a valid ObjectId'), null); }
292
+ if (!options) { return cb(new Error('options is required'), null); }
293
+ if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
294
+ if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
295
+
296
+ let query = self.findOne({
297
+ 'organization': orgId,
298
+ 'customer': options.CUSTOMER_ID
299
+ });
300
+
301
+ query.populate({ path: 'documents', match: { customer: options.CUSTOMER_ID } });
302
+ query.populate({ path: 'messages', match: { customer: options.CUSTOMER_ID } });
303
+ query.populate('organization', 'name logoUrl website websiteAliases');
304
+ query.populate('referrer.person', 'name avatarUrl title');
305
+ query.populate('source.person', 'name avatarUrl title');
306
+
307
+ query.exec(function(err, deal) {
308
+ if (deal && deal.history) {
309
+ deal.deepPopulate('history.account.person', {
310
+ populate: { 'history.account.person': { select: 'name avatarUrl title doNotDisplay' } }
311
+ }, cb);
312
+ }
313
+ else cb(err, deal);
314
+ });
220
315
 
221
- self
222
- .findOne({
223
- 'organization': orgId,
224
- 'customer': config.CUSTOMER_ID
225
- })
226
- .populate('organization', 'name logoUrl website websiteAliases')
227
- .populate('messages')
228
- .populate('referrer.person', 'name avatarUrl title')
229
- .populate({
230
- path: 'documents',
231
- match: { customer: config.CUSTOMER_ID }
232
- })
233
- .exec(cb);
234
316
  };
235
317
 
236
- Deal.statics.modifyById = function(id, update, cb) {
318
+ Deal.statics.modifyById = function(id, update, options, cb) {
237
319
 
238
320
  // VERY IMPORTANT NOTE
239
321
  // findByIdAndUpdate and findOneAndUpdate do not trigger pre-save hook so that code will not run here
240
322
 
241
- if (!cb) throw new Error('cb is required');
323
+ let self = this;
324
+
325
+ if (!cb) { throw new Error('cb is required'); }
242
326
  if (!id) { return cb(new Error('id is required'), null); }
327
+ if (!mongoose.Types.ObjectId.isValid(id)) { return cb(new Error('id is not a valid ObjectId'), null); }
243
328
  if (!update) { return cb(new Error('update is required'), null); }
244
-
245
- var self = this;
329
+ if (!options) { return cb(new Error('options is required'), null); }
330
+ if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
331
+ if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
246
332
 
247
333
  // https://mongoosejs.com/docs/api.html#model_Model.findOneAndUpdate
248
334
  // options runValidators defaults false which is ok since we have upsert false
249
335
  // new returns the updated document
250
336
 
251
- self.findByIdAndUpdate(id, update, { upsert: false, new: true }, cb);
337
+ let query = self.findOneAndUpdate({ _id: id, customer: options.CUSTOMER_ID }, update, { upsert: false, new: true });
338
+
339
+ // No population so no need to scrub
340
+
341
+ query.exec(cb);
252
342
 
253
343
  };
254
344
 
255
- Deal.statics.upsert = function upsert(deal, user, cb) {
345
+ Deal.statics.upsert = function upsert(deal, options, cb) {
256
346
 
257
347
  // Deal is dumb and doesn't validate customer stages or duplicate deals. That responsibility is with the org.
258
348
 
349
+ if (!cb) { throw new Error('cb is required'); }
259
350
  if (!deal) { return cb(new Error('deal is required'), null); }
351
+ if (!options) { return cb(new Error('options is required'), null); }
352
+ if (!options.account) { return cb(new Error('options.account is required'), null); }
353
+ if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
354
+ if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
260
355
 
261
- var getLastStage = function() {
356
+ let getLastStage = function() {
262
357
 
263
- if (!deal.history || deal.history.length == 0) return null;
358
+ if (!deal.history || deal.history.length === 0) return null;
264
359
 
265
- var history = _.sortBy(deal.history);
266
- var last = history[history.length - 1];
360
+ let history = _.sortBy(deal.history);
361
+ let last = history[history.length - 1];
267
362
 
268
363
  return last.stage;
269
364
 
270
365
  };
271
366
 
272
- var getLastFundraiseAmount = function() {
367
+ let getLastFundraiseAmount = function() {
273
368
 
274
- if (!deal.history || deal.history.length == 0) return null;
369
+ if (!deal.history || deal.history.length === 0) return null;
275
370
 
276
- var history = _.sortBy(deal.history);
277
- var last = history[history.length - 1];
371
+ let history = _.sortBy(deal.history);
372
+ let last = history[history.length - 1];
278
373
 
279
374
  return last.fundraise.amount;
280
375
 
281
376
  };
282
377
 
283
- var getLastFundraiseValuation = function() {
378
+ let getLastFundraiseValuation = function() {
284
379
 
285
- if (!deal.history || deal.history.length == 0) return null;
380
+ if (!deal.history || deal.history.length === 0) return null;
286
381
 
287
- var history = _.sortBy(deal.history);
288
- var last = history[history.length - 1];
382
+ let history = _.sortBy(deal.history);
383
+ let last = history[history.length - 1];
289
384
 
290
385
  return last.fundraise.valuation;
291
386
 
292
387
  };
293
388
 
294
- var getLastFundraiseDetails = function () {
389
+ let getLastFundraiseDetails = function () {
295
390
 
296
- if (!deal.history || deal.history.length == 0 || !deal.fundraise.details) return null;
391
+ if (!deal.history || deal.history.length === 0 || !deal.fundraise.details) return null;
297
392
 
298
- var history = _.sortBy(deal.history);
299
- var last = history[history.length - 1];
393
+ let history = _.sortBy(deal.history);
394
+ let last = history[history.length - 1];
300
395
 
301
396
  return last.fundraise.details;
302
397
 
303
398
  };
304
399
 
305
- var formatMoney = function(n, c, d, t) {
400
+ let formatMoney = function(n, c, d, t) {
401
+
306
402
  var c = isNaN(c = Math.abs(c)) ? 2 : c,
307
403
  d = d == undefined ? "." : d,
308
404
  t = t == undefined ? "," : t,
@@ -311,31 +407,32 @@ module.exports = function(mongoose, config) {
311
407
  j = (j = i.length) > 3 ? j % 3 : 0;
312
408
 
313
409
  return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
410
+
314
411
  };
315
412
 
316
- var description = '';
317
- var lastStage = getLastStage();
318
- var lastFundraiseAmount = getLastFundraiseAmount();
319
- var lastFundraiseValuation = getLastFundraiseValuation();
320
- var lastFundraiseDetails = getLastFundraiseDetails();
413
+ let description = '';
414
+ let lastStage = getLastStage();
415
+ let lastFundraiseAmount = getLastFundraiseAmount();
416
+ let lastFundraiseValuation = getLastFundraiseValuation();
417
+ let lastFundraiseDetails = getLastFundraiseDetails();
321
418
 
322
- if (lastStage == null) description = 'The deal was created';
419
+ if (lastStage === null) { description = 'The deal was created'; }
323
420
  else {
324
- if (lastStage != deal.stage) description += 'The deal was updated from ' + lastStage + ' to ' + deal.stage;
325
- if (lastFundraiseAmount != deal.fundraise.amount) description += 'Fundraise amount was updated from $' + formatMoney(lastFundraiseAmount, 0) + ' to $' + formatMoney(deal.fundraise.amount, 0);
326
- if (lastFundraiseValuation != deal.fundraise.valuation) description += 'Fundraise valuation was updated from $' + formatMoney(lastFundraiseValuation, 0) + ' to $' + formatMoney(deal.fundraise.valuation, 0);
327
- if (lastFundraiseDetails && lastFundraiseDetails != deal.fundraise.details) description += 'Fundraise details were updated from ' + lastFundraiseDetails + ' to ' + deal.fundraise.details;
328
- if (!lastFundraiseDetails && deal.fundraise.details) description += 'Fundraise details were added: ' + deal.fundraise.details;
421
+ if (lastStage !== deal.stage) { description += 'The deal was updated from ' + lastStage + ' to ' + deal.stage; }
422
+ if (lastFundraiseAmount !== deal.fundraise.amount) { description += 'Fundraise amount was updated from $' + formatMoney(lastFundraiseAmount, 0) + ' to $' + formatMoney(deal.fundraise.amount, 0); }
423
+ if (lastFundraiseValuation !== deal.fundraise.valuation) { description += 'Fundraise valuation was updated from $' + formatMoney(lastFundraiseValuation, 0) + ' to $' + formatMoney(deal.fundraise.valuation, 0); }
424
+ if (lastFundraiseDetails && lastFundraiseDetails !== deal.fundraise.details) { description += 'Fundraise details were updated from ' + lastFundraiseDetails + ' to ' + deal.fundraise.details; }
425
+ if (!lastFundraiseDetails && deal.fundraise.details) { description += 'Fundraise details were added: ' + deal.fundraise.details; }
329
426
  }
330
427
 
331
428
  // only add history if the description is set from the above conditions
332
- if(description != '') {
429
+ if (description !== '') {
333
430
 
334
431
  deal.history = deal.history || [];
335
432
 
336
433
  deal.history.push({
337
434
  timestamp: new Date(),
338
- account: user,
435
+ account: options.account,
339
436
  stage: deal.stage,
340
437
  fundraise: deal.fundraise,
341
438
  description: description
@@ -345,54 +442,21 @@ module.exports = function(mongoose, config) {
345
442
 
346
443
  // Required for mixed types
347
444
  deal.markModified('customFields');
348
-
445
+
349
446
  deal.save(cb);
350
447
 
351
448
  };
352
-
353
- Deal.plugin(postFind, {
354
-
355
- find: function(results, done) {
356
-
357
- var CUSTOMER_ID = config.CUSTOMER_ID;
358
-
359
- if (CUSTOMER_ID == 'GLOBAL_PROCESS') return done(null, results);
360
-
361
- // Reject any item that is for a different customer
362
- results = _.reject(results, function(item) {
363
- return item.customer.toString() != CUSTOMER_ID;
364
- });
365
-
366
- return done(null, results);
367
-
368
- },
369
-
370
- findOne: function(result, done) {
371
-
372
- var CUSTOMER_ID = config.CUSTOMER_ID;
373
-
374
- if (!result) return done(null, null);
375
- else if (CUSTOMER_ID == 'GLOBAL_PROCESS') return done(null, result);
376
- else if (result.customer.toString() == CUSTOMER_ID) return done(null, result);
377
- else return done(null, null);
378
-
379
- }
380
-
381
- });
382
-
383
-
384
-
449
+
385
450
  ///////////////////////////////////////////////////////////////////////////////////////
386
451
  // CONFIG
387
452
  ///////////////////////////////////////////////////////////////////////////////////////
388
453
 
389
- Deal.set('usePushEach', true);
390
454
  Deal.set('toJSON', { virtuals: true });
391
455
  Deal.set('autoIndex', false);
392
- Deal.set('usePushEach', true);
456
+
393
457
  Deal.on('index', function(err) { console.log('error building deal indexes: ' + err); });
394
458
 
395
- var deepPopulate = require('mongoose-deep-populate')(mongoose);
459
+ const deepPopulate = require('mongoose-deep-populate')(mongoose);
396
460
  Deal.plugin(deepPopulate);
397
461
 
398
462
  mongoose.model('Deal', Deal);