@dhyasama/totem-models 1.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/.npmignore +14 -0
- package/changeBSON.js +27 -0
- package/dictionaries/nicknames.json +703 -0
- package/index.js +42 -0
- package/lib/Account.js +312 -0
- package/lib/Activity.js +138 -0
- package/lib/CalendarEvent.js +150 -0
- package/lib/Document.js +357 -0
- package/lib/Fund.js +143 -0
- package/lib/LimitedPartner.js +780 -0
- package/lib/List.js +217 -0
- package/lib/News.js +161 -0
- package/lib/Note.js +170 -0
- package/lib/Organization.js +1011 -0
- package/lib/Person.js +1163 -0
- package/package.json +37 -0
- package/test/Account.js +139 -0
- package/test/Activity.js +167 -0
- package/test/CalendarEvent.js +59 -0
- package/test/Fund.js +113 -0
- package/test/LimitedPartner.js +554 -0
- package/test/List.js +270 -0
- package/test/News.js +109 -0
- package/test/Note.js +304 -0
- package/test/Organization.js +750 -0
- package/test/Person.js +1042 -0
- package/test/config.js +49 -0
|
@@ -0,0 +1,1011 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
module.exports = function(mongoose, config) {
|
|
4
|
+
|
|
5
|
+
var LPS_ENABLED = config.LPS_ENABLED || false;
|
|
6
|
+
|
|
7
|
+
var
|
|
8
|
+
|
|
9
|
+
_ = require('underscore'),
|
|
10
|
+
async = require('async'),
|
|
11
|
+
utils = require('@dhyasama/ffvc-utilities'),
|
|
12
|
+
Schema = mongoose.Schema,
|
|
13
|
+
Note = mongoose.model('Note'),
|
|
14
|
+
|
|
15
|
+
Organization = new Schema({
|
|
16
|
+
|
|
17
|
+
name: { type: String, required: true, index: true },
|
|
18
|
+
|
|
19
|
+
slug: { type: String, required: true, unique: true, sparse: false, trim: true, lowercase: true },
|
|
20
|
+
|
|
21
|
+
logoUrl: { type: String, trim: true },
|
|
22
|
+
|
|
23
|
+
description: { type: String, trim: true },
|
|
24
|
+
|
|
25
|
+
website: { type: String, unique: true, sparse: true, required: false, trim: true, lowercase: true },
|
|
26
|
+
|
|
27
|
+
websiteAliases: [ { type: String, unique: true, sparse: true, trim: true, lowercase: true } ],
|
|
28
|
+
|
|
29
|
+
social: {
|
|
30
|
+
twitter: { type: String, trim: true, unique: true, sparse: true },
|
|
31
|
+
facebook: { type: String, trim: true, unique: true, sparse: true },
|
|
32
|
+
linkedin: { type: String, trim: true, unique: true, sparse: true }
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
contact: {
|
|
36
|
+
|
|
37
|
+
phone: [{
|
|
38
|
+
type: { type: String, default: null, trim: true },
|
|
39
|
+
number: { type: String, default: null, trim: true },
|
|
40
|
+
primary: { type: Boolean, default: false }
|
|
41
|
+
}],
|
|
42
|
+
|
|
43
|
+
email: [{
|
|
44
|
+
type: { type: String, default: null, trim: true },
|
|
45
|
+
email: { type: String, default: null, trim: true, lowercase: true },
|
|
46
|
+
primary: { type: Boolean, default: false }
|
|
47
|
+
}],
|
|
48
|
+
|
|
49
|
+
address: [{
|
|
50
|
+
type: { type: String, default: '', trim: true },
|
|
51
|
+
street: { type: String, default: '', trim: true },
|
|
52
|
+
suite: { type: String, default: '', trim: true },
|
|
53
|
+
city: { type: String, default: '', trim: true },
|
|
54
|
+
state: { type: String, default: '', trim: true },
|
|
55
|
+
country: { type: String, default: '', trim: true },
|
|
56
|
+
postalCode: { type: String, default: '', trim: true },
|
|
57
|
+
primary: { type: Boolean, default: false }
|
|
58
|
+
}]
|
|
59
|
+
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
people: [{
|
|
63
|
+
person: { type: Schema.ObjectId, ref: 'Person', required: true, index: true },
|
|
64
|
+
title: { type: String, default: null, trim: true },
|
|
65
|
+
current: { type: Boolean, default: false, index: true },
|
|
66
|
+
leader: { type: Boolean, default: false, index: true },
|
|
67
|
+
board: { type: String, enum: ['chairman', 'director', 'observer', 'none'], default: 'none', index: true }
|
|
68
|
+
}],
|
|
69
|
+
|
|
70
|
+
entered: {
|
|
71
|
+
by: { type: String, default: '', trim: true },
|
|
72
|
+
on: { type: Date, default: Date.now }
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
flagged: {
|
|
76
|
+
by: { type: String, default: '', trim: true },
|
|
77
|
+
on: { type: Date, default: Date.now },
|
|
78
|
+
text: { type: String, default: '', trim: true },
|
|
79
|
+
resolved: { type: Boolean, default: false }
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
merged: [{ type: Schema.ObjectId, ref: 'Organization' }],
|
|
83
|
+
|
|
84
|
+
deleted: { type: Boolean, default: false },
|
|
85
|
+
|
|
86
|
+
aliases: [ { type: String, trim: true } ],
|
|
87
|
+
|
|
88
|
+
foundedOn: { type: Date, default: null },
|
|
89
|
+
closedOn: { type: Date, default: null },
|
|
90
|
+
|
|
91
|
+
exit: {
|
|
92
|
+
|
|
93
|
+
acquisition: {
|
|
94
|
+
by: { type: Schema.ObjectId, ref: 'Organization' },
|
|
95
|
+
year: { type: Date, default: null },
|
|
96
|
+
amount: { type: Number, default: 0 }
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
ipo: {
|
|
100
|
+
ticker: {type: String, default: null, trim: true},
|
|
101
|
+
year: {type: Date, default: null}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
filters: [{
|
|
107
|
+
group: { type: String, trim: true },
|
|
108
|
+
items: [{ type: String, trim: true }],
|
|
109
|
+
customer: { type: Schema.ObjectId, ref: 'Organization' }
|
|
110
|
+
}],
|
|
111
|
+
|
|
112
|
+
crunchbase: {
|
|
113
|
+
uuid: { type: String, unique: true, sparse: true, trim: true },
|
|
114
|
+
url: { type: String, unique: true, sparse: true, trim: true }
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
eshares: {
|
|
118
|
+
companyid: { type: String, unique: true, sparse: true, trim: true }
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
related: [{ type: Schema.ObjectId, ref: 'Organization' }],
|
|
122
|
+
|
|
123
|
+
// customer specific (customer id is stored on note)
|
|
124
|
+
notes: [ { type: Schema.ObjectId, ref: 'Note' } ],
|
|
125
|
+
|
|
126
|
+
customer: {
|
|
127
|
+
|
|
128
|
+
active: { type: Boolean, default: false },
|
|
129
|
+
|
|
130
|
+
totemUrl: { type: String, trim: true, unique: true, sparse: true },
|
|
131
|
+
|
|
132
|
+
style: {
|
|
133
|
+
backgroundColor: { type: String, default: '#666666', trim: true },
|
|
134
|
+
textColor: { type: String, default: '#ffffff', trim: true }
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
investmentsSheet: {
|
|
138
|
+
sheet: { type: String, trim: true, unique: true, sparse: true },
|
|
139
|
+
googleSheetCreds: {
|
|
140
|
+
private_key_id: { type: String, trim: true, unique: true, sparse: true },
|
|
141
|
+
private_key: { type: String, trim: true, unique: true, sparse: true },
|
|
142
|
+
client_email: { type: String, trim: true, unique: true, sparse: true },
|
|
143
|
+
client_id: { type: String, trim: true, unique: true, sparse: true },
|
|
144
|
+
type: { type: String, trim: true }
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
eshares: {
|
|
149
|
+
firmid: { type: String, unique: true, sparse: true, trim: true }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
docs: [{
|
|
155
|
+
customer: { type: Schema.ObjectId, ref: 'Organization' },
|
|
156
|
+
url: { type: String, trim: true, unique: true, sparse: true }
|
|
157
|
+
}],
|
|
158
|
+
|
|
159
|
+
// Link to deeper data on iLevel
|
|
160
|
+
// iLevelUser must be true at the account level
|
|
161
|
+
iLevel: [{
|
|
162
|
+
customer: { type: Schema.ObjectId, ref: 'Organization' },
|
|
163
|
+
url: { type: String, trim: true, unique: true, sparse: true }
|
|
164
|
+
}],
|
|
165
|
+
|
|
166
|
+
chairs: [{
|
|
167
|
+
customer: { type: Schema.ObjectId, ref: 'Organization' },
|
|
168
|
+
first: { type: Schema.ObjectId, ref: 'Person' },
|
|
169
|
+
second: { type: Schema.ObjectId, ref: 'Person' }
|
|
170
|
+
}],
|
|
171
|
+
|
|
172
|
+
// Funds belonging to this organization (which is typically, but not necessarily, what we'd call an investor)
|
|
173
|
+
// An example of a non-"investor" with funds would be Slack with it's Slack Fund
|
|
174
|
+
// Another way to put it, these are funds from which this organization makes investments
|
|
175
|
+
funds: [{ type: Schema.ObjectId, ref: 'Fund' }],
|
|
176
|
+
|
|
177
|
+
// Private
|
|
178
|
+
capTable: [{
|
|
179
|
+
|
|
180
|
+
customer: { type: Schema.ObjectId, ref: 'Organization', required: true },
|
|
181
|
+
shares: { type: Number, default: 0 },
|
|
182
|
+
fullyDiluted: { type: Number, default: 0 },
|
|
183
|
+
asOfDate: { type: Date },
|
|
184
|
+
|
|
185
|
+
// todo - ???
|
|
186
|
+
optionsAndWarrants: {
|
|
187
|
+
unissued: { type: Number },
|
|
188
|
+
issuedToUnspecified: { type: Number }
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
}],
|
|
192
|
+
|
|
193
|
+
// Equity or debt this organization has taken from other organizations
|
|
194
|
+
rounds: [{
|
|
195
|
+
|
|
196
|
+
roundName: { type: String, trim: true, default: null },
|
|
197
|
+
|
|
198
|
+
// used when cap table round names are changed and need to be matched to investment data
|
|
199
|
+
oldRoundName: { type: String, trim: true, default: null },
|
|
200
|
+
|
|
201
|
+
// these three values are public and will be used in the absence of overriding private data
|
|
202
|
+
preMoneyValuation: { type: Number, default: 0 },
|
|
203
|
+
amountRaised: { type: Number, default: 0 },
|
|
204
|
+
closingDate: { type: Date, default: null },
|
|
205
|
+
|
|
206
|
+
// Funds that participated in this round
|
|
207
|
+
// Recall that a fund belongs to an organization
|
|
208
|
+
// This is a public list of funds/orgs that invested
|
|
209
|
+
funds: [{
|
|
210
|
+
|
|
211
|
+
fund: { type: Schema.ObjectId, ref: 'Fund', required: true },
|
|
212
|
+
|
|
213
|
+
// the lead on this round from the fund/org
|
|
214
|
+
partner: { type: Schema.ObjectId, ref: 'Person', required: false },
|
|
215
|
+
|
|
216
|
+
// not strictly necessary since we have fund
|
|
217
|
+
// include because it makes filtering investments data, which is private, much easier
|
|
218
|
+
customer: { type: Schema.ObjectId, ref: 'Organization', required: false },
|
|
219
|
+
|
|
220
|
+
// Investments in this round from this fund
|
|
221
|
+
// this is all private data
|
|
222
|
+
investments: [{
|
|
223
|
+
|
|
224
|
+
amount: { type: Number, default: 0 },
|
|
225
|
+
date: { type: Date, default: null },
|
|
226
|
+
lead: { type: Boolean, default: false },
|
|
227
|
+
participatingPreferred: { type: Boolean, default: false },
|
|
228
|
+
pricePerShare: { type: Number, default: 0 },
|
|
229
|
+
fairMarketValue: { type: Number, default: 0 },
|
|
230
|
+
notes: { type: String, trim: true, default: null },
|
|
231
|
+
|
|
232
|
+
// note these fields are duplicative at the round level
|
|
233
|
+
// this is private data and will override the round level numbers where appropriate
|
|
234
|
+
preMoneyValuation: { type: Number, default: 0 },
|
|
235
|
+
amountRaised: { type: Number, default: 0 },
|
|
236
|
+
closingDate: { type: Date, default: null },
|
|
237
|
+
|
|
238
|
+
convertibleNotes: [{
|
|
239
|
+
cap: { type: Number, default: 0 },
|
|
240
|
+
discount: { type: Number, default: 0 },
|
|
241
|
+
interestRate: { type: Number, default: 0 },
|
|
242
|
+
maturityDate: { type: Date, default: null },
|
|
243
|
+
fairMarketValue: { type: Number, default: 0 },
|
|
244
|
+
notes: { type: String, trim: true, default: null }
|
|
245
|
+
}]
|
|
246
|
+
|
|
247
|
+
}]
|
|
248
|
+
|
|
249
|
+
}],
|
|
250
|
+
|
|
251
|
+
// People that participated in this round
|
|
252
|
+
// This is a public list of people that invested
|
|
253
|
+
people: [{
|
|
254
|
+
person: {type: Schema.ObjectId, ref: 'Person'}
|
|
255
|
+
}]
|
|
256
|
+
|
|
257
|
+
}],
|
|
258
|
+
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
///////////////////////////////
|
|
262
|
+
|
|
263
|
+
Organization.virtual('status').get(function () {
|
|
264
|
+
|
|
265
|
+
var self = this;
|
|
266
|
+
|
|
267
|
+
// infer from data
|
|
268
|
+
|
|
269
|
+
if (self.closedOn) return 'Dead';
|
|
270
|
+
else if (self.ipo.year) return 'IPO';
|
|
271
|
+
else if (self.acquisition.year) return 'Acquired';
|
|
272
|
+
else return 'Active';
|
|
273
|
+
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
Organization.virtual('address.primary').get(function () {
|
|
277
|
+
var primary = _.find(this.contact.address, function(address) {
|
|
278
|
+
return address.primary;
|
|
279
|
+
});
|
|
280
|
+
return primary ? primary.address : null;
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
Organization.virtual('sources').get(function () {
|
|
284
|
+
|
|
285
|
+
// Aggregate sources from all people at org
|
|
286
|
+
// Sort by interactions descending, full name ascending
|
|
287
|
+
|
|
288
|
+
var self = this;
|
|
289
|
+
|
|
290
|
+
var result = _.compact(_.pluck(self.people, 'person'));
|
|
291
|
+
if (result.length == 0) return [];
|
|
292
|
+
|
|
293
|
+
result = _.compact(_.pluck(result, 'sources'));
|
|
294
|
+
if (result.length == 0) return [];
|
|
295
|
+
|
|
296
|
+
result = _.flatten(result);
|
|
297
|
+
result = _.compact(result);
|
|
298
|
+
|
|
299
|
+
if (result.length == 0) return [];
|
|
300
|
+
if (mongoose.Types.ObjectId.isValid(result[0].person)) return []; // not populated
|
|
301
|
+
|
|
302
|
+
result = _.uniq(result, function(item) { return item.person._id.toString(); });
|
|
303
|
+
result = _.sortBy(result, 'interactions.count');
|
|
304
|
+
result.reverse();
|
|
305
|
+
result = _.sortBy(result, 'name.full');
|
|
306
|
+
|
|
307
|
+
return result;
|
|
308
|
+
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
///////////////////////////////
|
|
312
|
+
|
|
313
|
+
Organization.methods.merge = function(id) {
|
|
314
|
+
if (this.merged.indexOf(id) == -1) {
|
|
315
|
+
this.merged.push(id);
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
Organization.methods.deleteOrg = function() {
|
|
324
|
+
// Soft delete
|
|
325
|
+
this.deleted = true;
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
Organization.methods.relateOrg = function(id) {
|
|
329
|
+
|
|
330
|
+
var self = this;
|
|
331
|
+
|
|
332
|
+
var match = _.find(self.related, function(org) {
|
|
333
|
+
var orgId = utils.isValidObjectId(org) ? org : org.id;
|
|
334
|
+
return orgId.toString() == id.toString();
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
if (!match) { self.related.push(id); }
|
|
338
|
+
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// todo - what are "basic" fields and how are they used?
|
|
342
|
+
Organization.methods.newBasic = function(field, value) {
|
|
343
|
+
|
|
344
|
+
var compoundField = false;
|
|
345
|
+
if(field.split(".").length > 1) {
|
|
346
|
+
compoundField = true;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if(!compoundField) {
|
|
350
|
+
this[field] = value;
|
|
351
|
+
} else {
|
|
352
|
+
var parentField = field.split('.')[0];
|
|
353
|
+
var childField = field.split('.')[1];
|
|
354
|
+
|
|
355
|
+
this[parentField][childField] = value;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
Organization.methods.updateBasic = function(field, value) {
|
|
361
|
+
|
|
362
|
+
var compoundField = false;
|
|
363
|
+
if(field.split(".").length > 1) {
|
|
364
|
+
compoundField = true;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if(!compoundField) {
|
|
368
|
+
var currentVal = this[field];
|
|
369
|
+
this[field] = value;
|
|
370
|
+
} else {
|
|
371
|
+
var parentField = field.split('.')[0];
|
|
372
|
+
var childField = field.split('.')[1];
|
|
373
|
+
|
|
374
|
+
var currentVal = this[parentField][childField];
|
|
375
|
+
this[parentField][childField] = value;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
Organization.methods.updateContact = function(field, value) {
|
|
381
|
+
|
|
382
|
+
var parentField = field.split('.')[0];
|
|
383
|
+
var childField = field.split('.')[1];
|
|
384
|
+
|
|
385
|
+
this[parentField][childField] = value;
|
|
386
|
+
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
Organization.methods.addPerson = function(personToAdd) {
|
|
390
|
+
|
|
391
|
+
var self = this;
|
|
392
|
+
|
|
393
|
+
// Some simple validation
|
|
394
|
+
if (!personToAdd) throw new Error('Must supply person to add');
|
|
395
|
+
if (!personToAdd.person) throw new Error('Person to add must have person reference');
|
|
396
|
+
if (!utils.isValidObjectId(personToAdd.person)) throw new Error('Person reference must be a valid objectid');
|
|
397
|
+
|
|
398
|
+
// Check if person is already added
|
|
399
|
+
var match = _.find(self.people, function(existingPerson) {
|
|
400
|
+
var id = utils.isValidObjectId(existingPerson.person) ? existingPerson.person : existingPerson.person.id;
|
|
401
|
+
return id.toString() == personToAdd.person.toString();
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// If not, add it
|
|
405
|
+
if (!match) self.people.push(personToAdd);
|
|
406
|
+
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
Organization.methods.removePerson = function(personIdToRemove) {
|
|
410
|
+
|
|
411
|
+
this.people = _.reject(this.people, function(existingPerson) {
|
|
412
|
+
var id = utils.isValidObjectId(existingPerson.person) ? existingPerson.person : existingPerson.person._id;
|
|
413
|
+
return id.toString() == personIdToRemove.toString();
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
Organization.methods.updateFunds = function(funds) {
|
|
419
|
+
|
|
420
|
+
var self = this;
|
|
421
|
+
|
|
422
|
+
// todo - ??? - funds invested into this org or funds this org invests from?
|
|
423
|
+
|
|
424
|
+
if (!self.portfolioCompany) return;
|
|
425
|
+
|
|
426
|
+
// wipe funds and start fresh
|
|
427
|
+
self.portfolioCompany.fundsInvested = funds;
|
|
428
|
+
|
|
429
|
+
// Checking if it's a valid object id is a shortcut
|
|
430
|
+
// TODO: The alternative is to get a list of funds and verify against that
|
|
431
|
+
// TODO: also would be good to check for dupes
|
|
432
|
+
// This is used internally, in admin, so let's punt for now
|
|
433
|
+
|
|
434
|
+
//_.each(fundIds, function(fundId) {
|
|
435
|
+
// if (utilities.isValidObjectId(fundId.id)) {
|
|
436
|
+
// self.fundsInvested.push(fundId);
|
|
437
|
+
// };
|
|
438
|
+
//});
|
|
439
|
+
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
Organization.methods.updateChair = function(chairObj) {
|
|
443
|
+
|
|
444
|
+
var self = this;
|
|
445
|
+
|
|
446
|
+
if (!self.portfolioCompany) return;
|
|
447
|
+
|
|
448
|
+
// todo - needs customer id
|
|
449
|
+
|
|
450
|
+
switch(chairObj.num) {
|
|
451
|
+
case 'chair1':
|
|
452
|
+
if(chairObj.val && chairObj.val != "") {
|
|
453
|
+
self.portfolioCompany.chairs.first = chairObj.val;
|
|
454
|
+
} else {
|
|
455
|
+
self.portfolioCompany.chairs.first = {};
|
|
456
|
+
}
|
|
457
|
+
break;
|
|
458
|
+
|
|
459
|
+
case 'chair2':
|
|
460
|
+
if(chairObj.val && chairObj.val != "") {
|
|
461
|
+
self.portfolioCompany.chairs.second = chairObj.val;
|
|
462
|
+
} else {
|
|
463
|
+
self.portfolioCompany.chairs.second = {}
|
|
464
|
+
}
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
// todo - change this to exit (with acquisition or ipo)
|
|
471
|
+
Organization.methods.updateAcquisition = function(aObj) {
|
|
472
|
+
|
|
473
|
+
var self = this;
|
|
474
|
+
|
|
475
|
+
if (!self.portfolioCompany) return;
|
|
476
|
+
|
|
477
|
+
self.portfolioCompany.acquisition = aObj;
|
|
478
|
+
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
Organization.methods.removeInvestmentsAndDebtsByCustomer = function(investor) {
|
|
482
|
+
|
|
483
|
+
var self = this;
|
|
484
|
+
|
|
485
|
+
// todo - change to round.funds and capTable.customer
|
|
486
|
+
|
|
487
|
+
if (!self.portfolioCompany) return;
|
|
488
|
+
|
|
489
|
+
if(self.portfolioCompany.investments.length > 0) {
|
|
490
|
+
self.portfolioCompany.investments = _.reject(self.portfolioCompany.investments, function(i) {
|
|
491
|
+
return String(i.source) == String(investor._id);
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if(self.portfolioCompany.debts.length > 0) {
|
|
496
|
+
self.portfolioCompany.debts = _.reject(self.portfolioCompany.debts, function(d) {
|
|
497
|
+
return String(d.source) == String(investor._id);
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if(self.portfolioCompany.fundsInvested.length > 0) {
|
|
502
|
+
self.portfolioCompany.fundsInvested = _.reject(self.portfolioCompany.fundsInvested, function(f) {
|
|
503
|
+
return String(f.investor._id || f.investor) == String(investor._id);
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
Organization.methods.addCompanyToInvestor = function(idOfCompanyToAdd) {
|
|
510
|
+
|
|
511
|
+
var self = this;
|
|
512
|
+
if (!self.investor) return;
|
|
513
|
+
|
|
514
|
+
var match = _.find(self.investor.companies, function(company) {
|
|
515
|
+
var companyId = utils.isValidObjectId(company) ? company : company.id;
|
|
516
|
+
return companyId.toString() == idOfCompanyToAdd.toString();
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
if (!match) self.investor.companies.push(idOfCompanyToAdd);
|
|
520
|
+
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
Organization.methods.addFundToInvestor = function(idOfFundToAdd) {
|
|
524
|
+
|
|
525
|
+
var self = this;
|
|
526
|
+
if (!self.investor) return;
|
|
527
|
+
|
|
528
|
+
var match = _.find(self.investor.funds, function(fund) {
|
|
529
|
+
var fundId = utils.isValidObjectId(fund) ? fund : fund.id;
|
|
530
|
+
return fundId.toString() == idOfFundToAdd.toString();
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
if (!match) self.investor.funds.push(idOfFundToAdd);
|
|
534
|
+
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
///////////////////////
|
|
538
|
+
|
|
539
|
+
// search for orgs based on fields provided
|
|
540
|
+
// data format, each field is optional
|
|
541
|
+
// {
|
|
542
|
+
// domain: ''
|
|
543
|
+
// name: ''
|
|
544
|
+
// }
|
|
545
|
+
// options:
|
|
546
|
+
// fuzzy: searches within strings, e.g., searching for "venture" will match "ff Venture Capital"
|
|
547
|
+
// non-fuzzy does a full-string match, e.g., "ffvc" will match "ffvc" but "ff" will not
|
|
548
|
+
// both fuzzy and non are case-insensitive
|
|
549
|
+
// queryFirstWord: tokenizes first word and searches for it
|
|
550
|
+
// removeRelatedFromTopLevel: related people are removed from top-level results
|
|
551
|
+
Organization.statics.search = function search(data, options, cb) {
|
|
552
|
+
|
|
553
|
+
// validate input
|
|
554
|
+
if (!cb) throw new Error('cb is required');
|
|
555
|
+
if (!data || (!data.name && !data.domain)) return cb(null, []);
|
|
556
|
+
|
|
557
|
+
var defaultOptions = {
|
|
558
|
+
fuzzy: true,
|
|
559
|
+
queryFirstWord: false,
|
|
560
|
+
removeRelatedFromTopLevel: true
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
// combine provided and default options
|
|
564
|
+
options = utils.setDefaults(options, defaultOptions);
|
|
565
|
+
|
|
566
|
+
// need to be admin or provide investor id
|
|
567
|
+
if (!options.admin && !options.investor) return cb(null, []);
|
|
568
|
+
|
|
569
|
+
data.name = data.name || '';
|
|
570
|
+
data.domain = data.domain || '';
|
|
571
|
+
|
|
572
|
+
var removeRelatedFromTopLevel = function(items) {
|
|
573
|
+
|
|
574
|
+
// All entities are searched initially, including entities that are listed as related to other entities
|
|
575
|
+
// Remove related entities from top-level results so they only come up as related
|
|
576
|
+
|
|
577
|
+
var getRelatedIds = function getRelatedIds(items) {
|
|
578
|
+
|
|
579
|
+
var result = [];
|
|
580
|
+
|
|
581
|
+
_.each(items, function(item) {
|
|
582
|
+
if (item.related && item.related.length >= 1) {
|
|
583
|
+
var ids = _.pluck(item.related, 'id');
|
|
584
|
+
result = result.concat(ids);
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
return _.uniq(result);
|
|
589
|
+
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
var relatedIds = getRelatedIds(items);
|
|
593
|
+
items = _.reject(items, function(item) {
|
|
594
|
+
return relatedIds.indexOf(item.id) >= 0;
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
return items;
|
|
598
|
+
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
// use first word in org name to look for fuzzy matches which is a simple
|
|
602
|
+
// but effective technique as the first word is commonly the most important
|
|
603
|
+
var tokenizeName = function tokenizeName(name, query) {
|
|
604
|
+
|
|
605
|
+
if (!name) throw new Error('name is required');
|
|
606
|
+
if (!query) throw new Error('query is required');
|
|
607
|
+
|
|
608
|
+
var parts = [];
|
|
609
|
+
|
|
610
|
+
try { parts = name.toString().split(' '); }
|
|
611
|
+
catch (err) { throw err; }
|
|
612
|
+
|
|
613
|
+
// fuzzy search first word if more than one word in phrase
|
|
614
|
+
if (parts.length >= 2) {
|
|
615
|
+
query['$or'].push({ 'name': new RegExp(parts[0], 'i') });
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
var buildQuery = function buildQuery(data, options) {
|
|
621
|
+
|
|
622
|
+
// prepare query
|
|
623
|
+
var query = {};
|
|
624
|
+
query['$or'] = []; // these will be added dynamically from data parameter
|
|
625
|
+
query['$and'] = []; // these will be added dynamically from data parameter
|
|
626
|
+
query['deleted'] = { $ne: true }; // always include this
|
|
627
|
+
|
|
628
|
+
// conditionally include name
|
|
629
|
+
if (data.name) {
|
|
630
|
+
|
|
631
|
+
// an exact match is always a good thing
|
|
632
|
+
query['$or'].push({ 'name': new RegExp('^' + data.name + '$', 'i') });
|
|
633
|
+
|
|
634
|
+
if (options.fuzzy) {
|
|
635
|
+
// fuzzy search full phrase
|
|
636
|
+
query['$or'].push({'name': new RegExp(data.name, 'i')});
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
if (options.queryFirstWord) {
|
|
640
|
+
// fuzzy search first word
|
|
641
|
+
tokenizeName(data.name, query);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
var aliasRegExp = options.fuzzy ? new RegExp(data.name, 'i') : new RegExp('^' + data.name + '$', 'i');
|
|
645
|
+
query['$or'].push({ 'aliases': aliasRegExp });
|
|
646
|
+
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// conditionally include domain
|
|
650
|
+
if (data.domain) {
|
|
651
|
+
var domainRegExp = options.fuzzy ? new RegExp(data.domain, 'i') : new RegExp('^' + data.domain + '$', 'i');
|
|
652
|
+
query['$or'].push({ 'website': domainRegExp });
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// remove if unused
|
|
656
|
+
if (query['$or'].length == 0) delete query['$or'];
|
|
657
|
+
if (query['$and'].length == 0) delete query['$and'];
|
|
658
|
+
|
|
659
|
+
return query;
|
|
660
|
+
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
var query = buildQuery(data, options);
|
|
664
|
+
|
|
665
|
+
this
|
|
666
|
+
.find(query)
|
|
667
|
+
.populate('merged')
|
|
668
|
+
.select('logoUrl name description related')
|
|
669
|
+
.exec(function(err, orgs) {
|
|
670
|
+
|
|
671
|
+
if (err) return cb(err, null);
|
|
672
|
+
else if (!orgs) return cb(null, null);
|
|
673
|
+
else {
|
|
674
|
+
|
|
675
|
+
// remove related from top level
|
|
676
|
+
if (options.removeRelatedFromTopLevel) orgs = removeRelatedFromTopLevel(orgs);
|
|
677
|
+
|
|
678
|
+
// sort by name
|
|
679
|
+
orgs = _.sortBy(orgs, function(orgs) { return orgs.name; });
|
|
680
|
+
|
|
681
|
+
return cb(null, orgs);
|
|
682
|
+
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
Organization.statics.findDupes = function findDupes(org, options, cb) {
|
|
690
|
+
|
|
691
|
+
if (!cb) throw new Error('cb is required');
|
|
692
|
+
if (!org || (!org.name && !org.website)) return cb(null, []);
|
|
693
|
+
|
|
694
|
+
this.search({
|
|
695
|
+
name: org.name,
|
|
696
|
+
domain: utils.getDomain(org.website)
|
|
697
|
+
}, options, function(err, orgs) {
|
|
698
|
+
|
|
699
|
+
if (err) return cb(err, null);
|
|
700
|
+
if (!orgs) return cb(null, null);
|
|
701
|
+
|
|
702
|
+
// exclude passed in org from results
|
|
703
|
+
orgs = _.reject(orgs, function(o) {
|
|
704
|
+
return o.id == org.id;
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
return cb(null, orgs);
|
|
708
|
+
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
Organization.statics.stats = function stats(cb) {
|
|
714
|
+
this.collection.stats(cb);
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
Organization.statics.findByPerson = function findByPerson(personId, cb) {
|
|
718
|
+
this.find({ 'people.person': personId, 'deleted': {$ne: true} }).exec(cb);
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
Organization.statics.findBySlug = function findBySlug(slug, cb) {
|
|
722
|
+
this.find({ slug: slug, 'deleted': {$ne: true} }).exec(cb);
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
Organization.statics.findBySlugs = function findBySlugs(slugs, cb) {
|
|
726
|
+
this.find({ 'slug': { $in : slugs }, 'deleted': {$ne: true} }).exec(cb);
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
Organization.statics.findByDomains = function findByDomains(domains, cb) {
|
|
730
|
+
var query = this.find({ $or: [ {'website': { $in : domains }}, {'websiteAliases': { $in : domains }} ], 'deleted': {$ne: true} });
|
|
731
|
+
query.exec(cb);
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
Organization.statics.findByIds = function findByIds(ids, cb) {
|
|
735
|
+
|
|
736
|
+
var self = this;
|
|
737
|
+
|
|
738
|
+
self
|
|
739
|
+
.find({ '_id': { $in : ids }})
|
|
740
|
+
.populate('people.person', 'name avatarUrl title calendarEventSummaries sources')
|
|
741
|
+
.populate('related', 'name logoUrl')
|
|
742
|
+
.populate('notes')
|
|
743
|
+
.populate('chairs.first', 'name avatarUrl title')
|
|
744
|
+
.populate('chairs.second', 'name avatarUrl title')
|
|
745
|
+
.populate('funds', 'name hexColorCode')
|
|
746
|
+
.populate('rounds.funds.fund', 'name hexColorCode')
|
|
747
|
+
.populate('rounds.funds.partner', 'name avatarUrl title')
|
|
748
|
+
.populate('rounds.people.person', 'name avatarUrl title')
|
|
749
|
+
.deepPopulate([
|
|
750
|
+
'people.person.sources.person',
|
|
751
|
+
'people.person.calendarEventSummaries.attendees.internal',
|
|
752
|
+
'people.person.calendarEventSummaries.attendees.external',
|
|
753
|
+
'people.person.calendarEventSummaries.notes.createdBy',
|
|
754
|
+
'notes.createdBy'
|
|
755
|
+
], {
|
|
756
|
+
populate: {
|
|
757
|
+
'people.person.sources.person': { select: 'name avatarUrl title' },
|
|
758
|
+
'people.person.calendarEventSummaries.attendees.internal': { select: 'name avatarUrl title' },
|
|
759
|
+
'people.person.calendarEventSummaries.attendees.external': { select: 'name avatarUrl title' },
|
|
760
|
+
'people.person.calendarEventSummaries.notes.createdBy': { select: 'name avatarUrl title' },
|
|
761
|
+
'notes.createdBy': { select: 'name avatarUrl title' }
|
|
762
|
+
}
|
|
763
|
+
})
|
|
764
|
+
.exec(cb);
|
|
765
|
+
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
Organization.statics.getById = function getById(id, cb) {
|
|
769
|
+
|
|
770
|
+
var self = this;
|
|
771
|
+
|
|
772
|
+
self
|
|
773
|
+
.findById(id)
|
|
774
|
+
.populate('people.person', 'name avatarUrl title calendarEventSummaries sources')
|
|
775
|
+
.populate('related', 'name logoUrl')
|
|
776
|
+
.populate('notes')
|
|
777
|
+
.populate('chairs.first', 'name avatarUrl title')
|
|
778
|
+
.populate('chairs.second', 'name avatarUrl title')
|
|
779
|
+
.populate('funds', 'name hexColorCode')
|
|
780
|
+
.populate('rounds.funds.fund', 'name hexColorCode')
|
|
781
|
+
.populate('rounds.funds.partner', 'name avatarUrl title')
|
|
782
|
+
.populate('rounds.people.person', 'name avatarUrl title')
|
|
783
|
+
.deepPopulate([
|
|
784
|
+
'people.person.sources.person',
|
|
785
|
+
'people.person.calendarEventSummaries.attendees.internal',
|
|
786
|
+
'people.person.calendarEventSummaries.attendees.external',
|
|
787
|
+
'people.person.calendarEventSummaries.notes.createdBy',
|
|
788
|
+
'notes.createdBy'
|
|
789
|
+
], {
|
|
790
|
+
populate: {
|
|
791
|
+
'people.person.sources.person': { select: 'name avatarUrl title' },
|
|
792
|
+
'people.person.calendarEventSummaries.attendees.internal': { select: 'name avatarUrl title' },
|
|
793
|
+
'people.person.calendarEventSummaries.attendees.external': { select: 'name avatarUrl title' },
|
|
794
|
+
'people.person.calendarEventSummaries.notes.createdBy': { select: 'name avatarUrl title' },
|
|
795
|
+
'notes.createdBy': { select: 'name avatarUrl title' }
|
|
796
|
+
}
|
|
797
|
+
})
|
|
798
|
+
.exec(cb);
|
|
799
|
+
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
Organization.statics.getPortfolio = function getPortfolio(orgId, cb) {
|
|
803
|
+
|
|
804
|
+
var self = this;
|
|
805
|
+
|
|
806
|
+
self
|
|
807
|
+
.find({ 'rounds.funds.customer': orgId })
|
|
808
|
+
.select('name logoUrl description exit filters contact.addresses')
|
|
809
|
+
.sort('name')
|
|
810
|
+
.exec(cb);
|
|
811
|
+
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
Organization.statics.listPage = function (skip, cb) {
|
|
815
|
+
|
|
816
|
+
this
|
|
817
|
+
.find({'deleted': {$ne: true}})
|
|
818
|
+
.sort('name')
|
|
819
|
+
.skip(skip)
|
|
820
|
+
.limit(100)
|
|
821
|
+
.exec(cb)
|
|
822
|
+
|
|
823
|
+
};
|
|
824
|
+
|
|
825
|
+
Organization.statics.listAllFlagged = function (cb) {
|
|
826
|
+
|
|
827
|
+
var query = this.find({'flagged.resolved': false, 'flagged.text': { $ne: '' }, 'deleted': {$ne: true}});
|
|
828
|
+
query.exec(function (err, orgs) {
|
|
829
|
+
|
|
830
|
+
if (err) return cb(err, null);
|
|
831
|
+
|
|
832
|
+
orgs = _.sortBy(orgs, function(org){
|
|
833
|
+
var timestamp = new Date(org.flagged.on);
|
|
834
|
+
timestamp = timestamp.getTime();
|
|
835
|
+
return timestamp;
|
|
836
|
+
});
|
|
837
|
+
orgs = orgs.reverse();
|
|
838
|
+
|
|
839
|
+
return cb(null, orgs);
|
|
840
|
+
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
Organization.statics.upsert = function(org, username, cb) {
|
|
846
|
+
|
|
847
|
+
if (!org) { return cb(new Error('Organization is required'), null); }
|
|
848
|
+
if (!username) { return cb(new Error('Username is required'), null); }
|
|
849
|
+
|
|
850
|
+
org.entered.by = username;
|
|
851
|
+
org.entered.on = new Date();
|
|
852
|
+
|
|
853
|
+
org.save(cb);
|
|
854
|
+
|
|
855
|
+
};
|
|
856
|
+
|
|
857
|
+
Organization.statics.removeById = function(id, cb) {
|
|
858
|
+
|
|
859
|
+
if (!id) { return cb(new Error('id is required'), null); }
|
|
860
|
+
if (!cb) { throw new Error('cb is required'); }
|
|
861
|
+
|
|
862
|
+
this.remove({_id: id}, cb);
|
|
863
|
+
|
|
864
|
+
};
|
|
865
|
+
|
|
866
|
+
Organization.statics.flag = function(orgId, resolved, text, username, cb) {
|
|
867
|
+
|
|
868
|
+
this.findById(orgId).exec(function(err, org) {
|
|
869
|
+
|
|
870
|
+
org.flagged.resolved = resolved;
|
|
871
|
+
org.flagged.text = text;
|
|
872
|
+
org.flagged.by = username;
|
|
873
|
+
org.flagged.on = Date.now();
|
|
874
|
+
|
|
875
|
+
org.save(cb);
|
|
876
|
+
|
|
877
|
+
});
|
|
878
|
+
|
|
879
|
+
};
|
|
880
|
+
|
|
881
|
+
Organization.statics.addNote = function(organizationId, creatorPersonId, text, cb) {
|
|
882
|
+
|
|
883
|
+
Note.createForModel({
|
|
884
|
+
createdBy: creatorPersonId,
|
|
885
|
+
text: text
|
|
886
|
+
}, this, organizationId, cb);
|
|
887
|
+
|
|
888
|
+
};
|
|
889
|
+
|
|
890
|
+
Organization.statics.deleteNote = function(noteId, cb) {
|
|
891
|
+
|
|
892
|
+
// Delete the note itself along with any references
|
|
893
|
+
|
|
894
|
+
var self = this;
|
|
895
|
+
|
|
896
|
+
var removeReferences = function removeReferences(callback) {
|
|
897
|
+
self.update({}, {
|
|
898
|
+
$pull: { 'notes' : [noteId] }
|
|
899
|
+
}, callback);
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
async.series([
|
|
903
|
+
Note.delete.bind(Note, noteId),
|
|
904
|
+
removeReferences
|
|
905
|
+
], function(err, results) {
|
|
906
|
+
return cb(err, null);
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
};
|
|
910
|
+
|
|
911
|
+
///////////////////////////////
|
|
912
|
+
|
|
913
|
+
Organization.pre('save', function(next) {
|
|
914
|
+
|
|
915
|
+
var self = this;
|
|
916
|
+
|
|
917
|
+
// Format website
|
|
918
|
+
if (self.website) self.website = utils.getDomain(self.website);
|
|
919
|
+
|
|
920
|
+
// Format website aliases
|
|
921
|
+
if (self.websiteAliases && self.websiteAliases.length >= 1) {
|
|
922
|
+
self.websiteAliases = _.map(self.websiteAliases, function(alias) {
|
|
923
|
+
return utils.getDomain(alias);
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
return next();
|
|
928
|
+
|
|
929
|
+
});
|
|
930
|
+
|
|
931
|
+
Organization.post('init', function() {
|
|
932
|
+
|
|
933
|
+
// Protect private data and merge public and private data
|
|
934
|
+
|
|
935
|
+
var self = this;
|
|
936
|
+
var CUSTOMER_ID = config.CUSTOMER_ID;
|
|
937
|
+
|
|
938
|
+
if (CUSTOMER_ID == 'GLOBAL_PROCESS') return;
|
|
939
|
+
|
|
940
|
+
var protectCustomerData = function protectCustomerData() {
|
|
941
|
+
|
|
942
|
+
// remove customer details if not viewing self
|
|
943
|
+
if (self._id.toString() != CUSTOMER_ID) self.customer = {};
|
|
944
|
+
|
|
945
|
+
self.filters = _.reject(self.filters, function(item) { return item.customer != CUSTOMER_ID; });
|
|
946
|
+
self.docs = _.reject(self.docs, function(item) { return item.customer != CUSTOMER_ID; });
|
|
947
|
+
self.iLevel = _.reject(self.iLevel, function(item) { return item.customer != CUSTOMER_ID; });
|
|
948
|
+
self.chairs = _.reject(self.chairs, function(item) { return item.customer != CUSTOMER_ID; });
|
|
949
|
+
self.capTable = _.reject(self.capTable, function(item) { return item.customer != CUSTOMER_ID; });
|
|
950
|
+
|
|
951
|
+
// investment data
|
|
952
|
+
_.each(self.rounds, function(round) {
|
|
953
|
+
_.each(round.funds, function(fund) {
|
|
954
|
+
if (fund.customer != CUSTOMER_ID) fund.investments = [];
|
|
955
|
+
});
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
var mergePublicAndPrivateData = function mergePublicAndPrivateData() {
|
|
961
|
+
|
|
962
|
+
// rounds are public
|
|
963
|
+
_.each(self.rounds, function(round) {
|
|
964
|
+
|
|
965
|
+
// note that public versions of the following data exist at this level:
|
|
966
|
+
// preMoneyValuation
|
|
967
|
+
// amountRaised
|
|
968
|
+
// closingDate
|
|
969
|
+
// this data will be overwritten if private data belonging to the current customer is present
|
|
970
|
+
|
|
971
|
+
// participating funds are public
|
|
972
|
+
_.each(round.funds, function(fund) {
|
|
973
|
+
|
|
974
|
+
if (fund.customer != CUSTOMER_ID) return;
|
|
975
|
+
if (!fund.investments || fund.investments.length == 0) return;
|
|
976
|
+
|
|
977
|
+
// there can be multiple investments in a single round from the same fund
|
|
978
|
+
// use the most recent
|
|
979
|
+
var mostRecentInvestment = _.max(fund.investments, function(investment) { return investment.date; });
|
|
980
|
+
|
|
981
|
+
// this fund belongs to the current customer
|
|
982
|
+
// if we have private data, use it rather than the public data
|
|
983
|
+
if (mostRecentInvestment) {
|
|
984
|
+
if (mostRecentInvestment.preMoneyValuation) round.preMoneyValuation = mostRecentInvestment.preMoneyValuation;
|
|
985
|
+
if (mostRecentInvestment.amountRaised) round.amountRaised = mostRecentInvestment.amountRaised;
|
|
986
|
+
if (mostRecentInvestment.closingDate) round.closingDate = mostRecentInvestment.closingDate;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
};
|
|
994
|
+
|
|
995
|
+
protectCustomerData();
|
|
996
|
+
mergePublicAndPrivateData();
|
|
997
|
+
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
Organization.set('toJSON', { virtuals: true });
|
|
1001
|
+
Organization.set('autoIndex', true);
|
|
1002
|
+
|
|
1003
|
+
Organization.index({ website: 1, websiteAliases: 1 }, { unique: true, sparse: true });
|
|
1004
|
+
Organization.on('index', function(err) { console.log('error building organization_new indexes: ' + err); });
|
|
1005
|
+
|
|
1006
|
+
var deepPopulate = require('mongoose-deep-populate')(mongoose);
|
|
1007
|
+
Organization.plugin(deepPopulate);
|
|
1008
|
+
|
|
1009
|
+
mongoose.model('Organization', Organization, 'Organization_new');
|
|
1010
|
+
|
|
1011
|
+
};
|