Files
PetitTetonMeteor/imports/api/Product.js

268 lines
7.9 KiB
JavaScript
Raw Normal View History

import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
Products = new Mongo.Collection('Products');
const ProductsSchema = new SimpleSchema({
name: {
type: String,
label: "Name",
optional: false,
trim: 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],
label: "Tags",
optional: false,
defaultValue: []
},
measures: { //A JSON array of Measure ID's.
type: Array,
label: "Measures",
optional: false,
defaultValue: []
},
'measures.$': {
type: String,
label: "Measure ID",
regEx: SimpleSchema.RegEx.Id
},
aliases: { //A JSON array of alternate names.
type: Array,
label: "Aliases",
optional: false,
defaultValue: []
},
'aliases.$': {
type: String
},
prices: { //A JSON object mapping Measure ID's to price data for this item. Price data is an object with price, effectiveDate, previousPrice. Example: prices: {XZ5Z3CM49NDrJNADA: {price: 10.5, effectiveDate: ISODate("2017-01-12T13:14:18.876-08:00"), previousPrice: 9}, ...}
type: Object,
//blackbox: true,
custom: function() {
console.log("In custom validation for prices");
return true;
// if(this.value != undefined) {
// console.log(this.value);
// //check(this, Object);
// if(!_.isObject(this.value)) {
// return "expectObject";
// }
//
// for(let measureId of _.allKeys(this.value)) {
// //check(measureId, String); //Should be a Mongo ID
// if(!_.isString(measureId)) {
// console.log("Expected a Mongo ID as attribute names of the Product.prices object.");
// return "expectString";
// }
// //check(this.value[measureId], Object);
// let measureData = this.value[measureId];
// if(!_.isObject(measureData)) {
// console.log("Expected an Object containing price, and optionally (previousPrice & effectiveDate).");
// return "expectObject";
// }
//
// if(_.has(measureData, "price")) {
// //check(measureData.price, Number);
// if(!_.isNumber(measureData.price)) {
// console.log("Expected a Number for 'price'.");
// return "expectNumber";
// }
//
// //If previous price exists then it must be a number, and effective date must exist and be a date.
// if(_.has(measureData, "previousPrice")) {
// //check(measureData.effectiveDate, Date);
// if(!_.isDate(measureData.effectiveDate)) {
// console.log("Expected a Date for 'effectiveDate'.");
// return "expectDate";
// }
// //check(measureData.previousPrice, Number);
// if(!_.isDate(measureData.previousPrice)) {
// console.log("Expected a Number for 'previousPrice'.");
// return "expectNumber";
// }
// }
// else {
// //check(measureData.effectiveDate, undefined);
// if(_.isSet(measureData.effectiveDate)) {
// console.log("Expected 'effectiveDate' to be undefined.");
// return "notAllowed";
// }
// }
// }
// else {
// //check(measureData.effectiveDate, undefined);
// if(_.isSet(measureData.effectiveDate)) {
// console.log("Expected 'effectiveDate' to be undefined.");
// return "notAllowed";
// }
// //check(measureData.previousPrice, undefined);
// if(_.isSet(measureData.previousPrice)) {
// console.log("Expected 'previousPrice' to be undefined.");
// return "notAllowed";
// }
// }
// }
// }
}
},
createdAt: {
type: Date,
label: "Created On",
optional: false
},
updatedAt: {
type: Date,
label: "Updated On",
optional: true
},
deletedAt: {
type: Date,
label: "Deleted On",
optional: true
},
deletedBy: {
type: String,
label: "Deleted By",
optional: true
},
restoredAt: {
type: Date,
label: "Restored On",
optional: true
},
restoredBy: {
type: String,
label: "Restored By",
optional: true
}
});
//Note: Could not figure out how to setup a schema for prices where prices is an object whose keys are id's of measures, and whose values are objects containing price data (see comments above for more details on price data).
// 'prices.$': {
// type: new SimpleSchema({
// measureId: {
// type: String,
// label: "Measure Id",
// trim: false,
// regEx: SimpleSchema.RegEx.Id,
// optional: false
// },
// price: {
// type: Number,
// label: "Price",
// min: 0,
// exclusiveMin: true,
// optional: false
// }
// })
// },
//Products.attachSchema(ProductsSchema);
//https://github.com/zimme/meteor-collection-softremovable
Products.attachBehaviour("softRemovable", {
removed: 'deleted',
removedAt: 'deletedAt',
removedBy: 'removedBy',
restoredAt: 'restoredAt',
restoredBy: 'restoredBy'
});
if(Meteor.isServer) {
Meteor.publish('products', function() {
// let dbQuery = {};
//
// if(query) {
// _.each(_.keys(query), function(key) {
// if(_.isObject(query[key])) dbQuery[key] = query[key];
// else if(_.isNumber(query[key])) dbQuery[key] = query[key];
// else dbQuery[key] = {$regex: RegExp.escape(query[key]), $options: 'i'};
// })
// }
//
// return Products.find(dbQuery, {sort: {date: -1}});
return Products.find({}, {sort: {name: 1}});
});
Meteor.methods({
insertProduct: function(product) {
check(product, {
name: String
});
product.createdAt = new Date();
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Products.insert(product, function(err, id) {
if(err) console.log(err);
});
}
else throw new Meteor.Error(403, "Not authorized.");
},
deleteProduct: function(id) {
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Products.remove(id);
}
else throw new Meteor.Error(403, "Not authorized.");
},
updateProduct: function(product) {
check(product, {
_id: String,
name: String
});
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
Products.update(id, {$set: {name: product.name, updateAt: new Date()}});
}
else throw new Meteor.Error(403, "Not authorized.");
},
setProductPrice: function(productId, measureId, price, setPrevious, effectiveDate) {
check(productId, String);
check(measureId, String);
check(price, Number);
check(setPrevious, Boolean);
check(effectiveDate, Date);
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
let product = Products.findOne(productId, {fields: {prices: 1}});
if(product) {
console.log("Product: " + JSON.stringify(product));
let prices = product.prices ? product.prices : {};
let measurePriceData = prices[measureId];
if(!measurePriceData) {
measurePriceData = {};
prices[measureId] = measurePriceData;
}
console.log("Old Price Data: " + JSON.stringify(prices));
if(setPrevious && measurePriceData.price) {
measurePriceData.previousPrice = measurePriceData.price;
measurePriceData.effectiveDate = effectiveDate;
}
measurePriceData.price = price;
console.log("New Price Data: " + JSON.stringify(prices));
//console.log(ProductsSchema.validate(product));
//
// check(prices, ProductsSchema);
if(ProductsSchema.newContext().isValid()) {
console.log("Valid schema for product");
Products.update(productId, {$set: {prices: prices, updateAt: new Date()}}, {validate: false, bypassCollection2: true});
}
else console.log("Invalid schema for product");
}
else throw new Meteor.ERROR(400, "Could not find the requested product: " + productId);
}
else throw new Meteor.Error(403, "Not authorized.");
}
});
}
export default Products;