Avatar of Bruce Gust
Bruce Gust
Flag for United States of America

asked on 

How can I create a basic delete for this Node application?

I'm working on an app that has a lot of functioning architecture which is good, because I'm new to Node.

I've got create a "delete" function for a note and I've gotten this far:

          $(document).on('click', '.note-delete', function () {
        //let data = { companies: [$(this).attr('data-prospect-id')] };
            let data = $(this).attr('data-activityId');
        CAMS.confirm('Are You Sure?', 'You want to delete the selected note from your account?', function (result) {
            if (!result) {
                return false;
            }
            $.post('/companies/delete-note',data, function (resp) {
                if (resp.error) {
                    CAMS.alert('Error', resp.msg, 'error');
                    return false;
                }
                CAMS.alert('Success', 'Your note has been deleted. Please refresh the page to update the view.');                
            }, 'json').fail(function () {
                CAMS.alert('Unexpected Error', 'We encountered an unexpected error while trying to process your request.', 'error');
            });
        });
    });

At the point, I get my alert which allows me to either confirm or delete my request.

My route is pure fiction at this point because I'm depending on something that's already functional to serve as my template. Most of what I have access to, however, involves having to delete several tiers of information including nested collections and I just need a plain ol, delete query.

Here's the current "company.js" route file:

const { ObjectId } = require('mongodb');

const express = require('express');
const router = express.Router();
const twig = require('twig');
const moment = require('moment-timezone');
const fs = require('fs');
const { Util } = require('node-utils');

const Company = require('../services/company.js');
const CompanyContact = require('../services/companycontact.js');
const CompanyAddress = require('../services/companyaddress.js');
const Proposal = require('../services/proposal.js');
const Activity = require('../services/activity.js');

const Prospect = require('../services/prospect.js');
const ServiceModel = require('../services/servicemodel');
const AccountManagement = require('../services/account-management');
const GeoLocations = require('../services/GeoLocations');

// set the default variables we'll be passing to the template engine for ease
// of use.
let base = {
    selected: '/companies',
    error: false,
    msg: '',
    data: {}
};

router.post('/companies/ignore', async (req, res) => {
    if (typeof req.body.companies == 'undefined' || req.body.companies.length === 0) {
        return res.send(Prospect.error('Please provide one or more company ID\'s.'));
    }
    try {
        let deleted = await Company.ignoreTargetSelected(req.body.companies);
        res.send(Prospect.success(deleted));
    } catch (e) {
        res.send(Prospect.error('An unexpected error was encountered.'));
    }   
});

router.post('/companies/target', async (req, res) => {
    if ((typeof req.body.companies == 'undefined' || req.body.companies.length === 0) && (typeof req.body.user == 'undefined')) {
        return res.send(Prospect.error('Please provide one or more company ID\'s.'));
    }
    try {
        let _target = await Company.targetSelected(req.body.companies, req.body.user);

        res.send(Prospect.success());
    } catch (e) {
        res.send(Prospect.error('An unexpected error was encountered.'));
    }   
});


// get the full list/index view
router.get('/companies', async (req, res) => {
    let vars = Object.assign({}, base);

	//I made a little change here. vars.service_models, in my opinion needs to be called once and only after it's been conclusively determined that there's data in place to be retrieved. So I established "brucesters" as a var to hold the results of the "getList()" function.
	// If it's empty, then you seed the appropriate collection. If not, you simply move forward to the "vars.service_models = await ServiceModel.getList) function and all is well. What you see commented out below is the original code
	vars.service_models = await ServiceModel.getList();
    //Seed data
    if (vars.service_models.length == 0) {
        let defaultServiceModels = [{ key: 'perFte', name: 'Per FTE' }, { key: 'oneTime', name: 'One Time' }, { key: 'perPerson', name: 'Per Person' }, { key: 'perMonth', name: 'Per Month' }, { key: 'perQuarter', name: 'Per Quarter' }, { key: 'semiAnnually', name: 'Semiannually' }, { key: 'perYear', name: 'Per Year' }];
        defaultServiceModels.forEach(async (item) => {
            let _id = await ServiceModel.save('new', item);
        });
        service_models = await ServiceModel.getList();
    }
  
	//console.log(vars.service_models);
    vars.pipelineColumns = await Company.getPipelineColumns();
    let resolveDatesArray = await Company.resolveDatesArray();
    res.render('companies.html.twig', vars);
});

router.post('/companies/datatable', async (req, res) => {
    let resp = await Company.datatable(req.body, { pipeline: { $ne: '' } });
	//console.log(resp);
    res.send(resp);
});
router.post('/companies/heatmap', async (req, res) => {
    try {
        let resp = await Company.heatmap(req.body.ids);
        res.send(Company.success(resp));
    } catch (err) {
        res.status(500).send(Company.error(err));
    }
});
router.post('/companies/heatmap/click/draw', async (req, res) => {
    try {
        let resp = await GeoLocations.heatmapdraw(req.body, 'companies');
        res.send(Company.success(resp));
    } catch (err) {
        console.log(err);
        res.status(500).send(Company.error(err));
    }
});
router.get('/companies/map/:id?', async (req, res) => {
    let vars = Object.assign({}, base);
    vars.service_models = await ServiceModel.getList();
    vars.pipelineColumns = await Company.getPipelineColumns();
    if (typeof req.params.id != 'undefined' || req.params.id != '') {
        vars._id = req.params.id;
    }
    res.render('companies-map.html.twig', vars);
});
router.post('/companies/map/:id?', async (req, res) => {
    try {
        var pipeline_filters = {};
        pipeline_filters.$or = [{ active: { $exists: false } }, { active: true }];
        if (typeof req.params.id != 'undefined') {
            req.body.filters = typeof req.body.filters == 'undefined' ? [] : req.body.filters;
            req.body.filters.push({ "field": "_id", "op": "$eq", "val": Company._getId(req.params.id) });
        }
        let resp = await Company.datatable(req.body, pipeline_filters);
        res.send(resp);
    } catch (err) {
        console.log('Datatables Error: ', err);
        res.status(500).send(Company.error(err));
    }
});

router.get('/companies/select2', async (req, res) => {
    try {
        let data = await Company.select2(req.query.q.term);

        res.send(data);
    } catch (err) {
        res.status(500).send({});
    }
});

router.get('/companies/saved-filters', async (req, res) => {
    try {
        var data = await Company.getSavedFilters();

        res.send(data);
    } catch (err) {
        console.log('Error: ', err);
        res.status(500).send(Company.error());
    }
});

router.post('/companies/save-filters', async (req, res) => {
    try {
        let id = await Company.saveFilter(req.body.id, {
            type: 'companies',
            name: req.body.name,
            filters: req.body.filters,
            account: global._user.account._id,
            active: true,
            owner: global._user._id
        });

        res.send(Company.success({
            id: id
        }));
    } catch (err) {
        console.log('Error: ', err);
        res.send(Company.error('An unexpected error was encountered. Please try again.'));
    }
});

router.post('/companies/delete-saved-filter', async (req, res) => {
    try {
        await Company.deleteSavedFilter(req.body.id);

        res.send(Company.success());
    } catch (err) {
        res.status(500).send(Company.error());
    }
});

// bulk assign companies to a single user - must come before the edit route
router.post('/companies/assign', async (req, res) => {
    let vars = Object.assign({}, base);

    if (typeof req.body.user == 'undefined' || typeof req.body.companies == 'undefined') {
        return res.send(Company.error('Invalid or missing parameters.'));
    } else if (typeof req.body.companies == 'string') {
        req.body.companies = [req.body.companies];
    }

    try {
        let resp = await Company.assign(req.body.companies, req.body.user);
    } catch (err) {
        console.log('Error: ', err);
        return res.send(Company.error('Invalid parameters provided.'));
    }

    res.send(vars);
});

router.post('/companies/unassign', (req, res) => {
    if (req.body.companies == 'undefined') {
        return res.send(Company.error('Invalid or missing parameters.'));
    } else if (typeof req.body.companies == 'string') {
        req.body.companies = [req.body.companies];
    }

    try {
        let success = Company.unassign(req.body.companies);

        res.send(Company.success());
    } catch (err) {
        res.send(Company.error('An unexpected error was encountered while trying to unassign the user associated with the provided companies. Please try again.'));
    }
});

router.post('/companies/add-activity', async (req, res) => {
    // make sure the request has a type
    if (typeof req.body.type == 'undefined' || req.body.type === '') {
        return res.send({ error: true, msg: 'Invalid or missing service/object type. Please refresh the page and try again.' });
    } else if (typeof req.body.activityType == 'undefined' || req.body.activityType === '') {
        req.body.activityType = req.body.type;
        //return res.send({ error: true, msg: 'Invalid or missing service/object type. Please refresh the page and try again.' });
    } else if (typeof req.body.company == 'undefined' || req.body.company === '') {
        return res.send({ error: true, msg: 'Invalid or missing company ID. Please refresh the page and try again.' });
    }

    // set the activity type as the main type - I added this in after a request to
    // combine several types
    req.body.type = req.body.activityType;

    // validate the content based on the type
    try {
        if (Array.isArray(req.body.company)) {
            let ids = [];
            //var companies = new Set(req.body.company); 
            var companies = req.body.company.filter(function (elem, pos) { return req.body.company.indexOf(elem) == pos; });// Remove duplicates
            var counter = companies.length;
            for (var i = 0; i < companies.length; i++) {
                try {
                    counter -= 1;
                    let _companyId = companies[i];
                    if (_companyId == 'bulk') {
                        ids.push("_0");
                        if (counter === 0) {
                            let topId = ids[0];
                            res.send(Company.success({ id: companies[0] }));
                        }
                    } else {
                        let id = await Company.addActivity(_companyId, req.body.type, req.body);
                        //let data = { id: id, company: bdata.company };
                        ids.push(id);
                        if (counter === 0) {
                            let topId = ids[0];
                            res.send(Company.success({ id: companies[0] }));
                        }
                    }
                } catch (e) {
                    console.log(e);
                }
            }
        } else {
            let id = await Company.addActivity(req.body.company, req.body.type, req.body);
            res.send(Company.success({ id }));
        }

    } catch (err) {
        res.send(Company.error(err));
    }
});

//update activity 

router.post('/companies/update-activity', async (req, res) => {

    try {
        let _data = {
            notes : req.body.notes,
            date : req.body.date,
            company : req.body.company,
            type : req.body.type
        }

        Activity.model.updateOne({ _id: req.body.activityId }, {
            $set: {
                // set up the data you want to persist to the DB
                notes: _data.notes,
                "meta.$.date": _data.date, 
				type: _data.type, 
            }
        }, 
		{ runValidators: true }, (err) => {
            if (err) {
                console.log(err)
                return res.send(Activity.error('Hmmm. Something went wrong!'))
            }
            res.send(Company.success( { message : "Updated!" } ))
        });

    } catch (err) {
        res.send(Company.error(err))
    }

});

// add an address or contact to a company record
router.post('/companies/:orgId/:type(contacts|addresses|brokers)/:id', async (req, res) => {
    // make sure the request has a type
    if (typeof req.params.type == 'undefined' || req.params.type === '') {
        return res.send({ error: true, msg: 'Invalid or missing service/object type. Please refresh the page and try again.' });
    } else if (typeof req.params.orgId == 'undefined' || req.params.orgId === '') {
        return res.send({ error: true, msg: 'Invalid or missing company ID. Please refresh the page and try again.' });
    }

    let resp;

    // enforce some scoping
    req.body.account = global._user.account._id;
    req.body.company = Company._getId(req.params.orgId);

    // validate the content based on the type
    try {
        let oldRec = {}, newRec = {}, meta = {};
        switch (req.params.type) {
            case 'contacts':
                if (req.params.id != 'new') { oldRec = await Company.getOneContact(req.params.id, req.params.orgId); }
                resp = await Company.saveContact(req.params.id, req.body);
                //newRec = await resp;
                meta = Company.jsonDiff(oldRec, resp);
                Company.addHistory((req.params.id == 'new' ? 'Contact added' : 'Contact updated'), req.params.orgId, '', global._user._id, meta);
                break;

            case 'addresses':
                if (req.params.id != 'new') { oldRec = await Company.getOneAddress(req.params.id, req.params.orgId); }
                resp = await Company.saveAddress(req.params.id, req.body);                
                meta = Company.jsonDiff(oldRec, resp);
                Company.addHistory((req.params.id == 'new' ? 'Address added' : 'Address updated'), req.params.orgId, '', global._user._id, meta);
                break;
            case 'brokers':
                if (req.params.id != 'new') { oldRec = await Company.getOneBroker(req.params.id, req.params.orgId); }
                resp = await Company.saveBroker(req.params.id, req.body);
                delete req.body.company;
                delete req.body.account;
                newRec = req.body;//await Company.getOneBroker(req.params.id, req.params.orgId);
                meta = Company.jsonDiff(oldRec, newRec);
                Company.addHistory((req.params.id == 'new' ? 'Broker info added' : 'Broker info updated'), req.params.orgId, '', global._user._id, meta);
                break;

            default:
                return res.send(Company.error('Invalid type provided.'));
                break;
        }
    } catch (err) {
        console.log('Save Contacts/Addresses/Brokers Error: ', err);

        // if the user is having issues with an invalid address from google, we need to let them know
        let msg = (typeof err == 'string' && err.indexOf('Google') > -1) ? err : 'An unexpected error was encountered.';

        return res.send(Company.error(msg));
    }

    res.send(resp);
});

router.get('/companies/:orgId/:type(contacts|addresses|brokers)/:id', async (req, res) => {
    try {
        let data = null;

        switch (req.params.type) {
            case 'contacts':
                data = await Company.getOneContact(req.params.id, req.params.orgId);//await CompanyContact.getOne(req.params.id);
                break;

            case 'addresses':
                data = await Company.getOneAddress(req.params.id, req.params.orgId);//await CompanyAddress.getOne(req.params.id);
                break;
            case 'brokers':
                data = await Company.getOneBroker(req.params.id, req.params.orgId);
                break;

            default:
                return res.status(500).send(Company.error('Invalid type provided.'));
                break;
        }
	
        //res.send(Company.success(data));
    } catch (err) {
        console.log('Get Contact/Address/Brokers Error: ', err);
        res.status(500).send(Company.error());
    }
});

router.get('/companies/:orgId/:type(contacts|addresses)/delete/:id', async (req, res) => {
	console.log("post");
    try {
        switch (req.params.type) {
            case 'contacts':
                await CompanyContact.deleteItem(req.params.id);
                break;

            case 'addresses':
                await CompanyAddress.deleteItem(req.params.id);
                break;

            default:
                return res.status(500).send(Company.error('Invalid type provided.'));
                break;
        }

        res.send(Company.success());
    } catch (err) {
        console.log('Delete Contact/Address Error: ', err);
        res.status(500).send(Company.error());
    }
});

router.post('/companies/export', async (req, res) => {
    try {
        let rows = await Company.exportToCSV(req.body);

        res.send(Company.success(rows));
    } catch (err) {
        console.log('Export Error: ', err);
        res.status(500).send(Company.error());
    }
});

router.get('/companies/proposals/:proposalId/:route', async (req, res) => {
    if (typeof req.params.proposalId == 'undefined') {
        return res.send(Company.error('It looks like your request was improperly defined. Please try again.'));
    }

    try {
        var paths = [
            { path: 'owner', model: 'User' },
            { path: 'company', model: 'Company' },
            { path: 'company.addresses', model: 'CompanyAddress' },
            { path: 'company.contacts', model: 'CompanyContact' },
            { path: 'services.service', model: 'Service' }
        ];

        if (typeof req.params.route != 'undefined') {
            if (req.params.route == 'market') {
                paths[1] = { path: 'prospect', model: 'prospect' };
                //paths.push({ path: 'prospect', model: 'prospect' });
                let proposal = await Proposal.getOne(req.params.proposalId, paths);
                if (paths[1].path == 'prospect') {
                    if (proposal) {
                        var prospect_proposal = {
                            _id: proposal._id,
                            name: proposal.name,
                            pipelineIndex: proposal.pipelineIndex,
                            _pipelineDate: proposal._pipelineDate,
                            pipeline: proposal.pipeline,
                            company: await Prospect.getOne(proposal.company),
                            owner: proposal.owner,
                            dates: proposal.dates,
                            _range: proposal._range,
                            files: proposal.files,
                            services: proposal.services
                        };
                    }
                    return res.send(Proposal.success(prospect_proposal));
                }
            }
        }
        let proposal = await Proposal.getOne(req.params.proposalId);        
        res.send(Proposal.success(proposal));
    } catch (err) {
        res.send(Proposal.error('An unexpected error was encountered while trying to load your proposal. Please try again.' + '\nDetails:' + JSON.stringify(err)));
    }
});

router.get('/companies/proposal/pdf/:proposalId/', async (req, res) => {
    if (typeof req.params.proposalId == 'undefined') {
        return res.send(Company.error('It looks like your request was improperly defined. Please try again.'));
    }

    try {        
        let proposal = await Proposal.getOne(req.params.proposalId);        
        proposal.account = await AccountManagement.getOne(proposal.account);
        
        proposal.value = 0;
        proposal.services.forEach((service, index) => {
            let price = service.amount || service.price;
            if (service.model === 'perFte' || service.model === 'oneTime' || service.model ==='perPerson') {                
                proposal.services[index].total = parseFloat(price) * service.numOfEmp;
            } else {
                proposal.services[index].total = parseFloat(price) * service.numOfEmp * service.duration;
            }
            proposal.value = proposal.value + proposal.services[index].total;
        });
        proposal.company.addresses = proposal.company.addresses.length > 0 ? proposal.company.addresses[proposal.company.addresses.length - 1] : proposal.company.addresses;
        proposal.company.contacts = proposal.company.contacts.length > 0 ? proposal.company.contacts[proposal.company.contacts.length - 1] : proposal.company.contacts;
        res.render('proposal-template-pdf.html.twig', proposal, (error, html) => {
            if (error) {
                console.log(error);
                res.send(Proposal.error('An unexpected error was encountered while trying to load your proposal. Please try again.' + '\nDetails:' + JSON.stringify(error)));
            }
            res.send(Proposal.success({ pdf: html, logo: proposal.account.logo, address: proposal.account.address }));
        });
        
    } catch (err) {
        res.send(Proposal.error('An unexpected error was encountered while trying to load your proposal. Please try again.' + '\nDetails:' + JSON.stringify(error)));
    }
});

router.post('/companies/proposals/save/:proposalId', async (req, res) => {
    // make sure the proposal object was provided
    if (typeof req.body.proposal == 'undefined') {
        return res.send(Company.error('It looks like your request was improperly defined. Please try again.'));
    } else if (typeof req.body.proposal.services == 'undefined') {
        // make sure the services object is provided an not empty
        return res.send(Company.error('Please add at least one service to the proposal.'));
    }

    // properly format the services since I did it the lazy way - mitch
    let services = [];

    for (let i in req.body.proposal.services.service) {
        let service = {
            service: req.body.proposal.services.service[i],
            model: req.body.proposal.services.model[i],
            serviceDates: req.body.proposal.services.serviceDates[i],
            duration: req.body.proposal.services.duration[i],
            numOfEmp: req.body.proposal.services.numOfEmp[i],
            description: req.body.proposal.services.description[i]
        };
        service.price = (typeof req.body.proposal.services.price != 'undefined') ? req.body.proposal.services.price[i] : req.body.proposal.services.amount[i];
        //if (typeof req.body.proposal.services.pipelineIndex != 'undefined') { service.pipelineIndex = req.body.proposal.services.pipelineIndex[i];}
        services.push(service);
    }

    req.body.proposal.services = services;

    // format the file uploads - for the db schema we don't need the files to be
    // indexed by their key.
    req.body.proposal.files = Company.array_values(req.body.proposal.files);

    // in a couple of required fields
    req.body.proposal.owner = req.body.proposal.user;//global._user._id;
    req.body.proposal.active = true;

    // try to save it now
    try {
        //req.body.proposal._pipelineDate = Date.now;
        if (req.params.proposalId == 'new') {
            req.body.proposal._range = [new Date(moment().toISOString()), new Date(moment().toISOString())];
            req.body.proposal.pipelineDuration = [{
                pipelineFrom: req.body.proposal.pipeline, fDate: new Date(moment().toISOString()),
                pipelineTo: req.body.proposal.pipeline, tDate: new Date(moment().toISOString())
            }];
        } else {
            req.body.proposal._range = (typeof req.body.proposal._range == 'undefined') ? [] : (Array.isArray(req.body.proposal._range) ? req.body.proposal._range : [req.body.proposal._range]);
            req.body.proposal._range[0] = new Date(moment().toISOString());
            req.body.proposal._range[1] = (typeof req.body.proposal._range[1] == 'undefined') ? new Date(moment().toISOString()) : req.body.proposal._range[1];
            let exProposal = await Proposal.getOne(req.params.proposalId);
            if (exProposal) {
                //exProposal = exProposal.toObject();
                exProposal.pipelineDuration = typeof exProposal.pipelineDuration == 'undefined' ? [] : exProposal.pipelineDuration;
                exProposal.pipelineDuration.push({
                    pipelineFrom: exProposal.pipeline, fDate: exProposal._pipelineDate,
                    pipelineTo: req.body.proposal.pipeline, tDate: new Date(moment().toISOString())
                });
                req.body.proposal.pipelineDuration = exProposal.pipelineDuration;
            }
        }
        //update['$set']['_range.0'] = new Date(moment().toISOString());

        if (Array.isArray(req.body.proposal.company)) {
            let ids = [];
            var companies = req.body.proposal.company.filter(function (elem, pos) { return req.body.proposal.company.indexOf(elem) == pos; });// Remove duplicates;
            var counter = companies.length;
            for (var i = 0; i < companies.length; i++) {
                try {
                    counter -= 1;
                    var bdata = req.body.proposal;
                    bdata.company = companies[i];
                    let id = await Proposal.save(req.params.proposalId, bdata);
                    let data = { id: id, company: bdata.company };
                    ids.push(data);
                    //Include ProposalId in Prospect
                    Prospect.includeTargetId(id, bdata.company);
                    if (counter === 0) {
                        res.send(Proposal.success(ids[0]));
                    }
                } catch (e) {
                    console.log(e);
                }
            }
        } else {
            let oldProposal = req.params.proposalId == 'new' ? {} : await Proposal.getOneForHistory(req.params.proposalId);
            let id = await Proposal.save(req.params.proposalId, req.body.proposal);
            if (id) {
                let newProposal = await Proposal.getOneForHistory(id);                
                let meta = Proposal.jsonDiff(oldProposal, newProposal);
                await Proposal.addHistory((req.params.proposalId == 'new' ? 'Proposal created' : 'Proposal updated'), req.body.proposal.company, id, global._user._id, meta);
                let clientMessage = req.body['proposal-company-clientMessage'];
                if (typeof clientMessage != 'undefined' || clientMessage.length > 0) {
                    let oldMessage = await Company.getOne(req.body.proposal.company);
                    await Company.addClientMessage(req.body.proposal.company, clientMessage);
                    if (oldMessage.clientMessage != clientMessage) {
                        let meta = await Proposal.jsonDiff({ 'Message': oldMessage.clientMessage }, { 'Message': clientMessage });
                        await Proposal.addHistory('Company updated', req.body.proposal.company, '', global._user._id, meta);
                    }
                }
            }           
            let data = { id: id, company: req.body.proposal.company };
            res.send(Proposal.success(data));
        }


    } catch (err) {
        res.send(Proposal.error('An unexpected error was encountered while trying to save your proposal. Please try again.' + '\nDetails:' + JSON.stringify(err)));
    }
});

router.post('/companies/proposals/:proposalId/delete-file/:key', async (req, res) => {
    try {
        // load the proposal record
        let proposal = await Proposal.getOne(req.params.proposalId);

        // make sure the proposal even has files
        if (typeof proposal.files == 'undefined') {
            return res.send(Proposal.success());
        }
        let filterfiles = [];
        let fileUrl = '';

        proposal.files.forEach(function (file) {
            if (file.key == req.params.key) {
                fileUrl = file.url;
            } else {
                filterfiles.push(file);
            }
        });
        // update the proposal
        Proposal.model.updateOne({ _id: proposal._id }, { $set: { files: filterfiles } }, { runValidators: true }, (err) => {
            if (err) {
                return res.send(Proposal.error('An unexpected error was encountered while trying to delete the file. Please try again.'));
            }
            // delete the file
            fs.unlinkSync(`public${fileUrl}`);
            // and wrap it up
            res.send(Proposal.success());
        });
    } catch (err) {
        console.log('Error: ', err);
        res.status(500).send();
    }
});

router.post('/companies/save-file/:companyId', async (req, res) => {
    try {
        // convert the file date to an object from string
        req.body.file.date = new Date(moment(req.body.file.date, 'MM/DD/YYYY hh:mm A'));

        // save the file to the company record
        Company.model.updateOne(Company._enforceScope({
            _id: ObjectId(req.params.companyId)
        }), {
                $push: {
                    files: req.body.file
                }
            }, {
                runValidators: true
            }, (err) => {
                if (err) {
                    res.status(500).send();
                } else {
                    let meta = Company.jsonDiff({}, { files: '<a href="' + req.body.file.url + '" target="_blank">' + req.body.file.name + '</a>' });
                    Company.addHistory('File attached', req.params.companyId, '', global._user._id, meta);
                    res.send({ success: true });
                }
            });
    } catch (err) {
        res.status(500).send();
    }
});

router.post('/companies/delete-file/:companyId/:key', async (req, res) => {
    try {

        // load the proposal record
        let company = await Company.getOne(req.params.companyId);

        // make sure the company even has files
        if (typeof company.files == 'undefined') {
            return res.send(Company.success());
        }
        let filterfiles = [];
        let fileUrl = '';

        company.files.forEach(function (file) {
            if (file.key == req.params.key) {
                fileUrl = file.url;
            } else {
                filterfiles.push(file);
            }
        });

        Company.model.updateOne({ _id: company._id }, { $set: { files: filterfiles } }, { runValidators: true }, (err) => {
            if (err) {
                return res.send(Company.error('An unexpected error was encountered while trying to delete the file. Please try again.'));
            }
            // delete the file
            fs.unlinkSync(`public${fileUrl}`);
            // and wrap it up
            res.send(Company.success());
        });
    } catch (err) {
        console.log('Error: ', err);
        res.status(500).send();
    }
});

// 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);
});

router.get('/companies/getActivity/:activityId', async(req, res) => {
	company.activity = await Company.getActivity(company._id);
});

// save a company record
router.post('/companies/save/:companyId', async (req, res) => {
    try {
        // save the record normally
        let oldObj = {}, newObj = {};
        if (req.params.companyId != 'new') { oldObj = await Company.getOneForHistory(req.params.companyId); }

        if (req.params.companyId === 'new') {
            // Newly added companies should be saved and then appear on the Companies and Market Analysis pages
            req.body.source = "Companies";
            req.body.clientImport = global._user.account._id; // Source Account ID
            let marketId = await Prospect.save(req.params.companyId, req.body);
            req.body.market = marketId;
        }

        let id = await Company.save(req.params.companyId, req.body);

        newObj = await Company.getOneForHistory(id);
        let meta = Company.jsonDiff(oldObj, newObj);
        let history = await Company.addHistory((req.params.companyId == 'new' ? 'Company created' : 'Company updated'), id, '', global._user._id, meta);

        res.send(Company.success({ id }));
    } catch (err) {
        console.log('Error: ', err);
        res.send(Company.error('An unexpected error was encountered while trying to save your company.'));
    }
});

router.get('/companies/:companyId/get/custom/fields', async (req, res) => {
    try {
        if (req.params.companyId === undefined) { return res.send(Company.error('Undefined parameter company ID')); }
        let customFields = await Company.getOne(req.params.companyId);
        res.send(Company.success(customFields));
    } catch (err) {
        console.log('Error: ', err);
        res.send(Company.error('An unexpected error was encountered while trying to save your company.'));
    }
});
router.post('/companies/:companyId/save/custom/fields', async (req, res) => {
    try {
        if (req.params.companyId === undefined) { return res.send(Company.error('Undefined parameter company ID')); }
        let body = {};
        Object.keys(req.body).forEach((key) => {
            body[req.body[key][0]] = req.body[key][1];  
        });
        Object.keys(body).forEach((key) => {
            if (key === null || key === '') {
                delete body[key];
            }            
        });
        let beforeCF = await Company.getOne(req.params.companyId);
        let customFields = await Company.save(req.params.companyId, { customFields: body });
        let afterCF = await Company.getOne(req.params.companyId);
        let diff = await Company.jsonDiff(beforeCF.customFields, afterCF.customFields);
        let history = await Company.addHistory('Custom Fields Updated', req.params.companyId, '', global._user._id, diff);
        res.send(Company.success(customFields));
    } catch (err) {
        console.log('Error: ', err);
        res.send(Company.error('An unexpected error was encountered while trying to save your company.'));
    }
});

router.post('/companies/delete', async (req, res) => {
    if (typeof req.body.companies == 'undefined' || req.body.companies.length === 0) {
        return res.send(Company.error('Please provide one or more company ID\'s.'));
    }

    try {
        await Company.softDeleteItems(req.body.companies);
        res.send(Company.success());
    } catch (err) {
        res.send(Company.error('An unexpected error was encountered.'));
    }
});

router.post('/companies/proposal/delete/:proposalId', async (req, res) => {
    if (typeof req.params.proposalId == 'undefined') {
        return res.send(Company.error('It looks like your request was improperly defined. Please try again.'));
    }

    try {
        await Proposal.softDeleteItems([req.params.proposalId]);
        res.send(Company.success());
    } catch (err) {
        res.send(Proposal.error('An unexpected error was encountered while trying to load your proposal. Please try again.'));
    }
});

router.get('/companies/activities/:activityId', async(req, res)=> {
	try {
		//console.log(req.params.activityId);
		let activityId=req.params.activityId;
		let activity = await Activity.getOne(activityId);
		res.send(activity);
    } catch (err) {
        res.send(error('An unexpected error was encountered while trying to load your proposal. Please try again.'));
    }
});

module.exports = router;

Open in new window


...and here's my service file:

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) {
						let today = moment(rows[i].activities[j].date).isSame(moment(), 'day'); // get true/false indicating today
						rows[i].activities[j].icon = this.getIconByType(rows[i].activities[j].type);                        
						rows[i].activities[j].prettyDate = today ? "today" : moment(rows[i].activities[j].date).fromNow(); // set prettyDate to 'today' if we have a match, otherwise use moment
						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');
					}
					/*
					=======================================================
					the above for loop was put in as a replacement for what you see below in order to facilitate "today" rather than 5-6 hours ago
					or whatever might be registered in hours rather than something more readable like "today" (Bruce Gust 4/13/2020)					
					=======================================================
					*/
					/*
                    for (let j in rows[i].activities) {
						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]);

                }
			//console.log(activity.activities);
                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();

Open in new window


Finally, here's a screenshot of my "activity" collection...

screenshot of activity collection
How can I craft a basic delete for an activity (note)?
Node.jsJavaScript

Avatar of undefined
Last Comment
leakim971

8/22/2022 - Mon