563 lines
21 KiB
JavaScript
563 lines
21 KiB
JavaScript
|
|
import './Sales.html';
|
|
import '/imports/util/selectize/selectize.js';
|
|
import swal from 'sweetalert2';
|
|
//import 'malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js';
|
|
//import 'jquery-mousewheel';
|
|
//import 'malihu-custom-scrollbar-plugin';
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
|
|
let QUERY_LIMIT = 20;
|
|
let PREFIX = "Sales.";
|
|
|
|
Template.Sales.onCreated(function() {
|
|
Session.set(PREFIX + "displayNewSale", false);
|
|
|
|
Meteor.subscribe("products");
|
|
Session.set(PREFIX + "sortOption", "date");
|
|
Session.set(PREFIX + "showOnlyComments", false);
|
|
|
|
Tracker.autorun(function() {
|
|
let sortOption = Session.get(PREFIX + "sortOption");
|
|
let sort = sortOption == 'createdAt' ? {createdAt: -1} : {date: -1, createdAt: -1};
|
|
let showOnlyComments = Session.get(PREFIX + "showOnlyComments");
|
|
let query = _.clone(Session.get(PREFIX + 'searchQuery'));
|
|
|
|
if(showOnlyComments) {
|
|
if(!query) query = {};
|
|
query.comment = {$exists: true};
|
|
}
|
|
|
|
//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')));
|
|
});
|
|
});
|
|
Template.Sales.onRendered(function() {
|
|
$(".tableContainer").mCustomScrollbar({
|
|
scrollButtons: {enable:true},
|
|
theme: "light-thick",
|
|
scrollbarPosition: "outside",
|
|
scrollEasing: "linear"
|
|
});
|
|
});
|
|
Template.Sales.onDestroyed(function() {
|
|
if(Template.Sales.salesSubscription) {
|
|
Template.Sales.salesSubscription.stop();
|
|
}
|
|
});
|
|
Template.Sales.helpers({
|
|
displayNewSale: function() {
|
|
return Session.get(PREFIX + "displayNewSale");
|
|
},
|
|
sales: function() {
|
|
let sortOption = Session.get(PREFIX + "sortOption");
|
|
|
|
return Meteor.collections.Sales.find({}, {sort: (sortOption == 'createdAt' ? {createdAt: -1} : {date: -1, createdAt: -1})});
|
|
},
|
|
disablePrev: function() {
|
|
return (Session.get(PREFIX + 'skipCount') || 0) == 0;
|
|
},
|
|
disableNext: function() {
|
|
return Session.get(PREFIX + 'saleCount') - (Session.get(PREFIX + 'skipCount') || 0) - QUERY_LIMIT <= 0;
|
|
},
|
|
editing: function() {
|
|
let editedSale = Session.get(PREFIX + "editedSale");
|
|
|
|
return editedSale == this._id;
|
|
}
|
|
});
|
|
Template.Sales.events({
|
|
'click .newSaleButton': function(event, template) {
|
|
if(template.$('.newSaleButton').hasClass('active')) {
|
|
Session.set(PREFIX + 'displayNewSale', false);
|
|
}
|
|
else {
|
|
Session.set(PREFIX + 'displayNewSale', true);
|
|
Session.set(PREFIX + "editedSale", undefined); //Clear the edited sale so that only one editor is open at a time.
|
|
|
|
//Set the focus to the date field of the form. Put this in a timeout so that it is queued for processing after the form is added to the DOM.
|
|
setTimeout(function() {
|
|
$("form[name='insertSaleForm'] input[name='date']").focus();
|
|
}, 10);
|
|
}
|
|
template.$('.newSaleButton').toggleClass('active');
|
|
},
|
|
'click .prevButton': function(event, template) {
|
|
if(!$(event.target).hasClass('disabled'))
|
|
Session.set(PREFIX + 'skipCount', Math.max(0, (Session.get(PREFIX + 'skipCount') || 0) - QUERY_LIMIT));
|
|
},
|
|
'click .nextButton': function(event, template) {
|
|
if(!$(event.target).hasClass('disabled'))
|
|
Session.set(PREFIX + 'skipCount', (Session.get(PREFIX + 'skipCount') || 0) + QUERY_LIMIT);
|
|
},
|
|
'change select[name="sortSelect"]': function(event, template) {
|
|
Session.get(PREFIX + 'skipCount', 0);
|
|
Session.set(PREFIX + "sortOption", $(event.target).val());
|
|
},
|
|
'click .showOnlyComments': function(event, template) {
|
|
let $button = $(event.target);
|
|
|
|
Session.set(PREFIX + "showOnlyComments", !$button.hasClass('on'));
|
|
$button.toggleClass('on');
|
|
},
|
|
'click .showDuplicates': function(event, template) {
|
|
FlowRouter.go('SaleDuplicates');
|
|
}
|
|
});
|
|
|
|
Template.Sale.onCreated(function() {
|
|
});
|
|
Template.Sale.helpers({
|
|
measureName: function(id) {
|
|
return Meteor.collections.Measures.findOne({_id: id}, {fields: {name: 1}}).name;
|
|
},
|
|
venueName: function(id) {
|
|
return Meteor.collections.Venues.findOne({_id: id}, {fields: {name: 1}}).name;
|
|
},
|
|
productName: function(id) {
|
|
return Meteor.collections.Products.findOne({_id: id}, {fields: {name: 1}}).name;
|
|
},
|
|
formatDateAndWeek: function(date, weekOfYear) {
|
|
return moment.utc(date.toString(), "YYYYMMDD").utc().format("MM/DD/YYYY") + "(" + weekOfYear + ")";
|
|
},
|
|
formatDateTime: function(date) {
|
|
return moment.utc(date).format("MM/DD/YYYY");
|
|
},
|
|
formatPrice: function(price) {
|
|
return price.toLocaleString("en-US", {style: 'currency', currency: 'USD', minimumFractionDigits: 2});
|
|
},
|
|
formatTotalPrice: function(price, amount) {
|
|
return (price * amount).toLocaleString("en-US", {style: 'currency', currency: 'USD', minimumFractionDigits: 2});
|
|
},
|
|
showTotalPrice: function(amount) {
|
|
return amount > 1;
|
|
},
|
|
commentClass: function() {
|
|
return this.comment ? "hasComment" : "";
|
|
}
|
|
});
|
|
Template.Sale.events({
|
|
"click .actionEdit": function(event, template) {
|
|
Session.set(PREFIX + "editedSale", this._id);
|
|
Session.set(PREFIX + 'displayNewSale', false); //Ensure the new sale editor is closed.
|
|
template.parentTemplate().$('.newSaleButton').removeClass('active');
|
|
},
|
|
"click .saleRemove": function(event, template) {
|
|
let _this = this;
|
|
swal({
|
|
title: "Are you sure?",
|
|
text: "This will permanently remove the sale.",
|
|
type: "question",
|
|
showCancelButton: true,
|
|
confirmButtonColor: "#DD6B55",
|
|
confirmButtonText: "Yes"
|
|
}).then(
|
|
function(isConfirm) {
|
|
if(isConfirm) {
|
|
// Meteor.collections.Sales.remove(_this._id);
|
|
Meteor.call('deleteSale', _this._id);
|
|
}
|
|
},
|
|
function(dismiss) {
|
|
|
|
}
|
|
);
|
|
},
|
|
"click .editComment": function(event, template) {
|
|
let _this = this;
|
|
|
|
swal({
|
|
title: "Sale Comment",
|
|
text: "Change the comment, or clear it to remove the comment.",
|
|
input: "textarea",
|
|
showCancelButton: true,
|
|
closeOnConfirm: true,
|
|
closeOnCancel: true,
|
|
animation: "slide-from-top",
|
|
inputPlaceholder: "Write a comment...",
|
|
allowEscapeKey: true,
|
|
inputValue: _this.comment ? _this.comment : ""
|
|
}).then(
|
|
function(text) {
|
|
Meteor.call('editSaleComment', _this._id, text);
|
|
},
|
|
function(dismiss) {}
|
|
);
|
|
}
|
|
});
|
|
|
|
Template.SaleEditor.onCreated(function() {
|
|
this.product = Meteor.collections.Products.findOne({_id: this.data.productId});
|
|
this.selectedDate = new ReactiveVar(moment(this.data.date.toString(), "YYYYMMDD").toDate());
|
|
this.selectedVenue = new ReactiveVar(Meteor.collections.Venues.findOne({_id: this.data.venueId}));
|
|
this.price = new ReactiveVar(this.data.price);
|
|
this.amount = new ReactiveVar(this.data.amount);
|
|
});
|
|
Template.SaleEditor.onRendered(function() {
|
|
this.$('form[name="editSaleForm"]').validator();
|
|
this.$('[name="venue"]').buildCombo({cursor: Meteor.collections.Venues.find({}), selection: this.selectedVenue, comparator: function(a, b) {return a._id == b._id;}, textAttr: 'name', listClass: 'comboList'});
|
|
this.$('input[name="date"]').val(moment(this.selectedDate.get()).format("YYYY-MM-DD"));
|
|
});
|
|
Template.SaleEditor.helpers({
|
|
measureName: function(id) {
|
|
let measure = Meteor.collections.Measures.findOne({_id: id}, {fields: {name: 1}});
|
|
|
|
return measure ? measure.name : "???";
|
|
},
|
|
productName: function() {
|
|
let product = Template.instance().product;
|
|
return product ? product.name : "???";
|
|
},
|
|
price: function() {
|
|
return Template.instance().price.get();
|
|
},
|
|
amount: function() {
|
|
return Template.instance().amount.get();
|
|
},
|
|
total: function() {
|
|
let template = Template.instance();
|
|
return (template.price.get() * template.amount.get()).toFixed(2);
|
|
}
|
|
});
|
|
Template.SaleEditor.events({
|
|
'click .setDefaultPrice': function(event, template) {
|
|
let date = template.selectedDate.get();
|
|
let prices = template.product.prices;
|
|
let priceData;
|
|
let price = 0;
|
|
|
|
if(prices) priceData = prices[template.data.measureId];
|
|
|
|
//If this product has pricing data for the given measure, then either use the price, or the previousPrice (if there is one and the effectiveDate is after the sale date).
|
|
if(priceData) {
|
|
if(priceData.effectiveDate && date && moment.utc(priceData.effectiveDate.toString(), "YYYYMMDD").isAfter(date))
|
|
price = priceData.previousPrice;
|
|
else
|
|
price = priceData.price
|
|
}
|
|
|
|
template.price.set(price);
|
|
},
|
|
'change input[name="date"]': function(event, template) {
|
|
template.selectedDate.set(moment(event.target.value, "YYYY-MM-DD").toDate());
|
|
},
|
|
'change .price': function(event, template) {
|
|
template.price.set(parseFloat($(event.target).val()));
|
|
},
|
|
'change .amount': function(event, template) {
|
|
template.amount.set(parseFloat($(event.target).val()));
|
|
},
|
|
"click .editorCancel": function(event, template) {
|
|
Session.set(PREFIX + "editedSale", undefined);
|
|
},
|
|
"click .editorApply": function(event, template) {
|
|
template.$('form[name="editSaleForm"]').data('bs.validator').validate(function(isValid) {
|
|
if(isValid) {
|
|
let id = template.data._id;
|
|
let date = ~~(moment(template.selectedDate.get()).format("YYYYMMDD")); // Note: The ~~ is a bitwise not that is a fast method of converting a string to a number.
|
|
let venue = template.selectedVenue.get();
|
|
let price = template.price.get();
|
|
let amount = template.amount.get();
|
|
|
|
Meteor.call("updateSale", id, date, venue._id, price, amount, function(error, result) {
|
|
if(error) sAlert.error(error);
|
|
else {
|
|
sAlert.success("Sale updated.");
|
|
Session.set(PREFIX + "editedSale", undefined);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
Template.SaleSearch.helpers({
|
|
searchValue: function() {
|
|
let searchFields = Session.get(PREFIX + 'searchFields');
|
|
|
|
return (searchFields && searchFields[this.columnName]) ? searchFields[this.columnName] : '';
|
|
}
|
|
});
|
|
Template.SaleSearch.events({
|
|
"keyup .searchInput": _.throttle(function(event, template) {
|
|
let searchQuery = Session.get(PREFIX + 'searchQuery') || {};
|
|
let searchFields = Session.get(PREFIX + 'searchFields') || {};
|
|
let searchValue = template.$(event.target).val();
|
|
|
|
if(searchValue) {
|
|
if(this.number) searchValue = parseFloat(searchValue);
|
|
|
|
// A collection name will be provided if there is a related table of data that will contain the text provided and will map to an ID that is then searched for in the current table of data.
|
|
// For example we are displaying a table of Sales which has the ID of a Product. The Product table has a Name field and the search box searches for Product Names. The ID's of the Products found should be used to filter the Sales by Product ID.
|
|
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];
|
|
}
|
|
|
|
Session.set(PREFIX + 'searchQuery', searchQuery);
|
|
Session.set(PREFIX + 'searchFields', searchFields);
|
|
Session.set(PREFIX + 'skipCount', 0); //Reset the paging of the results.
|
|
}, 500)
|
|
});
|
|
|
|
Template.DateRangeSearch.helpers({
|
|
startDate: function() {
|
|
let searchFields = Session.get(PREFIX + 'searchFields');
|
|
let searchValue = (searchFields && searchFields[this.columnName]) ? searchFields[this.columnName] : {};
|
|
|
|
return searchValue.start ? moment(searchValue.start.toString(), "YYYYMMDD").format("MM/DD/YYYY") : "";
|
|
},
|
|
endDate: function() {
|
|
let searchFields = Session.get(PREFIX + 'searchFields');
|
|
let searchValue = (searchFields && searchFields[this.columnName]) ? searchFields[this.columnName] : {};
|
|
|
|
return searchValue.end ? moment(searchValue.end.toString(), "YYYYMMDD").format("MM/DD/YYYY") : "";
|
|
}
|
|
});
|
|
Template.DateRangeSearch.events({
|
|
"change .searchDateStartInput": function(event, template) {Template.DateRangeSearch.dateChanged(true, event, template)},
|
|
"keyup .searchDateStartInput": _.throttle(function(event, template) {Template.DateRangeSearch.dateChanged(true, event, template)}, 500),
|
|
"change .searchDateEndInput": function(event, template) {Template.DateRangeSearch.dateChanged(false, event, template)},
|
|
"keyup .searchDateEndInput": _.throttle(function(event, template) {Template.DateRangeSearch.dateChanged(false, event, template)}, 500)
|
|
});
|
|
Template.DateRangeSearch.dateChanged = function(isStart, event, template) {
|
|
let searchQuery = Session.get(PREFIX + 'searchQuery') || {};
|
|
let searchFields = Session.get(PREFIX + 'searchFields') || {};
|
|
let searchValue = template.$(event.target).val();
|
|
let columnName = template.data.columnName;
|
|
|
|
if(searchValue) {
|
|
let search = searchQuery[columnName];
|
|
|
|
// Create a search object and attach it to the searchFields and searchQuery objects if needed.
|
|
if(!search) {
|
|
search = {type: 'dateRange'};
|
|
searchFields[columnName] = searchQuery[columnName] = search;
|
|
}
|
|
|
|
// Use moment to parse date and convert it to YYYYMMDD for searching the database.
|
|
searchValue = ~~(moment(searchValue, searchValue.includes("-") ? "YYYY-MM-DD" : "MM/DD/YYYY").format("YYYYMMDD")); // Note: ~~ performs a bitwise not which is a fast method of converting a string to a number.
|
|
// Save the search ending date.
|
|
isStart ? search.start = searchValue : search.end = searchValue;
|
|
}
|
|
else {
|
|
if(searchQuery[columnName]) {
|
|
// Remove columns from the search query whose values are empty so we don't bother the database with them.
|
|
if(isStart) {
|
|
delete searchQuery[columnName].start;
|
|
delete searchFields[columnName].start;
|
|
}
|
|
else {
|
|
delete searchQuery[columnName].end;
|
|
delete searchFields[columnName].end;
|
|
}
|
|
}
|
|
}
|
|
|
|
Session.set(PREFIX + 'searchQuery', searchQuery);
|
|
Session.set(PREFIX + 'searchFields', searchFields);
|
|
Session.set(PREFIX + 'skipCount', 0); //Reset the paging of the results.
|
|
};
|
|
|
|
Template.InsertSale.onCreated(function() {
|
|
this.selectedDate = new ReactiveVar();
|
|
this.selectedProduct = new ReactiveVar();
|
|
this.selectedVenue = new ReactiveVar();
|
|
});
|
|
Template.InsertSale.onRendered(function() {
|
|
this.$('.insertSaleForm').validator();
|
|
|
|
//TODO: Make the query for products reactive, by putting it inside an autorun block.
|
|
//TODO: Fix the combo's change event firing. It does not fire a change event when selecting an item for the first time. It's $(input).val() call also returns the name of the thing selected instead of the selected object.
|
|
// Note: The combo will automatically update our selection reactive variable. No need to capture change events.
|
|
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" : "";
|
|
}});
|
|
this.$('[name="venue"]').buildCombo({cursor: Meteor.collections.Venues.find({}), selection: this.selectedVenue, textAttr: 'name', listClass: 'comboList'});
|
|
});
|
|
Template.InsertSale.events({
|
|
//'change input[name="product"]': function(event, template) {
|
|
// let selectedId = template.$('input[name="product"]').val();
|
|
// let selected = Meteor.collections.Products.findOne(selectedId);
|
|
// template.selectedProduct.set(selected);
|
|
//},
|
|
'change input[name="date"]': function(event, template) {
|
|
template.selectedDate.set(moment(event.target.value, "YYYY-MM-DD").toDate());
|
|
},
|
|
'click input[type="submit"]': function(event, template) {
|
|
event.preventDefault();
|
|
template.$('.insertSaleForm').data('bs.validator').validate(function(isValid) {
|
|
if(isValid) {
|
|
let sales = [];
|
|
let insertSaleMeasures = template.$(".insertSaleMeasure");
|
|
|
|
let sale = {
|
|
date: ~~(moment(template.find("[name='date']").value, "YYYY-MM-DD").format("YYYYMMDD")), // Note: ~~ performs a bitwise not which is a fast method of converting a string to a number.
|
|
productId: template.selectedProduct.get()._id,
|
|
venueId: template.selectedVenue.get()._id
|
|
};
|
|
|
|
//Iterate over the measures for the sale (based on the product chosen) and collection amounts and prices.
|
|
for(let next = 0; next < insertSaleMeasures.length; next++) {
|
|
let nextMeasure = $(insertSaleMeasures[next]);
|
|
let measureId = nextMeasure.find(".measureId").val();
|
|
let price = parseFloat(nextMeasure.find(".price").val());
|
|
let amount = parseFloat(nextMeasure.find(".amount").val());
|
|
|
|
if(amount > 0) {
|
|
let nextSale = _.clone(sale);
|
|
|
|
nextSale.measureId = measureId;
|
|
nextSale.price = price;
|
|
nextSale.amount = amount;
|
|
sales.push(nextSale);
|
|
}
|
|
}
|
|
|
|
//Iterate over the product measures that have a quantity greater than zero and add them as a sale.
|
|
for(let index = 0; index < sales.length; index++) {
|
|
let next = sales[index];
|
|
//console.log("Inserting: " + JSON.stringify(next));
|
|
Meteor.call('insertSale', next, function(error) {
|
|
if(error) sAlert.error("Failed to insert the sale!\n" + error);
|
|
else {
|
|
sAlert.success("Sale Created");
|
|
|
|
//Clear the measure quantity fields so the user can enter another sale without the quantities already set.
|
|
for(let next = 0; next < insertSaleMeasures.length; next++) {
|
|
let nextMeasure = $(insertSaleMeasures[next]);
|
|
|
|
nextMeasure.find(".amount").val(0);
|
|
}
|
|
|
|
//Set the focus to the product field of the form.
|
|
$("form[name='insertSaleForm'] input[name='product']").focus();
|
|
//Clear the product since it is highly unlikely the same product will be added twice for the same date and market.
|
|
$("form[name='insertSaleForm'] input[name='product']").val("");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
},
|
|
"click .speechLink": function(event, template) {
|
|
try {
|
|
let SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
|
let r = new SpeechRecognition();
|
|
|
|
r.lang='en-US';
|
|
r.interimResult = false;
|
|
r.maxAlternatives = 0;
|
|
r.start();
|
|
r.onresult = function(event) {
|
|
let product = template.$('input[name="product"]');
|
|
product.val(event.results[0][0].transcript);
|
|
let combo = product.data("de.combo");
|
|
|
|
combo.show();
|
|
combo.filter();
|
|
//Count the number of leaf nodes that are visible.
|
|
combo.$list.find('li.visible[role="leaf"]').length
|
|
}
|
|
}
|
|
catch(e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
});
|
|
Template.InsertSale.helpers({
|
|
productMeasures: function() {
|
|
let product = Template.instance().selectedProduct.get();
|
|
let result = product ? product.measures : [];
|
|
|
|
for(let i = 0; i < result.length; i++) {
|
|
result[i] = Meteor.collections.Measures.findOne(result[i]);
|
|
}
|
|
|
|
// if(product) console.log("Found " + result.length + " measures for the product " + product.name);
|
|
// else console.log("No product!");
|
|
|
|
return result;
|
|
},
|
|
venues: function() {
|
|
return Meteor.collections.Venues.find({});
|
|
}
|
|
});
|
|
|
|
Template.InsertSaleMeasure.onCreated(function() {
|
|
let _this = this;
|
|
|
|
this.price = new ReactiveVar(0);
|
|
this.amount = new ReactiveVar(0);
|
|
|
|
Tracker.autorun(function() {
|
|
let date = _this.parentTemplate().selectedDate.get();
|
|
let prices = _this.parentTemplate().selectedProduct.get().prices;
|
|
let priceData;
|
|
let price = 0;
|
|
|
|
if(prices) priceData = prices[_this.data._id];
|
|
|
|
//If this product has pricing data for the given measure, then either use the price, or the previousPrice (if there is one and the effectiveDate is after the sale date).
|
|
if(priceData) {
|
|
if(priceData.effectiveDate && date && moment(priceData.effectiveDate).isAfter(date))
|
|
price = priceData.previousPrice;
|
|
else
|
|
price = priceData.price
|
|
}
|
|
|
|
_this.price.set(price);
|
|
});
|
|
});
|
|
Template.InsertSaleMeasure.events({
|
|
'change .price': function(event, template) {
|
|
template.price.set(parseFloat($(event.target).val()));
|
|
},
|
|
'change .amount': function(event, template) {
|
|
template.amount.set(parseFloat($(event.target).val()));
|
|
},
|
|
'focus input[name="amount"],input[name="price"]': function(event, template) {
|
|
//See http://stackoverflow.com/questions/3150275/jquery-input-select-all-on-focus
|
|
//Handle selecting the text in the field on receipt of focus.
|
|
let $this = $(this)
|
|
.one('mouseup.mouseupSelect', function() {
|
|
$this.select();
|
|
return false;
|
|
})
|
|
.one('mousedown', function() {
|
|
// compensate for untriggered 'mouseup' caused by focus via tab
|
|
$this.off('mouseup.mouseupSelect');
|
|
})
|
|
.select();
|
|
}
|
|
});
|
|
Template.InsertSaleMeasure.helpers({
|
|
price: function() {
|
|
return Template.instance().price.get().toFixed(2);
|
|
},
|
|
total: function() {
|
|
let template = Template.instance();
|
|
return (template.price.get() * template.amount.get()).toFixed(2);
|
|
},
|
|
amount: function() {
|
|
return Template.instance().amount.get();
|
|
}
|
|
}); |