2019-10-07 15:51:50 -07:00
import { Meteor } from 'meteor/meteor' ;
import { Mongo } from 'meteor/mongo' ;
import { check } from 'meteor/check' ;
import { SimpleSchema } from 'meteor/aldeed:simple-schema' ;
/ * *
* Notes :
* The Batch 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 ( batch . 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 .
* A Batch in this system refers to one or more instances of cooking or preparing a product on a given date . It does NOT refer to each instance of cooking the product on that date ( what might be called a batch in a kitchen ) . This might be more effectively called a Run , but that is a confusing word to use in a software system , so I chose to reuse the word Batch since we will not be tracking kitchen batches , just kitchen runs .
* /
let Batches = new Mongo . Collection ( 'Batches' ) ;
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
} ,
timestamp : { //This is based off the date with zero for the time and set to GMT (Zulu time).
type : Date ,
label : "Timestamp" ,
optional : true
} ,
weekOfYear : {
type : Number ,
label : "Week Of Year" ,
optional : true
} ,
amount : {
type : Number ,
label : "Amount" ,
optional : false ,
decimal : true
} ,
measureId : {
type : String ,
label : "Measure Id" ,
trim : false ,
regEx : SimpleSchema . RegEx . Id ,
index : 1
} ,
productId : {
type : String ,
label : "Product Id" ,
trim : false ,
regEx : SimpleSchema . RegEx . Id ,
index : 1 ,
optional : false
} ,
cookId : {
type : String ,
label : "Cook Worker Id" ,
trim : false ,
regEx : SimpleSchema . RegEx . Id ,
index : 1
} ,
cannerId : {
type : String ,
label : "Canner Worker Id" ,
trim : false ,
regEx : SimpleSchema . RegEx . Id ,
index : 1 ,
optional : false
} ,
hasLabels : {
type : Boolean ,
label : "Has Labels" ,
optional : false ,
defaultValue : false
} ,
comment : {
type : String ,
trim : false ,
optional : true
} ,
createdAt : {
type : Date ,
label : "Created On" ,
optional : false
} ,
deletedAt : {
type : Date ,
label : "Deleted On" ,
optional : true
}
} ) ;
Batches . attachSchema ( BatchesSchema ) ;
//Ensure that the product ID, measure ID, and date combination are unique.
// Note: I took this out because while it provides for cleaner views, it is overly complicated and could be easily done with a cleanup routine after the fact, or by aggregating the data in the queries.
// What makes this complicated is the notes, cook, and canner references which may not be the same.
//Batches.createIndex({productId: 1, measureId: 1, date: 1}, {unique: true, name: "ProductMeasureDateIndex"});
if ( Meteor . isServer ) {
Meteor . publish ( 'batches' , function ( query , sort , limit = 100 , skipCount ) {
let dbQuery = [ ] ;
if ( query ) {
_ . each ( _ . keys ( query ) , function ( key ) {
//if(_.isObject(query[key])) dbQuery.push({[key]: query[key]});
if ( _ . isObject ( query [ key ] ) ) {
if ( query [ key ] . type === 'dateRange' ) {
if ( query [ key ] . start && query [ key ] . end )
dbQuery . push ( { [ key ] : { $gte : query [ key ] . start , $lte : query [ key ] . end } } ) ;
else if ( query [ key ] . start )
dbQuery . push ( { [ key ] : { $gte : query [ key ] . start } } ) ;
else if ( query [ key ] . end )
dbQuery . push ( { [ key ] : { $lte : query [ key ] . end } } ) ;
// Do nothing if a start and/or end are not provided.
}
else {
dbQuery . push ( { [ key ] : query [ key ] } ) ;
}
}
else if ( _ . isNumber ( query [ key ] ) ) dbQuery . push ( { [ key ] : query [ key ] } ) ;
else {
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' } } ) ;
}
}
} ) ;
}
if ( ! _ . isNumber ( limit ) ) limit = 100 ;
if ( ! _ . isNumber ( skipCount ) || skipCount < 0 ) skipCount = 0 ;
dbQuery = dbQuery . length > 0 ? { $and : dbQuery } : { } ;
2020-01-16 09:31:12 -08:00
//console.log("dbQuery=" + JSON.stringify(dbQuery));
//console.log("Result Count: " + Batches.find(query).count());
return Batches . find ( dbQuery , { limit : limit , sort , skip : skipCount } ) ;
2019-10-07 15:51:50 -07:00
} ) ;
Meteor . methods ( {
getBatchCount : function ( query ) {
//TODO: Validate the query?
2020-01-16 09:31:12 -08:00
return Batches . find ( query ) . count ( ) ;
2019-10-07 15:51:50 -07:00
} ,
insertBatches : 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 ] ;
//Validate them all.
for ( let batch of batches ) {
check ( batch , {
date : Number , // TODO: Check that the format is YYYYMMDD
amount : Match . Where ( function ( x ) {
check ( x , Number ) ;
return x > 0 ;
} ) ,
measureId : String ,
productId : String ,
cookId : String ,
cannerId : String ,
comment : Match . Optional ( String )
} ) ;
}
for ( let batch of batches ) {
let dateString = batch . date . toString ( ) ;
batch . createdAt = new Date ( ) ;
batch . timestamp = new Date ( dateString . substring ( 0 , 4 ) + "-" + dateString . substring ( 4 , 6 ) + "-" + dateString . substring ( 6 , 8 ) + "T00:00:00Z" ) ;
batch . weekOfYear = batch . timestamp . getWeek ( ) . toString ( ) ;
if ( batch . hasLabels === undefined ) batch . hasLabels = false ;
}
for ( let batch of batches ) {
Batches . insert ( 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.
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 ) {
if ( err ) console . log ( err ) ;
} ) ;
}
else throw new Meteor . Error ( 403 , "Not authorized." ) ;
} ,
undeleteBatch : 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 ) {
if ( err ) console . log ( err ) ;
} ) ;
}
else throw new Meteor . Error ( 403 , "Not authorized." ) ;
} ,
2020-01-16 09:31:12 -08:00
//editBatchComment: function(id, comment) {
// check(id, String);
// check(comment, String);
// //Trim and convert empty comment to undefined.
// comment = comment ? comment.trim() : undefined;
// comment = comment && comment.length > 0 ? comment : undefined;
//
// if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
// console.log("Changed comment of " + id + " to: " + comment);
//
// if(comment) {
// Batches.update(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) {
// if(error) throw new Meteor.Error(400, "Unexpected database error: " + error);
// });
// }
// }
// else throw new Meteor.Error(403, "Not authorized.");
//},
updateBatch : function ( id , amount , comment ) {
2019-10-07 15:51:50 -07:00
check ( id , String ) ;
2020-01-16 09:31:12 -08:00
check ( amount , Number ) ;
check ( comment , Match . OneOf ( String , undefined ) ) ;
2019-10-07 15:51:50 -07:00
//Trim and convert empty comment to undefined.
comment = comment ? comment . trim ( ) : undefined ;
comment = comment && comment . length > 0 ? comment : undefined ;
2020-01-16 09:31:12 -08:00
//let dateString = date.toString();
//let timestamp = new Date(dateString.substring(0, 4) + "-" + dateString.substring(4, 6) + "-" + dateString.substring(6, 8) + "T00:00:00Z");
//let weekOfYear = timestamp.getWeek().toString();
2019-10-07 15:51:50 -07:00
if ( Roles . userIsInRole ( this . userId , [ Meteor . UserRoles . ROLE _UPDATE ] ) ) {
2020-01-16 09:31:12 -08:00
Batches . update ( id , { $set : { comment , amount } } , function ( err , id ) {
2019-10-07 15:51:50 -07:00
if ( err ) console . log ( err ) ;
} , { bypassCollection2 : true } ) ;
}
else throw new Meteor . Error ( 403 , "Not authorized." ) ;
} ,
setBatchHasLabels : function ( id , hasLabels ) {
//console.log(id);
//console.log(hasLabels);
//check(id, Meteor.validators.ObjectID);
check ( id , String ) ;
check ( hasLabels , Boolean ) ;
if ( Roles . userIsInRole ( this . userId , [ Meteor . UserRoles . ROLE _UPDATE ] ) ) {
Batches . update ( id , { $set : { hasLabels } } , function ( err , id ) {
if ( err ) console . log ( err ) ;
} , { bypassCollection2 : true } ) ;
}
else throw new Meteor . Error ( 403 , "Not authorized." ) ;
}
} ) ;
}
//Allows the client to do DB interaction without calling server side methods, while still retaining control over whether the user can make changes.
Batches . allow ( {
insert : function ( ) { return false ; } ,
update : function ( ) { return false ; } ,
remove : function ( ) { return false ; }
} ) ;
export default Batches ;