@dhyasama/totem-models 7.58.0 → 8.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.
@@ -1,33 +1,25 @@
1
- "use strict";
1
+ 'use strict';
2
2
 
3
3
  module.exports = function(mongoose, config) {
4
4
 
5
- var
5
+ let
6
6
 
7
- CUSTOMER_ID = config.CUSTOMER_ID,
8
- LPS_ENABLED = config.LPS_ENABLED,
9
7
  Schema = mongoose.Schema,
10
8
  Flag = mongoose.model('Flag'),
11
- Fund = mongoose.model('Fund'),
12
9
  Note = mongoose.model('Note'),
13
10
  Organization = mongoose.model('Organization'),
14
- env = process.env.NODE_ENV || 'development',
15
- Crypto = require('@dhyasama/ffvc-crypto'),
16
- crypto = new Crypto({'key': config.crypto.key}),
17
11
  filter = require('mongoose-filter-denormalize').filter,
18
12
  _ = require('underscore'),
19
13
  async = require('async'),
20
- utils = require('@dhyasama/ffvc-utilities');
14
+ helpers = require('../helpers');
21
15
 
22
- var customerFunds = [];
23
-
24
- var LimitedPartner = new Schema({
16
+ let LimitedPartner = new Schema({
25
17
 
26
18
  name: { type: String, index: true, required: true },
27
19
 
28
20
  shortName: { type: String, default: '' },
29
21
 
30
- customer: { type: Schema.ObjectId, ref: 'Organization' },
22
+ customer: { type: Schema.ObjectId, ref: 'Organization', required: true },
31
23
 
32
24
  start: { type: String, default: '' },
33
25
 
@@ -45,11 +37,10 @@ module.exports = function(mongoose, config) {
45
37
  quarterlyAccountStatement: { type: String, default: '' }
46
38
  },
47
39
 
48
- transactions: [{
40
+ fundsInvested: [{
49
41
  fund: {type: Schema.ObjectId, ref: 'Fund'},
50
- type: { type: String, enum: ['commitment', 'contribution', 'distribution'] },
51
- amount: { type: Number, default: 0 },
52
- date: { type: Date, default: null },
42
+ committed: {type: Number, default: 0},
43
+ called: {type: Number, default: 0}
53
44
  }],
54
45
 
55
46
  contact: {
@@ -79,69 +70,6 @@ module.exports = function(mongoose, config) {
79
70
 
80
71
  },
81
72
 
82
- distributions: {
83
-
84
- typeOf: { type: String, required: false, enum: [null, '', 'ACH', 'Check'] },
85
-
86
- ach: {
87
-
88
- stripeRecipientId: { type: String, default: ''},
89
- legalName: { type: String, default: ''},
90
- accountType: { type: String, required: false, enum: [null, '', 'Checking', 'Savings']},
91
- taxId: { type: String, default: ''},
92
- verified: { type: Boolean, default: false },
93
-
94
- challengeDeposits: [{
95
- id: { type: String },
96
- created: { type: Date },
97
- amount: { type: Number },
98
- status: { type: String }
99
- }],
100
-
101
- bankAccount: {
102
- name: { type: String, default: '' },
103
- country: { type: String, default: '' },
104
- routingNumber: { type: String, default: '' },
105
- accountNumber: { type: String, default: '' }
106
- }
107
-
108
- },
109
-
110
- check: {
111
- address: {
112
- addressOne: { type: String, default: '' },
113
- addressTwo: { type: String, default: '' },
114
- city: { type: String, default: '' },
115
- state: { type: String, default: '' },
116
- country: { type: String, default: '' },
117
- postalCode: { type: String, default: '' }
118
- }
119
- }
120
-
121
- },
122
-
123
- k1: {
124
- nameOfInvestor: { type: String, default: '' },
125
- legalAddress: {
126
- addressOne: { type: String, default: '' },
127
- addressTwo: { type: String, default: '' },
128
- city: { type: String, default: '' },
129
- state: { type: String, default: '' },
130
- country: { type: String, default: '' },
131
- postalCode: { type: String, default: '' }
132
- },
133
- EIN: { type: String, default: null },
134
- entityType: { type: String, enum: [null, 'Individual', 'Trust', 'Partnership', 'Corporation', 'LLC-Partnership', 'LLC-Corporation'] },
135
- taxExempt: { type: Boolean, default: false },
136
- USBased: { type: Boolean, default: true },
137
- fundOrFundOfFunds: { type: Boolean, default: false },
138
- deliverTo: [{type: String }],
139
- consented: { type: Boolean, default: false },
140
- changedBy: { type: String, default: null },
141
- changedOn: { type: Date, default: null },
142
- complete: { type: Boolean, default: false }
143
- },
144
-
145
73
  entered: {
146
74
  by: { type: String, default: '' },
147
75
  on: { type: Date, default: Date.now }
@@ -166,96 +94,22 @@ module.exports = function(mongoose, config) {
166
94
 
167
95
  });
168
96
 
169
- var getSources = function getSources(people) {
170
-
171
- var result = _.compact(_.pluck(people, 'sources'));
172
- if (result.length == 0) return [];
173
-
174
- result = _.flatten(result);
175
- result = _.compact(result);
176
-
177
- if (result.length == 0) return [];
178
- if (mongoose.Types.ObjectId.isValid(result[0].person)) return []; // not populated
179
-
180
- result = _.uniq(result, function(item) {
181
- if (!item || !item.person) return '';
182
- else return item.person._id ? item.person._id.toString() : '';
183
- });
184
- result = _.compact(result);
185
- result = _.reject(result, function(item) { return !item.person; });
186
- result = _.sortBy(result, 'interactions.count');
187
- result.reverse();
188
- result = _.sortBy(result, 'name.full');
189
-
190
- console.log(result);
191
-
192
- return result;
193
-
194
- };
195
-
196
97
  /////////////////////
197
98
 
198
- LimitedPartner.virtual('totalCommitment').get(function () {
199
- var self = this;
200
- return _.reduce(self.transactions, function(memo, transaction) {
201
- return memo + ((transaction.type == 'commitment' && transaction.amount > 0) ? transaction.amount : 0);
202
- }, 0);
203
- });
204
-
205
- LimitedPartner.virtual('totalContribution').get(function () {
206
- var self = this;
207
- return _.reduce(self.transactions, function(memo, transaction) {
208
- return memo + ((transaction.type == 'contribution' && transaction.amount > 0) ? transaction.amount : 0);
209
- }, 0);
210
- });
211
-
212
- LimitedPartner.virtual('totalDistribution').get(function () {
213
- var self = this;
214
- return _.reduce(self.transactions, function(memo, transaction) {
215
- return memo + ((transaction.type == 'distribution' && transaction.amount > 0) ? transaction.amount : 0);
216
- }, 0);
217
- });
218
-
219
- LimitedPartner.virtual('fundsInvested').get(function () {
220
-
221
- var self = this;
222
- var fundsInvested = [];
223
-
224
- _.each(self.transactions, function(transaction) {
225
-
226
- var fundMatch = _.find(fundsInvested, function(fund) { return fund._id == transaction.fund._id; });
227
-
228
- if(!fundMatch) {
229
- fundsInvested.push({
230
- _id: transaction.fund._id,
231
- name: transaction.fund.name,
232
- shortName: transaction.fund.shortName,
233
- abbreviation: transaction.fund.abbreviation,
234
- commitment: 0,
235
- contribution: 0,
236
- distribution: 0
237
- });
238
- fundMatch = _.last(fundsInvested);
239
- }
240
-
241
- // and increment the transaction type by the transaction amount
242
- fundMatch[transaction.type] += transaction.amount;
243
-
244
- });
245
-
246
- return fundsInvested;
247
-
99
+ LimitedPartner.virtual('totalCommitted').get(function () {
100
+ const self = this;
101
+ return _.reduce(self.fundsInvested, function(memo, fund) { return memo + (fund.committed > 0 ? fund.committed : 0); }, 0);
248
102
  });
249
103
 
250
104
  LimitedPartner.virtual('email.primary').get(function () {
251
- var primary = _.find(this.contact.email, function(email) {
105
+ let primary = _.find(this.contact.email, function(email) {
252
106
  return email.primary;
253
107
  });
254
108
  return primary ? primary.email : '';
255
109
  });
256
110
 
257
111
  LimitedPartner.virtual('phone.primary').get(function () {
258
- var primary = _.find(this.contact.phone, function(phone) {
112
+ let primary = _.find(this.contact.phone, function(phone) {
259
113
  return phone.primary;
260
114
  });
261
115
  return primary ? primary.number : '';
@@ -263,7 +117,7 @@ module.exports = function(mongoose, config) {
263
117
 
264
118
  LimitedPartner.virtual('address.primary').get(function () {
265
119
 
266
- var primary = _.find(this.contact.address, function(address) {
120
+ let primary = _.find(this.contact.address, function(address) {
267
121
  return address.primary;
268
122
  });
269
123
 
@@ -289,7 +143,7 @@ module.exports = function(mongoose, config) {
289
143
 
290
144
  LimitedPartner.methods.newBasic = function(field, value) {
291
145
 
292
- var compoundField = false;
146
+ let compoundField = false;
293
147
  if(field.split(".").length > 1) {
294
148
  compoundField = true;
295
149
  }
@@ -297,8 +151,8 @@ module.exports = function(mongoose, config) {
297
151
  if(!compoundField) {
298
152
  this[field] = value;
299
153
  } else {
300
- var parentField = field.split('.')[0];
301
- var childField = field.split('.')[1];
154
+ let parentField = field.split('.')[0];
155
+ let childField = field.split('.')[1];
302
156
 
303
157
  this[parentField][childField] = value;
304
158
  }
@@ -307,21 +161,19 @@ module.exports = function(mongoose, config) {
307
161
 
308
162
  LimitedPartner.methods.updateBasic = function(field, value) {
309
163
 
310
- var compoundField = false;
311
- if(field.split(".").length > 1) {
312
- compoundField = true;
313
- }
164
+ const self = this;
314
165
 
315
- if(!compoundField) {
316
- var currentVal = this[field];
317
- this[field] = value;
318
- } else {
319
- var parentField = field.split('.')[0];
320
- var childField = field.split('.')[1];
166
+ let isCompoundField = field.split(".").length > 1;
167
+
168
+ if (isCompoundField) {
169
+
170
+ let parentField = field.split('.')[0];
171
+ let childField = field.split('.')[1];
172
+
173
+ self[parentField][childField] = value;
321
174
 
322
- var currentVal = this[parentField][childField];
323
- this[parentField][childField] = value;
324
175
  }
176
+ else { self[field] = value; }
325
177
 
326
178
  };
327
179
 
@@ -329,10 +181,12 @@ module.exports = function(mongoose, config) {
329
181
 
330
182
  LimitedPartner.methods.updateContact = function(field, value) {
331
183
 
332
- var parentField = field.split('.')[0];
333
- var childField = field.split('.')[1];
184
+ const self = this;
185
+
186
+ let parentField = field.split('.')[0];
187
+ let childField = field.split('.')[1];
334
188
 
335
- this[parentField][childField] = value;
189
+ self[parentField][childField] = value;
336
190
 
337
191
  };
338
192
 
@@ -340,12 +194,14 @@ module.exports = function(mongoose, config) {
340
194
 
341
195
  LimitedPartner.methods.addPerson = function(idOfPersonToAdd) {
342
196
 
343
- var match = _.find(this.people, function(person) {
344
- var personId = utils.isValidObjectId(person) ? person : person.id;
345
- return personId.toString() == idOfPersonToAdd.toString();
197
+ const self = this;
198
+
199
+ let match = _.find(self.people, function(person) {
200
+ let personId = mongoose.Types.ObjectId.isValid(person) ? person : person.id;
201
+ return personId.toString() === idOfPersonToAdd.toString();
346
202
  });
347
203
 
348
- if (!match) this.people.push(idOfPersonToAdd);
204
+ if (!match) { self.people.push(idOfPersonToAdd); }
349
205
 
350
206
  };
351
207
 
@@ -356,381 +212,325 @@ module.exports = function(mongoose, config) {
356
212
  });
357
213
 
358
214
  };
359
-
360
- LimitedPartner.methods.docSlugs = function() {
361
-
362
- var slugs = this.documentSlugs;
363
- var fullName = this.name;
364
215
 
365
- slugs.annualAudit = slugs.annualAudit || fullName;
366
- slugs.capitalCall = slugs.capitalCall || fullName;
367
- slugs.portfolioHolding = slugs.portfolioHolding || fullName;
368
- slugs.k1 = slugs.k1 || fullName;
369
- slugs.distribution = slugs.distribution || fullName;
370
- slugs.quarterlyAccountStatement = slugs.quarterlyAccountStatement || fullName;
371
-
372
- return slugs;
216
+ ////////////////////////
373
217
 
374
- };
218
+ LimitedPartner.statics.addFlag = function(lpid, creatorPersonId, text, customerId, cb) {
375
219
 
376
- ////////////////////////
220
+ Flag.createForModel({
221
+ createdBy: creatorPersonId,
222
+ text: text,
223
+ customer: customerId
224
+ }, this, lpid, cb);
377
225
 
378
- LimitedPartner.statics.findByTransaction = function (id, role, cb) {
379
- this
380
- .find({ 'distributions.ach.challengeDeposits.id': id }, this.getReadFilterKeys(role))
381
- .exec(cb);
382
226
  };
383
227
 
384
- LimitedPartner.statics.getById = function(customerId, lpId, role, cb) {
385
-
386
- var self = this;
387
- var query;
388
- var getLp = function() {
389
- query.populate('transactions.fund');
390
- query.populate('people');
391
- query.populate('notes');
392
- query.deepPopulate([
393
- 'people.sources.person',
394
- 'people.calendarEventSummaries.attendees.internal',
395
- 'people.calendarEventSummaries.attendees.external',
396
- 'notes.createdBy',
397
- 'people.calendarEventSummaries.notes.createdBy'
398
- ], {
399
- populate: {
400
- 'people.sources.person': { select: 'name avatarUrl title' },
401
- 'people.calendarEventSummaries.attendees.internal': { select: 'name avatarUrl title' },
402
- 'people.calendarEventSummaries.attendees.external': { select: 'name avatarUrl title' },
403
- 'notes.createdBy': { select: 'name avatarUrl' },
404
- 'people.calendarEventSummaries.notes.createdBy': {select: ' avatarUrl name' }
405
- }
406
- });
407
- query.exec(cb);
408
- };
228
+ LimitedPartner.statics.addNote = function(lpid, creatorPersonId, text, isPrivate, customerId, cb) {
409
229
 
410
- if (CUSTOMER_ID == 'GLOBAL_PROCESS') {
411
- query = self.findById(lpId, self.getReadFilterKeys(role))
412
- getLp();
413
- }
414
- else {
415
- query = self.findOne({ '_id': lpId, 'customer': customerId, 'transactions.fund': { $in : self.customerFunds }}, self.getReadFilterKeys(role));
416
- getLp();
417
- }
230
+ Note.createForModel({
231
+ createdBy: creatorPersonId,
232
+ text: text,
233
+ isPrivate: isPrivate,
234
+ customer: customerId
235
+ }, this, lpid, cb);
418
236
 
419
237
  };
420
238
 
421
- LimitedPartner.statics.getByPersonId = function(personId, role, cb) {
239
+ LimitedPartner.statics.deleteFlag = function(flagId, cb) {
422
240
 
423
- var self = this;
424
- var query;
241
+ // Delete the flag itself along with any references
425
242
 
426
- var getLps = function(q) {
427
- q.populate('transactions.fund')
428
- q.populate('people', 'name')
429
- q.exec(function (err, lps) {
430
- if (err) return cb(err, null);
431
- lps = _.sortBy(lps, function(lp) { return lp.name ? lp.name.toLowerCase() : ''; });
432
- return cb(null, lps);
433
- });
243
+ const self = this;
244
+
245
+ let removeReferences = function removeReferences(callback) {
246
+ self.update({}, {
247
+ $pull: { 'flags' : [flagId] }
248
+ }, callback);
434
249
  };
435
250
 
436
- if (CUSTOMER_ID == 'GLOBAL_PROCESS') {
437
- query = self.find({'people': personId}, this.getReadFilterKeys(role))
438
- getLps(query);
439
- }
440
- else {
441
- query = self.find({'people': personId, 'transactions.fund': { $in : self.customerFunds }}, self.getReadFilterKeys(role));
442
- getLps(query);
443
- }
251
+ async.series([
252
+ Flag.delete.bind(Flag, flagId),
253
+ removeReferences
254
+ ], function(err, results) {
255
+ return cb(err, null);
256
+ });
444
257
 
445
258
  };
446
259
 
447
- LimitedPartner.statics.getByName = function(customerId, lpName, role, cb) {
260
+ LimitedPartner.statics.deleteNote = function(noteId, customerId, cb) {
448
261
 
449
- var self = this;
450
- var query;
262
+ // Delete the note itself along with any references
451
263
 
452
- var getLps = function() {
453
- query.populate('transactions.fund');
454
- query.exec(cb);
264
+ const self = this;
265
+
266
+ let removeReferences = function removeReferences(callback) {
267
+ self.update({}, {
268
+ $pull: { 'notes' : [noteId] }
269
+ }, callback);
455
270
  };
456
271
 
457
- query = self.find({'customer': customerId, 'name': lpName}, self.getReadFilterKeys(role));
458
- getLps();
272
+ async.series([
273
+ Note.delete.bind(Note, noteId, customerId),
274
+ removeReferences
275
+ ], function(err, results) {
276
+ return cb(err, null);
277
+ });
459
278
 
460
279
  };
461
280
 
462
- LimitedPartner.statics.getFlags = function getFlags(lpid, cb) {
281
+ LimitedPartner.statics.getById = function(id, options, cb) {
463
282
 
464
- var self = this;
465
- var query = self.findById(lpid)
283
+ const self = this;
466
284
 
467
- query.select('flags')
468
- query.populate({
469
- path: 'flags',
470
- match: { customer: config.CUSTOMER_ID }
471
- })
472
- query.deepPopulate([
473
- 'flags.createdBy',
474
- ], {
475
- populate: {
476
- 'flags.createdBy': { select: 'name avatarUrl title' }
477
- }
478
- })
479
- query.sort({'createdOn':-1})
480
- query.exec(cb);
285
+ if (!cb) { throw new Error('cb is required'); }
286
+ if (!id) { return cb(new Error('id is required'), null); }
287
+ if (!mongoose.Types.ObjectId.isValid(id)) { return cb(new Error('id is not a valid ObjectId'), null); }
288
+ if (!options) { return cb(new Error('options is required'), null); }
289
+ if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
290
+ if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
481
291
 
482
- };
292
+ options = helpers.getDefaultOptions(options);
483
293
 
484
- LimitedPartner.statics.getNotes = function getNotes(lpid, cb) {
294
+ let query;
485
295
 
486
- var self = this;
487
- var query = self.findById(lpid)
296
+ if (options.isWorkerProcess) {
297
+ query = self.findById(id, self.getReadFilterKeys(options.role))
298
+ }
299
+ else {
300
+ query = self.findOne({ '_id': id, customer: options.CUSTOMER_ID }, self.getReadFilterKeys(options.role));
301
+ }
488
302
 
489
- query.select('notes people')
490
- query.populate('people', 'name avatarUrl title calendarEventSummaries')
303
+ query.populate('fundsInvested.fund');
304
+ query.populate('people');
491
305
  query.populate({
492
306
  path: 'notes',
493
- match: { customer: config.CUSTOMER_ID }
494
- })
307
+ match: { customer: options.CUSTOMER_ID }
308
+ });
309
+
495
310
  query.deepPopulate([
496
- 'notes.createdBy',
497
- 'people.calendarEventSummaries.notes.createdBy'
311
+ 'people.sources.person',
312
+ 'notes.createdBy'
498
313
  ], {
499
314
  populate: {
500
- 'notes.createdBy': { select: 'name avatarUrl title' },
501
- 'people.calendarEventSummaries.notes.createdBy': { select: 'name avatarUrl title' }
315
+ 'people.sources.person': { select: 'name avatarUrl title' },
316
+ 'notes.createdBy': { select: 'name avatarUrl' }
502
317
  }
503
- })
504
- query.sort({'createdOn':-1})
505
- query.exec(cb);
506
-
507
- };
318
+ });
508
319
 
509
- LimitedPartner.statics.getSources = function(lpid, cb) {
320
+ query.exec(function(err, lp) {
510
321
 
511
- var self = this;
322
+ if (err) { return cb(err, null); }
323
+ else if (!lp) { return cb(null, null); }
512
324
 
513
- self
514
- .findById(lpid)
515
- .populate('people', 'name avatarUrl title doNotDisplay sources')
516
- .deepPopulate([
517
- 'people.sources.person'
518
- ], {
519
- populate: {
520
- 'people.sources.person': { select: 'name avatarUrl title doNotDisplay' }
521
- }
522
- })
523
- .exec(function(err, lp) {
325
+ lp.people = _.map(lp.people, function(person) {
524
326
 
525
- if (err) return cb(err, null);
526
- if (!lp) return cb(null, null);
327
+ person.sources = _.filter(person.sources, function(s) {
328
+ return s.customer.toString() === options.CUSTOMER_ID;
329
+ });
527
330
 
528
- Organization.findById(config.CUSTOMER_ID, function(err, customer) {
331
+ return person;
529
332
 
530
- if (err) return cb(err, null);
531
- if (!customer) return cb(null, null);
333
+ });
532
334
 
533
- var current = [];
534
- var past = [];
535
- var all = [];
536
- var lpSources = getSources(lp.people);
335
+ return cb(null, lp);
537
336
 
538
- _.each(customer.people, function(p) {
337
+ });
539
338
 
540
- var source = _.find(lpSources, function(ls) {
541
- if (!ls || !ls.person || !ls.person._id) return false;
542
- return ls.person._id.toString() == p.person.toString();
543
- });
339
+ };
544
340
 
545
- if (!source) return;
341
+ LimitedPartner.statics.getByName = function(lpName, options, cb) {
546
342
 
547
- if (p.current) current.push(source);
548
- if (!p.current) past.push(source);
549
- all.push(source);
343
+ const self = this;
550
344
 
551
- });
345
+ if (!cb) { throw new Error('cb is required'); }
346
+ if (!lpName) { return cb(new Error('lpName is required'), null); }
552
347
 
553
- return cb(null, {
554
- all: all,
555
- current: current,
556
- past: past
557
- });
348
+ options = helpers.getDefaultOptions(options);
558
349
 
559
- });
350
+ if (!options.isWorkerProcess) { return cb(null, []); }
560
351
 
561
- });
352
+ let query = self.findOne({'name': lpName}, this.getReadFilterKeys(options.role));
353
+ query.populate('fundsInvested.fund');
354
+ query.exec(cb);
562
355
 
563
356
  };
564
357
 
565
- LimitedPartner.statics.list = function (role, cb) {
358
+ LimitedPartner.statics.getByPersonId = function(personId, options, cb) {
566
359
 
567
- // sorted by name ascending
568
- // sort is done post retrieval because query sort is done pre-decryption
569
- // filter by customer
360
+ const self = this;
570
361
 
571
- var self = this;
572
- var query;
362
+ if (!cb) { throw new Error('cb is required'); }
363
+ if (!personId) { return cb(new Error('personId is required'), null); }
364
+ if (!mongoose.Types.ObjectId.isValid(personId)) { return cb(new Error('personId is not a valid ObjectId'), null); }
365
+ if (!options) { return cb(new Error('options is required'), null); }
366
+ if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
367
+ if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
573
368
 
574
- var getLps = function(q) {
575
- q.populate('transactions.fund')
576
- q.exec(function (err, lps) {
577
- if (err) return cb(err, null);
578
- lps = _.sortBy(lps, function(lp) { return lp.name ? lp.name.toLowerCase() : ''; });
579
- return cb(null, lps);
580
- });
581
- };
369
+ options = helpers.getDefaultOptions(options);
582
370
 
583
- if (CUSTOMER_ID == 'GLOBAL_PROCESS') {
584
- query = self.find({}, self.getReadFilterKeys(role));
585
- getLps(query);
371
+ let query;
372
+
373
+ if (options.isWorkerProcess) {
374
+ query = self.find({'people': personId}, this.getReadFilterKeys(options.role));
586
375
  }
587
376
  else {
588
- query = self.find({ 'transactions.fund': { $in : self.customerFunds }}, self.getReadFilterKeys(role));
589
- getLps(query);
377
+ query = self.find({'people': personId, customer: options.CUSTOMER_ID }, self.getReadFilterKeys(options.role));
590
378
  }
591
379
 
592
- };
593
-
594
- LimitedPartner.statics.listForOrganization = function (orgid, role, cb) {
380
+ query.populate('fundsInvested.fund');
381
+ query.populate('people', 'name');
595
382
 
596
- // sorted by name ascending
597
- // sort is done post retrieval because query sort is done pre-decryption
383
+ query.exec(function (err, lps) {
384
+ if (err) { return cb(err, null); }
385
+ lps = _.sortBy(lps, function(lp) { return lp.name ? lp.name.toLowerCase() : ''; });
386
+ return cb(null, lps);
387
+ });
598
388
 
599
- var self = this;
600
- var query;
389
+ };
601
390
 
602
- var getLps = function(q) {
603
- q.populate('transactions.fund')
604
- q.exec(function (err, lps) {
605
- if (err) return cb(err, null);
606
- lps = _.sortBy(lps, function(lp) { return lp.name ? lp.name.toLowerCase() : ''; });
607
- return cb(null, lps);
608
- });
609
- };
391
+ LimitedPartner.statics.getFlags = function getFlags(lpid, options, cb) {
610
392
 
611
- query = self.find({ 'transactions.fund': { $in : self.customerFunds }}, self.getReadFilterKeys(role));
612
- getLps(query);
393
+ const self = this;
613
394
 
614
- };
395
+ if (!cb) { throw new Error('cb is required'); }
396
+ if (!lpid) { return cb(new Error('lpid is required'), null); }
397
+ if (!mongoose.Types.ObjectId.isValid(lpid)) { return cb(new Error('lpid is not a valid ObjectId'), null); }
398
+ if (!options) { return cb(new Error('options is required'), null); }
399
+ if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
400
+ if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
615
401
 
616
- LimitedPartner.statics.listAllUnverifiedLps = function (role, cb) {
402
+ let query = self.findOne({ '_id': lpid, customer: options.CUSTOMER_ID });
617
403
 
618
- if (role != 'admin') return cb(new Error('Unauthorized'), []);
404
+ query.select('flags');
405
+ query.populate({
406
+ path: 'flags',
407
+ match: { customer: options.CUSTOMER_ID },
408
+ options: { sort: { createdOn: -1 } }
409
+ });
410
+ query.deepPopulate([
411
+ 'flags.createdBy',
412
+ ], {
413
+ populate: {
414
+ 'flags.createdBy': { select: 'name avatarUrl title' }
415
+ }
416
+ });
619
417
 
620
- this
621
- .find({'status.verified': false}, this.getReadFilterKeys(role))
622
- .populate('transactions.fund')
623
- .exec(function (err, lps) {
624
- cb(err, _.sortBy(lps, function(lp) { return lp.name; }));
625
- });
418
+ query.exec(cb);
626
419
 
627
420
  };
628
421
 
629
- LimitedPartner.statics.listAllFlaggedLps = function (role, cb) {
422
+ LimitedPartner.statics.getNotes = function getNotes(lpid, options, cb) {
630
423
 
631
- if (role != 'admin') return cb(new Error('Unauthorized'), []);
424
+ const self = this;
632
425
 
633
- this
634
- .find({'flagged.resolved': false, 'flagged.text': { $ne: '' }}, this.getReadFilterKeys(role))
635
- .populate('transactions.fund')
636
- .exec(function (err, lps) {
637
- cb(err, _.sortBy(lps, function(lp) { return lp.name; }));
638
- });
426
+ if (!cb) { throw new Error('cb is required'); }
427
+ if (!lpid) { return cb(new Error('lpid is required'), null); }
428
+ if (!mongoose.Types.ObjectId.isValid(lpid)) { return cb(new Error('lpid is not a valid ObjectId'), null); }
429
+ if (!options) { return cb(new Error('options is required'), null); }
430
+ if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
431
+ if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
639
432
 
640
- };
433
+ let query = self.findOne({ '_id': lpid, customer: options.CUSTOMER_ID });
641
434
 
642
- LimitedPartner.statics.listByFund = function (fundId, role, cb) {
643
-
644
- // all lps in a fund, sorted by name ascending
645
- // sort is done post retrieval because query sort is done pre-decryption
646
-
647
- var self = this;
435
+ query.select('notes people');
436
+ query.populate({
437
+ path: 'notes',
438
+ match: { customer: options.CUSTOMER_ID },
439
+ options: { sort: { createdOn: -1 } }
440
+ });
441
+ query.deepPopulate([
442
+ 'notes.createdBy'
443
+ ], {
444
+ populate: {
445
+ 'notes.createdBy': { select: 'name avatarUrl title' }
446
+ }
447
+ });
648
448
 
649
- self
650
- .find({ 'transactions.fund': fundId }, self.getReadFilterKeys(role))
651
- .populate('transactions.fund')
652
- .exec(function(err, lps) {
449
+ query.exec(cb);
653
450
 
654
- if (err) return cb(err, null);
451
+ };
655
452
 
656
- lps = _.sortBy(lps, function(lp) {
657
- return lp.name ? lp.name.toLowerCase() : '';
658
- });
453
+ LimitedPartner.statics.getSources = function(lpid, options, cb) {
659
454
 
660
- return cb(null, lps);
455
+ const self = this;
456
+
457
+ if (!cb) { throw new Error('cb is required'); }
458
+ if (!lpid) { return cb(new Error('lpid is required'), null); }
459
+ if (!mongoose.Types.ObjectId.isValid(lpid)) { return cb(new Error('lpid is not a valid ObjectId'), null); }
460
+ if (!options) { return cb(new Error('options is required'), null); }
461
+ if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
462
+ if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
661
463
 
662
- });
464
+ let query = self.findById(lpid);
663
465
 
664
- };
466
+ query.populate('people', 'name avatarUrl title doNotDisplay sources');
467
+ query.deepPopulate([
468
+ 'people.sources.person'
469
+ ], {
470
+ populate: {
471
+ 'people.sources.person': { select: 'name avatarUrl title doNotDisplay' }
472
+ }
473
+ });
665
474
 
666
- LimitedPartner.statics.listPeople = function(lpid, role, cb) {
475
+ query.exec(function(err, lp) {
667
476
 
668
- var self = this;
477
+ if (err) { return cb(err, null); }
478
+ if (!lp) { return cb(null, null); }
669
479
 
670
- self.getById(lpid, role, function(err, lp) {
480
+ Organization.collection.findById(options.CUSTOMER_ID, function(err, customer) {
671
481
 
672
- if (err) return cb(err, null);
482
+ if (err) { return cb(err, null); }
483
+ if (!customer) { return cb(null, null); }
673
484
 
674
- var people = _.map(lp.people, function(person) {
675
- return {
676
- _id: person._id,
677
- name: person.name
678
- }
679
- });
485
+ let current = [];
486
+ let past = [];
487
+ let all = [];
488
+ let lpSources = helpers.getPeopleSources(lp.people);
680
489
 
681
- return cb(null, people);
490
+ _.each(customer.people, function(p) {
682
491
 
683
- });
492
+ let source = _.find(lpSources, function(ls) {
493
+ if (!ls || !ls.person || !ls.person._id) return false;
494
+ return ls.person._id.toString() === p.person.toString();
495
+ });
684
496
 
685
- };
497
+ if (!source) { return; }
686
498
 
687
- LimitedPartner.statics.search = function (terms, role, cb) {
499
+ if (p.current) { current.push(source); }
500
+ if (!p.current) { past.push(source); }
501
+ all.push(source);
688
502
 
689
- var self = this;
690
- var query;
691
- var getLps = function() {
692
- query.populate('transactions.fund');
693
- query.exec(function (err, lps) {
503
+ });
694
504
 
695
- if (err) return cb(err, null);
505
+ return cb(null, {
506
+ all: helpers.sortPeopleSources(all, options.CUSTOMER_ID),
507
+ current: helpers.sortPeopleSources(current, options.CUSTOMER_ID),
508
+ past: helpers.sortPeopleSources(past, options.CUSTOMER_ID)
509
+ });
696
510
 
697
- lps = _.sortBy(lps, function(lp) {
698
- return lp.name.toLowerCase();
699
511
  });
700
512
 
701
- return cb(null, lps);
702
513
  });
703
- };
704
-
705
- if (role == 'none') return cb(null, []);
706
514
 
707
- if (CUSTOMER_ID == 'GLOBAL_PROCESS') {
708
- query = self.find({'name': new RegExp(terms, "i")}, self.getReadFilterKeys(role));
709
- getLps();
710
- }
711
- else {
712
- query = self.find({ 'name': new RegExp(terms, "i"), 'transactions.fund': { $in : self.customerFunds }}, self.getReadFilterKeys(role));
515
+ };
713
516
 
714
- getLps();
715
- }
517
+ LimitedPartner.statics.listByFund = function (fundId, options, cb) {
716
518
 
717
- };
519
+ const self = this;
718
520
 
719
- LimitedPartner.statics.setCustomerFunds = function setCustomerFunds(fundids) {
720
- var self = this;
721
- self.customerFunds = fundids;
722
- return self.customerFunds;
723
- };
521
+ if (!cb) { throw new Error('cb is required'); }
522
+ if (!fundId) { return cb(new Error('fundId is required'), null); }
523
+ if (!mongoose.Types.ObjectId.isValid(fundId)) { return cb(new Error('fundId is not a valid ObjectId'), null); }
524
+ if (!options) { return cb(new Error('options is required'), null); }
525
+ if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
526
+ if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
724
527
 
725
- LimitedPartner.statics.removeCustomerTransactions = function removeCustomerTransactions(customerId, cb) {
528
+ options = helpers.getDefaultOptions(options);
726
529
 
727
- var self = this;
530
+ let query = self.find({ 'fundsInvested.fund': fundId, customer: options.CUSTOMER_ID }, self.getReadFilterKeys(options.role));
728
531
 
729
- var query = self.update(
730
- { 'customer': customerId },
731
- { $set: { transactions: [] } },
732
- { multi : true }
733
- );
532
+ query.populate('fundsInvested.fund');
533
+ query.sort({ 'name': -1 });
734
534
  query.exec(cb);
735
535
 
736
536
  };
@@ -740,11 +540,12 @@ module.exports = function(mongoose, config) {
740
540
  // VERY IMPORTANT NOTE
741
541
  // findByIdAndUpdate and findOneAndUpdate do not trigger pre-save hook so that code will not run here
742
542
 
743
- if (!cb) throw new Error('cb is required');
543
+ if (!cb) { throw new Error('cb is required'); }
744
544
  if (!id) { return cb(new Error('id is required'), null); }
745
- if (!update) { return cb(new Error('update is required'), null); }
545
+ if (!mongoose.Types.ObjectId.isValid(id)) { return cb(new Error('id is not a valid ObjectId'), null); }
546
+ if (!update) { return cb(new Error('options is required'), null); }
746
547
 
747
- var self = this;
548
+ const self = this;
748
549
 
749
550
  // https://mongoosejs.com/docs/api.html#model_Model.findOneAndUpdate
750
551
  // options runValidators defaults false which is ok since we have upsert false
@@ -754,217 +555,85 @@ module.exports = function(mongoose, config) {
754
555
 
755
556
  };
756
557
 
757
- LimitedPartner.statics.upsert = function(lp, username, role, cb) {
758
-
759
- if (!lp) { return cb(new Error('lp is required'), null); }
760
- if (!username) { return cb(new Error('username is required'), null); }
761
-
762
- lp.entered.by = username;
763
- lp.entered.on = new Date();
764
-
765
- lp.extendWithWriteFilter(lp, role);
766
- lp.save(function(err, result) {
767
- if (err) return cb(err, null);
768
- result.applyReadFilter(role);
769
- return cb(null, result);
770
- });
771
-
772
- };
773
-
774
- // Deprecated
775
- LimitedPartner.statics.flag = function(lpId, resolved, text, username, role, cb) {
558
+ LimitedPartner.statics.search = function (terms, options, cb) {
776
559
 
777
- var context = this;
560
+ const self = this;
778
561
 
779
- context
780
- .findById(lpId, this.getReadFilterKeys(role))
781
- .exec(function(err, lp) {
562
+ if (!cb) { throw new Error('cb is required'); }
563
+ if (!terms) { return cb(new Error('terms is required'), null); }
564
+ if (!options) { return cb(new Error('options is required'), null); }
565
+ if (!options.CUSTOMER_ID) { return cb(new Error('options.CUSTOMER_ID is required'), null); }
566
+ if (!mongoose.Types.ObjectId.isValid(options.CUSTOMER_ID)) { return cb(new Error('options.CUSTOMER_ID is not a valid ObjectId'), null); }
782
567
 
783
- lp.flagged.resolved = resolved;
784
- lp.flagged.text = text;
785
- lp.flagged.by = username;
786
- lp.flagged.on = Date.now();
568
+ options = helpers.getDefaultOptions(options);
787
569
 
788
- if(!resolved) {
789
- lp.status.verified = false;
790
- }
570
+ let query;
791
571
 
792
- return context.upsert(lp, username, role, cb);
572
+ if (options.role === 'none') { return cb(null, []); }
793
573
 
794
- });
795
-
796
- };
797
-
798
- LimitedPartner.statics.addFlag = function(lpid, creatorPersonId, text, cb) {
799
-
800
- Flag.createForModel({
801
- createdBy: creatorPersonId,
802
- text: text
803
- }, this, lpid, cb);
804
-
805
- };
806
-
807
- LimitedPartner.statics.addNote = function(lpid, creatorPersonId, text, isPrivate, cb) {
808
-
809
- Note.createForModel({
810
- createdBy: creatorPersonId,
811
- text: text,
812
- isPrivate: isPrivate
813
- }, this, lpid, cb);
814
-
815
- };
816
-
817
- LimitedPartner.statics.deleteFlag = function(flagId, cb) {
818
-
819
- // Delete the flag itself along with any references
820
-
821
- var self = this;
822
-
823
- var removeReferences = function removeReferences(callback) {
824
- self.update({}, {
825
- $pull: { 'flags' : [flagId] }
826
- }, callback);
827
- };
574
+ if (options.isWorkerProcess) {
575
+ query = self.find({'name': new RegExp(terms, 'i')}, self.getReadFilterKeys(options.role));
576
+ }
577
+ else {
578
+ query = self.find({ 'name': new RegExp(terms, 'i'), customer: options.CUSTOMER_ID }, self.getReadFilterKeys(options.role));
579
+ }
828
580
 
829
- async.series([
830
- Flag.delete.bind(Flag, flagId),
831
- removeReferences
832
- ], function(err, results) {
833
- return cb(err, null);
834
- });
581
+ query.populate('fundsInvested.fund');
582
+ query.sort({ 'name': -1 });
583
+ query.exec(cb);
835
584
 
836
585
  };
837
586
 
838
- LimitedPartner.statics.deleteNote = function(noteId, cb) {
587
+ LimitedPartner.statics.upsert = function(lp, username, options, cb) {
839
588
 
840
- // Delete the note itself along with any references
841
-
842
- var self = this;
589
+ if (!cb) { throw new Error('cb is required'); }
590
+ if (!lp) { return cb(new Error('lp is required'), null); }
591
+ if (!username) { return cb(new Error('username is required'), null); }
592
+ if (!options) { return cb(new Error('options is required'), null); }
593
+ if (!options.role) { return cb(new Error('options.role is required'), null); }
843
594
 
844
- var removeReferences = function removeReferences(callback) {
845
- self.update({}, {
846
- $pull: { 'notes' : [noteId] }
847
- }, callback);
848
- };
595
+ lp.entered.by = username;
596
+ lp.entered.on = new Date();
849
597
 
850
- async.series([
851
- Note.delete.bind(Note, noteId),
852
- removeReferences
853
- ], function(err, results) {
854
- return cb(err, null);
598
+ lp.extendWithWriteFilter(lp, options.role);
599
+ lp.save(function(err, result) {
600
+ if (err) return cb(err, null);
601
+ result.applyReadFilter(options.role);
602
+ return cb(null, result);
855
603
  });
856
604
 
857
605
  };
858
606
 
859
- //////////////////////////////
860
-
861
- LimitedPartner.pre('save', function(next) {
862
-
863
- var self = this;
864
-
865
- var encryptSensitiveData = function(cb) {
866
-
867
- if (self.distributions.ach.accountType) { self.distributions.ach.accountType = crypto.encrypt(self.distributions.ach.accountType); }
868
- if (self.distributions.ach.stripeRecipientId) { self.distributions.ach.stripeRecipientId = crypto.encrypt(self.distributions.ach.stripeRecipientId); }
869
- if (self.distributions.ach.legalName) { self.distributions.ach.legalName = crypto.encrypt(self.distributions.ach.legalName); }
870
- if (self.distributions.ach.taxId) { self.distributions.ach.taxId = crypto.encrypt(self.distributions.ach.taxId); }
871
- if (self.distributions.ach.bankAccount.name) { self.distributions.ach.bankAccount.name = crypto.encrypt(self.distributions.ach.bankAccount.name); }
872
- if (self.distributions.ach.bankAccount.country) { self.distributions.ach.bankAccount.country = crypto.encrypt(self.distributions.ach.bankAccount.country); }
873
- if (self.distributions.ach.bankAccount.routingNumber) { self.distributions.ach.bankAccount.routingNumber = crypto.encrypt(self.distributions.ach.bankAccount.routingNumber); }
874
- if (self.distributions.ach.bankAccount.accountNumber) { self.distributions.ach.bankAccount.accountNumber = crypto.encrypt(self.distributions.ach.bankAccount.accountNumber); }
875
- if (self.k1.EIN) { self.k1.EIN = crypto.encrypt(self.k1.EIN); }
876
-
877
- return cb();
878
-
879
- };
880
-
881
- encryptSensitiveData(function() {
882
- return next();
883
- });
884
-
885
- });
886
-
887
- LimitedPartner.pre('init', function(next) {
888
- if (CUSTOMER_ID != 'GLOBAL_PROCESS' && !LPS_ENABLED) throw new Error('LPS_ENABLED is false');
889
- else next();
890
- });
891
-
892
- LimitedPartner.post('init', function(doc, next) {
893
-
894
- if (CUSTOMER_ID != 'GLOBAL_PROCESS' && !LPS_ENABLED) {
895
-
896
- // Don't return doc if lps aren't enabled or lp doesn't belong to current customer
897
- if (!LPS_ENABLED || doc.customer != CUSTOMER_ID) {
898
- doc = null;
899
- return next();
900
- }
901
-
902
- }
903
-
904
- // Decrypt sensitive data
905
- if (this.distributions && this.distributions.ach) {
906
- if (this.distributions.ach.accountType) {
907
- this.distributions.ach.accountType = crypto.decrypt(this.distributions.ach.accountType);
908
- }
909
- if (this.distributions.ach.stripeRecipientId) {
910
- this.distributions.ach.stripeRecipientId = crypto.decrypt(this.distributions.ach.stripeRecipientId);
911
- }
912
- if (this.distributions.ach.legalName) {
913
- this.distributions.ach.legalName = crypto.decrypt(this.distributions.ach.legalName);
914
- }
915
- if (this.distributions.ach.taxId) {
916
- this.distributions.ach.taxId = crypto.decrypt(this.distributions.ach.taxId);
917
- }
918
- if (this.distributions.ach.bankAccount.name) {
919
- this.distributions.ach.bankAccount.name = crypto.decrypt(this.distributions.ach.bankAccount.name);
920
- }
921
- if (this.distributions.ach.bankAccount.country) {
922
- this.distributions.ach.bankAccount.country = crypto.decrypt(this.distributions.ach.bankAccount.country);
923
- }
924
- if (this.distributions.ach.bankAccount.routingNumber) {
925
- this.distributions.ach.bankAccount.routingNumber = crypto.decrypt(this.distributions.ach.bankAccount.routingNumber);
926
- }
927
- if (this.distributions.ach.bankAccount.accountNumber) {
928
- this.distributions.ach.bankAccount.accountNumber = crypto.decrypt(this.distributions.ach.bankAccount.accountNumber);
929
- }
930
- }
931
- if (this.k1 && this.k1.EIN) { this.k1.EIN = crypto.decrypt(this.k1.EIN); }
932
-
933
- next();
934
-
935
- });
936
-
937
607
  //////////////////////////////
938
608
 
939
609
  LimitedPartner.plugin(filter, {
940
- "readFilter": {
941
- "admin": ["name", "shortName", "start", "people", "documentSlugs", "transactions", "contact", "distributions", "k1", "entered", "status", "flagged", "flags", "notes"],
942
- "lp-full": ["name", "shortName", "start", "people", "documentSlugs", "transactions", "contact", "distributions", "k1", "entered", "status", "flagged", "flags", "notes"],
943
- "lp-names": ["name", "shortName", "flagged", "flags"],
944
- "none": []
610
+ 'readFilter': {
611
+ 'admin': ['name', 'shortName', 'customer', 'start', 'people', 'documentSlugs', 'fundsInvested', 'contact', 'entered', 'status', 'flagged', 'flags', 'notes'],
612
+ 'lp-full': ['name', 'shortName', 'customer', 'start', 'people', 'documentSlugs', 'fundsInvested', 'contact', 'entered', 'status', 'flagged', 'flags', 'notes'],
613
+ 'lp-names': ['name', 'shortName', 'flagged', 'flags', 'customer'],
614
+ 'none': []
945
615
  },
946
- "writeFilter": {
947
- "admin": ["name", "shortName", "start", "people", "documentSlugs", "transactions", "contact", "distributions", "k1", "entered", "status", "flagged", "flags", "notes"],
948
- "lp-full": ["name", "shortName", "start", "people", "documentSlugs", "transactions", "contact", "distributions", "k1", "entered", "status", "flagged", "flags", "notes"],
949
- "lp-names": ["flagged", "flags"],
950
- "none": []
616
+ 'writeFilter': {
617
+ 'admin': ['name', 'shortName', 'customer', 'start', 'people', 'documentSlugs', 'fundsInvested', 'contact', 'entered', 'status', 'flagged', 'flags', 'notes'],
618
+ 'lp-full': ['name', 'shortName', 'customer', 'start', 'people', 'documentSlugs', 'fundsInvested', 'contact', 'entered', 'status', 'flagged', 'flags', 'notes'],
619
+ 'lp-names': ['flagged', 'flags', 'customer'],
620
+ 'none': []
951
621
  },
952
- "defaultFilterRole": "none",
953
- "sanitize": false,
954
- "compat": false
622
+ 'defaultFilterRole': 'none',
623
+ 'sanitize': false,
624
+ 'compat': false
955
625
  });
956
626
 
957
627
  //////////////////////////////
958
628
 
959
629
  LimitedPartner.set('autoIndex', false);
960
- LimitedPartner.set('usePushEach', true);
961
630
  LimitedPartner.on('index', function(err) { console.log('error building limited partner indexes: ' + err); });
962
631
 
963
632
  LimitedPartner.set('toJSON', { virtuals: true });
964
633
 
965
- var deepPopulate = require('mongoose-deep-populate')(mongoose);
634
+ let deepPopulate = require('mongoose-deep-populate')(mongoose);
966
635
  LimitedPartner.plugin(deepPopulate);
967
636
 
968
637
  mongoose.model('LimitedPartner', LimitedPartner);
969
638
 
970
- };
639
+ };