1 Commits

32 changed files with 549 additions and 373 deletions

View File

@@ -5,27 +5,26 @@
# but you can also edit it by hand.
meteor-base@1.5.1 # Packages every Meteor app needs to have
mobile-experience@1.1.0 # Packages for a great mobile UX
mongo@1.16.5 # The database Meteor supports right now
blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views
mobile-experience@1.1.1 # Packages for a great mobile UX
mongo@1.16.10 # The database Meteor supports right now
blaze-html-templates@2.0.1 # Compile .html files into Meteor Blaze views
reactive-var@1.0.12 # Reactive variable for tracker
tracker@1.3.1 # Meteor's client-side reactive programming library
tracker@1.3.3 # Meteor's client-side reactive programming library
tomwasd:history-polyfill # Adds IE 8/9 support for HTML5 history.
email@2.2.4 # Adds the Meteor/Email package for sending lost password emails
email@2.2.6 # Adds the Meteor/Email package for sending lost password emails
standard-minifier-css@1.9.0 # CSS minifier run for production mode
standard-minifier-css@1.9.2 # CSS minifier run for production mode
standard-minifier-js@2.8.1 # JS minifier run for production mode
es5-shim@4.8.0 # ECMAScript 5 compatibility for older browsers.
poorvavyas:es6-shim
ecmascript@0.16.6 # Enable ECMAScript2015+ syntax in app code
ecmascript@0.16.8 # Enable ECMAScript2015+ syntax in app code
#accounts-ui
#accounts-base
accounts-password@2.3.4
accounts-password@2.4.0
useraccounts:core
useraccounts:unstyled
useraccounts:flow-routing # Configures email flows. Used for AccountsTemplates class.
alanning:roles # Adds roles to the user mix. https://atmospherejs.com/alanning/roles && https://github.com/alanning/meteor-roles/blob/master/examples/flow-router/
kadira:flow-router
arillo:flow-router-helpers # Provides various template helpers such as {{pathFor 'templateName'}}
@@ -34,14 +33,12 @@ kadira:blaze-layout
#shell-server@0.4.0 # ???
#meteortoys:allthings
stylus@2.513.13
session@1.2.1
##browser-policy # Adds support for specifying browser level security rules related to content and what's allowed to laod on the page.
check@1.3.2 # Allows for checking the structure and types of arguments passed to Meteor methods and publications.
check@1.4.1 # Allows for checking the structure and types of arguments passed to Meteor methods and publications.
#audit-argument-checks # Used in combination with the Check package for checking the structure and types of arguments passed to Meteor methods and publications. Automatically alerts when a method or publication does not use a check() call.
aldeed:simple-schema@1.5.3
aldeed:collection2
#aldeed:simple-schema@1.5.3 #Should now be included in collection2 v4+
#matb33:collection-hooks # Allows the collections to register handlers that run before or after database interactions.
#zimme:collection-softremovable
@@ -60,10 +57,13 @@ juliancwirko:s-alert # Client error/alert handling
jcbernack:reactive-aggregate # Allows us to create a new client collection (from the server) with the contents being an aggregate of server data. Note that aggregation can only be done on the server currently as mini-mongo does not support it.
ostrio:logger
ostrio:loggermongo
dynamic-import@0.7.2
markdown@1.0.12
dynamic-import@0.7.3
markdown@2.0.0
wcrisman:jquery-custom-scrollbar
underscore@1.0.12
underscore@1.6.1
meteorhacks:aggregate # Allows databaseName.aggragate(pipeline) calls the exact same way you would on the command line in the mongo tool.
#babrahams:constellation # Provides client side debugging when the server is not run in production mode.
jquery
aldeed:collection2
fourseven:scss
alanning:roles

View File

@@ -1 +1 @@
METEOR@2.11.0
METEOR@2.16

View File

@@ -1,90 +1,87 @@
accounts-base@2.2.7
accounts-password@2.3.4
alanning:roles@1.3.0
aldeed:collection2@2.10.0
aldeed:collection2-core@1.2.0
aldeed:schema-deny@1.1.0
aldeed:schema-index@1.1.1
aldeed:simple-schema@1.5.4
accounts-base@2.2.11
accounts-password@2.4.0
alanning:roles@3.6.3
aldeed:collection2@4.0.2
aldeed:simple-schema@1.13.1
aldeed:template-extension@4.1.0
allow-deny@1.1.1
arillo:flow-router-helpers@0.5.2
autoupdate@1.8.0
babel-compiler@7.10.3
babel-compiler@7.10.5
babel-runtime@1.5.1
base64@1.0.12
binary-heap@1.0.11
blaze@2.5.0
blaze-html-templates@1.2.1
blaze-tools@1.1.3
boilerplate-generator@1.7.1
blaze@2.9.0
blaze-html-templates@2.0.1
blaze-tools@1.1.4
boilerplate-generator@1.7.2
caching-compiler@1.2.2
caching-html-compiler@1.2.0
callback-hook@1.5.0
check@1.3.2
caching-html-compiler@1.2.2
callback-hook@1.5.1
check@1.4.1
coffeescript@1.0.17
ddp@1.4.1
ddp-client@2.6.1
ddp-common@1.4.0
ddp-rate-limiter@1.1.1
ddp-server@2.6.0
ddp-client@2.6.2
ddp-common@1.4.1
ddp-rate-limiter@1.2.1
ddp-server@2.7.1
diff-sequence@1.1.2
dynamic-import@0.7.2
ecmascript@0.16.6
ecmascript-runtime@0.8.0
dynamic-import@0.7.3
ecmascript@0.16.8
ecmascript-runtime@0.8.1
ecmascript-runtime-client@0.12.1
ecmascript-runtime-server@0.11.0
ejson@1.1.3
email@2.2.4
email@2.2.6
es5-shim@4.8.0
fetch@0.1.3
fetch@0.1.4
fortawesome:fontawesome@4.7.0
fourseven:scss@4.16.0
geojson-utils@1.0.11
hot-code-push@1.0.4
html-tools@1.1.3
htmljs@1.1.1
http@1.4.4
html-tools@1.1.4
htmljs@1.2.1
http@2.0.0
id-map@1.1.1
inter-process-messaging@0.1.1
jcbernack:reactive-aggregate@0.7.0
jcbernack:reactive-aggregate@1.0.0
jquery@1.11.11
juliancwirko:s-alert@3.2.0
kadira:blaze-layout@2.3.0
kadira:flow-router@2.12.1
launch-screen@1.3.0
launch-screen@2.0.0
localstorage@1.2.0
logging@1.3.2
markdown@1.0.14
mdg:validation-error@0.2.0
meteor@1.11.1
logging@1.3.4
markdown@2.0.0
meteor@1.11.5
meteor-base@1.5.1
meteorhacks:aggregate@1.3.0
meteorhacks:collection-utils@1.2.0
minifier-css@1.6.2
minifier-js@2.7.5
minimongo@1.9.2
mobile-experience@1.1.0
minifier-css@1.6.4
minifier-js@2.8.0
minimongo@1.9.4
mobile-experience@1.1.1
mobile-status-bar@1.1.0
modern-browsers@0.1.9
modules@0.19.0
modern-browsers@0.1.10
modules@0.20.0
modules-runtime@0.13.1
momentjs:moment@2.29.3
mongo@1.16.5
momentjs:moment@2.30.1
mongo@1.16.10
mongo-decimal@0.1.3
mongo-dev-server@1.1.0
mongo-id@1.0.8
mongo-livedata@1.0.12
npm-mongo@4.14.0
observe-sequence@1.0.20
npm-mongo@4.17.2
observe-sequence@1.0.22
ordered-dict@1.1.0
ostrio:logger@2.1.1
ostrio:loggermongo@2.1.0
poorvavyas:es6-shim@0.21.1
promise@0.12.2
raix:eventemitter@0.1.3
raix:eventemitter@1.0.0
random@1.2.1
rate-limit@1.0.9
react-fast-refresh@0.2.6
rate-limit@1.1.1
react-fast-refresh@0.2.8
reactive-dict@1.3.1
reactive-var@1.0.12
reload@1.3.1
@@ -92,25 +89,26 @@ retry@1.1.0
routepolicy@1.1.1
session@1.2.1
sha@1.0.9
socket-stream-client@0.5.0
spacebars@1.2.0
spacebars-compiler@1.2.1
standard-minifier-css@1.9.0
socket-stream-client@0.5.2
spacebars@1.6.0
spacebars-compiler@1.3.2
standard-minifier-css@1.9.2
standard-minifier-js@2.8.1
stylus@2.513.14
templating@1.4.1
templating-compiler@1.4.1
templating-runtime@1.5.0
templating-tools@1.2.0
templating@1.4.3
templating-compiler@1.4.2
templating-runtime@1.6.4
templating-tools@1.2.3
tomwasd:history-polyfill@0.0.1
tracker@1.3.1
tracker@1.3.3
typescript@4.9.5
ui@1.0.13
underscore@1.0.12
underscore@1.6.1
url@1.3.2
useraccounts:core@1.16.2
useraccounts:flow-routing@1.15.0
useraccounts:unstyled@1.14.2
wcrisman:jquery-custom-scrollbar@3.0.0
webapp@1.13.4
webapp@1.13.8
webapp-hashing@1.1.1
zimme:active-route@2.3.2
zodern:types@1.0.13

View File

@@ -1,5 +1,10 @@
# Current Configuration
## NodeJS
Use [NVM](https://github.com/coreybutler/nvm-windows) on Windows to set the available versions of NodeJS. By default they are stored in `C:\Users\<user>\AppData\Roaming\nvm`.
## Setup
See below for initial setup and updating instructions.
Currently we have three servers in two locations. Media and FS2 are computers residing on the 18501 property (big barn back top room, and Wynne/Sarah house), and FS1 is a computer residing in SF. All three are accessible via Putty (a windows SSH client - any SSH client will work) via the IP's 192.168.3.101 (Media), 192.168.2.239 (FS1), and 192.168.3.164 (FS2). Note that FS1 is in SF, but is accessible via the LAN because we have a VPN setup between our routers (192.168.3.1, and 192.168.2.1). The VPN makes them look like they are on the same network.

View File

@@ -1,9 +1,20 @@
import {Mongo} from "meteor/mongo";
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
import 'meteor/aldeed:collection2/static'
import SimpleSchema from 'meteor/aldeed:simple-schema';
import Products from "./Product";
import Measures from "./Measure";
Barcodes = new Mongo.Collection('Barcodes');
let Barcodes = new Mongo.Collection('Barcodes');
if(Meteor.isServer) {
//Set MongoDB indexes (or remove them) here.
try {
Barcodes.rawCollection().createIndex({barcodeId: -1}, {unique: true})
Barcodes.rawCollection().createIndex({productAndMeasureId: -1}, {unique: true})
} catch(e) {console.log("Caught exception while setting indexes in MongoDB"); console.error(e)}
}
// A simple mapping between a concatenation of the product & measure ID and a unique sequential number for the barcode. This allows us to have a small number to keep our barcodes simple, while maintaining the more traditional MongoDB ID's for the Product and Measure.
const BarcodesSchema = new SimpleSchema({
@@ -11,15 +22,15 @@ const BarcodesSchema = new SimpleSchema({
type: Number,
label: "Barcode ID",
optional: false,
index: 1,
unique: true
//index: 1,
//unique: true
},
productAndMeasureId: { //Just the two ids jammed together with a single space between them.
type: String,
label: "Product And Measure ID",
optional: false,
index: 1,
unique: true
//index: 1,
//unique: true
}
});
@@ -29,15 +40,15 @@ if(Meteor.isServer) {
//});
Meteor.methods({
getBarcodeId: function(productId, measureId) {
getBarcodeId: async function(productId, measureId) {
check(productId, String);
check(measureId, String);
let hasProduct = Meteor.collections.Products.findOne({_id: productId}, {fields: {}});
let hasMeasure = Meteor.collections.Measures.findOne({_id: measureId}, {fields: {}});
let hasProduct = await Meteor.collections.Products.findOneAsync({_id: productId}, {fields: {}});
let hasMeasure = await Meteor.collections.Measures.findOneAsync({_id: measureId}, {fields: {}});
if(hasProduct && hasMeasure) {
let existing = Barcodes.findOne({productAndMeasureId: productId + ' ' + measureId});
let existing = await Barcodes.findOneAsync({productAndMeasureId: productId + ' ' + measureId});
if(existing) {
return existing.barcodeId;
@@ -48,10 +59,12 @@ if(Meteor.isServer) {
//Try a thousand times before failing. Should never fail, should also not ever need to try a thousand times (unless we somehow automate label generation to the point where a 1000 processes at once are requesting labels that have never been generated before - highly unlikely).
while(c++ < 1000) {
//Lookup the most likely next barcode id from the db, then attempt to insert with it. If it fails due to duplication, then increment and repeat.
let cursor = Products.find({}, {barcodeId: 1}).sort({barcodeId: -1}).limit(1); //Since currently products are never removed, we shouldn't need to detect sequence gaps and fill them in (odds are we will never use more than 10k numbers anyway).
let barcodeId = cursor.hasNext() ? cursor.next().barcodeId + 1 : 1;
//let cursor = Products.find({}, {barcodeId: 1}).sort({barcodeId: -1}).limit(1); //Since currently products are never removed, we shouldn't need to detect sequence gaps and fill them in (odds are we will never use more than 10k numbers anyway).
//let barcodeId = cursor.hasNext() ? cursor.next().barcodeId + 1 : 1;
let product = await Products.findOneAsync({barcodeId: {$exists: true}}, {sort: {barcodeId: -1}})
let barcodeId = product ? product.barcodeId : 1;
Barcodes.insert({productAndMeasureId: productId + ' ' + measureId, barcodeId}, function(err, id) {
await Barcodes.insertAsync({productAndMeasureId: productId + ' ' + measureId, barcodeId}, function(err, id) {
if(err) console.log(err);
else return barcodeId;
});

View File

@@ -1,7 +1,9 @@
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
import 'meteor/aldeed:collection2/static'
import SimpleSchema from 'meteor/aldeed:simple-schema';
import Measures from "./Measure";
/**
* Notes:
@@ -10,12 +12,24 @@ import {SimpleSchema} from 'meteor/aldeed:simple-schema';
*/
let Batches = new Mongo.Collection('Batches');
if(Meteor.isServer) {
//Set MongoDB indexes (or remove them) here.
try {
Batches.rawCollection().createIndex({date: -1}, {unique: false})
Batches.rawCollection().createIndex({measureId: -1}, {unique: false})
Batches.rawCollection().createIndex({productId: -1}, {unique: false})
Batches.rawCollection().createIndex({cookId: -1}, {unique: false})
Batches.rawCollection().createIndex({cannerId: -1}, {unique: false})
} catch(e) {console.log("Caught exception while setting indexes in MongoDB"); console.error(e)}
}
let BatchesSchema = new SimpleSchema({
date: {
type: Number, // A number in the format of YYYYMMDD to allow for searching using greater and less than, and to prevent timezones from messing everything up.
label: "Date",
optional: false,
index: 1
//index: 1
},
timestamp: { //This is based off the date with zero for the time and set to GMT (Zulu time).
type: Date,
@@ -31,21 +45,21 @@ let BatchesSchema = new SimpleSchema({
type: Number,
label: "Amount",
optional: false,
decimal: true
//decimal: true
},
measureId: {
type: String,
label: "Measure Id",
trim: false,
regEx: SimpleSchema.RegEx.Id,
index: 1
//index: 1
},
productId: {
type: String,
label: "Product Id",
trim: false,
regEx: SimpleSchema.RegEx.Id,
index: 1,
//index: 1,
optional: false
},
cookId: {
@@ -53,14 +67,14 @@ let BatchesSchema = new SimpleSchema({
label: "Cook Worker Id",
trim: false,
regEx: SimpleSchema.RegEx.Id,
index: 1
//index: 1
},
cannerId: {
type: String,
label: "Canner Worker Id",
trim: false,
regEx: SimpleSchema.RegEx.Id,
index: 1,
//index: 1,
optional: false
},
hasLabels: {
@@ -134,11 +148,11 @@ if(Meteor.isServer) {
});
Meteor.methods({
getBatchCount: function(query) {
getBatchCount: async function(query) {
//TODO: Validate the query?
return Batches.find(query).count();
return await Batches.countDocuments(query);
},
insertBatches: function(batches) { //Insert one or more batches (if one, you can pass just the batch).
insertBatches: async function(batches) { //Insert one or more batches (if one, you can pass just the batch).
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
//Force it to be an array if it isn't.
if(!Array.isArray(batches)) batches = [batches];
@@ -170,37 +184,37 @@ if(Meteor.isServer) {
}
for(let batch of batches) {
Batches.insert(batch, function(err, id) {
await Batches.insertAsync(batch, function(err, id) {
if(err) console.log(err);
}, {bypassCollection2: true});
}
}
else throw new Meteor.Error(403, "Not authorized.");
},
deleteBatch: function(id) { //Does not actually delete the batch, but rather just marks it for deleting by applying a deletion date.
deleteBatch: async function(id) { //Does not actually delete the batch, but rather just marks it for deleting by applying a deletion date.
check(id, String);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
let deletedAt = new Date();
//Batches.remove(id);
Batches.update(id, {$set: {deletedAt}}, function(err, id) {
await Batches.updateAsync(id, {$set: {deletedAt}}, function(err, id) {
if(err) console.log(err);
});
}
else throw new Meteor.Error(403, "Not authorized.");
},
undeleteBatch: function(id) { //Revokes the previous deletion.
undeleteBatch: async function(id) { //Revokes the previous deletion.
check(id, String);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Batches.update(id, {$unset: {deletedAt:""}}, function(err, id) {
await Batches.updateAsync(id, {$unset: {deletedAt:""}}, function(err, id) {
if(err) console.log(err);
});
}
else throw new Meteor.Error(403, "Not authorized.");
},
//editBatchComment: function(id, comment) {
//editBatchComment: async function(id, comment) {
// check(id, String);
// check(comment, String);
// //Trim and convert empty comment to undefined.
@@ -211,19 +225,19 @@ if(Meteor.isServer) {
// console.log("Changed comment of " + id + " to: " + comment);
//
// if(comment) {
// Batches.update(id, {$set: {comment}}, function(error, count) {
// await Batches.updateAsync(id, {$set: {comment}}, function(error, count) {
// if(error) throw new Meteor.Error(400, "Unexpected database error: " + error);
// });
// }
// else {
// Batches.update(id, {$unset: {comment: ""}}, function(error, count) {
// await Batches.updateAsync(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.");
//},
updateBatch: function(id, amount, comment) {
updateBatch: async function(id, amount, comment) {
check(id, String);
check(amount, Number);
check(comment, Match.OneOf(String, undefined));
@@ -237,13 +251,13 @@ if(Meteor.isServer) {
//let weekOfYear = timestamp.getWeek().toString();
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Batches.update(id, {$set: {comment, amount}}, function(err, id) {
await Batches.updateAsync(id, {$set: {comment, amount}}, function(err, id) {
if(err) console.log(err);
}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
setBatchHasLabels: function(id, hasLabels) {
setBatchHasLabels: async function(id, hasLabels) {
//console.log(id);
//console.log(hasLabels);
//check(id, Meteor.validators.ObjectID);
@@ -251,7 +265,7 @@ if(Meteor.isServer) {
check(hasLabels, Boolean);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Batches.update(id, {$set: {hasLabels}}, function(err, id) {
await Batches.updateAsync(id, {$set: {hasLabels}}, function(err, id) {
if(err) console.log(err);
}, {bypassCollection2: true});
}

View File

@@ -1,7 +1,8 @@
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
import 'meteor/aldeed:collection2/static'
import SimpleSchema from 'meteor/aldeed:simple-schema';
import LabelFormats from '/imports/LabelFormats.js';
@@ -36,13 +37,13 @@ if(Meteor.isServer) {
});
//Returns a JSON containing a denormalized list of products {product_id, measure_id, product_name, measure_name, price, }
WebApp.connectHandlers.use("/labels/GetBarCodeData", (req, res, next) => {
WebApp.connectHandlers.use("/labels/GetBarCodeData", async (req, res, next) => {
try {
let barcodes = Meteor.collections.Barcodes.find({}, {fields: {_id: 0, barcodeId: 1, productAndMeasureId: 1}});
let measures = Meteor.collections.Measures.find({}, {fields: {_id: 1, name: 1}, sort: {order: 1}}).fetch();
let measures = await Meteor.collections.Measures.find({}, {fields: {_id: 1, name: 1}, sort: {order: 1}}).fetchAsync();
//Note: Price data looks like this: {XZ5Z3CM49NDrJNADA /* MeasureID */: {price: 10.5, effectiveDate: ISODate("2017-01-12T13:14:18.876-08:00"), previousPrice: 9}, ...}
//Measures is an array of MeasureIDs valid for this product.
let products = Meteor.collections.Products.find({}, {fields: {_id: 1, name: 1, measures: 1, prices: 1}, sort: {order: 1}}).fetch();
let products = await Meteor.collections.Products.find({}, {fields: {_id: 1, name: 1, measures: 1, prices: 1}, sort: {order: 1}}).fetchAsync();
let measuresById = {};
let barcodesByProductAndMeasureIds = {};
let result = {};

View File

@@ -6,7 +6,7 @@ import { LoggerMongo } from 'meteor/ostrio:loggermongo';
// The logging tool is primarily for managing administrative functions such that administrators can view the app logs and issue commands that might generate administrative logging.
Meteor.log = new Logger();
Logs = new Mongo.Collection('Logs');
let Logs = new Mongo.Collection('Logs');
let logMongo = new LoggerMongo(Meteor.log, {
collection: Logs
@@ -23,8 +23,8 @@ if(Meteor.isServer) {
return Logs.find({}, {limit: 10000});
});
Meteor.methods({
clearLogs: function() {
return Logs.remove({}, function(err) {
clearLogs: async function() {
return await Logs.removeAsync({}, function(err) {
if(err) Meteor.log.error(err);
});
}

View File

@@ -1,17 +1,26 @@
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
import 'meteor/aldeed:collection2/static'
import SimpleSchema from 'meteor/aldeed:simple-schema';
let Measures = new Mongo.Collection('Measures');
if(Meteor.isServer) {
//Set MongoDB indexes (or remove them) here.
try {
Measures.rawCollection().createIndex({name: -1}, {unique: true})
} catch(e) {console.log("Caught exception while setting indexes in MongoDB"); console.error(e)}
}
Measures.attachSchema(new SimpleSchema({
name: {
type: String,
label: "Name",
optional: false,
trim: true,
index: 1,
unique: true
// index: 1, Requires aldeed:schema-index which requires an older version fo aldeed:collection2 (3.5.0 vs 4.0.2) This can be achieved by adding indexes to MongoDB directly.
//unique: true
},
postfix: {
type: String,
@@ -63,39 +72,39 @@ if(Meteor.isServer) Meteor.publish('measures', function() {
if(Meteor.isServer) {
Meteor.methods({
createMeasure: function(name, postfix, order) {
createMeasure: async function(name, postfix, order) {
check(name, String);
check(postfix, String);
check(order, Number);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Measures.insert({name, postfix, order, createdAt: new Date()});
await Measures.insertAsync({name, postfix, order, createdAt: new Date()});
}
else throw new Meteor.Error(403, "Not authorized.");
},
deactivateMeasure: function(id) {
deactivateMeasure: async function(id) {
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
//Measures.remove(id);
Measures.update(id, {$set: {deactivated: true}}, {bypassCollection2: true});
await Measures.updateAsync(id, {$set: {deactivated: true}}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
reactivateMeasure: function(id) {
reactivateMeasure: async function(id) {
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Measures.update(id, {$set: {deactivated: false}}, {bypassCollection2: true});
await Measures.updateAsync(id, {$set: {deactivated: false}}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
hideMeasure: function(id) { //One step past deactivated - will only show in the measures list if hidden measures are enabled.
hideMeasure: async function(id) { //One step past deactivated - will only show in the measures list if hidden measures are enabled.
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
//Measures.remove(id);
Measures.update(id, {$set: {hidden: true}}, {bypassCollection2: true});
await Measures.updateAsync(id, {$set: {hidden: true}}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
showMeasure: function(id) { //Returns the measure to being simply deactivated. Will again show in lists.
showMeasure: async function(id) { //Returns the measure to being simply deactivated. Will again show in lists.
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Measures.update(id, {$set: {hidden: false}}, {bypassCollection2: true});
await Measures.updateAsync(id, {$set: {hidden: false}}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
@@ -106,14 +115,14 @@ if(Meteor.isServer) {
// }
// else throw new Meteor.Error(403, "Not authorized.");
//},
updateMeasure: function(id, name, postfix, order) {
updateMeasure: async function(id, name, postfix, order) {
check(id, String);
check(name, String);
check(postfix, String);
check(order, Number);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Measures.update(id, {$set: {name, postfix, order, updatedAt: new Date()}});
await Measures.updateAsync(id, {$set: {name, postfix, order, updatedAt: new Date()}});
}
else throw new Meteor.Error(403, "Not authorized.");
}

View File

@@ -1,7 +1,9 @@
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
import 'meteor/aldeed:collection2/static'
import SimpleSchema from 'meteor/aldeed:simple-schema';
import Measures from "./Measure";
/**
* Notes:
@@ -16,21 +18,31 @@ import {SimpleSchema} from 'meteor/aldeed:simple-schema';
let Products = new Mongo.Collection('Products');
if(Meteor.isServer) {
//Set MongoDB indexes (or remove them) here.
try {
Products.rawCollection().createIndex({name: -1}, {unique: true})
} catch(e) {console.log("Caught exception while setting indexes in MongoDB"); console.error(e)}
}
const ProductsSchema = new SimpleSchema({
name: {
type: String,
label: "Name",
optional: false,
trim: true,
index: 1,
unique: true
//index: 1,
//unique: true
},
tags: { //An array of ProductTag names. Note that we are not using the ProductTag ID's because I want a looser connection (if a ProductTag is deleted, it isn't a big deal if it isn't maintained in the Product records).
type: [String],
type: Array, //[String],
label: "Tags",
optional: false,
defaultValue: []
},
'tags.$': {
type: String,
},
measures: { //A JSON array of Measure ID's.
type: Array,
label: "Measures",
@@ -181,7 +193,7 @@ if(Meteor.isServer) {
});
Meteor.methods({
createProduct: function(name, tags, aliases, measures) {
createProduct: async function(name, tags, aliases, measures) {
check(name, String);
if(tags) check(tags, [String]);
if(aliases) check(aliases, [String]);
@@ -192,48 +204,48 @@ if(Meteor.isServer) {
//let cursor = Products.find({}, {barCodeId: 1}).sort({barCodeId: -1}).limit(1); //Since currently products are never removed, we shouldn't need to detect sequence gaps and fill them in (odds are we will never use more than 10k numbers anyway).
//let barCodeId = cursor.hasNext() ? cursor.next().barCodeId : 1;
//
Products.insert({name, tags, aliases, measures, createdAt: new Date()}, {bypassCollection2: true}, function(err, id) {
await Products.insertAsync({name, tags, aliases, measures, createdAt: new Date()}, {bypassCollection2: true}, function(err, id) {
if(err) console.log(err);
});
}
else throw new Meteor.Error(403, "Not authorized.");
},
convertProduct: function(productId, alternateProductId) {
convertProduct: async function(productId, alternateProductId) {
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
check(productId, String);
check(alternateProductId, String);
// Replace all sale references to the given ID with the provided alternate product ID.
Meteor.collections.Sales.update({productId: productId}, {$set: {productId: alternateProductId}}, {multi: true});
await Meteor.collections.Sales.updateAsync({productId: productId}, {$set: {productId: alternateProductId}}, {multi: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
deactivateProduct: function(id) {
deactivateProduct: async function(id) {
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
//Products.remove(id);
Products.update(id, {$set: {deactivated: true}}, {bypassCollection2: true});
await Products.updateAsync(id, {$set: {deactivated: true}}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
reactivateProduct: function(id) {
reactivateProduct: async function(id) {
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Products.update(id, {$set: {deactivated: false}}, {bypassCollection2: true});
await Products.updateAsync(id, {$set: {deactivated: false}}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
hideProduct: function(id) { //One step past deactivated - will only show in the products list if hidden products are enabled.
hideProduct: async function(id) { //One step past deactivated - will only show in the products list if hidden products are enabled.
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
//Products.remove(id);
Products.update(id, {$set: {hidden: true}}, {bypassCollection2: true});
await Products.updateAsync(id, {$set: {hidden: true}}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
showProduct: function(id) { //Returns the product to being simply deactivated. Will again show in lists.
showProduct: async function(id) { //Returns the product to being simply deactivated. Will again show in lists.
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Products.update(id, {$set: {hidden: false}}, {bypassCollection2: true});
await Products.updateAsync(id, {$set: {hidden: false}}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
updateProduct: function(id, name, tags, aliases, measures) {
updateProduct: async function(id, name, tags, aliases, measures) {
check(id, String);
check(name, String);
if(tags) check(tags, [String]);
@@ -241,22 +253,22 @@ if(Meteor.isServer) {
if(measures) check(measures, [String]);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Products.update(id, {$set: {name: name, tags: tags, aliases: aliases, measures: measures, updatedAt: new Date()}}, {bypassCollection2: true});
await Products.updateAsync(id, {$set: {name: name, tags: tags, aliases: aliases, measures: measures, updatedAt: new Date()}}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
clearProductPrice: function(productIds, measureId) {
clearProductPrice: async function(productIds, measureId) {
check(productIds, [String]);
check(measureId, String);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
let attr = "prices." + measureId;
Products.update({_id: {$in: productIds}}, {$unset: {[attr]: true}}, {validate: false, bypassCollection2: true});
await Products.updateAsync({_id: {$in: productIds}}, {$unset: {[attr]: true}}, {validate: false, bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
setProductPrice: function(productIds, measureId, price, setPrevious, effectiveDate) {
setProductPrice: async function(productIds, measureId, price, setPrevious, effectiveDate) {
check(productIds, [String]);
check(measureId, String);
check(price, Number);
@@ -283,14 +295,14 @@ if(Meteor.isServer) {
measurePriceData.price = price;
if(ProductsSchema.newContext().isValid()) {
Products.update(product._id, {$set: {prices: prices, updateAt: new Date()}}, {validate: false, bypassCollection2: true});
await Products.updateAsync(product._id, {$set: {prices: prices, updateAt: new Date()}}, {validate: false, bypassCollection2: true});
}
else console.log("Invalid schema for product");
}
}
else throw new Meteor.Error(403, "Not authorized.");
},
tagProducts: function(productIds, tagId) {
tagProducts: async function(productIds, tagId) {
//Tags the products if any products don't have the tag, otherwise removes the tag from all products.
check(productIds, [String]);
check(tagId, String);
@@ -299,10 +311,10 @@ if(Meteor.isServer) {
let productsWithTag = Products.find({_id: {$in: productIds}, tags: {$all: [tagId]}}).count();
if(productsWithTag == productIds.length) {
Products.update({_id: {$in: productIds}}, {$pullAll: {tags: [tagId]}}, {bypassCollection2: true, multi: true});
await Products.updateAsync({_id: {$in: productIds}}, {$pullAll: {tags: [tagId]}}, {bypassCollection2: true, multi: true});
}
else {
Products.update({_id: {$in: productIds}}, {$addToSet: {tags: tagId}}, {bypassCollection2: true, multi: true});
await Products.updateAsync({_id: {$in: productIds}}, {$addToSet: {tags: tagId}}, {bypassCollection2: true, multi: true});
}
}
else throw new Meteor.Error(403, "Not authorized.");

View File

@@ -1,17 +1,19 @@
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check'
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
import 'meteor/aldeed:collection2/static'
import SimpleSchema from 'meteor/aldeed:simple-schema';
import Measures from "./Measure";
ProductTags = new Mongo.Collection('ProductTags', {
let ProductTags = new Mongo.Collection('ProductTags', {
schema: new SimpleSchema({
name: {
type: String,
label: "Name",
optional: false,
trim: true,
index: 1,
unique: true
//index: 1,
//unique: true
},
createdAt: {
type: Date,
@@ -26,6 +28,13 @@ ProductTags = new Mongo.Collection('ProductTags', {
})
});
if(Meteor.isServer) {
//Set MongoDB indexes (or remove them) here.
try {
ProductTags.rawCollection().createIndex({name: -1}, {unique: true})
} catch(e) {console.log("Caught exception while setting indexes in MongoDB"); console.error(e)}
}
//Allows the client to do DB interaction without calling server side methods, while still retaining control over whether the user can make changes.
ProductTags.allow({
insert: function() {return false;},
@@ -39,23 +48,23 @@ if(Meteor.isServer) {
});
Meteor.methods({
insertProductTag: function(productTag) {
insertProductTag: async function(productTag) {
check(productTag, String);
productTag = {name: productTag, createdAt: new Date()};
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
ProductTags.insert(productTag, function(err, id) {
await ProductTags.insertAsync(productTag, function(err, id) {
if(err) console.log(err);
});
}
else throw new Meteor.Error(403, "Not authorized.");
},
deleteProductTag: function(id) {
deleteProductTag: async function(id) {
check(id, String);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
//Remove all references to the tag in the products.
Meteor.collections.Products.update({tags: {$all: [id]}}, {$pullAll: {tags: [id]}}, {bypassCollection2: true, multi: true});
await Meteor.collections.Products.updateAsync({tags: {$all: [id]}}, {$pullAll: {tags: [id]}}, {bypassCollection2: true, multi: true});
// let products = Meteor.collections.Products.find({tags: {$all: [id]}}, {_id: 1, tags: 1}).fetch();
//
// //Try to remove all tags from products. Log/Ignore any errors.
@@ -73,17 +82,17 @@ if(Meteor.isServer) {
// }
// }
ProductTags.remove(id);
await ProductTags.removeAsync(id);
}
else throw new Meteor.Error(403, "Not authorized.");
},
updateProductTag: function(tag) {
updateProductTag: async function(tag) {
check(tag, {
_id: String,
name: String
});
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
ProductTags.update(tag._id, {$set: {name: tag.name, updatedAt: new Date()}});
await ProductTags.updateAsync(tag._id, {$set: {name: tag.name, updatedAt: new Date()}});
}
else throw new Meteor.Error(403, "Not authorized.");
}

View File

@@ -66,7 +66,7 @@ if(Meteor.isServer) {
}
}]);
result.toArray().then(function(result) {
result.toArray().then(async function(result) {
let totalByYear = {};
//Create a map of maps: year -> measure id -> sales count.
@@ -80,7 +80,7 @@ if(Meteor.isServer) {
}
//Create a list of ordered measures. We could use a map, but then getting the ordering correct would be difficult.
let measures = Meteor.collections.Measures.find({}, {fields: {_id: 1, name: 1}, sort: {order: 1}}).fetch();
let measures = await Meteor.collections.Measures.find({}, {fields: {_id: 1, name: 1}, sort: {order: 1}}).fetchAsync();
//Collect the years in ascending oder.
let years = Object.keys(totalByYear).sort(function(a, b) {return parseInt(a) - parseInt(b);});
@@ -130,7 +130,7 @@ if(Meteor.isServer) {
}
}]);
result.toArray().then(function(result) {
result.toArray().then(async function(result) {
let productSalesTotalsMapByYear = {};
//Create a map of maps: year -> product id -> sales totals.
@@ -145,7 +145,7 @@ if(Meteor.isServer) {
//Now create a mapping between the tag id's and tag names for later use.
let tagIdToTagNameMap = {};
let tags = Meteor.collections.ProductTags.find({}, {fields: {_id: 1, name: 1}}).fetch();
let tags = await Meteor.collections.ProductTags.find({}, {fields: {_id: 1, name: 1}}).fetchAsync();
for(let tag of tags) {
tagIdToTagNameMap[tag._id] = tag.name;
@@ -153,7 +153,7 @@ if(Meteor.isServer) {
//Now create a map between tag names -> [product ids] so that we can build a table below.
let tagProductIdsMap = {};
let products = Meteor.collections.Products.find({}, {fields: {_id: 1, tags: 1}}).fetch();
let products = await Meteor.collections.Products.find({}, {fields: {_id: 1, tags: 1}}).fetchAsync();
for(let product of products) {
for(let tagId of product.tags) {
@@ -234,7 +234,7 @@ if(Meteor.isServer) {
}
}]);
result.toArray().then(function(result) {
result.toArray().then(async function(result) {
let totalByYear = {};
//Create a map of maps: year -> product id -> measure id -> sales count.
@@ -252,10 +252,10 @@ if(Meteor.isServer) {
}
//Create a list of ordered measures. We could use a map, but then getting the ordering correct would be difficult.
let measures = Meteor.collections.Measures.find({}, {fields: {_id: 1, name: 1}, sort: {order: 1}}).fetch();
let measures = await Meteor.collections.Measures.find({}, {fields: {_id: 1, name: 1}, sort: {order: 1}}).fetcAsync();
//Now create a mapping between the product id's and product names for later use.
let products = Meteor.collections.Products.find({}, {fields: {_id: 1, name: 1}, sort: {name: 1}}).fetch();
let products = await Meteor.collections.Products.find({}, {fields: {_id: 1, name: 1}, sort: {name: 1}}).fetcAsync();
//Collect the years in ascending oder.
let years = Object.keys(totalByYear).sort(function(a, b) {return parseInt(a) - parseInt(b);});
@@ -367,7 +367,7 @@ if(Meteor.isServer) {
}
}]);
result.toArray().then(function(result) {
result.toArray().then(async function(result) {
let totalByYear = {};
//Create a map of maps: year -> product id -> measure id -> sales count.
@@ -385,11 +385,11 @@ if(Meteor.isServer) {
}
//Create a list of ordered measures. We could use a map, but then getting the ordering correct would be difficult.
let measures = Meteor.collections.Measures.find({}, {fields: {_id: 1, name: 1}, sort: {order: 1}}).fetch();
let measures = await Meteor.collections.Measures.find({}, {fields: {_id: 1, name: 1}, sort: {order: 1}}).fetcAsync();
//Now create a mapping between the tag id's and tag names for later use.
let tagIdToTagNameMap = {};
let tags = Meteor.collections.ProductTags.find({}, {fields: {_id: 1, name: 1}}).fetch();
let tags = await Meteor.collections.ProductTags.find({}, {fields: {_id: 1, name: 1}}).fetcAsync();
for(let tag of tags) {
tagIdToTagNameMap[tag._id] = tag.name;
@@ -397,7 +397,7 @@ if(Meteor.isServer) {
//Now create a map between tag names -> [product ids] so that we can build a table below.
let tagProductIdsMap = {};
let products = Meteor.collections.Products.find({}, {fields: {_id: 1, tags: 1}}).fetch();
let products = await Meteor.collections.Products.find({}, {fields: {_id: 1, tags: 1}}).fetcAsync();
for(let product of products) {
for(let tagId of product.tags) {

View File

@@ -1,11 +1,21 @@
import { Roles } from 'meteor/alanning:roles'
if(Meteor.isServer) {
Meteor.publish('roles', function() {
//console.log("Checking if user is in the manage role: " + Meteor.userId() + " === " + Roles.userIsInRole(this.userId, ['manage']))
if(Roles.userIsInRole(this.userId, ['manage'])) {
return Meteor.roles.find({}, {fields: {name: 1}});
//Meteor.roles.find({}, {fields: {name: 1}}).fetchAsync().then(roles => {console.log(roles)})
//return Meteor.roles.find({}, {fields: {name: 1}});
return Meteor.roles.find({});
}
else throw new Meteor.Error(403, "Not authorized to view roles.");
});
Meteor.publish("roleAssignments", function() {
if(Roles.userIsInRole(this.userId, ['manage'])) {
return Meteor.roleAssignment.find({});
}
else throw new Meteor.Error(403, "Not authorized to view roles.");
})
}
let ROLE_MANAGE = "manage";
@@ -13,5 +23,12 @@ let ROLE_UPDATE = "update";
Meteor.UserRoles = {ROLE_MANAGE, ROLE_UPDATE};
// This is the collection that maps users to roles (v3 of alanning:roles).
//Meteor.roleAssignment
// This is where you will find the roles defained in MongoDB:
//Meteor.roles
Roles.createRoleAsync(ROLE_MANAGE)
Roles.createRoleAsync(ROLE_UPDATE)
export default Meteor.roles;

View File

@@ -1,21 +1,34 @@
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
import 'meteor/aldeed:collection2/static'
import SimpleSchema from 'meteor/aldeed:simple-schema';
import Batches from "./Batch";
import Measures from "./Measure";
/**
* Notes:
* The Sale object has a date field which stores the date as a number in the format YYYYMMDD. Converting this number into a local date is done with moment(sale.date.toString(), "YYYYMMDD").toDate(), and converting it to a number from a date can be accomplished with ~~(moment(date).format("YYYYMMDD")), where the ~~ is a bitwise not and converts a string to a number quickly and reliably.
*/
Sales = new Mongo.Collection('Sales');
let Sales = new Mongo.Collection('Sales');
if(Meteor.isServer) {
//Set MongoDB indexes (or remove them) here.
try {
Sales.rawCollection().createIndex({date: -1}, {unique: false})
Sales.rawCollection().createIndex({productId: -1}, {unique: false})
Sales.rawCollection().createIndex({measureId: -1}, {unique: false})
Sales.rawCollection().createIndex({venueId: -1}, {unique: false})
} catch(e) {console.log("Caught exception while setting indexes in MongoDB"); console.error(e)}
}
let SalesSchema = new SimpleSchema({
date: {
type: Number, // A number in the format of YYYYMMDD to allow for searching using greater and less than, and to prevent timezones from messing everything up.
label: "Date",
optional: false,
index: 1
//index: 1
},
timestamp: {
type: Date,
@@ -31,7 +44,7 @@ let SalesSchema = new SimpleSchema({
type: Number,
label: "Amount",
optional: false,
decimal: true
//decimal: true
},
price: {
type: Number,
@@ -39,28 +52,29 @@ let SalesSchema = new SimpleSchema({
optional: false,
min: 0,
exclusiveMin: true,
decimal: true
//decimal: true
},
measureId: {
type: String,
label: "Measure Id",
trim: false,
regEx: SimpleSchema.RegEx.Id,
index: 1
//index: 1
},
productId: {
type: String,
label: "Product Id",
trim: false,
regEx: SimpleSchema.RegEx.Id,
index: 1
//index: 1
},
venueId: {
type: String,
label: "Vendor Id",
trim: false,
regEx: SimpleSchema.RegEx.Id,
index: 1
//index: 1
// autoform: {
// type: 'relation',
// settings: {
@@ -280,11 +294,11 @@ if(Meteor.isServer) {
});
Meteor.methods({
getSalesCount: function(query) {
getSalesCount: async function(query) {
//TODO: Validate the query?
return Sales.find(query).count();
return await Sales.countDocuments(query);
},
insertSale: function(sale) {
insertSale: async function(sale) {
check(sale, {
date: Number, // TODO: Check that the format is YYYYMMDD
amount: Match.Where(function(x) {
@@ -308,21 +322,21 @@ if(Meteor.isServer) {
sale.weekOfYear = sale.timestamp.getWeek().toString();
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Sales.insert(sale, function(err, id) {
await Sales.insertAsync(sale, function(err, id) {
if(err) console.log(err);
}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
deleteSale: function(id) {
deleteSale: async function(id) {
check(id, String);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Sales.remove(id);
await Sales.removeAsync(id);
}
else throw new Meteor.Error(403, "Not authorized.");
},
editSaleComment: function(id, comment) {
editSaleComment: async function(id, comment) {
check(id, String);
check(comment, String);
//Trim and convert empty comment to undefined.
@@ -333,19 +347,19 @@ if(Meteor.isServer) {
console.log("Changed comment of " + id + " to: " + comment);
if(comment) {
Sales.update(id, {$set: {comment}}, function(error, count) {
await Sales.updateAsync(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) {
await Sales.updateAsync(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) {
updateSale: async function(id, date, venueId, price, amount) {
check(id, String);
check(date, Number); // TODO: Check that the format is YYYYMMDD
check(venueId, String);
@@ -357,45 +371,45 @@ if(Meteor.isServer) {
let weekOfYear = timestamp.getWeek().toString();
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Sales.update(id, {$set: {date, venueId, price, amount, timestamp, weekOfYear}}, function(err, id) {
await Sales.updateAsync(id, {$set: {date, venueId, price, amount, timestamp, weekOfYear}}, function(err, id) {
if(err) console.log(err);
}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
countSales: function() {
countSales: async function() {
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
return Sales.find({}).count();
return await Sales.countDocuments({});
}
else throw new Meteor.Error(403, "Not authorized.");
},
removeDuplicateSales: function(id, justOne) { // Expects the id of the sale that has duplicates and an optional boolean flag (justOne) indicating whether just one duplicate should be removed, or all of them (default).
removeDuplicateSales: async function(id, justOne) { // Expects the id of the sale that has duplicates and an optional boolean flag (justOne) indicating whether just one duplicate should be removed, or all of them (default).
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
// Remove either one or all of the duplicates of the Sale with the given ID.
if(justOne) {
let sale = Sales.findOne({isDuplicateOf: id});
let sale = await Sales.findOneAsync({isDuplicateOf: id});
if(sale) {
Sales.remove({_id: sale._id});
await Sales.removeAsync({_id: sale._id});
}
}
else {
Sales.remove({isDuplicateOf: id});
await Sales.removeAsync({isDuplicateOf: id});
}
}
},
ignoreDuplicateSales: function(id) { // Expects the id of the sale that has duplicates. Will mark this sale and all duplicates to be ignored in future duplicate checks.
ignoreDuplicateSales: async function(id) { // Expects the id of the sale that has duplicates. Will mark this sale and all duplicates to be ignored in future duplicate checks.
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
// Mark to ignore duplicates for this Sale (id) and all its duplicates, and clear any duplicate counts and references.
//Sales.update({$or: [{_id: id}, {isDuplicateOf: id}]}, {$set: {ignoreDuplicates: true}, $unset: {isDuplicateOf: "", duplicateCount: ""}});
// Mark to ignore duplicates for this Sale (id). We will leave the duplicate count and references so that the duplicates will show in a query if we want to revisit those marked as ignored.
Sales.update({$or: [{_id: id}, {isDuplicateOf: id}]}, {$set: {ignoreDuplicates: true}});
await Sales.updateAsync({$or: [{_id: id}, {isDuplicateOf: id}]}, {$set: {ignoreDuplicates: true}});
}
},
markDuplicateSales: function() {
markDuplicateSales: async function() {
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
let sales = Sales.find({}, {sort: {date: 1, venueId: 1, productId: 1, price: 1, amount: 1, measureId: 1, createdAt: 1}}).fetch();
let sales = await Sales.find({}, {sort: {date: 1, venueId: 1, productId: 1, price: 1, amount: 1, measureId: 1, createdAt: 1}}).fetchAsync();
// Iterate over all the sales looking for sales that have duplicates.
// Since the sales are sorted by sale date, venueId, productId, price, amount, and measureId which all must be identical to be considered a possible duplicate sale, we only have to check subsequent sales until a non-duplicate is found.
@@ -405,7 +419,7 @@ if(Meteor.isServer) {
// If this is marked as a duplicate of another sale, but we got to this point in the loop then the sale it is a duplicate of must have been removed or marked to ignore duplicates.
if(sale.isDuplicateOf) {
delete sale.isDuplicateOf;
Sales.update(sale._id, {$unset: {isDuplicateOf: ""}}, function(err, id) {
await Sales.updateAsync(sale._id, {$unset: {isDuplicateOf: ""}}, function(err, id) {
if(err) console.log(err);
}, {bypassCollection2: true});
}
@@ -426,7 +440,7 @@ if(Meteor.isServer) {
if(checkSale && sale.productId === checkSale.productId && sale.venueId === checkSale.venueId && sale.price === checkSale.price && sale.amount === checkSale.amount && sale.measureId === checkSale.measureId) {
// Mark the next sale as a duplicate of the currently examined sale.
checkSale.isDuplicateOf = sale._id;
Sales.update(checkSale._id, {$set: {isDuplicateOf: checkSale.isDuplicateOf}}, function(err, id) {
await Sales.updateAsync(checkSale._id, {$set: {isDuplicateOf: checkSale.isDuplicateOf}}, function(err, id) {
if(err) console.log(err);
}, {bypassCollection2: true});
duplicateCount++;
@@ -442,7 +456,7 @@ if(Meteor.isServer) {
if(sale.duplicateCount !== duplicateCount) {
// Update the sale's duplicate count.
sale.duplicateCount = duplicateCount;
Sales.update(sale._id, {$set: {duplicateCount: sale.duplicateCount}}, function(err, id) {
await Sales.updateAsync(sale._id, {$set: {duplicateCount: sale.duplicateCount}}, function(err, id) {
if(err) console.log(err);
}, {bypassCollection2: true});
}
@@ -450,7 +464,7 @@ if(Meteor.isServer) {
else if(sale.duplicateCount) {
// Remove the duplicate count if it is set.
delete sale.duplicateCount;
Sales.update(sale._id, {$unset: {duplicateCount: ""}}, function(err, id) {
await Sales.updateAsync(sale._id, {$unset: {duplicateCount: ""}}, function(err, id) {
if(err) console.log(err);
}, {bypassCollection2: true});
}

View File

@@ -1,9 +1,18 @@
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
import 'meteor/aldeed:collection2/static'
import SimpleSchema from 'meteor/aldeed:simple-schema';
import Measures from "./Measure";
SalesSheets = new Mongo.Collection('SalesSheets');
let SalesSheets = new Mongo.Collection('SalesSheets');
if(Meteor.isServer) {
//Set MongoDB indexes (or remove them) here.
try {
SalesSheets.rawCollection().createIndex({name: -1}, {unique: false})
} catch(e) {console.log("Caught exception while setting indexes in MongoDB"); console.error(e)}
}
const SalesSheetSchema = new SimpleSchema({
name: {
@@ -11,8 +20,8 @@ const SalesSheetSchema = new SimpleSchema({
label: "Name",
optional: false,
trim: true,
index: 1,
unique: false
//index: 1,
//unique: false
},
products: { //An ordered array of product id's included on the sheet.
type: Array,
@@ -27,7 +36,7 @@ const SalesSheetSchema = new SimpleSchema({
label: "Name",
optional: false,
trim: true,
unique: false
//unique: false
},
productId: { //Note: Will be non-existent for headings.
type: String,
@@ -37,10 +46,13 @@ const SalesSheetSchema = new SimpleSchema({
optional: true
},
measureIds: { //Note: Will be non-existent for headings.
type: [String],
type: Array, //[String],
label: "Measure IDs",
optional: true
}
},
'measureIds.$': {
type: String,
},
//measureIds: {
// type: Array,
// label: "Measure IDs",
@@ -74,11 +86,11 @@ if(Meteor.isServer) {
});
Meteor.methods({
createSalesSheet: function(name) {
createSalesSheet: async function(name) {
check(name, String);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
return SalesSheets.insert({name, products: [], createdAt: new Date()});
return await SalesSheets.insertAsync({name, products: [], createdAt: new Date()});
}
else throw new Meteor.Error(403, "Not authorized.");
},
@@ -98,7 +110,7 @@ if(Meteor.isServer) {
//updateSalesSheet_updateProduct_removeMeasure: function(id, productId, productName, productMeasures) {
//
//},
updateSalesSheet: function(id, name, products) {
updateSalesSheet: async function(id, name, products) {
check(id, String);
check(name, String);
check(products, [{
@@ -128,7 +140,7 @@ if(Meteor.isServer) {
//console.log("db.SalesSheet.update({_id: '" + id + "'}, {$set: {name: '" + name + "', updatedAt: " + new Date() + "}, $pull: {$exists: true}, $pushAll: [" + productList + "]})");
// Forces the object to be re-written, versus piecemeal updated.
SalesSheets.update({_id: id}, {$set: {name: name, products: products, updatedAt: new Date()}}, {validate: false}, function(err, count) {
await SalesSheets.updateAsync({_id: id}, {$set: {name: name, products: products, updatedAt: new Date()}}, {validate: false}, function(err, count) {
if(err) console.log(err);
});
@@ -146,11 +158,11 @@ if(Meteor.isServer) {
}
else throw new Meteor.Error(403, "Not authorized.");
},
removeSalesSheet: function(id) {
removeSalesSheet: async function(id) {
check(id, String);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
SalesSheets.remove(id);
await SalesSheets.removeAsync(id);
}
else throw new Meteor.Error(403, "Not authorized.");
}

View File

@@ -9,12 +9,13 @@ if(Meteor.isServer) {
});
Meteor.methods({
"insertUser": function(user) {
"insertUser": function(user, roles) {
check(user, {
username: String,
emails: [{address: String, verified: Match.Maybe(Boolean)}],
roles: [String]
//roles: [String]
});
check(roles, [String])
//Verify the currently logged in user has authority to manage users.
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_MANAGE])) {
@@ -33,7 +34,7 @@ if(Meteor.isServer) {
}
else throw new Meteor.Error(403, "Not authorized to add users.");
},
"updateUser": function(user) {
"updateUser": async function(user, roles) {
check(user, {
_id: String,
username: String,
@@ -41,15 +42,36 @@ if(Meteor.isServer) {
address: String,
verified: Boolean
}],
roles: [String]
//roles: [String]
});
check(roles, [String])
//Verify the currently logged in user has authority to manage users.
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_MANAGE])) {
//Verify the user name isn't already used with a different ID.
if(Meteor.collections.Users.findOne({username: user.username, _id: {$ne: user._id}}) == undefined) {
//Update the user. Note: I am using direct mongo modification, versus attempting to go through the Accounts and Roles objects. This could cause problems in the future if these packages change their data structures.
Meteor.collections.Users.update(user._id, {$set: {username: user.username, emails: user.emails, roles: user.roles}});
await Meteor.collections.Users.updateAsync(user._id, {$set: {username: user.username, emails: user.emails/*, roles: user.roles*/}});
let currentRoles = await Roles.getRolesForUserAsync(user._id)
//console.log(currentRoles)
//console.log(roles)
//TODO: Figure out which roles to add, and which roles to remove.
// Add roles not in the database already.
//console.log("Adding the user " + user._id + " to the following roles: ")
//for(let next of roles) console.log(next)
let rolesToAdd = roles.filter(x => !currentRoles.includes(x))
let rolesToRemove = currentRoles.filter(x => !roles.includes(x))
console.log('Roles to remove: ' + rolesToRemove)
console.log('Roles to add: ' + rolesToAdd)
if(rolesToAdd.length) Roles.addUsersToRoles([user._id], rolesToAdd)
if(rolesToRemove.length) Roles.removeUsersFromRoles([user._id], rolesToRemove)
}
else {
throw new Meteor.Error(400, "User name already exists.");
@@ -57,10 +79,10 @@ if(Meteor.isServer) {
}
else throw new Meteor.Error(403, "Not authorized to update users.");
},
"deleteUser": function(id) {
"deleteUser": async function(id) {
check(id, String);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_MANAGE])) {
Meteor.collections.Users.remove(id);
await Meteor.collections.Users.removeAsync(id);
}
else throw new Meteor.Error(403, "Not authorized to remove users.");
}

View File

@@ -1,20 +1,30 @@
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
import 'meteor/aldeed:collection2/static'
import SimpleSchema from 'meteor/aldeed:simple-schema';
import Measures from "./Measure";
const TYPES = ['Retail', "Farmer's Market", "Restaurant", "Mail"];
const FREQUENCIES = ['Daily', 'Weekly'];
let Venues = new Mongo.Collection('Venues');
if(Meteor.isServer) {
//Set MongoDB indexes (or remove them) here.
try {
Venues.rawCollection().createIndex({name: -1}, {unique: true})
} catch(e) {console.log("Caught exception while setting indexes in MongoDB"); console.error(e)}
}
let VenuesSchema = new SimpleSchema({
name: {
type: String,
label: "Name",
optional: false,
trim: true,
index: 1,
unique: true
//index: 1,
//unique: true
},
type: {
type: String,
@@ -70,13 +80,13 @@ if(Meteor.isServer) {
//});
Meteor.methods({
createVenue: function(name, type, frequency) {
createVenue: async function(name, type, frequency) {
check(name, String);
check(type, String);
check(frequency, String);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Venues.insert({name, type, frequency, createdAt: new Date()});
await Venues.insertAsync({name, type, frequency, createdAt: new Date()});
}
else throw new Meteor.Error(403, "Not authorized.");
},
@@ -86,40 +96,40 @@ if(Meteor.isServer) {
// }
// else throw new Meteor.Error(403, "Not authorized.");
//},
updateVenue: function(id, name, type, frequency) {
updateVenue: async function(id, name, type, frequency) {
check(id, String);
check(name, String);
check(type, String);
check(frequency, String);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Venues.update(id, {$set: {name, type, frequency, updatedAt: new Date()}});
await Venues.updateAsync(id, {$set: {name, type, frequency, updatedAt: new Date()}});
}
else throw new Meteor.Error(403, "Not authorized.");
},
deactivateVenue: function(id) {
deactivateVenue: async function(id) {
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
//Venues.remove(id);
Venues.update(id, {$set: {deactivated: true}}, {bypassCollection2: true});
await Venues.updateAsync(id, {$set: {deactivated: true}}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
reactivateVenue: function(id) {
reactivateVenue: async function(id) {
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Venues.update(id, {$set: {deactivated: false}}, {bypassCollection2: true});
await Venues.updateAsync(id, {$set: {deactivated: false}}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
hideVenue: function(id) { //One step past deactivated - will only show in the venues list if hidden venues are enabled.
hideVenue: async function(id) { //One step past deactivated - will only show in the venues list if hidden venues are enabled.
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
//Venues.remove(id);
Venues.update(id, {$set: {hidden: true}}, {bypassCollection2: true});
await Venues.updateAsync(id, {$set: {hidden: true}}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
showVenue: function(id) { //Returns the venue to being simply deactivated. Will again show in lists.
showVenue: async function(id) { //Returns the venue to being simply deactivated. Will again show in lists.
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Venues.update(id, {$set: {hidden: false}}, {bypassCollection2: true});
await Venues.updateAsync(id, {$set: {hidden: false}}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
}

View File

@@ -1,9 +1,19 @@
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
import 'meteor/aldeed:collection2/static'
import SimpleSchema from 'meteor/aldeed:simple-schema';
import Measures from "./Measure";
let Workers = new Mongo.Collection('Workers');
if(Meteor.isServer) {
//Set MongoDB indexes (or remove them) here.
try {
Workers.rawCollection().createIndex({name: -1}, {unique: true})
} catch(e) {console.log("Caught exception while setting indexes in MongoDB"); console.error(e)}
}
let WORKER_ACTIVITIES = ['sales', 'prep', 'canning', 'farming'];
let workersSchema = new SimpleSchema({
name: {
@@ -11,15 +21,18 @@ let workersSchema = new SimpleSchema({
label: "Name",
optional: false,
trim: true,
index: 1,
unique: true
//index: 1,
//unique: true
},
activities: {
type: [String],
type: Array, //[String],
label: "Activities",
optional: false,
trim: true
},
'activities.$': {
type: String,
},
hourlyRate: {
type: SimpleSchema.Integer,
label: "HourlyRate",
@@ -66,13 +79,13 @@ if(Meteor.isServer) Meteor.publish('workers', function() {
if(Meteor.isServer) {
Meteor.methods({
createWorker: function(name, activities, hourlyRate) {
createWorker: async function(name, activities, hourlyRate) {
check(name, String);
check(activities, [String]);
check(hourlyRate, Number);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Workers.insert({name, activities, hourlyRate, createdAt: new Date()});
await Workers.insertAsync({name, activities, hourlyRate, createdAt: new Date()});
}
else throw new Meteor.Error(403, "Not authorized.");
},
@@ -83,39 +96,39 @@ if(Meteor.isServer) {
// }
// else throw new Meteor.Error(403, "Not authorized.");
//},
updateWorker: function(id, name, activities, hourlyRate) {
updateWorker: async function(id, name, activities, hourlyRate) {
check(id, String);
check(name, String);
check(activities, [String]);
check(hourlyRate, Number);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Workers.update(id, {$set: {name, activities, hourlyRate, updatedAt: new Date()}});
await Workers.updateAsync(id, {$set: {name, activities, hourlyRate, updatedAt: new Date()}});
}
else throw new Meteor.Error(403, "Not authorized.");
},
deactivateWorker: function(id) {
deactivateWorker: async function(id) {
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
//Workers.remove(id);
Workers.update(id, {$set: {deactivated: true}}, {bypassCollection2: true});
await Workers.updateAsync(id, {$set: {deactivated: true}}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
reactivateWorker: function(id) {
reactivateWorker: async function(id) {
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Workers.update(id, {$set: {deactivated: false}}, {bypassCollection2: true});
await Workers.updateAsync(id, {$set: {deactivated: false}}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
hideWorker: function(id) { //One step past deactivated - will only show in the Workers list if hidden Workers are enabled.
hideWorker: async function(id) { //One step past deactivated - will only show in the Workers list if hidden Workers are enabled.
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Workers.update(id, {$set: {hidden: true}}, {bypassCollection2: true});
await Workers.updateAsync(id, {$set: {hidden: true}}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
},
showWorker: function(id) { //Returns the measure to being simply deactivated. Will again show in lists.
showWorker: async function(id) { //Returns the measure to being simply deactivated. Will again show in lists.
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Workers.update(id, {$set: {hidden: false}}, {bypassCollection2: true});
await Workers.updateAsync(id, {$set: {hidden: false}}, {bypassCollection2: true});
}
else throw new Meteor.Error(403, "Not authorized.");
}

View File

@@ -20,26 +20,28 @@ Meteor.collections = {Measures, Venues, Products, ProductTags, Sales, SalesSheet
//If this is the server then setup the default admin user if none exist.
if(Meteor.isServer) {
//Change this to find admin users, create a default admin user if none exists.
if(Users.find({}).count() === 0) {
try {
console.log("Creating a default admin user: admin/admin");
Users.countDocuments({}).then((userCount) => {
if(userCount === 0) {
try {
console.log("Creating a default admin user: admin/admin");
let id = Accounts.createUser({password: 'admin', username: 'admin'});
//Requires the alanning:roles package.
Roles.addUsersToRoles(id, [Meteor.UserRoles.ROLE_MANAGE, Meteor.UserRoles.ROLE_UPDATE]);
}
catch(err) {
console.log(err);
}
}
Meteor.validators = {};
Meteor.validators.ObjectID = Match.Where(function(id) {
if(id instanceof Mongo.ObjectID) {
id = id._str;
let id = Accounts.createUser({password: 'admin', username: 'admin'});
//Requires the alanning:roles package.
Roles.addUsersToRoles(id, [Meteor.UserRoles.ROLE_MANAGE, Meteor.UserRoles.ROLE_UPDATE]);
}
catch(err) {
console.log(err);
}
}
check(id, String);
return /[0-9a-fA-F]{24}/.test(id);
});
Meteor.validators = {};
Meteor.validators.ObjectID = Match.Where(function(id) {
if(id instanceof Mongo.ObjectID) {
id = id._str;
}
check(id, String);
return /[0-9a-fA-F]{24}/.test(id);
});
})
}

View File

@@ -1,4 +1,5 @@
//Look in /libs/startup/both/useraccounts.js for the login/logout/signup routing.
import { AccountsTemplates } from 'meteor/useraccounts:core';
let pri = FlowRouter.group({
triggersEnter: [AccountsTemplates.ensureSignedIn]

View File

@@ -1,3 +1,6 @@
import 'meteor/aldeed:collection2/static'
import SimpleSchema from 'meteor/aldeed:simple-schema';
let AppVersion = new Mongo.Collection('AppVersion');
let AppVersionSchema = new SimpleSchema({
version: {
@@ -9,16 +12,16 @@ let AppVersionSchema = new SimpleSchema({
AppVersion.attachSchema(AppVersionSchema);
try {
let appVersions = AppVersion.find({}).fetch();
let appVersions = await AppVersion.find({}).fetchAsync();
let appVersion;
if(!appVersions || appVersions.length == 0) { //This will happen only when first creating a database.
appVersion = {version: 0};
appVersion._id = AppVersion.insert(appVersion);
appVersion._id = await AppVersion.insertAsync(appVersion);
}
else if(appVersions.length > 1) { //This should never happen. Remove all but the first app version.
for(let i = 1; i < appVersions.length; i++) {
AppVersion.remove(appVersions[i]._id);
await AppVersion.removeAsync(appVersions[i]._id);
}
}
else {

View File

@@ -21,7 +21,7 @@ Template.Sales.onCreated(function() {
Session.set(PREFIX + "sortOption", "date");
Session.set(PREFIX + "showOnlyComments", false);
Tracker.autorun(function() {
Tracker.autorun(async function() {
let sortOption = Session.get(PREFIX + "sortOption");
let sort = sortOption == 'createdAt' ? {createdAt: -1} : {date: -1, createdAt: -1};
let showOnlyComments = Session.get(PREFIX + "showOnlyComments");
@@ -34,7 +34,7 @@ Template.Sales.onCreated(function() {
//if(Template.Sales.salesSubscription) Template.Sales.salesSubscription.stop();
Template.Sales.salesSubscription = Meteor.subscribe("sales", query, sort, QUERY_LIMIT, Session.get(PREFIX + 'skipCount'));
Session.set(PREFIX + 'saleCount', Meteor.call('getSalesCount', Session.get(PREFIX + 'searchQuery')));
Session.set(PREFIX + 'saleCount', await Meteor.callAsync('getSalesCount', Session.get(PREFIX + 'searchQuery')));
});
});
Template.Sales.onRendered(function() {

View File

@@ -2,6 +2,7 @@
import './SalesSheetEditor.html';
import swal from 'sweetalert2';
import dragula from 'dragula';
import { ReactiveDict } from 'meteor/reactive-dict'
let PREFIX = "SalesSheetEditor.";

View File

@@ -1,6 +1,7 @@
import './SalesSheetForm.html';
import swal from 'sweetalert2';
import { ReactiveDict } from 'meteor/reactive-dict'
let PREFIX = "SalesSheetForm.";

View File

@@ -60,7 +60,8 @@
<div class="rolesContainer editorDiv"><label>Roles:</label>
<div class="roles center" style="font-size: 1.2em">
{{#each allRoles}}
<span class="role {{getRoleState this}} noselect">{{name}}</span>
<!-- Roles no longer have a name, they now put the name as the _id in Mongo -->
<span class="role {{getRoleState this}} noselect">{{_id}}</span>
{{/each}}
</div>
</div>

View File

@@ -9,6 +9,7 @@ let PREFIX = "UserManagement";
Tracker.autorun(function() {
Meteor.subscribe("users", Session.get(PREFIX + 'searchQuery'));
Meteor.subscribe("roleAssignments");
Meteor.subscribe("roles");
});
@@ -132,6 +133,15 @@ Template.User.helpers({
let editedUser = Session.get(PREFIX + "editedUser");
return editedUser == this._id;
},
roles: function() {
let roles = Meteor.roleAssignment.find({"user._id": this._id}, {"sort": [["role._id", "asc"]]}).fetch();
let result = []
for(let next of roles)
result.push(next.role._id)
return result
}
});
@@ -145,7 +155,15 @@ Template.UserEditor.helpers({
getRoleState: function(role) {
let user = Template.parentData(1);
return !user.isNew && user.roles.includes(role.name) ? "selected" : "";
//Debug code...
// console.log("GetRoleState")
// console.log(role._id)
// console.log(user._id)
// console.log(Meteor.roleAssignment.find({"user._id": user._id, "role._id": role._id}).count())
//console.log("Role: " + role._id + " === " + Roles.userIsInRole(user._id, [role._id]))
// Note: Meteor.roleAssignment.countDocuments({...}) returns a Promise and not a count. Cannot use that here.
return Roles.userIsInRole(user._id, [role._id]) ? "selected" : ""
}
});
Template.UserEditor.events({
@@ -166,10 +184,10 @@ Template.UserEditor.events({
roles.push($(roleSpans[i]).text());
}
user.roles = roles;
//user.roles = roles;
if(Session.get(PREFIX + 'displayNewUser')) {
Meteor.call('insertUser', user, function(error, result) {
Meteor.call('insertUser', user, roles, function(error, result) {
if(error) {
sAlert.error(error);
}
@@ -182,7 +200,7 @@ Template.UserEditor.events({
}
else {
user._id = this._id;
Meteor.call("updateUser", user, function(error, result) {
Meteor.call("updateUser", user, roles, function(error, result) {
if(error) sAlert.error(error);
else {
sAlert.success("User updated.");

View File

@@ -2,11 +2,11 @@
Meteor.methods({
// Cleans up all Date objects that don't have a time component. Removes any time component.
"cleanDates": function() {
"cleanDates": async function() {
// Update the sales dates.
let sales = Sales.find({}).fetch();
let sales = await Sales.find({}).fetchAsync();
for(let i = 0; i < sales.length; i++) {
if(sales[i].date && _.isDate(sales[i].date)) {
@@ -15,7 +15,7 @@ Meteor.methods({
console.log("Converted " + sales[i].date + " to " + date);
// Save to the database.
Sales.update(sales[i]._id, {$set: {date}}, function(err, id) {
await Sales.updateAsync(sales[i]._id, {$set: {date}}, function(err, id) {
if(err) console.log(err);
}, {bypassCollection2: true});
}
@@ -25,7 +25,7 @@ Meteor.methods({
// Update the product price effective dates.
let products = Products.find({}).fetch();
let products = await Products.find({}).fetchAsync();
for(let i = 0; i < products.length; i++) {
let product = products[i];
@@ -48,7 +48,7 @@ Meteor.methods({
// Save the changes.
if(hasChanges) {
Products.update(product._id, {$set: {prices}}, function(err, id) {
await Products.updateAsync(product._id, {$set: {prices}}, function(err, id) {
if(err) console.log(err);
}, {validate: false, bypassCollection2: true});
}

View File

@@ -11,8 +11,8 @@
Meteor.methods({
// Cleans up all Date objects that don't have a time component. Removes any time component.
"enhanceSalesDateFields": function() {
let sales = Sales.find({}).fetch();
"enhanceSalesDateFields": async function() {
let sales = await Sales.find({}).fetchAsync();
for(let i = 0; i < sales.length; i++) {
let dateString = sales[i].date.toString();
@@ -25,7 +25,7 @@ Meteor.methods({
}
// Save to the database.
Sales.update(sales[i]._id, {$set: {"timestamp": timestamp.getTime(), weekOfYear}}, {bypassCollection2: true}, function(err, id) {
await Sales.updateAsync(sales[i]._id, {$set: {"timestamp": timestamp.getTime(), weekOfYear}}, {bypassCollection2: true}, function(err, id) {
if(err) console.log(err);
});
}

View File

@@ -1,15 +1,15 @@
let fs = require('fs');
Meteor.methods({
"exportMissingSales": function() {
"exportMissingSales": async function() {
try {
let products = Products.find({}).fetch();
let venues = Venues.find({}).fetch();
let measures = Measures.find({}).fetch();
let products = await Products.find({}).fetchAsync();
let venues = await Venues.find({}).fetchAsync();
let measures = await Measures.find({}).fetchAsync();
let productNameMap = {};
let venueNameMap = {};
let measureNameMap = {};
let sales = Sales.find({createdAt: {$gte: moment.utc("2017-01-28", "YYYY-MM-DD").toDate(), $lt: moment.utc("2017-01-29", "YYYY-MM-DD").toDate()}}).fetch();
let sales = await Sales.find({createdAt: {$gte: moment.utc("2017-01-28", "YYYY-MM-DD").toDate(), $lt: moment.utc("2017-01-29", "YYYY-MM-DD").toDate()}}).fetchAsync();
let output = "";
for(let i = 0; i < products.length; i++) {

View File

@@ -1,12 +1,12 @@
Meteor.methods({
"checkDuplicateSales": function() {
"checkDuplicateSales": async function() {
try {
let sales = Sales.find({}).fetch();
let sales = await Sales.find({}).fetchAsync();
let duplicateCount = 0;
for(let i = 0; i < sales.length; i++) {
let next = missingData[i];
let count = Sales.find({date: next.date, measureId: next.measureId, productId: next.productId, venueId: next.venueId, price: next.price, amount: next.amount}).count();
let count = await Sales.countDocuments({date: next.date, measureId: next.measureId, productId: next.productId, venueId: next.venueId, price: next.price, amount: next.amount});
if(count > 1) {
console.log("This sale has a duplicate:");

View File

@@ -2,13 +2,13 @@ import readCSV from '/imports/util/csv.js';
//Returns the ID of the created or found object.
//This is needed because for some crazy reason upsert only returns the affected document if a document is inserted, otherwise it does not return the ID of the affected document.
function insertOrCreate(collection, selector, object) {
async function insertOrCreate(collection, selector, object) {
let id;
let findResult = collection.findOne(selector, {fields: {_id: 1}});
let findResult = await collection.findOneAsync(selector, {fields: {_id: 1}});
if(findResult && findResult._id) id = findResult._id;
else id = collection.insert(object);
else id = await collection.insertAsync(object);
//
// let result = collection.upsert(selector, {$setOnInsert: object}, {multi: false});
@@ -26,7 +26,7 @@ function insertOrCreate(collection, selector, object) {
}
Meteor.methods({
"importBasics": function() {
"importBasics": async function() {
const measures = [
{name: "Jar 4oz", postfix: "4oz", order: 0, createdAt: new Date()},
{name: "Jar 8oz", postfix: "8oz", order: 1, createdAt: new Date()},
@@ -55,22 +55,22 @@ Meteor.methods({
{name: "Website Order", type: "Mail", createdAt: new Date()}
];
Measures.remove({});
Venues.remove({});
await Measures.removeAsync({});
await Venues.removeAsync({});
for(let next of measures) {
Measures.insert(next, function(error, _id) {
await Measures.insertAsync(next, function(error, _id) {
if(error) console.log("Failed to insert measure: " + JSON.stringify(next) + " ERROR: " + error);
});
}
for(let next of venues) {
Venues.insert(next, function(error, _id) {
await Venues.insertAsync(next, function(error, _id) {
if(error) console.log("Failed to insert venue: " + JSON.stringify(next) + " ERROR: " + error);
});
}
},
"importItems": function() {
"importItems": async function() {
let fileName = "importItems.csv";
//The mapping of model attributes to CSV columns. The oz sizes are arrays of columns since there are multiple.
let map = {};
@@ -81,31 +81,31 @@ Meteor.methods({
{
let result;
result = Measures.findOne({name: 'Jar 4oz'}, {fields: {_id: 1}});
result = await Measures.findOneAsync({name: 'Jar 4oz'}, {fields: {_id: 1}});
if(result) measureIdMap['4oz'] = result._id;
else {console.log("Error: Couldn't find the _id for Jar 4oz"); hasError = true;}
result = Measures.findOne({name: 'Jar 8oz'}, {fields: {_id: 1}});
result = await Measures.findOneAsync({name: 'Jar 8oz'}, {fields: {_id: 1}});
if(result) measureIdMap['8oz'] = result._id;
else {console.log("Error: Couldn't find the _id for Jar 8oz"); hasError = true;}
result = Measures.findOne({name: 'Jar 12oz'}, {fields: {_id: 1}});
result = await Measures.findOneAsync({name: 'Jar 12oz'}, {fields: {_id: 1}});
if(result) measureIdMap['12oz'] = result._id
else {console.log("Error: Couldn't find the _id for Jar 12oz"); hasError = true;}
result = Measures.findOne({name: 'Jar 16oz'}, {fields: {_id: 1}});
result = await Measures.findOneAsync({name: 'Jar 16oz'}, {fields: {_id: 1}});
if(result) measureIdMap['16oz'] = result._id;
else {console.log("Error: Couldn't find the _id for Jar 16oz"); hasError = true;}
result = Measures.findOne({name: 'Jar 32oz'}, {fields: {_id: 1}});
result = await Measures.findOneAsync({name: 'Jar 32oz'}, {fields: {_id: 1}});
if(result) measureIdMap['32oz'] = result._id;
else {console.log("Error: Couldn't find the _id for Jar 32oz"); hasError = true;}
result = Measures.findOne({name: 'Each'}, {fields: {_id: 1}});
result = await Measures.findOneAsync({name: 'Each'}, {fields: {_id: 1}});
if(result) measureIdMap['Each'] = result._id;
else {console.log("Error: Couldn't find the _id for Each"); hasError = true;}
result = Measures.findOne({name: 'Bags'}, {fields: {_id: 1}});
result = await Measures.findOneAsync({name: 'Bags'}, {fields: {_id: 1}});
if(result) measureIdMap['Bags'] = result._id;
else {console.log("Error: Couldn't find the _id for Bags"); hasError = true;}
}
@@ -178,7 +178,7 @@ Meteor.methods({
}
//Reads a single row of CSV data and adds it to the database.
function readRow(row) {
async function readRow(row) {
let category = row[map.category].trim();
let subcategory = row[map.subcategory].trim();
let item = row[map.item];
@@ -219,7 +219,7 @@ Meteor.methods({
}
// categoryId = insertOrCreate(Categories, {name: category}, {name: category, createdAt: new Date()});
categoryId = insertOrCreate(ProductTags, {name: category}, {name: category, createdAt: new Date()});
let categoryId = await insertOrCreate(ProductTags, {name: category}, {name: category, createdAt: new Date()});
if(categoryId) {
// subcategoryId = insertOrCreate(Subcategories, {name: subcategory}, {
@@ -227,7 +227,7 @@ Meteor.methods({
// categoryId: categoryId,
// createdAt: new Date()
// });
subcategoryId = insertOrCreate(ProductTags, {name: subcategory}, {name: subcategory, createdAt: new Date()});
let subcategoryId = await insertOrCreate(ProductTags, {name: subcategory}, {name: subcategory, createdAt: new Date()});
if(subcategoryId) {
let weightedMeasures = [];
@@ -258,7 +258,7 @@ Meteor.methods({
//Here we can just insert since we don't care about the resulting ID.
let obj = {name: item, tags: [categoryId, subcategoryId], measures: measures, createdAt: new Date()};
Products.insert(obj, function(error) {
await Products.insertAsync(obj, function(error) {
if(error) console.log("Could not insert the Product: " + JSON.stringify(obj) + "\n ERROR: " + error);
});
}
@@ -271,7 +271,7 @@ Meteor.methods({
}
}
readCSV(fileName, Meteor.bindEnvironment(function(error, data) {
readCSV(fileName, Meteor.bindEnvironment(async function(error, data) {
//csv.read(fileName, function(error, csvData) {
if(error) console.log("Unable to read the importItems.csv file:" + error);
else {
@@ -282,19 +282,19 @@ Meteor.methods({
// Items.remove({});
// Subcategories.remove({});
// Categories.remove({});
Products.remove({});
await Products.removeAsync({});
// console.log("CSV Column Mapping: " + JSON.stringify(map));
// readRow(data[1]);
for(let i = 1; i < data.length; i++) {
readRow(data[i]);
await readRow(data[i]);
}
}
}));
}
},
"importSales": function() {
"importSales": async function() {
let fileName = "importSales.csv";
//The mapping of model attributes to CSV columns. The oz sizes are arrays of columns since there are multiple.
let map = {};
@@ -543,82 +543,82 @@ Meteor.methods({
{ //Load the object ids for the measures and venues we will encounter.
let result;
result = Measures.findOne({name: 'Jar 4oz'}, {fields: {_id: 1}});
result = await Measures.findOneAsync({name: 'Jar 4oz'}, {fields: {_id: 1}});
if(result) measureIdMap['4oz'] = result._id;
else {console.log("Error: Couldn't find the _id for Jar 4oz"); hasError = true;}
result = Measures.findOne({name: 'Jar 8oz'}, {fields: {_id: 1}});
result = await Measures.findOneAsync({name: 'Jar 8oz'}, {fields: {_id: 1}});
if(result) measureIdMap['8oz'] = result._id;
else {console.log("Error: Couldn't find the _id for Jar 8oz"); hasError = true;}
result = Measures.findOne({name: 'Jar 12oz'}, {fields: {_id: 1}});
result = await Measures.findOneAsync({name: 'Jar 12oz'}, {fields: {_id: 1}});
if(result) measureIdMap['12oz'] = result._id
else {console.log("Error: Couldn't find the _id for Jar 12oz"); hasError = true;}
result = Measures.findOne({name: 'Jar 16oz'}, {fields: {_id: 1}});
result = await Measures.findOneAsync({name: 'Jar 16oz'}, {fields: {_id: 1}});
if(result) measureIdMap['16oz'] = result._id;
else {console.log("Error: Couldn't find the _id for Jar 16oz"); hasError = true;}
result = Measures.findOne({name: 'Jar 32oz'}, {fields: {_id: 1}});
result = await Measures.findOneAsync({name: 'Jar 32oz'}, {fields: {_id: 1}});
if(result) measureIdMap['32oz'] = result._id;
else {console.log("Error: Couldn't find the _id for Jar 32oz"); hasError = true;}
result = Measures.findOne({name: 'Each'}, {fields: {_id: 1}});
result = await Measures.findOneAsync({name: 'Each'}, {fields: {_id: 1}});
if(result) measureIdMap['Each'] = result._id;
else {console.log("Error: Couldn't find the _id for Each"); hasError = true;}
result = Measures.findOne({name: 'Bags'}, {fields: {_id: 1}});
result = await Measures.findOneAsync({name: 'Bags'}, {fields: {_id: 1}});
if(result) measureIdMap['Bags'] = result._id;
else {console.log("Error: Couldn't find the _id for Bags"); hasError = true;}
result = Venues.findOne({name: 'Boonville'}, {fields: {_id: 1}});
result = await Venues.findOneAsync({name: 'Boonville'}, {fields: {_id: 1}});
if(result) venueIdMap['bv'] = result._id;
else {console.log("Error: Couldn't find the _id for Boonville"); hasError = true;}
result = Venues.findOne({name: 'Clement St'}, {fields: {_id: 1}});
result = await Venues.findOneAsync({name: 'Clement St'}, {fields: {_id: 1}});
if(result) venueIdMap['sf'] = result._id;
else {console.log("Error: Couldn't find the _id for Clement St"); hasError = true;}
result = Venues.findOne({name: 'Ukiah'}, {fields: {_id: 1}});
result = await Venues.findOneAsync({name: 'Ukiah'}, {fields: {_id: 1}});
if(result) venueIdMap['uk'] = result._id;
else {console.log("Error: Couldn't find the _id for Ukiah"); hasError = true;}
result = Venues.findOne({name: 'Mendocino'}, {fields: {_id: 1}});
result = await Venues.findOneAsync({name: 'Mendocino'}, {fields: {_id: 1}});
if(result) venueIdMap['men'] = result._id;
else {console.log("Error: Couldn't find the _id for Mendocino"); hasError = true;}
result = Venues.findOne({name: 'Ft Bragg'}, {fields: {_id: 1}});
result = await Venues.findOneAsync({name: 'Ft Bragg'}, {fields: {_id: 1}});
if(result) venueIdMap['fb'] = result._id;
else {console.log("Error: Couldn't find the _id for Ft Bragg"); hasError = true;}
result = Venues.findOne({name: 'On Farm'}, {fields: {_id: 1}});
result = await Venues.findOneAsync({name: 'On Farm'}, {fields: {_id: 1}});
if(result) venueIdMap['of'] = result._id;
else {console.log("Error: Couldn't find the _id for On Farm"); hasError = true;}
result = Venues.findOne({name: 'Unknown Restaurant'}, {fields: {_id: 1}});
result = await Venues.findOneAsync({name: 'Unknown Restaurant'}, {fields: {_id: 1}});
if(result) {venueIdMap['res'] = result._id; venueIdMap['w'] = result._id;}
else {console.log("Error: Couldn't find the _id for Unknown Restaurant"); hasError = true;}
result = Venues.findOne({name: 'Yorkville Market'}, {fields: {_id: 1}});
result = await Venues.findOneAsync({name: 'Yorkville Market'}, {fields: {_id: 1}});
if(result) venueIdMap['ym'] = result._id;
else {console.log("Error: Couldn't find the _id for Yorkville Market"); hasError = true;}
result = Venues.findOne({name: 'Yorkville Cellars'}, {fields: {_id: 1}});
result = await Venues.findOneAsync({name: 'Yorkville Cellars'}, {fields: {_id: 1}});
if(result) venueIdMap['yc'] = result._id;
else {console.log("Error: Couldn't find the _id for Yorkville Cellars"); hasError = true;}
result = Venues.findOne({name: 'Mail Order'}, {fields: {_id: 1}});
result = await Venues.findOneAsync({name: 'Mail Order'}, {fields: {_id: 1}});
if(result) venueIdMap['mo'] = result._id;
else {console.log("Error: Couldn't find the _id for Mail Order"); hasError = true;}
// result = Items.find({}, {fields: {_id: 1, name: 1}, sort: {name: 1}}).fetch();
result = Products.find({}, {fields: {_id: 1, name: 1}, sort: {name: 1}}).fetch();
result = await Products.find({}, {fields: {_id: 1, name: 1}, sort: {name: 1}}).fetchAsync();
for(let i = 0; i < result.length; i++) itemIdMap[result[i].name.toLowerCase()] = result[i]._id;
//console.log(JSON.stringify(itemIdMap));
}
readCSV(fileName, Meteor.bindEnvironment(function(error, data) {
readCSV(fileName, Meteor.bindEnvironment(async function(error, data) {
//csv.read(fileName, function(error, csvData) {
//Data is an array of arrays. data[0] = array of headers. data[1] = first row of data.
@@ -628,14 +628,14 @@ Meteor.methods({
collectMetadata(data[0]);
//Remove everything first.
Sales.remove({"importTag": "1"});
await Sales.removeAsync({"importTag": "1"});
// console.log("CSV Column Mapping: " + JSON.stringify(map));
// readRow(data[1]);
let undefinedItems = {};
for(let i = 1; i < data.length; i++) {
readRow(data[i], undefinedItems);
await readRow(data[i], undefinedItems);
}
// let output = "";
// output += "----------------\n";
@@ -714,7 +714,7 @@ Meteor.methods({
}
//Reads a single row of CSV data and adds it to the database.
function readRow(row, undefinedItems) {
async function readRow(row, undefinedItems) {
let date = moment(row[map.date], "M/D/YYYY").toDate();
let venue = row[map.venue].toLowerCase();
let item = row[map.item].trim();
@@ -757,7 +757,7 @@ Meteor.methods({
if(price != undefined) price = price['oz32'];
if(price == undefined) price = priceMap[year]['oz32'];
if(price) insertSale({date: date, amount: oz32, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['32oz']});
if(price) await insertSale({date: date, amount: oz32, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['32oz']});
else console.log("Could not find a price in the year " + year + " for " + item + " in the size oz32");
}
@@ -766,7 +766,7 @@ Meteor.methods({
if(price != undefined) price = price['oz16'];
if(price == undefined) price = priceMap[year]['oz16'];
if(price) insertSale({date: date, amount: oz16, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['16oz']});
if(price) await insertSale({date: date, amount: oz16, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['16oz']});
else console.log("Could not find a price in the year " + year + " for " + item + " in the size oz16");
}
@@ -775,7 +775,7 @@ Meteor.methods({
if(price != undefined) price = price['oz12'];
if(price == undefined) price = priceMap[year]['oz12'];
if(price) insertSale({date: date, amount: oz12, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['12oz']});
if(price) await insertSale({date: date, amount: oz12, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['12oz']});
else console.log("Could not find a price in the year " + year + " for " + item + " in the size oz12");
}
@@ -784,7 +784,7 @@ Meteor.methods({
if(price != undefined) price = price['oz8'];
if(price == undefined) price = priceMap[year]['oz8'];
if(price) insertSale({date: date, amount: oz8, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['8oz']});
if(price) await insertSale({date: date, amount: oz8, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['8oz']});
else console.log("Could not find a price in the year " + year + " for " + item + " in the size oz8");
}
@@ -793,7 +793,7 @@ Meteor.methods({
if(price != undefined) price = price['oz4'];
if(price == undefined) price = priceMap[year]['oz4'];
if(price) insertSale({date: date, amount: oz4, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['4oz']});
if(price) await insertSale({date: date, amount: oz4, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['4oz']});
else console.log("Could not find a price in the year " + year + " for " + item + " in the size oz4");
}
@@ -802,7 +802,7 @@ Meteor.methods({
if(price != undefined) price = price['bags'];
if(price == undefined) price = priceMap[year]['bags'];
if(price) insertSale({date: date, amount: bags, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['Bags']});
if(price) await insertSale({date: date, amount: bags, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['Bags']});
else {
console.log("Could not find a price in the year " + year + " for " + item + " in the size bags");
}
@@ -813,16 +813,16 @@ Meteor.methods({
if(price != undefined) price = price['each'];
if(price == undefined) price = priceMap[year]['each'];
if(price) insertSale({date: date, amount: each, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['Each']});
if(price) await insertSale({date: date, amount: each, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['Each']});
else console.log("Could not find a price in the year " + year + " for " + item + " in the size each");
}
}
}
function insertSale(sale) {
async function insertSale(sale) {
sale.createdAt = new Date();
sale.importTag = "1";
Sales.insert(sale, function(error) {
await Sales.insertAsync(sale, function(error) {
if(error) console.log("Failed to insert the sale: " + JSON.stringify(sale) + "\n ERROR: " + error);
}, {bypassCollection2: true});
}

View File

@@ -1,10 +1,10 @@
Meteor.methods({
"importMissingSales": function() {
"importMissingSales": async function() {
console.log("Importing Missing Sales...");
try {
let products = Products.find({}).fetch();
let venues = Venues.find({}).fetch();
let measures = Measures.find({}).fetch();
let products = await Products.find({}).fetchAsync();
let venues = await Venues.find({}).fetchAsync();
let measures = await Measures.find({}).fetchAsync();
let productNameMap = {};
let venueNameMap = {};
let measureNameMap = {};