Started adding async calls for upgrading to Meteor 3.0. Numerous other fixes.

This commit is contained in:
2025-07-02 11:18:09 -07:00
parent e1216741d6
commit 2e99ad007c
32 changed files with 549 additions and 373 deletions

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");
let id = Accounts.createUser({password: 'admin', username: 'admin'});
//Requires the alanning:roles package.
Roles.addUsersToRoles(id, [Meteor.UserRoles.ROLE_MANAGE, Meteor.UserRoles.ROLE_UPDATE]);
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);
}
}
catch(err) {
console.log(err);
}
}
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);
});
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);
});
})
}