this.displayActivityForm = function (companyId, proposalId, type) {
//console.log(type);
$.get('/companies/proposal/delete/' + id, function (resp) {
if (resp.error) {
CAMS.alert('Error', resp.msg, 'error');
return false;
}
CAMS.alert('Success', 'Your proposal has been deleted successfully');
$this.parents('.card').remove();
}, 'json').fail(function () {
CAMS.alert('Unexpected Error', 'We encountered an unexpected error while trying to process your request.', 'error');
});
var model = objectModels[type],
fc = model.singular.substring(0, 1),
config = {
title: 'Add a' + ((fc == 'a' || fc == 'e' || fc == 'i' || fc == 'o' || fc == 'u') ? 'n' : '') + ' ' + ucwords(model.singular),
directionsText: 'Make your changes below and then click, "SAVE."',
fields: model.fields,
onShow: function (self) {
// add in the additional events and plugins
// add in datetime support
self.$el.find('input.field-datetime').datepicker({
autoClose: true,
language: 'en',
timepicker: true,
dateFormat: 'mm/dd/yyyy',
timeFormat: 'h:ii AA',
showEvent: 'focus',
minutesStep: 5
});
self.$el.find('#formit-notes').val("hello");
self.$el.find('#formit-date').val("04/04/2020");
self.$el.find('#formit-activityType').val("Call Note");
},
ajax: {
path: '/companies/edit-activity',
params: {
company: companyId,
proposal: proposalId,
type: type,
},
onComplete: function (resp, self) {
if (resp.error) {
self.addMsg(resp.msg);
return false;
}
companyId = (companyId == 'bulk' ? resp.data.id : companyId);
recordId = companyId;
$(window).trigger('open-company-details', [companyId, ((typeof route != 'undefined') ? route : '/companies/'), 'history']);
}
}
};
// show the form
formIt.show(config);
};
$.get('/companies/proposal/delete/' + id, function (resp) {
if (resp.error) {
CAMS.alert('Error', resp.msg, 'error');
return false;
}
CAMS.alert('Success', 'Your proposal has been deleted successfully');
$this.parents('.card').remove();
}, 'json').fail(function () {
CAMS.alert('Unexpected Error', 'We encountered an unexpected error while trying to process your request.', 'error');
});
/ get the details of a single company
router.get('/companies/:orgId', async (req, res) => {
let vars = Object.assign({
userSettings: global._user.account.settings
}, base);
try {
//let company = await Prospect.getOne(req.params.orgId);
//vars.data = Prospect.success(company);
let company = await Company.getOne(req.params.orgId);
if (company) {
let meta = company.meta;
if (meta) { meta.fundingStatus = (typeof meta.fundingType != 'undefined') ? meta.fundingType : meta.fundingStatus; }
}
// get the company proposals
company.proposals = typeof company.proposals == 'undefined' ? {} : company.proposals;
company.proposals = await Company.getProposals(company._id);
if (company.proposals) {
if (company.proposals.length > 0) {
company.proposals.forEach((row, rsi) => {
if (typeof row.services != 'undefined') {
row.services.forEach(async (item, ri) => {
if (item.service) {
company.proposals[rsi].services[ri].service = await Company.getService(item.service);
}
});
}
});
}
}
// get the company activity
company.activity = typeof company.activity == 'undefined' ? [] : company.activity;
company.activity = await Company.getActivity(company._id);
// make sure there is a default file key
company.files = typeof company.files == 'undefined' ? {} : company.files;
company.files = company.files || {};
// load the proposal files
company.proposalFiles = typeof company.proposalFiles == 'undefined' ? [] : company.proposalFiles;
company.proposalFiles = await Company.getFiles(company._id);
company.route = 'companies';
// check if the request just wants a JSON payload - Used for Proposal form
if (req.query.json) {
return res.send(Company.success(company));
}
vars.data = company;
} catch (err) {
vars.msg = `<div class="alert alert-danger">${err.message}</div>`;
}
res.render('companies-edit.html.twig', vars);
});
const { ObjectId } = require('mongodb');
const { Util } = require('node-utils');
const moment = require('moment-timezone');
const mongoose = require('mongoose');
const uuid = require('uuid/v4');
const Service = require('./service');
const CompanySchema = require('../schemas/CompanySchema');
const ActivitySchema = require('../schemas/ActivitySchema');
const EventSchema = require('../schemas/EventSchema');
const ProposalSchema = require('../schemas/ProposalSchema');
const SavedFilterSchema = require('../schemas/SavedFilterSchema');
const UserSchema = require('../schemas/UserSchema');
const CompanyContactSchema = require('../schemas/CompanyContactSchema');
const CompanyAddressSchema = require('../schemas/CompanyAddressSchema');
const CompanyAddress = require('../services/companyaddress');
const GeoLocations = require('../services/GeoLocations');
const objectModels = require('../lib/object-models');
const Vendors = require('../lib/Vendors');
const ServiceSchema = require('../schemas/ServiceSchema');
const ProductsAndService = mongoose.model('Service', ServiceSchema);
const Proposal = mongoose.model('Proposal', ProposalSchema);
const User = require('../services/user.js');
let userModel = mongoose.model('User', UserSchema);
class Company extends Service {
constructor() {
super();
this.collection = 'companies';
this.plural = 'companies';
this.singular = 'company';
this.model = mongoose.model('Company', CompanySchema);
this.fields = require('../lib/model-company');
this.fields._id = {};
this.fields._id.type = ObjectId;
this.corePipeline = [
{
$lookup: {
from: 'users',
foreignField: '_id',
localField: 'owner',
as: 'owner'
}
},
{$unwind: {path: '$owner',preserveNullAndEmptyArrays: true}},
//{ "$addFields": { "brokers": { "$slice": ["$brokers", 1] } } },
//{ "$unwind": { path: "$brokers", preserveNullAndEmptyArrays: true } },
//{ "$addFields": { "contacts": { "$slice": ["$contacts", 1] } } },
//{ "$unwind": { path: "$contacts", preserveNullAndEmptyArrays: true } },
//{ '$match': { 'addresses.postal': { '$in': global._user.account.postalCodes } } }
];
}
datatable(input, baseFilters = {}) {
//console.log(global._user.account.postalCodes);
return new Promise((resolve, reject) => {
let config = this._getDataTablesFilters(input);
let pipeline = [];
let countPipeline = []; // the filters used for counts
// add in the base filters
config.filters = Util.merge(baseFilters, config.filters);
// set the base pipeline where/match clause
pipeline.push({ $match: this._enforceScope() });
// add sorting
if (Object.keys(config.sort).length > 0) {
if (typeof config.sort[0] == 'undefined') {
pipeline.push({ $sort: config.sort });
}
}
// Owner
pipeline.push(
{$lookup: {from: 'users',let: { 'ownerId': '$owner' },pipeline: [{$match: {$expr: {$and: [{ $eq: ["$_id", "$$ownerId"] }]}}},{$project: {name: {$concat: ['$firstName', ' ', '$lastName']}}}],as: 'owner'}},
{ $unwind: { path: '$owner', preserveNullAndEmptyArrays: true } }
);
// Broker
pipeline.push(
{ "$addFields": { "brokers": { "$slice": ["$brokers", 1] } } },
{ "$unwind": { path: "$brokers", preserveNullAndEmptyArrays: true } }
);
pipeline.push(
{ "$addFields": { "contacts": { "$slice": ["$contacts", 1] } } },
{ "$unwind": { path: "$contacts", preserveNullAndEmptyArrays: true } }
);
//pipeline.push(
// { "$addFields": { "addresses": { "$slice": ["$addresses", 1] } } },
// { "$unwind": { path: "$addresses", preserveNullAndEmptyArrays: true } }
//);
if(global._user.account.postalCodes) {
pipeline.push({
$match: {
$or: [
{ 'addresses.postal': { $in: global._user.account.postalCodes } },
{ clientImport: global._user.account._id }
]
}
});
}
// Search text
if (typeof input.search != 'undefined') {
if (input.search.value) {
var searchQuery = this._getDataTablesSearch(input.search.value);
if (typeof searchQuery != 'undefined') {
pipeline.push({ $match: searchQuery });
}
}
}
// set the filters pipeline where/match clause
if (Object.keys(config.filters).length > 0) {
pipeline.push({ $match: config.filters });
}
// set the count pipeline here since all of the filters should be applied
// by this point
countPipeline = Array.from(pipeline);
// add the limits
pipeline.push({ $skip: config.limits[0] });
pipeline.push({ $limit: config.limits[1] });
// add in a datatable specific field
pipeline.push({
$addFields: {
DT_RowId: '$_id',
currentUserId: global._user._id //for 'Assign to me' link
}
});
// call the lifecycle method to allow extending classes to customize just
// the pipeline without having to overwrite the whole method
pipeline = this._beforeDatatablesPipeline(pipeline);
this.model.aggregate(pipeline).then(async (data) => {
resolve({
draw: parseInt(input.draw),
data: data,
recordsFiltered: await this.__aggCount(countPipeline),//filter wrong? await this._count(this._enforceScope()),
recordsTotal: await this._count(this._enforceScope())
});
}).catch((err) => {
//console.log("it's here we've got a problem");
console.log('Aggregate Error: ', err);
reject(err);
});
});
}
getOne(companyId) {
return new Promise((resolve, reject) => {
companyId = this._getId(companyId);
if (!companyId) {
return reject('Invalid company ID provided.');
}
// create a clone of the pipeline so we don't alter the original
let pipeline = this.corePipeline.slice(0);
// add an initial match stage to the core pipeline
pipeline.unshift({
$match: this._enforceScope({
_id: companyId
})
});
this.model.aggregate(pipeline).then((data) => {
if (data.length === 0) {
reject('Could not find record.');
} else {
resolve(data[0]);
}
}).catch((err) => {
reject(err);
});
});
}
getOneContact(contactId, companyId) {
return new Promise((resolve, reject) => {
let company_Id = this._getId(companyId);
let contact_id = this._getId(contactId);
let pipeline = [{ $match: { _id: company_Id } },
{$project: {
contacts: {$filter: {
input: "$contacts",
as: "contacts",
cond: { $eq: ["$$contacts._id", contact_id] }
}}
}},
{$addFields:{'contacts.company':'$_id'}},
{$unwind:{path:"$contacts",preserveNullAndEmptyArrays:true}},
{ $project: { 'contacts': 1, _id: 0 } }];
this.model.aggregate(pipeline).then((rows) => {
resolve(rows[0].contacts);
}).catch((error) => {
console.log(error.message);
reject(error.message);
});
});
}
saveContact(contactId, contact) {
return new Promise((resolve, reject) => {
let companyId = contact.company;
contact.active = true;
if (contactId === 'new') {
delete contact.company;
this.model.updateOne({ _id: companyId }, { $push: { contacts: contact } }, { setDefaultsOnInsert: true }).then(response => {
contact.company = companyId;
resolve(contact);
}).catch(err => {
reject(err);
});
} else {
this.model.updateOne({ _id: companyId },
{
$set: {
'contacts.$[i].email': contact.email,
'contacts.$[i].name': contact.name,
'contacts.$[i].phone': contact.phone
}
},
{
arrayFilters: [{
"i._id": this._getId(contactId)
}], setDefaultsOnInsert: true
}
).then(response => {
contact.company = companyId;
resolve(contact);
}).catch(err => {
reject(err);
});
}
});
}
getOneAddress(addressId, companyId) {
return new Promise((resolve, reject) => {
let company_Id = this._getId(companyId);
let address_id = this._getId(addressId);
let pipeline = [{ $match: { _id: company_Id } },
{$project: {
addresses: {$filter: {
input: "$addresses",
as: "addresses",
cond: { $eq: ["$$addresses._id", address_id] }
}}
}},
{$addFields:{'addresses.company':'$_id'}},
{$unwind:{path:"$addresses",preserveNullAndEmptyArrays:true}},
{ $project: { 'addresses': 1, _id: 0 } }];
this.model.aggregate(pipeline).then((rows) => {
resolve(rows[0].addresses);
}).catch((error) => {
console.log(error.message);
reject(error.message);
});
});
}
saveAddress(addressId, address) {
return new Promise((resolve, reject) => {
let companyId = address.company;
address.active = true;
if (addressId === 'new') {
delete address.company;
this.model.updateOne({ _id: companyId }, { $push: { addresses: address } }, { setDefaultsOnInsert: true }).then(response => {
address.company = companyId;
resolve(address);
}).catch(err => {
reject(err);
});
} else {
this.model.updateOne({ _id: companyId },
{
$set: {
'addresses.$[i].street': address.street,
'addresses.$[i].city': address.city,
'addresses.$[i].state': address.state,
'addresses.$[i].postal': address.postal,
//'addresses.$[i].geo': geoaddress.geo,
'addresses.$[i].hash': address.hash,
'addresses.$[i].nonGeo': address.nonGeo
}
},
{
arrayFilters: [{
"i._id": this._getId(addressId)
}], setDefaultsOnInsert: true
}
).then(response => {
address.company = companyId;
resolve(address);
}).catch(err => {
reject(err);
});
}
});
}
getOneBroker(index, companyId) {
return new Promise((resolve, reject) => {
let company_Id = this._getId(companyId);
this.model.findOne({ _id: company_Id, account: global._user.account._id }, (err, res) => {
if (err) { console.log(err); reject(err); }
resolve(res.brokers[index]);
});
});
}
saveBroker(index, broker) {
return new Promise((resolve, reject) => {
let companyId = this._getId(broker.company);
let accountId = this._getId(broker.account);
delete broker.company;
delete broker.account;
this.model.findOne({ _id: companyId, account: accountId }, (err, res) => {
if (err) { console.log(err); reject(err); }
res.brokers = res.brokers ? res.brokers : [];
if (index == 'new') {
res.brokers.push(broker);
broker = res.brokers;
} else {
res.brokers[index] = broker;
broker = res.brokers;
}
this.model.updateOne({ _id: companyId, account: accountId }, { $set: { brokers: broker }}, (err, raw) => {
if (err) { console.log(err); reject(err); }
resolve(raw);
});
});
});
}
//Fix: This function will check for dates field type array and convert it into object. Converts array field to Object
resolveDatesArray() {
return new Promise((resolve, reject) => {
let condition = { dates: { $type: "array" } };
this.model.updateMany(condition, { dates: {} }, { upsert: true }, (err, raw) => {
if (err) {
console.log(err);
reject(err);
}
resolve(raw);
});
});
}
select2(query) {
return new Promise((resolve, reject) => {
this.model.aggregate([
{
'$match': this._enforceScope({
name: {
'$regex': query,
'$options': 'i'
}
})
},
{
'$sort': {
name: 1
}
},
{ '$limit': 25 },
{
'$project': {
id: '$_id',
text: '$name'
}
}
]).then((rows) => {
resolve(rows);
}).catch((err) => {
reject(err);
});
});
}
assign(companies, user) {
return new Promise((resolve, reject) => {
let where = this._enforceScope({ _id: { $in: [] } });
let update = { $set: {} };
if (typeof user == 'undefined' || companies == 'undefined') {
return reject('Invalid or missing parameters.');
}
let ids = [];
for (let i in companies) {
where._id['$in'].push(this._getId(companies[i]));
ids.push(this._getId(companies[i]));
}
let pipeline = [{ $match: { _id: { $in: ids } } }, { $lookup: { from: "users", localField: "owner", foreignField: "_id", as: "owner" } }];
this.model.aggregate(pipeline).then((beforeRows) => {
update = {
$set: {
owner: this._getId(user),
account: global._user.account._id,//new added
active: true,//new added
'dates.assigned': new Date(moment().toISOString())// new added for reporting, pipeline target column display
}
};
this.model.updateMany(where, update, (err, res) => {
if (err) {
reject(this.error('Invalid parameters provided.'));
} else {
let count = 0;
beforeRows.forEach((oldDoc) => {
pipeline = [{ $match: { _id: oldDoc._id } }, { $lookup: { from: "users", localField: "owner", foreignField: "_id", as: "owner" } }, { $limit: 1 }];
this.model.aggregate(pipeline).then(async (rows) => {
let newDoc = rows[0];
let meta = await this.jsonDiff(
{ owner: (oldDoc.owner.length > 0) ? '<a href="javascript:void(0);" class="user-info" data-user-id="' + oldDoc.owner[0]._id + '">' + oldDoc.owner[0].firstName + ' ' + oldDoc.owner[0].lastName + '</a>' : 'Unassigned' },
{ owner: (newDoc.owner.length > 0) ? '<a href="javascript:void(0);" class="user-info" data-user-id="' + newDoc.owner[0]._id + '">' + newDoc.owner[0].firstName + ' ' + newDoc.owner[0].lastName + '</a>' : 'Unassigned' }
);
let history = await this.addHistory('Owner change', newDoc._id, '', global._user._id, meta);
count = count + 1;
if (count = beforeRows.length) {
resolve(this.success());
}
});
});
}
});
});
});
}
unassign(companies) {
return new Promise((resolve, reject) => {
let update = { $set: { owner: null } };
let id;
if (companies == 'undefined') {
reject('Invalid or missing parameters.');
}
for (let i in companies) {
id = this._getId(companies[i]);
if (id) {
let pipeline = [
{ $match: { _id: id } },
{ $lookup: { from: "users", localField: "owner", foreignField: "_id", as: "owner" } },
{ $limit: 1 }];
this.model.aggregate(pipeline).then((res) => {
if (res.length > 0) {
this.model.updateOne({ _id: id }, update, async(err, doc) => {
if (err) { console.log(err); }
if (typeof res[0].owner != 'undefined') {
let meta = await this.jsonDiff(
{ owner: res[0].owner.length > 0 ? '<a href="javascript:void(0)" class="user-info" data-route="company" data-user-id="' + res[0].owner[0]._id + '">' + res[0].owner[0].firstName + " " + res[0].owner[0].lastName + '</a>' : "Unassigned" },
{ owner: 'Unassinged' }
);
await this.addHistory('Owner change', id, '', global._user._id, meta);
}
if ((parseInt(i)+1) === companies.length) {
resolve(this.success());
}
});
}
}).catch((error) => { console.log(error); });
}
}
});
}
softDeleteItems(companies) {
return new Promise((resolve, reject) => {
if (typeof companies == 'undefined' || companies === 0) {
return reject(Company.error('Please provide one or more company ID\'s.'));
}
for (let i in companies) {
let id = this._getId(companies[i]);
if (id) {
let pipeline = [
{ $match: { _id: id } },
{ $lookup: { from: "proposals", localField: "_id", foreignField: "company", as: "proposals" } },
{ $limit: 1 }];
this.model.aggregate(pipeline).then((res) => {
if (res.length > 0) {
this.model.updateOne({ _id: id }, {$set: {deleted: true,active: false}}, async (err, doc) => {
if (err) { console.log(err); }
// delete Proposals
if (res[0].proposals.length > 0) {
await Proposal.updateMany({ company: id }, { $set: { active: false } }, async (err, doc) => {
if (err) { console.log(err);}
// Todo nothing.
});
}
let meta = await this.jsonDiff(
{ active: true },
{ active: false, deleted:true }
);
await this.addHistory('Company deleted', id, '', global._user._id, meta);
if ((parseInt(i) + 1) === companies.length) {
resolve(this.success());
}
});
}
}).catch((error) => { console.log(error); });
}
}
});
}
getActivity(companyId) {
return new Promise((resolve, reject) => {
const Activity = mongoose.model('Activity', ActivitySchema);
//let pipeline = [
// {
// $match: {
// account: global._user.account._id,
// company: companyId
// }
// },
// {
// $sort: {
// date: -1
// }
// },
// { $limit: 100 },
// {
// $lookup: {
// from: 'users',
// foreignField: '_id',
// localField: 'user',
// as: 'user'
// }
// },
// { $unwind: '$user' }
// ];
// Activity.aggregate(pipeline).then((rows) => {
// var activity = [];
// for (let i in rows) {
// // get the icon by type
// rows[i].icon = this.getIconByType(rows[i].type);
// // add it to the list
// activity.push(rows[i]);
// }
// resolve(activity);
//}).catch((err) => {
// reject(err);
//});
let pipeline = [
{
$match: {
account: global._user.account._id,
company: companyId
}
},
{
$lookup: {
from: 'users',
foreignField: '_id',
localField: 'user',
as: 'user'
}
},
{ $unwind: '$user' },
{
$group: {
'_id': {
year: { $year: "$date" },
month: { $month: "$date" },
day: { $dayOfMonth: "$date" }
},
activities: {
$push: {
_id: '$_id',
type: '$type',
company: '$company',
proposal: '$proposal',
user: { _id: '$user._id', 'firstName': '$user.firstName', 'lastName': '$user.lastName' },
date: '$date',
meta: '$meta'
}
}
}
},
{ $project: { date: '$_id', activities: 1, _id: 0 } },
{ $sort: { "activities.date": -1 } }
];
Activity.aggregate(pipeline).then((rows) => {
var activity = [];
for (let i in rows) {
for (let j in rows[i].activities) {
// get the icon by type
rows[i].activities[j].icon = this.getIconByType(rows[i].activities[j].type);
rows[i].activities[j].prettyDate = moment(rows[i].activities[j].date).fromNow();
rows[i].activities[j].readableDate = moment(rows[i].activities[j].date).format('MMM Do - LT');
rows[i].activities[j].doableDate = moment(rows[i].activities[j].date).format('m/d/Y');
}
// add it to the list
rows[i].prettyDate = moment.utc(rows[i].date.year + '-' + rows[i].date.month + '-' + rows[i].date.day).local().fromNow();
activity.push(rows[i]);
}
resolve(activity);
}).catch((err) => {
reject(err);
});
});
}
getFiles(companyId) {
return new Promise((resolve, reject) => {
const Proposal = mongoose.model('Proposal', ProposalSchema);
Proposal.aggregate([
{
$match: {
company: companyId,
files: {
$exists: true
}
}
},
{ $unwind: '$files' },
{
$project: {
proposal: '$_id',
name: '$name',
file: '$files'
}
}
]).then((rows) => {
resolve(rows);
}).catch((err) => {
console.log('Error: ', err);
reject(err);
});
});
}
addActivity(companyId, type, data) {
return new Promise(async(resolve, reject) => {
if (typeof type != 'string' || ['call', 'visit', 'reminder', 'event'].indexOf(type.trim()) == -1) {
return reject('Invalid type provided');
}
const Schema = mongoose.Schema;
const Base = mongoose.model('Activity', ActivitySchema);
// to track activity on each pipeline stage
if (data.proposal != 'undefined') {
if (data.proposal != 'bulk') {
if (data.proposal) {
await Proposal.findById(this._getId(data.proposal), (err, res) => {
if (err) { console.log(err); }
if (res) {
data.pipeline = res.pipeline;
}
});
}
}
}
if (type == 'event') {
// define dynamic Schema
const Event = Base.discriminator('event', new mongoose.Schema({meta: {start: Date,end: Date,subject: String,participants: String,location: String,details: String}}));
let _data = {
company: new ObjectId(companyId),
type: type.trim(),
meta: {
start: data.start,
end: data.end,
subject: data.subject,
participants: data.participants,
location: data.location,
details: data.details
},
user: global._user._id,
account: global._user.account._id
};
if (data.proposal != 'undefined') {
if (data.proposal != 'bulk') {
if (data.proposal) {
_data.proposal = data.proposal;
_data.pipeline = data.pipeline;
}
}
}
Event.create(_data, (err, record) => {
if (err) {
console.log('Error: ', err);
reject('An unexpected error was encountered. Please try again.');
} else {
resolve(record._id);
}
});
} else {
// define dynamic Schema
//FIX: descriminator with name 'call' already exists.
const ActivityModel = Base.discriminators ? Object.keys(Base.discriminators).indexOf(type.trim()) >= 0 ? Base.discriminators[type.trim()] : Base.discriminator(type.trim(), new mongoose.Schema({ meta: { date: Date, notes: String } })) : Base.discriminator(type.trim(), new mongoose.Schema({ meta: { date: Date, notes: String } }));
//const ActivityModel = Base.discriminator(type.trim(), new mongoose.Schema({ meta: { date: Date, notes: String } }));
let _data = {
company: new ObjectId(companyId),
type: type.trim(),
meta: {
date: data.date,
notes: data.notes
},
user: global._user._id,
account: global._user.account._id
};
if (data.proposal != 'undefined') {
if (data.proposal != 'bulk') {
if (data.proposal) {
_data.proposal = data.proposal;
_data.pipeline = data.pipeline;
}
}
}
ActivityModel.create(_data, (err, record) => {
if (err) {
console.log('Error: ', err);
reject('An unexpected error was encountered. Please try again.');
} else {
resolve(record._id);
}
});
}
});
}
retrieveActivity(activityId) {
}
deleteActivity(activityId) {
return new Promise((resolve, reject) => {
let match = this._enforceScope({
_id: new ObjectId(activityId)
});
const Activity = mongoose.model('Activity', ActivitySchema);
Activity.deleteOne(match, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
exportToCSV(input) {
return new Promise((resolve, reject) => {
let config = this._getDataTablesFilters(input);
let pipeline = [];
let baseFilters = {
account: global._user.account._id,
active: true,
deleted: {
$exists: false
}
};
// add in the base filters
config.filters = Util.merge(baseFilters, config.filters);
// set the base pipeline where/match clause
pipeline.push({ $match: this._enforceScope() });
// lifecylce method to give the user the ability to inject setup stages to
// the pipeline before we start adding in user-defined filters, limits and
// sorting options
pipeline = this._startPipeline(pipeline);
// Search text
if (typeof input.search != 'undefined') {
if (input.search.value) {
var searchQuery = this._getDataTablesSearch(input.search.value);
if (typeof searchQuery != 'undefined') {
pipeline.push({ $match: searchQuery });
}
}
}
// set the filters pipeline where/match clause
if (Object.keys(config.filters).length > 0) {
pipeline.push({ $match: config.filters });
}
// add sorting
if (Object.keys(config.sort).length > 0) {
pipeline.push({ $sort: config.sort });
}
// we need to format the arrays - not sure what the heck is going on with
// using $addresses.0.street in the projection stage below, but I had some
// issues getting it to work
pipeline.push({
$addFields: {
_contact: {
$slice: ['$contacts', 1]
},
_address: {
$slice: ['$addresses', 1]
}
}
});
pipeline.push({
$unwind: {
path: '$owner',
preserveNullAndEmptyArrays: true
}
});
pipeline.push({
$unwind: {
path: '$_contact',
preserveNullAndEmptyArrays: true
}
});
pipeline.push({
$unwind: {
path: '$_address',
preserveNullAndEmptyArrays: true
}
});
// add the project stage
pipeline.push({
$project: {
name: '$name',
owner: '$owner.name',
contact_name: '$_contact.name',
contact_email: '$_contact.email',
contact_phone: '$_contact.phone',
address_street: '$_address.street',
address_city: '$_address.city',
address_state: '$_address.state',
address_postal: '$_address.postal',
address_county: '$_address.county',
industry: '$meta.industry',
ftes: '$meta.ftes',
fteGrouping: '$meta.fteGrouping',
premium: '$meta.premium',
premiumPerFte: '$meta.premiumPerFte',
brokerCommissionTotal: '$meta.brokerCommissionTotal',
brokerFeesTotal: '$mata.brokerFeesTotal',
fundingType: '$meta.fundingType',
ein: '$meta.ein'
}
});
// get the data
this.model.aggregate(pipeline).then((rows) => {
const papaparse = require('papaparse');
console.log('Data: ', JSON.stringify(rows, undefined, 2));
let csv = papaparse.unparse(rows);
resolve(csv);
}).catch((err) => {
reject(err);
});
});
}
_startPipeline(pipeline) {
let coreClone = this.corePipeline.slice(0);
// remove the first stage of the core to add in the below projection
coreClone.shift();
// add in the owner's info
coreClone.unshift({
$lookup: {
from: 'users',
let: { 'ownerId': '$owner' },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ["$_id", "$$ownerId"] }
]
}
}
},
{
$project: {
name: {
$concat: ['$firstName', ' ', '$lastName']
}
}
}
],
as: 'owner'
}
});
return pipeline.concat(coreClone);
}
targetSelected(companies = [], user) {
return new Promise(async (resolve, reject) => {
try {
if (companies.length > 0 && user.length > 0) {
let Targetpipelines = await this.getPipelineColumns();
var index = Targetpipelines.map(function (o) { return o.key; }).indexOf("target");
let Targetpipeline = Targetpipelines[index];
if (typeof Targetpipeline != 'undefined') {
let count = 0;
companies.forEach((company) => {
let targetProposal = {
name: '_target', owner: this._getId(user), company: this._getId(company), pipeline: Targetpipeline._id, active: true, services: [], files: [],
//dates: { target: new Date(moment().toISOString())},
_range: [new Date(moment().toISOString()), new Date(moment().toISOString())],
_pipelineDate: new Date(),
pipelineIndex: 0,
account: global._user.account._id,
pipelineDuration: [{
pipelineFrom: Targetpipeline._id, fDate: new Date(moment().toISOString()),
pipelineTo: Targetpipeline._id, tDate: new Date(moment().toISOString())
}]
};
targetProposal.dates = typeof targetProposal.dates == 'undefined' ? {} : targetProposal.dates;
targetProposal.dates[Targetpipeline._id] = new Date(moment().toISOString()), new Date(moment().toISOString());
Proposal.updateOne({ owner: this._getId(user), company: this._getId(company), name: '_target' }, targetProposal, { runValidators: true, upsert: true }, async(error, res) => {
if (error) {
console.log(error);
}
let targetUser = await this.getUserbyID(user);//await User.getOne(this._getId(user));
if (targetUser) {
let meta = { 'Target Selected to': "<a href='javascript:void(0);' class='user-info' data-user-id='" + targetUser._id + "' data-route='market'>" + targetUser.firstName + " " + targetUser.lastName + "</a>" };
this.addHistory('Target selected', this._getId(company), '', global._user._id, meta);
}
count = count + 1;
if (count == companies.length) {
resolve(true);
}
});
});
} else {
reject('Pipeline Column is undefined.');
}
} else {
reject('Invalid or undefined company id or user id provided.');
}
} catch (e) {
console.log(e);
reject(e);
}
});
}
getUserbyID(id) {
return new Promise(async (resolve, reject) => {
if (id) {
userModel.findById(this._getId(id), (err, res) => {
if (err) { console.log(err); resolve(false);}
resolve(res);
});
} else {
resolve(false);
}
});
}
ignoreTargetSelected(companies = []) {
return new Promise(async (resolve, reject) => {
try {
if (companies.length > 0) {
let Targetpipelines = await this.getPipelineColumns();
var index = Targetpipelines.map(function (o) { return o.key; }).indexOf("target");
let Targetpipeline = Targetpipelines[index];
if (typeof Targetpipeline != 'undefined') {
let companyIds = [];
companies.forEach(async (company) => {
companyIds.push(this._getId(company));
await this.addHistory('Target ignored', this._getId(company), '', global._user._id, {});
});
Proposal.deleteMany({ company: { $in: companyIds }, name: '_target' }, (error) => {
if (error) { reject(error); }
resolve(true);
});
} else {
reject('Pipeline Column is undefined.');
}
} else {
reject('Invalid or undefined company id provided.');
}
} catch (e) {
console.log(e);
reject(e);
}
});
}
getOneForHistory(id) {
return new Promise((resolve, reject) => {
id = this._getId(id);
if (!id) {
return reject('Invalid prospect ID provided.');
}
let pipeline = [{ $match: { _id: id } },
{
$lookup: {
from: "users",
let: { "ownerId": "$owner" },
pipeline: [
{ $match: { $expr: { $eq: ["$_id", "$$ownerId"] } } },
{
$project: {
_id: 0,
assigned: { $concat: ["<a href='javascript:void(0)' class='user-info' data-user-id='", { $toString: "$_id" }, "'>", "$firstName", " ", "$lastName", "</a>"] }
}
}
],
as: "owner"
}
},
{ $unwind: { path: "$owner", preserveNullAndEmptyArrays: true } },
{ $addFields: { owner: "$owner.assigned" } }
];
this.model.aggregate(pipeline).then((data) => {
if (data.length === 0) {
reject('Could not find record.');
} else {
resolve(data[0]);
}
}).catch((err) => {
reject(err);
});
});
}
getProposals(companyId) {
return new Promise((resolve, reject) => {
let scopedFilters = this._enforceScope();
var pipeline = [
{
$match: {
company: this._getId(companyId),
name: { "$ne": "_target" },// should not show _target proposals they are just 'Target Selected' pipeline
$or: [{ "active": { "$exists": false } }, { "active": true }]
}
},
{
$lookup: {
from: "pipelinecolumns",
localField: "pipeline",
foreignField: "_id",
as: "pipeline"
}
},
{ $unwind: "$pipeline" },
{
$lookup: {
from: "services",
localField: "services.service",
foreignField: "_id",
as: "products"
}
},
{
$lookup: {
from: "users",
localField: "owner",
foreignField: "_id",
as: "owner"
}
},
{
$project: {
"products.model": 0, "products.account": 0, "products.amount": 0,
"owner.settings": 0, "owner.password": 0, "owner.email": 0, "owner.mobile": 0, "owner.account": 0
}
},
{
$unwind: "$owner"
}];
Proposal.aggregate(pipeline, (err, rows) => {
if (err) {
reject(err);
} else {
resolve(rows);
}
});
});
}
getService(serviceId) {
return new Promise((resolve, reject) => {
let pipeline = [{ $match: { "_id": serviceId } }];
ProductsAndService.aggregate(pipeline, (err, rows) => {
if (err) {
reject(err);
} else {
resolve(rows[0]);
}
});
});
}
heatmap(ids) {
return new Promise((resolve, reject) => {
let pipeline = [];
let _ids = [];
if (ids && ids.length > 0) {
ids.forEach((id) => {
_ids.push(this._getId(id));
});
let match = { $match: this._enforceScope() };
delete match['$match']['account'];
match['$match']._id = { $in: _ids };
pipeline.push(match);
pipeline.push(
{ $unwind: { path: "$addresses", preserveNullAndEmptyArrays: true } },
{ "$addFields": { "company": { _id: "$_id", name: "$name" } } },
{
"$lookup": {
"from": "geo_locations", "let": { "addressId": "$addresses._id" }, "pipeline": [
{ "$match": { "$and": [{ "$expr": { "$eq": ["$address", "$$addressId"] } }, { "$expr": { "$eq": ["$active", true] } }] } }], "as": "geo_locations"
}
},
{ $unwind: { path: "$geo_locations", preserveNullAndEmptyArrays: true } },
{
"$project": {
"_id": 0,
"type": "Feature",
"properties": {
"name": '$name',
"popupContent": "$name",
"_id": "$_id", "street": "$addresses.street", "city": "$addresses.city", 'state': '$addresses.state', "postal": "$addresses.postal", "country": "$addresses.country", "county": "$addresses.county",
"addresses": "$addresses",
"company": "$company", "nonGeo": "$addresses.nonGeo"
},
"geometry": "$geo_locations.geo"
}
}
);
this.model.aggregate(pipeline).then(async (rows) => {
let count = 0;
rows.forEach(async (row, index) => {
if (row.geometry === undefined) {
row.properties.addresses = row.properties.addresses ? row.properties.addresses : {};
row.properties.addresses.company = row.properties.company._id;
let geoAddress = await this.getGeoAddress(row.properties.addresses);
rows[index].geometry = geoAddress.geo;
count = count + 1;
} else { count = count + 1; }
if (rows.length == count) {
resolve({ "type": "FeatureCollection", "features": rows });
}
});
}).catch((err) => {
console.log('Aggregate Error: ', err);
reject(err);
});
} else {
//reject('undefined parameter ids provided.');
resolve({ "type": "FeatureCollection", "features": [] });
}
});
}
getGeoAddress(address = {}) {
return new Promise(async (resolve, reject) => {
try {
if (typeof address.geo != 'undefined') {
resolve(address);
} else {
var isValidAddress = false;
address.street = address.street ? address.street.trim() : address.street;
if (!address || address.street === '' || address.city === '' || address.state === '' || address.postal === '' || address.street === undefined || address.city === undefined || address.state === undefined || address.postal === undefined) {
isValidAddress = false;
address.error = 'invalid';
resolve(address);
}
else {
isValidAddress = true;
}
if (isValidAddress) {
let geoaddress = await CompanyAddress.getGeocode(address);
if (geoaddress) {
if (geoaddress.error_message) {
address.error = geoaddress.error_message;
resolve(address);
} else {
GeoLocations.model.updateOne(
{ address: address._id },//, account: global._user.account._id
{ $set: { address: address._id, company: address.company, geo: geoaddress.geo } },//account: global._user.account._id,
{ upsert: true, setDefaultsOnInsert: true }, (err) => {
if (err) {
resolve(address);
} else {
this.model.updateOne({ _id: address.company },
{
$set: {
'addresses.$[i].street': geoaddress.street,
'addresses.$[i].city': geoaddress.city,
'addresses.$[i].state': geoaddress.state,
'addresses.$[i].postal': geoaddress.postal,
//'addresses.$[i].geo': geoaddress.geo,
'addresses.$[i].hash': geoaddress.hash,
'addresses.$[i].nonGeo': geoaddress.nonGeo
}
},
{
arrayFilters: [
{
"i._id": address._id
}]
}
).then(response => {
resolve(geoaddress);
}).catch(err => {
address.error = err.msg;
resolve(address);
});
}
});
//CompanyAddress.model.updateOne({ _id: address._id }, { '$set': { street: geoaddress.street, city: geoaddress.city, state: geoaddress.state, postal: geoaddress.postal, geo: geoaddress.geo, hash: geoaddress.hash, nonGeo: geoaddress.nonGeo } }, { runValidators: true }, (err) => {
// if (err) {
// address.error = err.msg;
// resolve(address);
// } else {
// resolve(geoaddress);
// //let ret = { _id: location._id, name: record.name, address: location.address, coordinates: location.address.geo.coordinates };
// //resolve(ret);
// }
//});
}
} else {
address.error = 'invalid';
resolve(address);
}
}
}
} catch (e) {
// return reject(e);
console.log(e);//console.log(e.message);
resolve(address);
}
});
}
}
module.exports = new Company();
const { ObjectId } = require('mongodb');
const { Util } = require('node-utils');
const moment = require('moment-timezone');
const Service = require('./service');
const mongoose = require('mongoose');
const ProposalSchema = require('../schemas/ProposalSchema');
const CompanySchema = require('../schemas/CompanySchema');
const UserSchema = require('../schemas/UserSchema');
const ActivitySchema = require('../schemas/ActivitySchema');
class Activity extends Service {
constructor() {
super();
this.collection = 'activities';
this.plural = 'activities';
this.singular = 'activity';
this.model = mongoose.model('Activity', ActivitySchema);
//this.fields = require('../lib/model-proposal');
}
}
module.exports = new Activity();
Network and collaborate with thousands of CTOs, CISOs, and IT Pros rooting for you and your success.
”The time we save is the biggest benefit of E-E to our team. What could take multiple guys 2 hours or more each to find is accessed in around 15 minutes on Experts Exchange.
Our community of experts have been thoroughly vetted for their expertise and industry experience.
The Fellow title is reserved for select members who demonstrate sustained contributions, industry leadership, and outstanding performance. We will announce the experts being inducted into the Experts Exchange Fellowship during the annual Expert Awards, but unlike other awards, Fellow is a lifelong status. This title may not be given every year if there are no obvious candidates.
The Most Valuable Expert award recognizes technology experts who passionately share their knowledge with the community, demonstrate the core values of this platform, and go the extra mile in all aspects of their contributions. This award is based off of nominations by EE users and experts. Multiple MVEs may be awarded each year.