2017-01-15 11:33:37 -08:00
|
|
|
|
|
|
|
|
import './Products.html';
|
2020-01-16 09:31:12 -08:00
|
|
|
import Batches from "../api/Batch";
|
2017-01-15 11:33:37 -08:00
|
|
|
|
2017-10-20 14:54:58 -07:00
|
|
|
let QUERY_LIMIT = 100;
|
|
|
|
|
let QUERY_LIMIT_INCREMENT = 100;
|
2017-01-17 22:31:43 -08:00
|
|
|
let PREFIX = "Products.";
|
|
|
|
|
|
2017-01-15 11:33:37 -08:00
|
|
|
Tracker.autorun(function() {
|
|
|
|
|
Meteor.subscribe("products");
|
|
|
|
|
Meteor.subscribe("productTags");
|
2017-01-17 22:31:43 -08:00
|
|
|
Meteor.subscribe("measures");
|
2017-01-15 11:33:37 -08:00
|
|
|
});
|
|
|
|
|
|
2017-01-17 22:31:43 -08:00
|
|
|
Template.Products.onCreated(function() {
|
2019-10-07 15:51:50 -07:00
|
|
|
Session.set(PREFIX + "displayNew", false);
|
2017-01-17 22:31:43 -08:00
|
|
|
Session.set(PREFIX + "showHidden", false);
|
2017-10-20 14:54:58 -07:00
|
|
|
Session.set(PREFIX + "queryLimit", QUERY_LIMIT);
|
|
|
|
|
});
|
|
|
|
|
Template.Products.onRendered(function() {
|
|
|
|
|
$(".tableContainer").mCustomScrollbar({
|
|
|
|
|
scrollButtons: {enable:true},
|
|
|
|
|
theme: "light-thick",
|
|
|
|
|
scrollbarPosition: "outside",
|
|
|
|
|
scrollEasing: "linear"
|
|
|
|
|
});
|
2017-01-17 22:31:43 -08:00
|
|
|
});
|
2017-01-15 11:33:37 -08:00
|
|
|
Template.Products.helpers({
|
2019-10-07 15:51:50 -07:00
|
|
|
displayNew: function() {
|
|
|
|
|
return Session.get(PREFIX + "displayNew");
|
2017-01-17 22:31:43 -08:00
|
|
|
},
|
2017-01-15 11:33:37 -08:00
|
|
|
products: function() {
|
2017-01-17 22:31:43 -08:00
|
|
|
let skipCount = Session.get(PREFIX + 'skipCount') || 0;
|
|
|
|
|
let query = Session.get(PREFIX + 'searchQuery');
|
|
|
|
|
let dbQuery = [];
|
2017-01-15 11:33:37 -08:00
|
|
|
|
|
|
|
|
if(query) {
|
|
|
|
|
_.each(_.keys(query), function(key) {
|
2017-01-17 22:31:43 -08:00
|
|
|
if(_.isFunction(query[key])) dbQuery.push({[key]: query[key]}); //dbQuery[key] = query[key]();
|
|
|
|
|
else if(_.isObject(query[key])) dbQuery.push({[key]: query[key]}); //dbQuery[key] = query[key]; //Will look something like: {$in: [xxx,xxx,xxx]}
|
|
|
|
|
else if(_.isNumber(query[key])) dbQuery.push({[key]: query[key]}); //dbQuery[key] = query[key];
|
|
|
|
|
else {
|
|
|
|
|
//dbQuery[key] = {$regex: query[key], $options: 'i'};
|
|
|
|
|
let searchValue = query[key];
|
|
|
|
|
let searches = searchValue && searchValue.length > 0 ? searchValue.split(/\s+/) : undefined;
|
|
|
|
|
|
|
|
|
|
for(let search of searches) {
|
|
|
|
|
dbQuery.push({[key]: {$regex: '\\b' + search, $options: 'i'}});
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-15 11:33:37 -08:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-17 22:31:43 -08:00
|
|
|
if(!Session.get(PREFIX + "showHidden")) {
|
|
|
|
|
//Ignore any hidden elements by showing those not hidden, or those without the hidden field.
|
|
|
|
|
dbQuery.push({$or: [{hidden: false}, {hidden: {$exists:false}}]});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dbQuery = dbQuery.length > 0 ? {$and: dbQuery} : {};
|
|
|
|
|
Session.set(PREFIX + 'productCount', Meteor.collections.Products.find(dbQuery).count()); //Always get a full count.
|
2020-01-16 09:31:12 -08:00
|
|
|
|
|
|
|
|
//console.log("dbQuery=" + JSON.stringify(dbQuery));
|
|
|
|
|
//console.log("Result Count: " + Meteor.collections.Products.find(dbQuery).count());
|
|
|
|
|
|
2017-10-20 14:54:58 -07:00
|
|
|
return Meteor.collections.Products.find(dbQuery, {limit: Session.get(PREFIX + "queryLimit"), skip: skipCount, sort: {name: 1}});
|
2017-01-17 22:31:43 -08:00
|
|
|
},
|
2017-10-20 14:54:58 -07:00
|
|
|
disableLoadMore: function() {
|
|
|
|
|
return Session.get(PREFIX + 'productCount') - (Session.get(PREFIX + 'skipCount') || 0) - Session.get(PREFIX + "queryLimit") <= 0;
|
2017-01-17 22:31:43 -08:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
Template.Products.events({
|
2017-10-20 14:54:58 -07:00
|
|
|
'click .loadMoreLink': function(event, template) {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
Session.set(PREFIX + 'queryLimit', Session.get(PREFIX + "queryLimit") + QUERY_LIMIT_INCREMENT);
|
2017-01-17 22:31:43 -08:00
|
|
|
},
|
2019-10-07 15:51:50 -07:00
|
|
|
'click .newButton': function(event, template) {
|
|
|
|
|
if(template.$('.newButton').hasClass('active')) {
|
|
|
|
|
Session.set(PREFIX + 'displayNew', false);
|
2017-01-17 22:31:43 -08:00
|
|
|
}
|
|
|
|
|
else {
|
2019-10-07 15:51:50 -07:00
|
|
|
Session.set(PREFIX + 'displayNew', true);
|
|
|
|
|
Session.set(PREFIX + "editedId", undefined); //Clear the edited product so that only one editor is open at a time.
|
2017-10-08 08:56:15 -07:00
|
|
|
Session.set(PREFIX + "convertedProduct", undefined); //Clear the converted product so that only one editor is open at a time.
|
2017-01-17 22:31:43 -08:00
|
|
|
}
|
2019-10-07 15:51:50 -07:00
|
|
|
template.$('.newButton').toggleClass('active');
|
2017-01-17 22:31:43 -08:00
|
|
|
},
|
|
|
|
|
'change input[name="showHidden"]': function(event, template) {
|
|
|
|
|
//console.log("changed " + $(event.target).prop('checked'));
|
|
|
|
|
Session.set(PREFIX + "showHidden", $(event.target).prop('checked'));
|
2017-01-15 11:33:37 -08:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Template.ProductSearch.events({
|
|
|
|
|
"keyup .searchInput": _.throttle(function(event, template) {
|
2017-01-17 22:31:43 -08:00
|
|
|
let searchQuery = Session.get(PREFIX + 'searchQuery') || {};
|
|
|
|
|
let searchFields = Session.get(PREFIX + 'searchFields') || {};
|
2017-01-15 11:33:37 -08:00
|
|
|
let searchValue = template.$('.searchInput').val();
|
|
|
|
|
|
|
|
|
|
if(searchValue) {
|
|
|
|
|
if(this.number) searchValue = parseFloat(searchValue);
|
|
|
|
|
|
|
|
|
|
if(this.collection) {
|
|
|
|
|
let ids = Meteor.collections[this.collection].find({[this.collectionQueryColumnName]: {$regex: searchValue, $options: 'i'}}, {fields: {[this.collectionResultColumnName]: 1}}).fetch();
|
|
|
|
|
|
|
|
|
|
//Convert the ids to an array of ids instead of an array of objects containing an id.
|
|
|
|
|
for(let i = 0; i < ids.length; i++) {ids[i] = ids[i]._id;}
|
|
|
|
|
searchQuery[this.columnName] = {$in: ids};
|
|
|
|
|
searchFields[this.columnName] = searchValue;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
searchFields[this.columnName] = searchQuery[this.columnName] = searchValue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//Remove columns from the search query whose values are empty so we don't bother the database with them.
|
|
|
|
|
delete searchQuery[this.columnName];
|
|
|
|
|
delete searchFields[this.columnName];
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-17 22:31:43 -08:00
|
|
|
Session.set(PREFIX + 'searchQuery', searchQuery);
|
|
|
|
|
Session.set(PREFIX + 'searchFields', searchFields);
|
2017-02-03 09:20:29 -08:00
|
|
|
Session.set(PREFIX + 'skipCount', 0); //Reset the paging of the results.
|
2017-01-15 11:33:37 -08:00
|
|
|
}, 500)
|
|
|
|
|
});
|
|
|
|
|
Template.ProductSearch.helpers({
|
|
|
|
|
searchValue: function() {
|
2017-01-17 22:31:43 -08:00
|
|
|
let searchFields = Session.get(PREFIX + 'searchFields');
|
2017-01-15 11:33:37 -08:00
|
|
|
|
|
|
|
|
return (searchFields && searchFields[this.columnName]) ? searchFields[this.columnName] : '';
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Template.Product.helpers({
|
|
|
|
|
measures: function() {
|
|
|
|
|
let result = "";
|
|
|
|
|
|
|
|
|
|
if(this.measures && this.measures.length > 0) {
|
|
|
|
|
let measureNames = [];
|
|
|
|
|
|
|
|
|
|
for(let i = 0; i < this.measures.length; i++) {
|
|
|
|
|
let measureObject = Meteor.collections.Measures.findOne(this.measures[i]);
|
|
|
|
|
|
|
|
|
|
if(measureObject && measureObject.name)
|
|
|
|
|
measureNames.push(measureObject.name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = measureNames.join(", ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
},
|
2017-01-17 22:31:43 -08:00
|
|
|
aliases: function() {
|
|
|
|
|
return this.aliases.join(', ');
|
|
|
|
|
},
|
2017-01-15 11:33:37 -08:00
|
|
|
tags: function() {
|
|
|
|
|
let result = "";
|
|
|
|
|
|
|
|
|
|
if(this.tags && this.tags.length > 0) {
|
|
|
|
|
let tagNames = [];
|
|
|
|
|
|
|
|
|
|
for(let i = 0; i < this.tags.length; i++) {
|
|
|
|
|
let obj = Meteor.collections.ProductTags.findOne(this.tags[i]);
|
|
|
|
|
|
|
|
|
|
if(obj && obj.name)
|
|
|
|
|
tagNames.push(obj.name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = tagNames.join(", ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
2017-01-17 22:31:43 -08:00
|
|
|
},
|
|
|
|
|
editing: function() {
|
2019-10-07 15:51:50 -07:00
|
|
|
let editedId = Session.get(PREFIX + "editedId");
|
2017-01-17 22:31:43 -08:00
|
|
|
|
2019-10-07 15:51:50 -07:00
|
|
|
return editedId == this._id;
|
2017-01-17 22:31:43 -08:00
|
|
|
},
|
2017-10-08 08:56:15 -07:00
|
|
|
converting: function() {
|
|
|
|
|
let convertedProduct = Session.get(PREFIX + "convertedProduct");
|
|
|
|
|
|
|
|
|
|
return convertedProduct == this._id;
|
|
|
|
|
},
|
2017-01-17 22:31:43 -08:00
|
|
|
getRowClass: function() {
|
|
|
|
|
return this.hidden ? "hidden" : this.deactivated ? "deactivated" : "";
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
Template.Product.events({
|
|
|
|
|
"click .actionEdit": function(event, template) {
|
2019-10-07 15:51:50 -07:00
|
|
|
Session.set(PREFIX + "editedId", this._id);
|
|
|
|
|
Session.set(PREFIX + 'displayNew', false); //Ensure the new editor is closed.
|
2017-10-08 08:56:15 -07:00
|
|
|
Session.set(PREFIX + "convertedProduct", undefined); //Clear the converted product so that only one editor is open at a time.
|
2019-10-07 15:51:50 -07:00
|
|
|
template.parentTemplate().$('.newButton').removeClass('active');
|
2017-01-17 22:31:43 -08:00
|
|
|
},
|
2017-10-08 08:56:15 -07:00
|
|
|
"click .actionDeactivate": function(event, template) {
|
2017-01-17 22:31:43 -08:00
|
|
|
Meteor.call('deactivateProduct', this._id, function(error, result) {
|
|
|
|
|
if(error) sAlert.error(error);
|
|
|
|
|
else sAlert.success("Product Deactivated");
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
'click .actionActivate': function(event, template) {
|
|
|
|
|
Meteor.call('reactivateProduct', this._id, function(error, result) {
|
|
|
|
|
if(error) sAlert.error(error);
|
|
|
|
|
else sAlert.success("Product Reactivated");
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
"click .actionShow": function(event, template) {
|
|
|
|
|
Meteor.call('showProduct', this._id, function(error, result) {
|
|
|
|
|
if(error) sAlert.error(error);
|
|
|
|
|
else sAlert.success("Product Visibility Enabled");
|
|
|
|
|
});
|
|
|
|
|
},
|
2017-10-08 08:56:15 -07:00
|
|
|
"click .actionConvert": function(event, template) {
|
|
|
|
|
Session.set(PREFIX + "convertedProduct", this._id);
|
2019-10-07 15:51:50 -07:00
|
|
|
Session.set(PREFIX + 'displayNew', false); //Ensure the new editor is closed.
|
|
|
|
|
Session.set(PREFIX + "editedId", undefined); //Clear the edited product so that only one editor is open at a time.
|
|
|
|
|
template.$('.newButton').removeClass('active');
|
2017-10-08 08:56:15 -07:00
|
|
|
},
|
2017-01-17 22:31:43 -08:00
|
|
|
'click .actionHide': function(event, template) {
|
|
|
|
|
Meteor.call('hideProduct', this._id, function(error, result) {
|
|
|
|
|
if(error) sAlert.error(error);
|
|
|
|
|
else sAlert.success("Product Visibility Disabled");
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Template.ProductEditor.onRendered(function() {
|
|
|
|
|
this.$(".productTagsEditor").select2();
|
|
|
|
|
this.$(".productAliasesEditor").select2({tags: true, tokenSeparators: [';', '.']});
|
|
|
|
|
this.$(".productMeasuresEditor").select2();
|
|
|
|
|
});
|
|
|
|
|
Template.ProductEditor.helpers({
|
|
|
|
|
measures: function() {
|
|
|
|
|
return Meteor.collections.Measures.find({});
|
|
|
|
|
},
|
|
|
|
|
measureSelected: function() {
|
|
|
|
|
let measure = this;
|
|
|
|
|
let product = Template.parentData();
|
|
|
|
|
|
|
|
|
|
return product.measures && product.measures.includes(measure._id) ? "selected" : "";
|
|
|
|
|
},
|
|
|
|
|
aliases: function() {
|
|
|
|
|
return this.aliases;
|
|
|
|
|
},
|
|
|
|
|
tags: function() {
|
|
|
|
|
return Meteor.collections.ProductTags.find({});
|
|
|
|
|
},
|
|
|
|
|
tagSelected: function() {
|
|
|
|
|
let tag = this;
|
|
|
|
|
let product = Template.parentData();
|
|
|
|
|
|
|
|
|
|
return product.tags && product.tags.includes(tag._id) ? "selected" : "";
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
Template.ProductEditor.events({
|
|
|
|
|
"click .editorCancel": function(event, template) {
|
2019-10-07 15:51:50 -07:00
|
|
|
Session.set(PREFIX + "editedId", undefined);
|
|
|
|
|
Session.set(PREFIX + 'displayNew', false);
|
|
|
|
|
template.parentTemplate().$('.newButton').removeClass('active');
|
2017-01-17 22:31:43 -08:00
|
|
|
},
|
|
|
|
|
"click .editorApply": function(event, template) {
|
|
|
|
|
let name = template.$("input[name='name']").val().trim();
|
|
|
|
|
let tags = template.$(".productTagsEditor").select2('data');
|
|
|
|
|
let aliases = template.$(".productAliasesEditor").select2('data');
|
|
|
|
|
let measures = template.$(".productMeasuresEditor").select2('data');
|
|
|
|
|
|
|
|
|
|
tags = tags.map((n)=>n.id);
|
|
|
|
|
aliases = aliases.map((n)=>n.id);
|
|
|
|
|
measures = measures.map((n)=>n.id);
|
|
|
|
|
|
2019-10-07 15:51:50 -07:00
|
|
|
if(Session.get(PREFIX + 'displayNew')) {
|
2017-01-17 22:31:43 -08:00
|
|
|
Meteor.call("createProduct", name, tags, aliases, measures, function(error, result) {
|
|
|
|
|
if(error) sAlert.error(error);
|
|
|
|
|
else {
|
|
|
|
|
sAlert.success("Product created.");
|
2019-10-07 15:51:50 -07:00
|
|
|
Session.set(PREFIX + 'displayNew', false);
|
|
|
|
|
template.parentTemplate().$('.newButton').removeClass('active');
|
2017-01-17 22:31:43 -08:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Meteor.call("updateProduct", this._id, name, tags, aliases, measures, function(error, result) {
|
|
|
|
|
if(error) sAlert.error(error);
|
|
|
|
|
else {
|
|
|
|
|
sAlert.success("Product updated.");
|
2019-10-07 15:51:50 -07:00
|
|
|
Session.set(PREFIX + "editedId", undefined);
|
|
|
|
|
template.parentTemplate().$('.newButton').removeClass('active');
|
2017-01-17 22:31:43 -08:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2017-01-15 11:33:37 -08:00
|
|
|
}
|
2017-10-08 08:56:15 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Template.ConvertProduct.onCreated(function() {
|
|
|
|
|
this.selectedProduct = new ReactiveVar();
|
|
|
|
|
});
|
|
|
|
|
Template.ConvertProduct.onRendered(function() {
|
|
|
|
|
this.$('[name="product"]').buildCombo({cursor: Meteor.collections.Products.find({$or: [{hidden: false}, {hidden: {$exists:false}}]}), selection: this.selectedProduct, textAttr: 'name', listClass: 'comboList', getClasses: function(data) {
|
|
|
|
|
return (data && data.deactivated) ? "deactivated" : "";
|
|
|
|
|
}});
|
|
|
|
|
});
|
|
|
|
|
Template.ConvertProduct.events({
|
|
|
|
|
"click .editorCancel": function(event, template) {
|
2019-10-07 15:51:50 -07:00
|
|
|
Session.set(PREFIX + "editedId", undefined);
|
2017-10-08 08:56:15 -07:00
|
|
|
Session.set(PREFIX + "convertedProduct", undefined);
|
2019-10-07 15:51:50 -07:00
|
|
|
Session.set(PREFIX + 'displayNew', false);
|
|
|
|
|
template.parentTemplate().$('.newButton').removeClass('active');
|
2017-10-08 08:56:15 -07:00
|
|
|
},
|
|
|
|
|
"click .editorApply": function(event, template) {
|
|
|
|
|
let productId = template.selectedProduct.get()._id;
|
|
|
|
|
|
|
|
|
|
Meteor.call("convertProduct", this._id, productId, function(error, result) {
|
|
|
|
|
if(error) sAlert.error(error);
|
|
|
|
|
else {
|
|
|
|
|
sAlert.success("Sales of this product were converted.");
|
2019-10-07 15:51:50 -07:00
|
|
|
Session.set(PREFIX + "editedId", undefined);
|
2017-10-08 08:56:15 -07:00
|
|
|
Session.set(PREFIX + "convertedProduct", undefined);
|
2019-10-07 15:51:50 -07:00
|
|
|
Session.set(PREFIX + 'displayNew', false);
|
|
|
|
|
template.parentTemplate().$('.newButton').removeClass('active');
|
2017-10-08 08:56:15 -07:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2017-01-15 11:33:37 -08:00
|
|
|
});
|