@dhyasama/totem-models 4.8.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +3 -0
- package/lib/Deal.js +243 -0
- package/lib/Document.js +1 -3
- package/lib/Organization.js +146 -38
- package/package.json +1 -1
- package/test/Deal.js +222 -0
- package/test/Document.js +71 -3
- package/test/Organization.js +48 -85
package/index.js
CHANGED
|
@@ -19,6 +19,9 @@ var bootstrap = function(mongoose, config) {
|
|
|
19
19
|
|
|
20
20
|
// todo - find a solution to load without worrying about order
|
|
21
21
|
|
|
22
|
+
// deal must be loaded before organization
|
|
23
|
+
require('./lib/Deal.js')(mongoose, config);
|
|
24
|
+
|
|
22
25
|
// flag must be loaded before fund
|
|
23
26
|
// flag must be loaded before limited partner
|
|
24
27
|
// flag must be loaded before organization
|
package/lib/Deal.js
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
module.exports = function(mongoose, config) {
|
|
4
|
+
|
|
5
|
+
var
|
|
6
|
+
|
|
7
|
+
Schema = mongoose.Schema,
|
|
8
|
+
postFind = require('mongoose-post-find'),
|
|
9
|
+
_ = require('underscore');
|
|
10
|
+
|
|
11
|
+
var Deal = new Schema({
|
|
12
|
+
|
|
13
|
+
customer: { type: Schema.ObjectId, ref: 'Organization', required: true },
|
|
14
|
+
|
|
15
|
+
organization: { type: Schema.ObjectId, ref: 'Organization', required: true },
|
|
16
|
+
|
|
17
|
+
stage: { type: String, trim: true, required: true },
|
|
18
|
+
|
|
19
|
+
sources: [{
|
|
20
|
+
person: { type: Schema.ObjectId, ref: 'Person', required: true },
|
|
21
|
+
type: { type: String, enum: ['Inbound', 'Outbound', 'Event', 'Referral', 'Other'], required: true }
|
|
22
|
+
}],
|
|
23
|
+
|
|
24
|
+
referrers: [{
|
|
25
|
+
person: { type: Schema.ObjectId, ref: 'Person', required: true },
|
|
26
|
+
type: { type: String, enum: ['Founder', 'Investor', 'Service provider', 'Friends and family', 'Limited partner', 'Other'], required: true }
|
|
27
|
+
}],
|
|
28
|
+
|
|
29
|
+
documents: [ { type: Schema.ObjectId, ref: 'Document' } ],
|
|
30
|
+
|
|
31
|
+
messages: [ { type: Schema.ObjectId, ref: 'Message' } ],
|
|
32
|
+
|
|
33
|
+
fundraise: {
|
|
34
|
+
amount: { type: Number, default: 0 },
|
|
35
|
+
valuation: { type: Number, default: 0 },
|
|
36
|
+
details: [{
|
|
37
|
+
key: { type: String, trim: true, required: true },
|
|
38
|
+
value: { type: String, trim: true, required: true },
|
|
39
|
+
}]
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
resources: [{
|
|
43
|
+
title: { type: String, trim: true, required: true },
|
|
44
|
+
link: { type: String, trim: true, required: true },
|
|
45
|
+
}],
|
|
46
|
+
|
|
47
|
+
history: [{
|
|
48
|
+
timestamp: { type: String, required: true },
|
|
49
|
+
account: { type: Schema.ObjectId, ref: 'Account', required: true },
|
|
50
|
+
stage: { type: String, required: true },
|
|
51
|
+
description: { type: String, trim: true, required: true }
|
|
52
|
+
}]
|
|
53
|
+
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
///////////////////////////////////////////////////////////////////////////////////////
|
|
59
|
+
// VIRTUALS
|
|
60
|
+
// Properties that are not persisted to the database
|
|
61
|
+
///////////////////////////////////////////////////////////////////////////////////////
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
///////////////////////////////////////////////////////////////////////////////////////
|
|
66
|
+
// METHODS
|
|
67
|
+
// Methods operate on a single document that has already been returned
|
|
68
|
+
// Note that running a method on a document does not save it
|
|
69
|
+
///////////////////////////////////////////////////////////////////////////////////////
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
//////////////////////////////////////////////////////
|
|
74
|
+
// STATICS
|
|
75
|
+
// Statics operate on the entire collection
|
|
76
|
+
//////////////////////////////////////////////////////
|
|
77
|
+
|
|
78
|
+
Deal.statics.delete = function(id, cb) {
|
|
79
|
+
|
|
80
|
+
var self = this;
|
|
81
|
+
|
|
82
|
+
// Not strictly necessary but provides verification that the document being deleted belongs to the customer doing the deleting
|
|
83
|
+
self.findOne({
|
|
84
|
+
'_id': id,
|
|
85
|
+
'customer': config.CUSTOMER_ID
|
|
86
|
+
}, function(err, deal) {
|
|
87
|
+
|
|
88
|
+
if (err) return cb(err, null);
|
|
89
|
+
else if (!deal) return cb(null, null);
|
|
90
|
+
|
|
91
|
+
deal.remove(cb);
|
|
92
|
+
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
Deal.statics.getAll = function (cb) {
|
|
98
|
+
this.find({}).exec(cb);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
Deal.statics.getById = function (id, cb) {
|
|
102
|
+
|
|
103
|
+
var self = this;
|
|
104
|
+
|
|
105
|
+
self.findOne({
|
|
106
|
+
'_id': id,
|
|
107
|
+
'customer': config.CUSTOMER_ID
|
|
108
|
+
})
|
|
109
|
+
.populate('organization', 'name logoUrl website websiteAliases')
|
|
110
|
+
.populate('messages')
|
|
111
|
+
.populate('sources.person', 'name avatarUrl title')
|
|
112
|
+
.populate({
|
|
113
|
+
path: 'documents',
|
|
114
|
+
match: { customer: config.CUSTOMER_ID }
|
|
115
|
+
})
|
|
116
|
+
.populate('history.account', 'person')
|
|
117
|
+
.exec(cb);
|
|
118
|
+
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Get all deals belonging to a customer
|
|
122
|
+
Deal.statics.getForCustomer = function (customerId, cb) {
|
|
123
|
+
|
|
124
|
+
var self = this;
|
|
125
|
+
|
|
126
|
+
console.log('Deal.statics.getForCustomer');
|
|
127
|
+
console.log('customerId', customerId);
|
|
128
|
+
|
|
129
|
+
self
|
|
130
|
+
.find({ 'customer': customerId })
|
|
131
|
+
// .populate('organization', 'name logoUrl website websiteAliases')
|
|
132
|
+
// .populate('messages')
|
|
133
|
+
// .populate('sources.person', 'name avatarUrl')
|
|
134
|
+
// .populate({
|
|
135
|
+
// path: 'documents',
|
|
136
|
+
// match: { customer: customerId }
|
|
137
|
+
// })
|
|
138
|
+
.exec(cb);
|
|
139
|
+
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Get a deal for a org, belonging to current customer
|
|
143
|
+
Deal.statics.getForOrg = function (orgId, cb) {
|
|
144
|
+
|
|
145
|
+
var self = this;
|
|
146
|
+
|
|
147
|
+
self
|
|
148
|
+
.findOne({
|
|
149
|
+
'organization': orgId,
|
|
150
|
+
'customer': config.CUSTOMER_ID
|
|
151
|
+
})
|
|
152
|
+
.populate('organization', 'name logoUrl website websiteAliases')
|
|
153
|
+
.populate('messages')
|
|
154
|
+
.populate('sources.person', 'name avatarUrl title')
|
|
155
|
+
.populate({
|
|
156
|
+
path: 'documents',
|
|
157
|
+
match: { customer: config.CUSTOMER_ID }
|
|
158
|
+
})
|
|
159
|
+
.populate('history.account', 'person')
|
|
160
|
+
.exec(cb);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
Deal.statics.upsert = function upsert(deal, user, cb) {
|
|
164
|
+
|
|
165
|
+
// Deal is dumb and doesn't validate customer stages or duplicate deals. That responsibility is with the org.
|
|
166
|
+
|
|
167
|
+
if (!deal) { return cb(new Error('deal is required'), null); }
|
|
168
|
+
|
|
169
|
+
var getLastStage = function() {
|
|
170
|
+
|
|
171
|
+
if (!deal.history || deal.history.length == 0) return null;
|
|
172
|
+
|
|
173
|
+
var history = _.sortBy(deal.history);
|
|
174
|
+
var last = history[history.length - 1];
|
|
175
|
+
|
|
176
|
+
return last.stage;
|
|
177
|
+
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
var description = '';
|
|
181
|
+
var lastStage = getLastStage();
|
|
182
|
+
|
|
183
|
+
if (lastStage == null) description = 'The deal was created';
|
|
184
|
+
else if (lastStage != deal.stage) description += 'The deal was updated from ' + lastStage + ' to ' + deal.stage;
|
|
185
|
+
else description = 'The deal was updated';
|
|
186
|
+
|
|
187
|
+
deal.history.push({
|
|
188
|
+
timestamp: new Date(),
|
|
189
|
+
account: user,
|
|
190
|
+
stage: deal.stage,
|
|
191
|
+
description: description
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
deal.save(cb);
|
|
195
|
+
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
Deal.plugin(postFind, {
|
|
199
|
+
|
|
200
|
+
find: function(results, done) {
|
|
201
|
+
|
|
202
|
+
var CUSTOMER_ID = config.CUSTOMER_ID;
|
|
203
|
+
|
|
204
|
+
if (CUSTOMER_ID == 'GLOBAL_PROCESS') return done(null, results);
|
|
205
|
+
|
|
206
|
+
// Reject any item that is for a different customer
|
|
207
|
+
results = _.reject(results, function(item) {
|
|
208
|
+
return item.customer.toString() != CUSTOMER_ID;
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
return done(null, results);
|
|
212
|
+
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
findOne: function(result, done) {
|
|
216
|
+
|
|
217
|
+
var CUSTOMER_ID = config.CUSTOMER_ID;
|
|
218
|
+
|
|
219
|
+
if (!result) return done(null, null);
|
|
220
|
+
else if (CUSTOMER_ID == 'GLOBAL_PROCESS') return done(null, result);
|
|
221
|
+
else if (result.customer.toString() == CUSTOMER_ID) return done(null, result);
|
|
222
|
+
else return done(null, null);
|
|
223
|
+
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
///////////////////////////////////////////////////////////////////////////////////////
|
|
231
|
+
// CONFIG
|
|
232
|
+
///////////////////////////////////////////////////////////////////////////////////////
|
|
233
|
+
|
|
234
|
+
Deal.set('toJSON', { virtuals: true });
|
|
235
|
+
Deal.set('autoIndex', false);
|
|
236
|
+
Deal.on('index', function(err) { console.log('error building deal indexes: ' + err); });
|
|
237
|
+
|
|
238
|
+
var deepPopulate = require('mongoose-deep-populate')(mongoose);
|
|
239
|
+
Deal.plugin(deepPopulate);
|
|
240
|
+
|
|
241
|
+
mongoose.model('Deal', Deal);
|
|
242
|
+
|
|
243
|
+
};
|
package/lib/Document.js
CHANGED
|
@@ -30,9 +30,7 @@ module.exports = function(mongoose, config) {
|
|
|
30
30
|
key: { type: String, required: true, trim: true }
|
|
31
31
|
},
|
|
32
32
|
|
|
33
|
-
type: { type: String,
|
|
34
|
-
|
|
35
|
-
subtype: { type: String, enum: [null, 'deck'] },
|
|
33
|
+
type: { type: String, enum: ['deck', 'other'], required: true },
|
|
36
34
|
|
|
37
35
|
});
|
|
38
36
|
|
package/lib/Organization.js
CHANGED
|
@@ -8,6 +8,7 @@ module.exports = function(mongoose, config) {
|
|
|
8
8
|
async = require('async'),
|
|
9
9
|
utils = require('@dhyasama/ffvc-utilities'),
|
|
10
10
|
Schema = mongoose.Schema,
|
|
11
|
+
Deal = mongoose.model('Deal'),
|
|
11
12
|
Flag = mongoose.model('Flag'),
|
|
12
13
|
Note = mongoose.model('Note'),
|
|
13
14
|
customerFunds = [],
|
|
@@ -33,16 +34,6 @@ module.exports = function(mongoose, config) {
|
|
|
33
34
|
linkedin: { type: String, trim: true, unique: true, partialFilterExpression : { type :"string" } }
|
|
34
35
|
},
|
|
35
36
|
|
|
36
|
-
deals: [{
|
|
37
|
-
customer: { type: Schema.ObjectId, ref: 'Organization' },
|
|
38
|
-
status: { type: String, trim: true },
|
|
39
|
-
history: [{
|
|
40
|
-
timestamp: { type: String },
|
|
41
|
-
account: { type: Schema.ObjectId, ref: 'Account' },
|
|
42
|
-
status: { type: String }
|
|
43
|
-
}]
|
|
44
|
-
}],
|
|
45
|
-
|
|
46
37
|
contact: {
|
|
47
38
|
|
|
48
39
|
phone: [{
|
|
@@ -162,6 +153,9 @@ module.exports = function(mongoose, config) {
|
|
|
162
153
|
// customer specific (customer id is stored on note)
|
|
163
154
|
notes: [ { type: Schema.ObjectId, ref: 'Note' } ],
|
|
164
155
|
|
|
156
|
+
// customer specific (customer id is stored on deal)
|
|
157
|
+
deals: [ { type: Schema.ObjectId, ref: 'Deal' } ],
|
|
158
|
+
|
|
165
159
|
// customer specific (customer id is stored on doc)
|
|
166
160
|
documents: [ { type: Schema.ObjectId, ref: 'Document' } ],
|
|
167
161
|
|
|
@@ -683,6 +677,111 @@ module.exports = function(mongoose, config) {
|
|
|
683
677
|
// Statics query the entire collection
|
|
684
678
|
//////////////////////////////////////////////////////
|
|
685
679
|
|
|
680
|
+
Organization.statics.addDeal = function(deal, user, cb) {
|
|
681
|
+
|
|
682
|
+
var self = this;
|
|
683
|
+
|
|
684
|
+
var checkForDuplicates = function checkForDuplicates(previous, callback) {
|
|
685
|
+
|
|
686
|
+
// get org and make sure not already a deal for current customer
|
|
687
|
+
|
|
688
|
+
self.getById(utils.getIdAsString(previous.deal, 'organization'), {}, function(err, org) {
|
|
689
|
+
|
|
690
|
+
if (err) return callback(err, null);
|
|
691
|
+
else if (!org) return callback(new Error('Org not found'), null);
|
|
692
|
+
else if (org.deleted) return callback(new Error('Org deleted'), null);
|
|
693
|
+
|
|
694
|
+
var match = _.find(org.deals, function(d) {
|
|
695
|
+
return utils.getIdAsString(d, 'customer') == deal.customer._id.toString();
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
if (match) return callback(new Error('This org already has a deal for this customer'), null);
|
|
699
|
+
|
|
700
|
+
previous.org = org;
|
|
701
|
+
|
|
702
|
+
return callback(null, previous);
|
|
703
|
+
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
var validateStage = function validateStage(previous, callback) {
|
|
709
|
+
|
|
710
|
+
// check allowable deal stages against the new deal
|
|
711
|
+
|
|
712
|
+
self.findById(deal.customer, function(err, org) {
|
|
713
|
+
|
|
714
|
+
if (err) return callback(err, null);
|
|
715
|
+
else if (!org) return callback(new Error('Customer not found'), null);
|
|
716
|
+
|
|
717
|
+
var statuses = org.customer.deals.activeStatuses;
|
|
718
|
+
|
|
719
|
+
var match = _.find(statuses, function(status) {
|
|
720
|
+
return status == previous.deal.stage;
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
if (!match) return callback(new Error('This stage is not valid for this customer'), null);
|
|
724
|
+
|
|
725
|
+
return callback(null, previous);
|
|
726
|
+
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
var createDeal = function createDeal(previous, callback) {
|
|
732
|
+
|
|
733
|
+
Deal.upsert(previous.deal, previous.user, function(err, result) {
|
|
734
|
+
|
|
735
|
+
if (err) return callback(err, null);
|
|
736
|
+
|
|
737
|
+
previous.deal = result;
|
|
738
|
+
|
|
739
|
+
return callback(null, previous);
|
|
740
|
+
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
var addDealToOrg = function addDealToOrg(previous, callback) {
|
|
746
|
+
|
|
747
|
+
previous.org.deals.push(previous.deal);
|
|
748
|
+
|
|
749
|
+
self.upsert(previous.org, previous.user.username, function(err, result) {
|
|
750
|
+
|
|
751
|
+
if (err) return callback(err, null);
|
|
752
|
+
|
|
753
|
+
previous.org = result;
|
|
754
|
+
|
|
755
|
+
return callback(null, previous);
|
|
756
|
+
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
var previous = {
|
|
762
|
+
deal: deal,
|
|
763
|
+
user: user,
|
|
764
|
+
org: null
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
async.waterfall([
|
|
768
|
+
checkForDuplicates.bind(null, previous),
|
|
769
|
+
validateStage,
|
|
770
|
+
createDeal,
|
|
771
|
+
addDealToOrg
|
|
772
|
+
], function(err, results) {
|
|
773
|
+
|
|
774
|
+
if (err) return cb(err, null);
|
|
775
|
+
|
|
776
|
+
return cb(null, {
|
|
777
|
+
org: results.org,
|
|
778
|
+
deal: results.deal
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
};
|
|
784
|
+
|
|
686
785
|
Organization.statics.addFlag = function(organizationId, creatorPersonId, text, cb) {
|
|
687
786
|
|
|
688
787
|
Flag.createForModel({
|
|
@@ -701,6 +800,27 @@ module.exports = function(mongoose, config) {
|
|
|
701
800
|
|
|
702
801
|
};
|
|
703
802
|
|
|
803
|
+
Organization.statics.deleteDeal = function(dealId, cb) {
|
|
804
|
+
|
|
805
|
+
// Delete the deal itself along with any references
|
|
806
|
+
|
|
807
|
+
var self = this;
|
|
808
|
+
|
|
809
|
+
var removeReferences = function removeReferences(callback) {
|
|
810
|
+
self.update({}, {
|
|
811
|
+
$pull: { 'deals' : [dealId] }
|
|
812
|
+
}, callback);
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
async.series([
|
|
816
|
+
Deal.delete.bind(Deal, dealId),
|
|
817
|
+
removeReferences
|
|
818
|
+
], function(err, results) {
|
|
819
|
+
return cb(err, null);
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
};
|
|
823
|
+
|
|
704
824
|
Organization.statics.deleteFlag = function(flagId, cb) {
|
|
705
825
|
|
|
706
826
|
// Delete the flag itself along with reference
|
|
@@ -981,6 +1101,7 @@ module.exports = function(mongoose, config) {
|
|
|
981
1101
|
if (config.CUSTOMER_ID != 'GLOBAL_PROCESS') lpPopulateOptions.match = { customer: config.CUSTOMER_ID };
|
|
982
1102
|
|
|
983
1103
|
var query = self.findById(id);
|
|
1104
|
+
|
|
984
1105
|
query.populate('people.person', 'name avatarUrl title contact doNotDisplay');
|
|
985
1106
|
query.populate('related', 'name logoUrl');
|
|
986
1107
|
query.populate('chairs.first', 'name avatarUrl title');
|
|
@@ -989,9 +1110,17 @@ module.exports = function(mongoose, config) {
|
|
|
989
1110
|
query.populate('operating.acquired.public.by', 'name logoUrl');
|
|
990
1111
|
query.populate('operating.acquired.private.by', 'name logoUrl');
|
|
991
1112
|
|
|
1113
|
+
query.populate({
|
|
1114
|
+
path: 'deals',
|
|
1115
|
+
match: { customer: config.CUSTOMER_ID }
|
|
1116
|
+
});
|
|
1117
|
+
|
|
992
1118
|
if (options.role != 'none') {
|
|
993
1119
|
query.populate(lpPopulateOptions);
|
|
994
1120
|
query.deepPopulate([
|
|
1121
|
+
'deals.sources.person',
|
|
1122
|
+
'documents',
|
|
1123
|
+
'history.account',
|
|
995
1124
|
'lps.fundsInvested.fund'
|
|
996
1125
|
], {
|
|
997
1126
|
populate: {
|
|
@@ -999,6 +1128,13 @@ module.exports = function(mongoose, config) {
|
|
|
999
1128
|
}
|
|
1000
1129
|
})
|
|
1001
1130
|
}
|
|
1131
|
+
else {
|
|
1132
|
+
query.deepPopulate([
|
|
1133
|
+
'deals.sources.person',
|
|
1134
|
+
'documents',
|
|
1135
|
+
'history.account'
|
|
1136
|
+
]);
|
|
1137
|
+
}
|
|
1002
1138
|
|
|
1003
1139
|
query.exec(cb);
|
|
1004
1140
|
|
|
@@ -1049,33 +1185,6 @@ module.exports = function(mongoose, config) {
|
|
|
1049
1185
|
|
|
1050
1186
|
};
|
|
1051
1187
|
|
|
1052
|
-
Organization.statics.getDeals = function getDeals(customerId, cb) {
|
|
1053
|
-
|
|
1054
|
-
var self = this;
|
|
1055
|
-
|
|
1056
|
-
self
|
|
1057
|
-
.find({ 'deals.customer': customerId, 'deleted': { $ne: true } })
|
|
1058
|
-
.select('name website websiteAliases deals')
|
|
1059
|
-
.exec(function(err, result) {
|
|
1060
|
-
|
|
1061
|
-
if (err) return cb(err, null);
|
|
1062
|
-
|
|
1063
|
-
result.deals = _.reject(result.deals, function(item) { return !item.customer || item.customer.toString() != customerId.toString(); });
|
|
1064
|
-
result = _.map(result, function(item) {
|
|
1065
|
-
return {
|
|
1066
|
-
name: item.name,
|
|
1067
|
-
website: item.website,
|
|
1068
|
-
websiteAliases: item.websiteAliases,
|
|
1069
|
-
deals: item.deals
|
|
1070
|
-
}
|
|
1071
|
-
});
|
|
1072
|
-
|
|
1073
|
-
return cb(null, result);
|
|
1074
|
-
|
|
1075
|
-
});
|
|
1076
|
-
|
|
1077
|
-
};
|
|
1078
|
-
|
|
1079
1188
|
Organization.statics.getFlags = function getFlags(orgid, cb) {
|
|
1080
1189
|
|
|
1081
1190
|
var self = this;
|
|
@@ -1632,7 +1741,6 @@ module.exports = function(mongoose, config) {
|
|
|
1632
1741
|
doc.chairs = _.reject(doc.chairs, function(item) { return !item.customer || item.customer.toString() != CUSTOMER_ID; });
|
|
1633
1742
|
doc.operating.acquired.private = _.find(doc.operating.acquired.private, function(item) { return !item.customer || item.customer.toString() == CUSTOMER_ID; });
|
|
1634
1743
|
doc.operating.closed.private = _.find(doc.operating.closed.private, function(item) { return !item.customer || item.customer.toString() == CUSTOMER_ID; });
|
|
1635
|
-
doc.deals = _.reject(doc.deals, function(item) { return !item.customer || item.customer.toString() != CUSTOMER_ID; });
|
|
1636
1744
|
|
|
1637
1745
|
// people
|
|
1638
1746
|
_.each(doc.people, function(person) {
|
package/package.json
CHANGED
package/test/Deal.js
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var
|
|
4
|
+
|
|
5
|
+
should = require("should"),
|
|
6
|
+
config = require('./config')['test'],
|
|
7
|
+
_ = require('underscore'),
|
|
8
|
+
mongoose = require('mongoose'),
|
|
9
|
+
clearDB = require('mocha-mongoose')(config.db.uri, {noClear: true}),
|
|
10
|
+
Account = mongoose.model('Account'),
|
|
11
|
+
Deal = mongoose.model('Deal'),
|
|
12
|
+
Organization = mongoose.model('Organization');
|
|
13
|
+
|
|
14
|
+
var org;
|
|
15
|
+
var customer;
|
|
16
|
+
var dealId;
|
|
17
|
+
var account;
|
|
18
|
+
|
|
19
|
+
describe('Deal', function() {
|
|
20
|
+
|
|
21
|
+
before(function(done) {
|
|
22
|
+
if (mongoose.connection.db) return done();
|
|
23
|
+
mongoose.connect(config.db.uri, done);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
before(function(done) {
|
|
27
|
+
clearDB(done);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
before(function(done) {
|
|
31
|
+
|
|
32
|
+
org = new Organization();
|
|
33
|
+
org.name = 'Big Org';
|
|
34
|
+
org.slug = 'big-org';
|
|
35
|
+
|
|
36
|
+
Organization.upsert(org, 'test', function(err, result) {
|
|
37
|
+
|
|
38
|
+
should.not.exist(err);
|
|
39
|
+
should.exist(result);
|
|
40
|
+
|
|
41
|
+
org = result;
|
|
42
|
+
|
|
43
|
+
return done();
|
|
44
|
+
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
before(function(done) {
|
|
50
|
+
|
|
51
|
+
customer = new Organization();
|
|
52
|
+
customer.name = 'HUGE VC';
|
|
53
|
+
customer.slug = 'huge-vc';
|
|
54
|
+
|
|
55
|
+
Organization.upsert(customer, 'test', function(err, result) {
|
|
56
|
+
|
|
57
|
+
should.not.exist(err);
|
|
58
|
+
should.exist(result);
|
|
59
|
+
|
|
60
|
+
customer = result;
|
|
61
|
+
|
|
62
|
+
config.CUSTOMER_ID = customer._id.toString();
|
|
63
|
+
|
|
64
|
+
return done();
|
|
65
|
+
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
before(function (done) {
|
|
71
|
+
|
|
72
|
+
account = new Account();
|
|
73
|
+
account.username = 'test-user';
|
|
74
|
+
account.email = 'test@totemvc.com';
|
|
75
|
+
account.person = new mongoose.Types.ObjectId();
|
|
76
|
+
|
|
77
|
+
Account.upsert(account, function (err, result) {
|
|
78
|
+
|
|
79
|
+
should.not.exist(err);
|
|
80
|
+
should.exist(result);
|
|
81
|
+
|
|
82
|
+
account = result;
|
|
83
|
+
|
|
84
|
+
return done();
|
|
85
|
+
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('creates a deal', function(done) {
|
|
91
|
+
|
|
92
|
+
var deal = new Deal({
|
|
93
|
+
customer: customer,
|
|
94
|
+
organization: org,
|
|
95
|
+
stage: 'New'
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
Deal.upsert(deal, account, function(err, result) {
|
|
99
|
+
|
|
100
|
+
should.not.exist(err);
|
|
101
|
+
should.exist(result);
|
|
102
|
+
|
|
103
|
+
dealId = result._id;
|
|
104
|
+
|
|
105
|
+
return done();
|
|
106
|
+
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('gets a deal by id', function(done) {
|
|
112
|
+
Deal.getById(dealId, function(err, result) {
|
|
113
|
+
should.not.exist(err);
|
|
114
|
+
should.exist(result);
|
|
115
|
+
result._id.toString().should.equal(dealId.toString());
|
|
116
|
+
return done();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('adds a document to a deal', function(done) {
|
|
121
|
+
|
|
122
|
+
Deal.getById(dealId, function(err, deal) {
|
|
123
|
+
|
|
124
|
+
should.not.exist(err);
|
|
125
|
+
should.exist(deal);
|
|
126
|
+
|
|
127
|
+
deal.documents.push(new mongoose.Types.ObjectId());
|
|
128
|
+
|
|
129
|
+
Deal.upsert(deal, account, function(err, result) {
|
|
130
|
+
|
|
131
|
+
should.not.exist(err);
|
|
132
|
+
should.exist(result);
|
|
133
|
+
return done();
|
|
134
|
+
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('changes the deal stage', function(done) {
|
|
142
|
+
|
|
143
|
+
Deal.getById(dealId, function(err, deal) {
|
|
144
|
+
|
|
145
|
+
should.not.exist(err);
|
|
146
|
+
should.exist(deal);
|
|
147
|
+
|
|
148
|
+
deal.stage = 'Pass';
|
|
149
|
+
|
|
150
|
+
Deal.upsert(deal, account, function(err, result) {
|
|
151
|
+
|
|
152
|
+
should.not.exist(err);
|
|
153
|
+
should.exist(result);
|
|
154
|
+
return done();
|
|
155
|
+
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('gets all deals for a customer', function(done) {
|
|
163
|
+
|
|
164
|
+
var d = new Deal({
|
|
165
|
+
customer: customer,
|
|
166
|
+
organization: new mongoose.Types.ObjectId(),
|
|
167
|
+
stage: 'New'
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
Deal.upsert(d, account, function(err, result) {
|
|
171
|
+
|
|
172
|
+
should.not.exist(err);
|
|
173
|
+
should.exist(result);
|
|
174
|
+
|
|
175
|
+
var d = new Deal({
|
|
176
|
+
customer: new mongoose.Types.ObjectId(),
|
|
177
|
+
organization: new mongoose.Types.ObjectId(),
|
|
178
|
+
stage: 'New'
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
Deal.upsert(d, account, function(err, result) {
|
|
182
|
+
|
|
183
|
+
should.not.exist(err);
|
|
184
|
+
should.exist(result);
|
|
185
|
+
|
|
186
|
+
Deal.getForCustomer(config.CUSTOMER_ID, function(err, result) {
|
|
187
|
+
should.not.exist(err);
|
|
188
|
+
should.exist(result)
|
|
189
|
+
result.length.should.equal(2);
|
|
190
|
+
return done();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('gets all deals', function(done) {
|
|
200
|
+
|
|
201
|
+
var temp = config.CUSTOMER_ID;
|
|
202
|
+
config.CUSTOMER_ID = 'GLOBAL_PROCESS';
|
|
203
|
+
|
|
204
|
+
Deal.getAll(function(err, result) {
|
|
205
|
+
should.not.exist(err);
|
|
206
|
+
should.exist(result)
|
|
207
|
+
result.length.should.equal(3);
|
|
208
|
+
config.CUSTOMER_ID = temp;
|
|
209
|
+
return done();
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('deletes a deal', function(done) {
|
|
215
|
+
Deal.delete(dealId, function(err, result) {
|
|
216
|
+
should.not.exist(err);
|
|
217
|
+
should.exist(result);
|
|
218
|
+
return done();
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
});
|
package/test/Document.js
CHANGED
|
@@ -7,13 +7,15 @@ var
|
|
|
7
7
|
_ = require('underscore'),
|
|
8
8
|
mongoose = require('mongoose'),
|
|
9
9
|
clearDB = require('mocha-mongoose')(config.db.uri, {noClear: true}),
|
|
10
|
+
Account = mongoose.model('Account'),
|
|
11
|
+
Deal = mongoose.model('Deal'),
|
|
10
12
|
Document = mongoose.model('Document'),
|
|
11
13
|
Interaction = mongoose.model('Interaction'),
|
|
12
14
|
Message = mongoose.model('Message'),
|
|
13
15
|
Organization = mongoose.model('Organization'),
|
|
14
16
|
Person = mongoose.model('Person');
|
|
15
17
|
|
|
16
|
-
var interaction, message, org, person, documentCreator, documentId;
|
|
18
|
+
var deal, account, interaction, message, org, person, documentCreator, documentId;
|
|
17
19
|
|
|
18
20
|
describe('Document', function() {
|
|
19
21
|
|
|
@@ -96,6 +98,45 @@ describe('Document', function() {
|
|
|
96
98
|
|
|
97
99
|
});
|
|
98
100
|
|
|
101
|
+
before(function (done) {
|
|
102
|
+
|
|
103
|
+
account = new Account();
|
|
104
|
+
account.username = 'test-user';
|
|
105
|
+
account.email = 'test@totemvc.com';
|
|
106
|
+
account.person = new mongoose.Types.ObjectId();
|
|
107
|
+
|
|
108
|
+
Account.upsert(account, function (err, result) {
|
|
109
|
+
|
|
110
|
+
should.not.exist(err);
|
|
111
|
+
should.exist(result);
|
|
112
|
+
|
|
113
|
+
account = result;
|
|
114
|
+
|
|
115
|
+
return done();
|
|
116
|
+
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
before(function(done) {
|
|
122
|
+
|
|
123
|
+
deal = new Deal();
|
|
124
|
+
deal.customer = new mongoose.Types.ObjectId();
|
|
125
|
+
deal.organization = new mongoose.Types.ObjectId();
|
|
126
|
+
deal.stage = 'New';
|
|
127
|
+
|
|
128
|
+
Deal.upsert(deal, account, function(err, result) {
|
|
129
|
+
|
|
130
|
+
should.not.exist(err);
|
|
131
|
+
should.exist(result);
|
|
132
|
+
deal = result;
|
|
133
|
+
|
|
134
|
+
return done();
|
|
135
|
+
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
});
|
|
139
|
+
|
|
99
140
|
before(function(done) {
|
|
100
141
|
|
|
101
142
|
person = new Person();
|
|
@@ -177,7 +218,7 @@ describe('Document', function() {
|
|
|
177
218
|
bucket: 'totem',
|
|
178
219
|
key: 'docs'
|
|
179
220
|
},
|
|
180
|
-
type: '
|
|
221
|
+
type: 'other'
|
|
181
222
|
}, Message, message._id, function(err, result) {
|
|
182
223
|
|
|
183
224
|
should.not.exist(err);
|
|
@@ -204,7 +245,7 @@ describe('Document', function() {
|
|
|
204
245
|
bucket: 'totem',
|
|
205
246
|
key: 'docs'
|
|
206
247
|
},
|
|
207
|
-
type: '
|
|
248
|
+
type: 'other'
|
|
208
249
|
}, Organization, org._id, function(err, result) {
|
|
209
250
|
|
|
210
251
|
should.not.exist(err);
|
|
@@ -248,6 +289,33 @@ describe('Document', function() {
|
|
|
248
289
|
|
|
249
290
|
});
|
|
250
291
|
|
|
292
|
+
it('creates a document for a deal', function(done) {
|
|
293
|
+
|
|
294
|
+
Document.createForModel({
|
|
295
|
+
customer: config.CUSTOMER_ID,
|
|
296
|
+
createdBy: documentCreator,
|
|
297
|
+
name: 'test.pdf',
|
|
298
|
+
contentType: 'application/pdf',
|
|
299
|
+
contentLength: 54321,
|
|
300
|
+
s3: {
|
|
301
|
+
bucket: 'totem',
|
|
302
|
+
key: 'docs'
|
|
303
|
+
},
|
|
304
|
+
type: 'deck'
|
|
305
|
+
}, Deal, deal._id, function(err, result) {
|
|
306
|
+
|
|
307
|
+
should.not.exist(err);
|
|
308
|
+
should.exist(result);
|
|
309
|
+
should.exist(result.document);
|
|
310
|
+
should.exist(result.addedTo);
|
|
311
|
+
result.addedTo.documents.length.should.equal(1);
|
|
312
|
+
|
|
313
|
+
return done();
|
|
314
|
+
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
});
|
|
318
|
+
|
|
251
319
|
it('gets a document by id', function(done) {
|
|
252
320
|
Document.getById(documentId, function(err, result) {
|
|
253
321
|
should.not.exist(err);
|
package/test/Organization.js
CHANGED
|
@@ -11,17 +11,17 @@ var
|
|
|
11
11
|
Person = mongoose.model('Person'),
|
|
12
12
|
LimitedPartner = mongoose.model('LimitedPartner'),
|
|
13
13
|
Fund = mongoose.model('Fund'),
|
|
14
|
-
|
|
14
|
+
Deal = mongoose.model('Deal'),
|
|
15
15
|
Account = mongoose.model('Account');
|
|
16
16
|
|
|
17
17
|
var person, person1, person2;
|
|
18
18
|
var noteId;
|
|
19
|
-
var eventNote;
|
|
20
19
|
var flagId;
|
|
21
20
|
var someOtherFund;
|
|
22
21
|
var someOtherLp;
|
|
23
22
|
var account;
|
|
24
23
|
var customer1, customer2;
|
|
24
|
+
var deal;
|
|
25
25
|
|
|
26
26
|
describe('Organization', function() {
|
|
27
27
|
|
|
@@ -1131,8 +1131,6 @@ describe('Organization', function() {
|
|
|
1131
1131
|
|
|
1132
1132
|
describe('Deals', function() {
|
|
1133
1133
|
|
|
1134
|
-
var org;
|
|
1135
|
-
|
|
1136
1134
|
it('gets deal statuses for a customer', function(done) {
|
|
1137
1135
|
|
|
1138
1136
|
var statuses = customer1.customer.deals.activeStatuses;
|
|
@@ -1148,29 +1146,23 @@ describe('Organization', function() {
|
|
|
1148
1146
|
|
|
1149
1147
|
it('adds a deal', function(done) {
|
|
1150
1148
|
|
|
1151
|
-
|
|
1152
|
-
org.name = 'New Co';
|
|
1153
|
-
org.slug = 'new-co';
|
|
1154
|
-
org.website = 'new.co';
|
|
1149
|
+
config.CUSTOMER_ID = customer1._id.toString();
|
|
1155
1150
|
|
|
1156
|
-
|
|
1151
|
+
deal = new Deal({
|
|
1152
|
+
customer: customer1,
|
|
1153
|
+
organization: org3,
|
|
1154
|
+
stage: 'New'
|
|
1155
|
+
});
|
|
1156
|
+
|
|
1157
|
+
Organization.addDeal(deal, account, function(err, result) {
|
|
1157
1158
|
|
|
1158
1159
|
should.not.exist(err);
|
|
1159
1160
|
should.exist(result);
|
|
1161
|
+
result.org.deals.length.should.equal(1);
|
|
1160
1162
|
|
|
1161
|
-
result.
|
|
1162
|
-
|
|
1163
|
-
Organization.upsert(result, 'test-user', function(err, result) {
|
|
1164
|
-
|
|
1165
|
-
should.not.exist(err);
|
|
1166
|
-
should.exist(result);
|
|
1167
|
-
result.deals.length.should.equal(1);
|
|
1168
|
-
|
|
1169
|
-
org = result;
|
|
1170
|
-
|
|
1171
|
-
return done();
|
|
1163
|
+
deal = result.deal;
|
|
1172
1164
|
|
|
1173
|
-
|
|
1165
|
+
return done();
|
|
1174
1166
|
|
|
1175
1167
|
});
|
|
1176
1168
|
|
|
@@ -1178,60 +1170,65 @@ describe('Organization', function() {
|
|
|
1178
1170
|
|
|
1179
1171
|
it('adds a deal again', function(done) {
|
|
1180
1172
|
|
|
1181
|
-
var
|
|
1173
|
+
var d = new Deal({
|
|
1174
|
+
customer: customer1,
|
|
1175
|
+
organization: org3,
|
|
1176
|
+
stage: 'New'
|
|
1177
|
+
});
|
|
1178
|
+
|
|
1179
|
+
Organization.addDeal(d, account, function(err, result) {
|
|
1182
1180
|
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
}
|
|
1186
|
-
catch (err) {
|
|
1187
|
-
threwError = true;
|
|
1188
|
-
}
|
|
1181
|
+
should.exist(err);
|
|
1182
|
+
should.not.exist(result);
|
|
1189
1183
|
|
|
1190
|
-
|
|
1191
|
-
org.deals.length.should.equal(1);
|
|
1192
|
-
org.deals[0].status.should.equal('Pass');
|
|
1184
|
+
return done();
|
|
1193
1185
|
|
|
1194
|
-
|
|
1186
|
+
});
|
|
1195
1187
|
|
|
1196
1188
|
});
|
|
1197
1189
|
|
|
1198
1190
|
it('adds a deal with an inactive state', function(done) {
|
|
1199
1191
|
|
|
1200
|
-
var
|
|
1192
|
+
var d = new Deal({
|
|
1193
|
+
customer: customer1,
|
|
1194
|
+
organization: org4,
|
|
1195
|
+
stage: 'Pending'
|
|
1196
|
+
});
|
|
1201
1197
|
|
|
1202
|
-
|
|
1203
|
-
org.addDeal('Pending', customer1, account);
|
|
1204
|
-
}
|
|
1205
|
-
catch (err) {
|
|
1206
|
-
threwError = true;
|
|
1207
|
-
}
|
|
1198
|
+
Organization.addDeal(d, account, function(err, result) {
|
|
1208
1199
|
|
|
1209
|
-
|
|
1200
|
+
should.exist(err);
|
|
1201
|
+
should.not.exist(result);
|
|
1210
1202
|
|
|
1211
|
-
|
|
1203
|
+
return done();
|
|
1204
|
+
|
|
1205
|
+
});
|
|
1212
1206
|
|
|
1213
1207
|
});
|
|
1214
1208
|
|
|
1215
1209
|
it('adds a deal for another customer', function(done) {
|
|
1216
1210
|
|
|
1211
|
+
config.CUSTOMER_ID = customer2._id.toString();
|
|
1212
|
+
|
|
1217
1213
|
var org2 = new Organization();
|
|
1218
1214
|
org2.name = 'Newest Co';
|
|
1219
1215
|
org2.slug = 'newest-co';
|
|
1220
1216
|
|
|
1221
|
-
Organization.upsert(org2, 'test-user', function(err,
|
|
1217
|
+
Organization.upsert(org2, 'test-user', function(err, result1) {
|
|
1222
1218
|
|
|
1223
1219
|
should.not.exist(err);
|
|
1224
|
-
should.exist(
|
|
1220
|
+
should.exist(result1);
|
|
1225
1221
|
|
|
1226
|
-
|
|
1222
|
+
var d = new Deal({
|
|
1223
|
+
customer: customer2,
|
|
1224
|
+
organization: result1,
|
|
1225
|
+
stage: 'Pass'
|
|
1226
|
+
});
|
|
1227
1227
|
|
|
1228
|
-
Organization.
|
|
1228
|
+
Organization.addDeal(d, account, function(err, result2) {
|
|
1229
1229
|
|
|
1230
1230
|
should.not.exist(err);
|
|
1231
|
-
should.exist(
|
|
1232
|
-
result.deals.length.should.equal(1);
|
|
1233
|
-
|
|
1234
|
-
org = result;
|
|
1231
|
+
should.exist(result2);
|
|
1235
1232
|
|
|
1236
1233
|
return done();
|
|
1237
1234
|
|
|
@@ -1241,45 +1238,11 @@ describe('Organization', function() {
|
|
|
1241
1238
|
|
|
1242
1239
|
});
|
|
1243
1240
|
|
|
1244
|
-
it('
|
|
1245
|
-
|
|
1246
|
-
var temp = config.CUSTOMER_ID;
|
|
1247
|
-
config.CUSTOMER_ID = 'GLOBAL_PROCESS';
|
|
1248
|
-
|
|
1249
|
-
Organization.getDeals(customer1._id, function(err, result) {
|
|
1250
|
-
|
|
1251
|
-
should.not.exist(err);
|
|
1252
|
-
should.exist(result);
|
|
1253
|
-
result.length.should.equal(1);
|
|
1254
|
-
result[0].name.should.equal('New Co');
|
|
1255
|
-
result[0].deals.length.should.equal(1);
|
|
1256
|
-
result[0].deals[0].customer.toString().should.equal(customer1._id.toString());
|
|
1257
|
-
result[0].deals[0].status.should.equal('New');
|
|
1258
|
-
|
|
1259
|
-
config.CUSTOMER_ID = temp;
|
|
1260
|
-
|
|
1261
|
-
return done();
|
|
1262
|
-
|
|
1263
|
-
});
|
|
1264
|
-
|
|
1265
|
-
});
|
|
1266
|
-
|
|
1267
|
-
it('gets deals for a customer', function(done) {
|
|
1268
|
-
|
|
1269
|
-
var temp = config.CUSTOMER_ID;
|
|
1270
|
-
config.CUSTOMER_ID = customer1._id.toString();
|
|
1241
|
+
it('deletes a deal', function (done) {
|
|
1271
1242
|
|
|
1272
|
-
Organization.
|
|
1243
|
+
Organization.deleteDeal(deal._id, function (err, result) {
|
|
1273
1244
|
|
|
1274
1245
|
should.not.exist(err);
|
|
1275
|
-
should.exist(result);
|
|
1276
|
-
result.length.should.equal(1);
|
|
1277
|
-
result[0].name.should.equal('New Co');
|
|
1278
|
-
result[0].deals.length.should.equal(1);
|
|
1279
|
-
result[0].deals[0].customer.toString().should.equal(customer1._id.toString());
|
|
1280
|
-
result[0].deals[0].status.should.equal('New');
|
|
1281
|
-
|
|
1282
|
-
config.CUSTOMER_ID = temp;
|
|
1283
1246
|
|
|
1284
1247
|
return done();
|
|
1285
1248
|
|