import { Meteor } from 'meteor/meteor'; import { Mongo } from 'meteor/mongo'; import { check } from 'meteor/check'; import {SimpleSchema} from 'meteor/aldeed:simple-schema'; Sales = new Mongo.Collection('Sales'); let SalesSchema = new SimpleSchema({ date: { type: Date, label: "Date", optional: false, index: 1 }, amount: { type: Number, label: "Amount", optional: false, decimal: true }, price: { type: Number, label: "Price", optional: false, min: 0, exclusiveMin: true, decimal: true }, measureId: { type: String, label: "Measure Id", trim: false, regEx: SimpleSchema.RegEx.Id, index: 1 }, productId: { type: String, label: "Product Id", trim: false, regEx: SimpleSchema.RegEx.Id, index: 1 }, venueId: { type: String, label: "Vendor Id", trim: false, regEx: SimpleSchema.RegEx.Id, index: 1 // autoform: { // type: 'relation', // settings: { // collection: 'Venues', // fields: ['name'] // } // } }, comment: { type: String, trim: false, optional: true }, createdAt: { type: Date, label: "Created On", optional: false } }); Sales.attachSchema(SalesSchema); if(Meteor.isServer) { Meteor.publish('sales', function(query, sort, limit = 100, skipCount) { let dbQuery = []; if(query) { // _.each(_.keys(query), function(key) { // if(_.isObject(query[key])) dbQuery[key] = query[key]; // else if(_.isNumber(query[key])) dbQuery[key] = query[key]; // else dbQuery[key] = {$regex: RegExp.escape(query[key]), $options: 'i'}; // }); _.each(_.keys(query), function(key) { if(_.isObject(query[key])) dbQuery.push({[key]: query[key]}); else if(_.isNumber(query[key])) dbQuery.push({[key]: query[key]}); else { let searchValue = query[key]; let searches = searchValue && searchValue.length > 0 ? searchValue.split(/\s+/) : undefined; for(let search of searches) { dbQuery.push({[key]: {$regex: '\\b' + search, $options: 'i'}}); } } }); } if(!_.isNumber(limit)) limit = 100; if(!_.isNumber(skipCount) || skipCount < 0) skipCount = 0; dbQuery = dbQuery.length > 0 ? {$and: dbQuery} : {}; return Meteor.collections.Sales.find(dbQuery, {limit: limit, sort, skip: skipCount}); }); // time: expects either undefined, 'weekly', or 'monthly' // options: expects either undefined, 'markets', or 'types' Meteor.publish('salesTotals', function(time, options) { let pipeline = []; let group = { $group: { _id: { year: {$dateToString: {format: '%Y', date: '$date'}}//{$year: '$date'} }, 'total': { $sum: { $multiply: ['$price', '$amount'] } } } }; let project = { $project: { year: '$_id.year', date: '$_id.year', total: true, _id: {$concat: ['$_id.year']} } }; pipeline.push(group); pipeline.push(project); //Annual is assumed if not week or month. if(time === 'weekly') { group.$group._id.week = {$dateToString: {format: '%U', date: '$date'}}; //{$week: '$date'}; project.$project.week = '$_id.week'; project.$project.date = {$concat: ['$_id.week', '-', '$_id.year']}; project.$project._id.$concat.push('$_id.week'); } else if(time === 'monthly') { group.$group._id.month = {$dateToString: {format: '%m', date: '$date'}}; //{$month: '$date'}; project.$project.month = '$_id.month'; project.$project.date = {$concat: ['$_id.month', '-', '$_id.year']}; project.$project._id.$concat.push('$_id.month'); } if(options === 'markets') { group.$group._id.venueId = '$venueId'; project.$project.venueId = '$_id.venueId'; project.$project._id.$concat.push('$_id.venueId'); pipeline.push({$lookup: {from: 'Venues', localField: 'venueId', foreignField: '_id', as: 'venue'}}); pipeline.push({$project: {year: 1, week: 1, month: 1, total: 1, venueId: 1, venue: {$arrayElemAt: ['$venue', 0]}}}); pipeline.push({$project: {year: 1, week: 1, month: 1, total: 1, venueId: 1, venue: '$venue.name'}}); } else if(options === 'types') { //query[0].$group.month = {$month: '$date'}; //TODO: Need to divide the sales up by: // Sweets // Savories // Meats // VAP // Egg // Other Produce // Total Produce // Jars } ReactiveAggregate(this, Sales, pipeline, {clientCollection: 'salesTotals'}); /* {$sales: { _id: Random.id(), year: $_id.$year, total: $total }} */ //ReactiveAggregate(this, Sales, [query], {clientCollection: 'salesTotals', transform: function(doc) { // console.log("Running transform function"); // Object.assign(doc._id, doc); // doc._id = Random.id(); // return doc; //}}); }); Meteor.methods({ getSalesCount: function(query) { //TODO: Validate the query? return Sales.find(query).count(); }, insertSale: function(sale) { check(sale, { date: Date, amount: Match.Where(function(x) { check(x, Number); return x > 0; }), price: Match.Where(function(x) { check(x, Number); return x > 0; }), measureId: String, productId: String, venueId: String, comment: Match.Optional(String) }); //TODO: Check the structure of sale. Use: check(sale, {name: String, ...}); sale.createdAt = new Date(); if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) { Sales.insert(sale, function(err, id) { if(err) console.log(err); }, {bypassCollection2: true}); } else throw new Meteor.Error(403, "Not authorized."); }, deleteSale: function(id) { check(id, String); if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) { Sales.remove(id); } else throw new Meteor.Error(403, "Not authorized."); }, editSaleComment: function(id, comment) { check(id, String); check(comment, String); //Trim and convert empty comment to undefined. comment = comment ? comment.trim() : undefined; comment = comment && comment.length > 0 ? comment : undefined; if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) { console.log("Changed comment of " + id + " to: " + comment); if(comment) { Sales.update(id, {$set: {comment}}, function(error, count) { if(error) throw new Meteor.Error(400, "Unexpected database error: " + error); }); } else { Sales.update(id, {$unset: {comment: ""}}, function(error, count) { if(error) throw new Meteor.Error(400, "Unexpected database error: " + error); }); } } else throw new Meteor.Error(403, "Not authorized."); }, updateSale: function(id, date, venueId, price, amount) { check(id, String); check(date, Date); check(venueId, String); check(price, Number); check(amount, Number); if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) { Sales.update(id, {$set: {date, venueId, price, amount}}, function(err, id) { if(err) console.log(err); }, {bypassCollection2: true}); } } }); } //Allows the client to do DB interaction without calling server side methods, while still retaining control over whether the user can make changes. Sales.allow({ insert: function() {return false;}, update: function() {return false;}, remove: function() {return false;} }); export default Sales;