text: /**
  Copyright 2017 ToManage
  
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
  
      http://www.apache.org/licenses/LICENSE-2.0
  
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  
  @author    ToManage SAS <contact@tomanage.fr>
  @copyright 2014-2017 ToManage SAS
  @license   http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
  International Registered Trademark & Property of ToManage SAS
  */
  
  
  
  "use strict";
  
  /**
   * Module dependencies.
   */
  var mongoose = require('mongoose'),
  		Schema = mongoose.Schema,
  		ObjectId = mongoose.Schema.Types.ObjectId,
  		timestamps = require('mongoose-timestamp'),
  		moment = require('moment'),
  		async = require('async'),
  		_ = require('lodash'),
  		fs = require('fs');
  
  var DataTable = require('mongoose-datatable');
  
  DataTable.configure({
  		verbose: false,
  		debug: false
  });
  mongoose.plugin(DataTable.init);
  
  var Dict = INCLUDE('dict');
  var Latex = INCLUDE('latex');
  
  var setPrice = MODULE('utils').setPrice;
  var setDate = MODULE('utils').setDate;
  
  var options = {
  		collection: 'Orders',
  		discriminatorKey: '_type',
  		toObject: {
  				virtuals: true
  		},
  		toJSON: {
  				virtuals: true
  		}
  };
  
  const pdfSchema = new Schema({
  		// _id needed for download pdf file
  		filename: {
  				type: String
  		}, //File to display in list
  		fileId: {
  				type: String
  		}, //FileId in directory
  		modelPdf: {
  				type: ObjectId,
  				ref: 'modelspdf'
  		},
  		datec: {
  				type: Date,
  				default: Date.now
  		}, // creation date
  		dateu: Date //date last download file
  }, {
  		toObject: {
  				virtuals: true
  		},
  		toJSON: {
  				virtuals: true
  		}
  });
  
  pdfSchema.virtual('token')
  		.get(function() {
  				var CryptoJS = require("crypto-js");
  
  				if (!CONFIG('sha1-secret'))
  						return "";
  
  				return CryptoJS.SHA1(CONFIG('sha1-secret') + this.fileId.toUpperCase()).toString();
  		});
  
  const baseSchema = new Schema({
  		forSales: {
  				type: Boolean,
  				default: true
  		},
  		isremoved: Boolean,
  		ref: {
  				type: String,
  				index: true
  		},
  		ID: {
  				type: Number
  		},
  
  		pdfModel: {
  				type: Schema.Types.ObjectId,
  				ref: 'modelspdf'
  		},
  		/*title: {//For internal use only
  		    ref: String,
  		    autoGenerated: {
  		        type: Boolean,
  		        default: false
  		    } //For automatic process generated deliveries
  		},*/
  		currency: {
  				_id: {
  						type: String,
  						ref: 'currency',
  						default: ''
  				},
  				rate: {
  						type: Number,
  						default: 1
  				} // changed default to '0' for catching errors
  		},
  
  		Status: {
  				type: String,
  				default: 'DRAFT'
  		},
  		cond_reglement_code: {
  				type: String,
  				default: 'RECEP'
  		},
  		mode_reglement_code: {
  				type: String,
  				default: 'TIP'
  		},
  		bank_reglement: {
  				type: ObjectId,
  				ref: 'bank'
  		},
  		//availability_code: {type: String, default: 'AV_NOW'},
  		type: {
  				type: String,
  				default: 'SRC_COMM'
  		},
  		supplier: {
  				type: Schema.Types.ObjectId,
  				ref: 'Customers',
  				require: true
  		},
  		contacts: [{
  				type: Schema.Types.ObjectId,
  				ref: 'Customers'
  		}],
  		ref_client: {
  				type: String,
  				default: ""
  		},
  		datec: {
  				type: Date,
  				default: Date.now,
  				set: setDate
  		},
  		datedl: {
  				type: Date,
  				default: Date.now,
  				set: setDate
  		},
  		notes: [{
  				title: String,
  				note: String,
  				public: {
  						type: Boolean,
  						default: false
  				},
  				edit: {
  						type: Boolean,
  						default: false
  				}
  		}],
  		discount: {
  				escompte: {
  						percent: {
  								type: Number,
  								default: 0
  						},
  						value: {
  								type: Number,
  								default: 0,
  								set: setPrice
  						} // total remise globale
  				},
  				discount: {
  						percent: {
  								type: Number,
  								default: 0
  						}, //discount
  						value: {
  								type: Number,
  								default: 0,
  								set: setPrice
  						} // total remise globale
  				}
  		},
  		total_ht: {
  				type: Number,
  				default: 0,
  				set: setPrice
  		},
  		total_taxes: [{
  				_id: false,
  				taxeId: {
  						type: Schema.Types.ObjectId,
  						ref: 'taxes'
  				},
  				value: {
  						type: Number,
  						default: 0
  				}
  		}],
  		total_ttc: {
  				type: Number,
  				default: 0,
  				set: setPrice
  		},
  		total_paid: {
  				type: Number,
  				default: 0,
  				set: setPrice
  		},
  		shipping: {
  				total_ht: {
  						type: Number,
  						default: 0,
  						set: setPrice
  				},
  				total_taxes: [{
  						_id: false,
  						taxeId: {
  								type: Schema.Types.ObjectId,
  								ref: 'taxes'
  						},
  						value: {
  								type: Number,
  								default: 0
  						}
  				}],
  				/*total_ttc: {
  				    type: Number,
  				    default: 0
  				}*/
  		},
  		createdBy: {
  				type: ObjectId,
  				ref: 'Users'
  		},
  		editedBy: {
  				type: ObjectId,
  				ref: 'Users'
  		},
  		salesPerson: {
  				type: ObjectId,
  				ref: 'Employees'
  		}, //commercial_id
  		salesTeam: {
  				type: ObjectId,
  				ref: 'Department'
  		},
  		entity: String,
  		optional: {},
  		order: {
  				type: ObjectId,
  				ref: 'order'
  		}, //Link to OrderRow
  		delivery_mode: {
  				type: String,
  				default: "Comptoir"
  		},
  		billing: {
  				type: Schema.Types.ObjectId,
  				ref: 'Customers'
  		},
  		//costList: { type: ObjectId, ref: 'priceList', default: null }, //Not used
  		//priceList: { type: ObjectId, ref: 'priceList', default: null },
  		address: {
  				name: {
  						type: String,
  						default: ''
  				},
  				street: {
  						type: String,
  						default: ''
  				},
  				city: {
  						type: String,
  						default: ''
  				},
  				state: {
  						type: String,
  						default: ''
  				},
  				zip: {
  						type: String,
  						default: ''
  				},
  				country: {
  						type: String,
  						ref: 'countries',
  						default: 'FR'
  				},
  				contact: {
  						name: {
  								type: String,
  								default: ''
  						},
  						phone: {
  								type: String,
  								set: MODULE('utils').setPhone,
  								default: ''
  						},
  						mobile: {
  								type: String,
  								set: MODULE('utils').setPhone,
  								default: ''
  						},
  						fax: {
  								type: String,
  								set: MODULE('utils').setPhone,
  								default: ''
  						},
  						email: {
  								type: String,
  								lowercase: true,
  								trim: true,
  								index: true
  						}
  				}
  		},
  		shippingAddress: {
  				_id: {
  						type: ObjectId,
  						default: null
  				},
  				name: {
  						type: String,
  						default: ''
  				},
  				street: {
  						type: String,
  						default: ''
  				},
  				city: {
  						type: String,
  						default: ''
  				},
  				state: {
  						type: String,
  						default: ''
  				},
  				zip: {
  						type: String,
  						default: ''
  				},
  				country: {
  						type: String,
  						ref: 'countries',
  						default: 'FR'
  				},
  				contact: {
  						name: {
  								type: String,
  								default: ''
  						},
  						phone: {
  								type: String,
  								set: MODULE('utils').setPhone,
  								default: ''
  						},
  						mobile: {
  								type: String,
  								set: MODULE('utils').setPhone,
  								default: ''
  						},
  						fax: {
  								type: String,
  								set: MODULE('utils').setPhone,
  								default: ''
  						},
  						email: {
  								type: String,
  								lowercase: true,
  								trim: true,
  								index: true
  						}
  				}
  		},
  		/*bl: [{
  		    label: String,
  		    name: String,
  		    contact: String,
  		    address: String,
  		    zip: String,
  		    town: String,
  		    products: [{
  		        id: Schema.Types.ObjectId,
  		        name: String,
  		        qty: {
  		            type: Number,
  		            default: 0
  		        } // QTY Order
  		    }],
  		    shipping: {
  		        id: String,
  		        label: String,
  		        address: Boolean,
  		        total_ht: {
  		            type: Number,
  		            default: 0
  		        }
  		    }
  		}],*/
  		weight: {
  				type: Number,
  				default: 0
  		}, // Poids total
  		/*lines: [{
  		    _id: false,
  		    //pu: {type: Number, default: 0},
  		    type: { type: String, default: 'product' }, //Used for subtotal
  		    refProductSupplier: String, //Only for an order Supplier
  		    qty: { type: Number, default: 0 },
  		    //price_base_type: String,
  		    //title: String,
  		    priceSpecific: { type: Boolean, default: false },
  		    pu_ht: {
  		        type: Number,
  		        default: 0
  		    },
  		    description: String,
  		    private: String, // Private note
  		    product_type: String,
  		    product: { type: Schema.Types.ObjectId, ref: "product" },
  		    total_taxes: [{
  		        _id: false,
  		        taxeId: { type: Schema.Types.ObjectId, ref: 'taxes' },
  		        value: { type: Number }
  		    }],
  		    discount: { type: Number, default: 0 },
  		    total_ht: { type: Number, default: 0, set: setPrice },
  		    //weight: { type: Number, default: 0 },
  		    optional: { type: Schema.Types.Mixed }
  		}],*/
  		history: [{
  				date: {
  						type: Date,
  						default: Date.now
  				},
  				author: {
  						type: ObjectId,
  						ref: 'Users'
  				},
  				mode: String, //email, order, alert, new, ...
  				Status: String,
  				msg: String
  		}],
  
  		//status: {},
  
  		whoCanRW: {
  				type: String,
  				enum: ['owner', 'group', 'everyOne'],
  				default: 'everyOne'
  		},
  
  		groups: {
  				owner: {
  						type: ObjectId,
  						ref: 'Users',
  						default: null
  				},
  				users: [{
  						type: ObjectId,
  						ref: 'Users',
  						default: null
  				}],
  				group: [{
  						type: ObjectId,
  						ref: 'Department',
  						default: null
  				}]
  		},
  
  		warehouse: {
  				type: ObjectId,
  				ref: 'warehouse'
  		},
  		project: {
  				type: ObjectId,
  				ref: 'Project',
  				default: null
  		},
  
  		shippingMethod: {
  				type: ObjectId,
  				ref: 'shippingMethod'
  		},
  
  		pdfs: [pdfSchema], //Link To PDF file in uploads/pdf directory
  
  		files: {
  				type: Array,
  				default: []
  		},
  
  		channel: {
  				type: ObjectId,
  				ref: 'integrations'
  		},
  		integrationId: String,
  		//sequence: Number,
  		//name: String
  		oldId: String
  }, options);
  
  baseSchema.plugin(timestamps);
  
  if (CONFIG('storing-files')) {
  		var gridfs = INCLUDE(CONFIG('storing-files'));
  		baseSchema.plugin(gridfs.pluginGridFs, {
  				root: 'Orders'
  		});
  }
  
  var orderCustomerSchema = new Schema({
  		offer: {
  				type: ObjectId,
  				ref: 'order'
  		},
  
  		status: {
  				allocateStatus: {
  						type: String,
  						default: 'NOR',
  						enum: ['NOR', 'NOT', 'NOA', 'ALL']
  				},
  				fulfillStatus: {
  						type: String,
  						default: 'NOR',
  						enum: ['NOR', 'NOT', 'NOA', 'ALL']
  				},
  				shippingStatus: {
  						type: String,
  						default: 'NOR',
  						enum: ['NOR', 'NOT', 'NOA', 'ALL']
  				},
  				invoiceStatus: {
  						type: String,
  						default: 'NOR',
  						enum: ['NOR', 'NOT', 'NOA', 'ALL']
  				}
  		}
  });
  
  baseSchema.statics.query = function(options, callback) {
  		const self = this;
  
  		var data = options.query;
  		var quickSearch = data.quickSearch;
  		const limit = options.limit;
  		const skip = options.skip;
  
  		const FilterMapper = MODULE('helper').filterMapper;
  		var filterMapper = new FilterMapper();
  
  		var accessRollSearcher;
  		var contentSearcher;
  		var waterfallTasks;
  		var contentType = data.contentType;
  		var sort = {};
  		var filter = data.filter && JSON.parse(data.filter) || {};
  		var key;
  		var filterObject = {
  				isremoved: {
  						$ne: true
  				}
  		};
  		var optionsObject = {};
  		var matchObject = {};
  		var regExp;
  		var pastDue = filter.pastDue;
  
  		if (quickSearch) {
  				regExp = new RegExp(quickSearch, 'ig');
  				matchObject['ref'] = {
  						$regex: regExp
  				};
  				filter = {};
  		}
  
  		//console.log(filter);
  
  		if (filter && filter.salesPerson && filter.salesPerson.value.length)
  				filter.Status.value = [];
  		if (filter && filter.supplier && filter.supplier.value.length)
  				filter.Status.value = [];
  
  		//TODO refresh Status on angular
  		if (filter && filter.Status && filter.Status.value[0] == "NEW") {
  				filter.Status.value = [];
  				filterObject.Status = {
  						$nin: ["CLOSED", "CANCELED"]
  				};
  		}
  
  		//if (filter && filter.Status && filter.Status.value[0] == "CLOSED") {
  		//    filter.Status.value[0] = "BILLED";
  		//filterObject.Status = { $ne: "BILLED" };
  		//}
  
  		filterObject.$and = [];
  
  		if (filter && typeof filter === 'object') {
  				filterObject.$and.push(filterMapper.mapFilter(filter, {
  						contentType: contentType
  				})); // caseFilter(filter);
  		}
  
  		//return console.log(filterObject.$and[0].$and[0].$or);
  
  		if (options.query.sort) {
  				sort = JSON.parse(options.query.sort);
  				sort._id = 1;
  		} else
  				sort = {
  						datedl: -1,
  						_id: 1
  				};
  
  		if (pastDue) {
  				optionsObject.$and.push({
  						expectedDate: {
  								$gt: new Date(filter.date.value[1])
  						}
  				}, {
  						'workflow.status': {
  								$ne: 'Done'
  						}
  				});
  		}
  
  		accessRollSearcher = function(cb) {
  				const accessRoll = MODULE('helper').accessRoll;
  
  				accessRoll(options.user, self, cb);
  		};
  
  		contentSearcher = function(ids, cb) {
  				var newQueryObj = {};
  				const ObjectId = MODULE('utils').ObjectId;
  
  				var salesManagerMatch = {
  						$and: [{
  										$eq: ['$$projectMember.projectPositionId', ObjectId("570e9a75785753b3f1d9c86e")]
  								}, //CONSTANTS.SALESMANAGER
  								{
  										$or: [{
  												$and: [{
  														$eq: ['$$projectMember.startDate', null]
  												}, {
  														$eq: ['$$projectMember.endDate', null]
  												}]
  										}, {
  												$and: [{
  														$lte: ['$$projectMember.startDate', '$datec']
  												}, {
  														$eq: ['$$projectMember.endDate', null]
  												}]
  										}, {
  												$and: [{
  														$eq: ['$$projectMember.startDate', null]
  												}, {
  														$gte: ['$$projectMember.endDate', '$datec']
  												}]
  										}, {
  												$and: [{
  														$lte: ['$$projectMember.startDate', '$datec']
  												}, {
  														$gte: ['$$projectMember.endDate', '$datec']
  												}]
  										}]
  								}
  						]
  				};
  
  				newQueryObj.$and = [];
  				//newQueryObj.$and.push(queryObject);
  				//console.log(JSON.stringify(filterObject));
  				newQueryObj.$and.push({
  						_id: {
  								$in: ids
  						}
  				});
  
  				var query = [{
  								$match: filterObject
  						},
  						{
  								$match: matchObject
  						},
  						{
  								$project: {
  										workflow: 1,
  										supplier: 1,
  										'currency': 1,
  										payments: 1,
  										salesManagers: {
  												$filter: {
  														input: '$projectMembers',
  														as: 'projectMember',
  														cond: salesManagerMatch
  												}
  										},
  										channel: 1,
  										salesPerson: 1,
  										orderRows: 1,
  										paymentInfo: 1,
  										datec: 1,
  										ref_client: 1,
  										datedl: 1,
  										total_ttc: 1,
  										total_ht: 1,
  										total_paid: 1,
  										Status: 1,
  										ID: 1,
  										ref: 1,
  										status: 1,
  										_type: 1,
  										forSales: 1
  								}
  						},
  						/*{
  						                    $lookup: {
  						                        from: 'projectMembers',
  						                        localField: 'project',
  						                        foreignField: 'projectId',
  						                        as: 'projectMembers'
  						                    }
  						            },*/
  						/*{
  						                   $lookup: {
  						                       from: 'Payment',
  						                       localField: '_id',
  						                       foreignField: 'order',
  						                       as: 'payments'
  						                   }
  						               },*/
  						{
  								$lookup: {
  										from: 'Customers',
  										localField: 'supplier',
  										foreignField: '_id',
  										as: 'supplier'
  								}
  						},
  						/*{
  						                   $lookup: {
  						                       from: 'workflows',
  						                       localField: 'workflow',
  						                       foreignField: '_id',
  						                       as: 'workflow'
  						                   }
  						               },*/
  						{
  								$lookup: {
  										from: 'currency',
  										localField: 'currency._id',
  										foreignField: '_id',
  										as: 'currency._id'
  								}
  						},
  						{
  								$lookup: {
  										from: 'Project',
  										localField: 'project',
  										foreignField: '_id',
  										as: 'project'
  								}
  						},
  						{
  								$lookup: {
  										from: 'Employees',
  										localField: 'salesPerson',
  										foreignField: '_id',
  										as: 'salesPerson'
  								}
  						},
  						/* {
  						                           $lookup: {
  						                               from: 'integrations',
  						                               localField: 'channel',
  						                               foreignField: '_id',
  						                               as: 'channel'
  						                           }
  						                       },*/
  						{
  								$project: {
  										workflow: {
  												$arrayElemAt: ['$workflow', 0]
  										},
  										supplier: {
  												$arrayElemAt: ['$supplier', 0]
  										},
  										'currency._id': {
  												$arrayElemAt: ['$currency._id', 0]
  										},
  										payments: 1,
  										'currency.rate': 1,
  										salesManagers: 1,
  										channel: {
  												$arrayElemAt: ['$channel', 0]
  										},
  										salesPerson: {
  												$arrayElemAt: ['$salesPerson', 0]
  										},
  										orderRows: 1,
  										paymentInfo: 1,
  										datec: 1,
  										ref_client: 1,
  										datedl: 1,
  										total_ttc: 1,
  										total_ht: 1,
  										total_paid: 1,
  										Status: 1,
  										ID: 1,
  										ref: 1,
  										status: 1,
  										_type: 1,
  										forSales: 1
  								}
  						}, {
  								$project: {
  										salesManager: {
  												$arrayElemAt: ['$salesManagers', 0]
  										},
  										supplier: {
  												_id: '$supplier._id',
  												fullName: {
  														$concat: ['$supplier.name.first', ' ', '$supplier.name.last']
  												}
  										},
  
  										workflow: {
  												_id: '$workflow._id',
  												status: '$workflow.status',
  												name: '$workflow.name'
  										},
  
  										tempWorkflow: {
  												_id: '$tempWorkflow._id',
  												status: '$tempWorkflow.status'
  										},
  
  										channel: {
  												_id: '$channel._id',
  												name: '$channel.channelName',
  												type: '$channel.type'
  										},
  
  										currency: 1,
  										paymentInfo: 1,
  										datec: 1,
  										ref_client: 1,
  										datedl: 1,
  										total_ttc: 1,
  										total_ht: 1,
  										total_paid: 1,
  										Status: 1,
  										ID: 1,
  										salesPerson: 1,
  										ref: 1,
  										isOrder: 1,
  										proformaCounter: 1,
  										payments: 1,
  										status: 1,
  										_type: 1,
  										forSales: 1
  								}
  						}, {
  								$lookup: {
  										from: 'Employees',
  										localField: 'salesManager.employeeId',
  										foreignField: '_id',
  										as: 'salesManager'
  								}
  						}, {
  								$project: {
  										salesPerson: {
  												$ifNull: ['$salesPerson', {
  														$arrayElemAt: ['$salesManager', 0]
  												}]
  										},
  										workflow: 1,
  										tempWorkflow: 1,
  										supplier: 1,
  										currency: 1,
  										paymentInfo: 1,
  										datec: 1,
  										ref_client: 1,
  										datedl: 1,
  										total_ttc: 1,
  										total_ht: 1,
  										total_paid: 1,
  										Status: 1,
  										ID: 1,
  										payments: 1,
  										ref: 1,
  										status: 1,
  										_type: 1,
  										forSales: 1,
  										channel: 1
  								}
  						}, {
  								$project: {
  										salesPerson: {
  												_id: '$salesPerson._id',
  												fullName: {
  														$concat: ['$salesPerson.name.first', ' ', '$salesPerson.name.last']
  												}
  										},
  										workflow: 1,
  										tempWorkflow: 1,
  										supplier: 1,
  										currency: 1,
  										paymentInfo: 1,
  										datec: 1,
  										ref_client: 1,
  										datedl: 1,
  										total_ttc: 1,
  										total_ht: 1,
  										total_paid: 1,
  										Status: 1,
  										ID: 1,
  										ref: 1,
  										status: 1,
  										_type: 1,
  										forSales: 1,
  										channel: 1,
  										payments: 1,
  										removable: {
  												$cond: {
  														if: {
  																$or: [{
  																		$eq: ['$workflow.status', 'Done']
  																}, {
  																		$eq: ['$tempWorkflow.status', 'Done']
  																}, {
  																		$and: [{
  																				$ne: ['$status.fulfillStatus', 'NOR']
  																		}, {
  																				$ne: ['$status.fulfillStatus', 'NOT']
  																		}]
  																}]
  														},
  														then: false,
  														else: true
  												}
  										}
  								}
  						},
  						{
  								$match: newQueryObj
  						},
  						{
  								$group: {
  										_id: null,
  										total: {
  												$sum: 1
  										},
  										total_ht: {
  												$sum: "$total_ht"
  										},
  										total_ttc: {
  												$sum: "$total_ttc"
  										},
  										total_paid: {
  												$sum: "$total_paid"
  										},
  										min: {
  												$min: "$total_ht"
  										},
  										max: {
  												$max: "$total_ht"
  										},
  										avg: {
  												$avg: "$total_ht"
  										},
  										root: {
  												$push: '$$ROOT'
  										}
  								}
  						}, {
  								$unwind: '$root'
  						}, {
  								$project: {
  										_id: '$root._id',
  										salesPerson: '$root.salesPerson',
  										workflow: '$root.workflow',
  										supplier: '$root.supplier',
  										currency: '$root.currency',
  										paymentInfo: '$root.paymentInfo',
  										datec: '$root.datec',
  										ref_client: '$root.ref_client',
  										datedl: '$root.datedl',
  										total_ttc: '$root.total_ttc',
  										total_ht: '$root.total_ht',
  										total_paid: '$root.total_paid',
  										Status: '$root.Status',
  										ID: '$root.ID',
  										ref: '$root.ref',
  										status: '$root.status',
  										removable: '$root.removable',
  										channel: '$root.channel',
  										payments: '$root.payments',
  										total: 1,
  										totalAll: {
  												count: "$total",
  												total_ht: "$total_ht",
  												total_ttc: "$total_ttc",
  												total_paid: "$total_paid",
  												min: "$min",
  												max: "$max",
  												avg: "$avg"
  										}
  								}
  						}, {
  								$project: {
  										salesPerson: 1,
  										workflow: 1,
  										supplier: 1,
  										currency: 1,
  										paymentInfo: 1,
  										datec: 1,
  										ref_client: 1,
  										datedl: 1,
  										total_ttc: 1,
  										total_ht: 1,
  										total_paid: 1,
  										Status: 1,
  										ref: 1,
  										ID: 1,
  										status: 1,
  										removable: 1,
  										channel: 1,
  										paymentsPaid: 1,
  										paymentBalance: {
  												$subtract: ['$paymentInfo.total', '$paymentsPaid']
  										},
  										total: 1,
  										totalAll: 1
  								}
  						},
  						{
  								$sort: sort
  						}
  				];
  
  				if (skip)
  						query.push({
  								$skip: skip
  						});
  
  				if (limit)
  						query.push({
  								$limit: limit
  						});
  
  				if (options.exec == false) // No execute aggregate : juste return query
  						return cb(null, query);
  
  				self.aggregate(query, cb);
  		};
  
  		waterfallTasks = [accessRollSearcher, contentSearcher];
  
  		async.waterfall(waterfallTasks, callback);
  };
  
  baseSchema.statics.generatePdfById = function(id, model, callback) {
  		// Generation de la facture PDF et download
  		const SocieteModel = MODEL('Customers').Schema;
  		const BankModel = MODEL('bank').Schema;
  		const ModelPDFModel = MODEL('modelspdf').Schema;
  
  		const self = this;
  
  		async.waterfall([
  				function(wCb) {
  						ModelPDFModel.findById(model, function(err, doc) {
  								if (err)
  										return wCb(err);
  
  								if (!doc)
  										return wCb("No model PDF found");
  
  								return wCb(null, doc);
  						});
  				},
  				function(modelPdf, wCb) {
  
  						var discount = false;
  						var cond_reglement_code = {};
  						Dict.dict({
  								dictName: "fk_payment_term",
  								object: true
  						}, function(err, docs) {
  								cond_reglement_code = docs;
  						});
  						var mode_reglement_code = {};
  						Dict.dict({
  								dictName: "fk_paiement",
  								object: true
  						}, function(err, docs) {
  								mode_reglement_code = docs;
  						});
  
  						self.getById(id, function(err, doc) {
  
  								var title = "";
  
  								var model = 'order'; //Latex model
  
  								if (self.query.proforma)
  										title = "Facture pro forma";
  								else
  										switch (doc._type) {
  												case 'orderCustomer':
  														title = 'Commande';
  														model = "order";
  														break;
  												case 'orderSupplier':
  														title = 'Commande fournisseur';
  														model = "order_supplier";
  														break;
  												case 'quotationCustomer':
  														title = 'Devis';
  														model = "offer";
  														break;
  												case 'quotationSupplier':
  														title = 'Demande d\'achat';
  														model = "offer_supplier";
  														break;
  										}
  
  								// check if discount
  								for (var i = 0; i < doc.lines.length; i++) {
  										if (doc.lines[i].discount > 0) {
  												model += "_discount";
  												discount = true;
  												break;
  										}
  								}
  
  								SocieteModel.findOne({
  										_id: doc.supplier._id
  								}, function(err, societe) {
  										BankModel.findOne({
  												_id: doc.bank_reglement
  										}, function(err, bank) {
  												if (bank)
  														var iban = bank.name_bank + "\n RIB : " + bank.code_bank + " " + bank.code_counter + " " + bank.account_number + " " + bank.rib + "\n IBAN : " + bank.iban + "\n BIC : " + bank.bic;
  
  												// Array of lines
  												var tabLines = [];
  
  												if (discount)
  														tabLines.push({
  																keys: [{
  																		key: "ref",
  																		type: "string"
  																}, {
  																		key: "description",
  																		type: "area"
  																}, {
  																		key: "qty",
  																		type: "number",
  																		precision: 3
  																}, {
  																		key: "pu_ht",
  																		type: "number",
  																		precision: 3
  																}, {
  																		key: "discount",
  																		type: "string"
  																}, {
  																		key: "total_ht",
  																		type: "euro"
  																}, {
  																		key: "tva_tx",
  																		type: "string"
  																}]
  														});
  												else
  														tabLines.push({
  																keys: [{
  																		key: "ref",
  																		type: "string"
  																}, {
  																		key: "description",
  																		type: "area"
  																}, {
  																		key: "qty",
  																		type: "number",
  																		precision: 0
  																}, {
  																		key: "pu_ht",
  																		type: "number",
  																		precision: 3
  																}, {
  																		key: "total_ht",
  																		type: "euro"
  																}, {
  																		key: "tva_tx",
  																		type: "string"
  																}]
  														});
  
  												for (var i = 0; i < doc.lines.length; i++) {
  														switch (doc.lines[i].type) {
  																case 'SUBTOTAL':
  																		tabLines.push({
  																				ref: "",
  																				description: "\\textbf{Sous-total}",
  																				tva_tx: null,
  																				pu_ht: "",
  																				discount: "",
  																				qty: "",
  																				total_ht: doc.lines[i].total_ht
  																		});
  																		break;
  																case 'COMMENT':
  																		tabLines.push({
  																				ref: "",
  																				description: /*"\\textbf{" + doc.lines[i].refProductSupplier + "}" + */ (doc.lines[i].description ? "\\\\" + doc.lines[i].description : ""),
  																				tva_tx: null,
  																				pu_ht: "",
  																				discount: "",
  																				qty: "",
  																				total_ht: ""
  																		});
  																		break;
  																default:
  																		tabLines.push({
  																				ref: doc.lines[i].product.info.SKU.substring(0, 12),
  																				description: "\\textbf{" + doc.lines[i].product.info.langs[0].name + "}" + (doc.lines[i].description ? "\\\\" + doc.lines[i].description : "") + (doc.lines[i].total_taxes.length > 1 ? "\\\\\\textit{" + doc.lines[i].total_taxes[1].taxeId.langs[0].name + " : " + doc.lines[i].product.taxes[1].value + " \\euro}" : ""),
  																				tva_tx: (doc.lines[i].total_taxes.length ? doc.lines[i].total_taxes[0].taxeId.rate : 0),
  																				pu_ht: doc.lines[i].pu_ht,
  																				discount: (doc.lines[i].discount ? (doc.lines[i].discount + " %") : ""),
  																				qty: {
  																						value: doc.lines[i].qty,
  																						unit: (doc.lines[i].product.unit ? " " + doc.lines[i].product.unit : "U")
  																				},
  																				total_ht: doc.lines[i].total_ht
  																		});
  
  																		/*if (doc.lines[i].total_taxes.length > 1) // Add ecotaxe
  																				tabLines.push({
  																				italic: true,
  																				ref: "",
  																				description: "\\textit{" + doc.lines[i].total_taxes[1].taxeId.langs[0].name + " : " + doc.lines[i].total_taxes[1].value + " \\euro}",
  																				//tva_tx: doc.lines[i].total_taxes[0].taxeId.rate,
  																				tva_tx: "",
  																				//pu_ht: doc.lines[i].product.taxes[1].value,
  																				pu_ht: "",
  																				discount: "",
  																				qty: "",
  																				//qty: { value: doc.lines[i].qty, unit: (doc.lines[i].product.unit ? " " + doc.lines[i].product.unit : "U") },
  																				//total_ht: doc.lines[i].total_taxes[1].value
  																				total_ht: ""
  																		});*/
  														}
  
  														if (doc.lines[i].type == 'kit') {
  																tabLines[tabLines.length - 1].italic = true;
  																if (doc.lines[i + 1] && doc.lines[i + 1].type != 'kit')
  																		tabLines.push({
  																				hline: 1
  																		});
  														}
  
  														if (doc.lines[i].type == 'SUBTOTAL') {
  																tabLines[tabLines.length - 1].italic = true;
  																tabLines.push({
  																		hline: 1
  																});
  														}
  
  														//tab_latex += " & \\specialcell[t]{\\\\" + "\\\\} & " +   + " & " + " & " +  "\\tabularnewline\n";
  												}
  
  												// Array of totals
  												var tabTotal = [{
  														keys: [{
  																key: "label",
  																type: "string"
  														}, {
  																key: "total",
  																type: "euro"
  														}]
  												}];
  
  												// Frais de port
  												if (doc.shipping && doc.shipping.total_ht)
  														tabTotal.push({
  																label: "Frais de port",
  																total: doc.shipping.total_ht
  														});
  
  												// Remise globale
  												if (doc.discount && doc.discount.discount && doc.discount.discount.percent)
  														tabTotal.push({
  																italic: true,
  																label: "Remise globale " + doc.discount.discount.percent + ' %',
  																total: doc.discount.discount.value * -1
  														});
  
  												// Escompte
  												if (doc.discount && doc.discount.escompte && doc.discount.escompte.percent)
  														tabTotal.push({
  																italic: true,
  																label: "Escompte " + doc.discount.escompte.percent + ' %',
  																total: doc.discount.escompte.value * -1
  														});
  
  
  												//Total HT
  												tabTotal.push({
  														label: "Total HT",
  														total: doc.total_ht
  												});
  
  												for (var i = 0; i < doc.total_taxes.length; i++) {
  														tabTotal.push({
  																label: "Total " + doc.total_taxes[i].taxeId.langs[0].label,
  																total: doc.total_taxes[i].value
  														});
  												}
  
  												//Total TTC
  												tabTotal.push({
  														label: "Total TTC",
  														total: doc.total_ttc
  												});
  
  												var reglement = "";
  												switch (doc.mode_reglement_code) {
  														case "VIR":
  																if (doc.bank_reglement) // Bank specific for payment
  																		reglement = "\n" + (bank.invoice ? bank.invoice : bank.iban.id);
  																else // Default IBAN
  																		reglement = "\n --IBAN--";
  																break;
  														case "CHQ":
  																if (doc.bank_reglement) // Bank specific for payment
  																		reglement = "\n" + (bank.invoice ? bank.invoice : "");
  																else
  																		reglement = "A l'ordre de --ENTITY--";
  																break;
  												}
  
  												//Periode de facturation
  												var period = "";
  												if (doc.dateOf && doc.dateTo)
  														period = "\\textit{P\\'eriode du " + moment(doc.dateOf).format(CONFIG('dateformatShort')) + " au " + moment(doc.dateTo).format(CONFIG('dateformatShort')) + "}\\\\";
  
  												Latex.Template(model + ".tex", doc.entity)
  														.apply({
  																"NUM": {
  																		"type": "string",
  																		"value": doc.ref
  																},
  																"DESTINATAIRE.NAME": {
  																		"type": "string",
  																		"value": doc.supplier.fullName
  																},
  																"DESTINATAIRE.ADDRESS": {
  																		"type": "area",
  																		"value": doc.address.street
  																},
  																"DESTINATAIRE.ZIP": {
  																		"type": "string",
  																		"value": doc.address.zip
  																},
  																"DESTINATAIRE.TOWN": {
  																		"type": "string",
  																		"value": doc.address.city
  																},
  																"DESTINATAIRE.TVA": {
  																		"type": "string",
  																		"value": societe.companyInfo.idprof6
  																},
  																"SHIPPING.NAME": {
  																		"type": "string",
  																		"value": doc.shippingAddress.name
  																},
  																"SHIPPING.ADDRESS": {
  																		"type": "area",
  																		"value": doc.shippingAddress.street
  																},
  																"SHIPPING.ZIP": {
  																		"type": "string",
  																		"value": doc.shippingAddress.zip
  																},
  																"SHIPPING.TOWN": {
  																		"type": "string",
  																		"value": doc.shippingAddress.city
  																},
  																"CODECLIENT": {
  																		"type": "string",
  																		"value": societe.salesPurchases.ref
  																},
  																"TITLE": {
  																		"type": "string",
  																		"value": title
  																},
  																"REFCLIENT": {
  																		"type": "string",
  																		"value": doc.ref_client
  																},
  																"DELIVERYMODE": {
  																		"type": "string",
  																		"value": doc.delivery_mode
  																},
  																"PERIOD": {
  																		"type": "string",
  																		"value": period
  																},
  																"DATEC": {
  																		"type": "date",
  																		"value": doc.datec,
  																		"format": CONFIG('dateformatShort')
  																},
  																"DATEEXP": {
  																		"type": "date",
  																		"value": doc.datedl,
  																		"format": CONFIG('dateformatShort')
  																},
  																"REGLEMENT": {
  																		"type": "string",
  																		"value": cond_reglement_code.values[doc.cond_reglement_code].label
  																},
  																"PAID": {
  																		"type": "string",
  																		"value": mode_reglement_code.values[doc.mode_reglement_code].label
  																},
  																"NOTES": {
  																		"type": "area",
  																		"value": (doc.notes.length ? doc.notes[0].note : ""),
  																},
  																"BK": {
  																		"type": "area",
  																		"value": reglement
  																},
  																"TABULAR": tabLines,
  																"TOTAL": tabTotal,
  																"APAYER": {
  																		"type": "euro",
  																		"value": doc.total_ttc || 0
  																}
  														})
  														.on('error', wCb)
  														.finalize(function(tex) {
  																//console.log('The document was converted.');
  														})
  														.compile()
  														.pipe(fs.createWriteStream(F.path.root() + '/uploads/pdf/' + doc._id + "_" + modelPdf.code + ".pdf"))
  														.on('end', function() {
  																//console.log('document written');
  
  																self.update({
  																		_id: doc._id,
  																		'pdfs.modelPdf': modelPdf._id
  																}, {
  																		$set: {
  																				"pdfs.$.datec": new Date()
  																		}
  																}, {
  																		upsert: false
  																}, function(err, res) {
  																		if (err)
  																				return wCb(err);
  
  																		if (res && res.nModified)
  																				return wCb(); // Already exist and updated
  
  																		//create new entry in order
  																		self.update({
  																				_id: doc._id
  																		}, {
  																				$push: {
  																						"pdfs": {
  																								filename: doc.ref + modelPdf.name,
  																								fileId: doc._id + "_" + modelPdf.code + ".pdf",
  																								modelPdf: modelPdf._id,
  																								datec: new Date()
  																						}
  																				}
  																		}, {
  																				upsert: false
  																		}, function(err, doc) {
  																				if (err)
  																						return wCb(err);
  
  																				wCb();
  																		});
  																});
  														});
  										});
  								});
  						});
  
  				}
  		], callback);
  };
  orderCustomerSchema.methods.setAllocated = function(newRows, callback) {
  		if (!newRows || !newRows.length)
  				return callback();
  
  		const Availability = MODEL('productsAvailability').Schema;
  
  		async.eachSeries(newRows, function(elem, eachCb) {
  
  				var lastSum = elem.qty;
  				var isFilled;
  
  				//console.log(elem);
  
  				Availability.find({
  						warehouse: elem.warehouse,
  						product: elem.product
  				}, function(err, avalabilities) {
  						if (err)
  								return eachCb(err);
  
  						if (avalabilities.length) {
  								async.each(avalabilities, function(availability, cb) {
  										var allocated = 0;
  										var resultOnHand;
  										var existedRow = {
  												qty: 0
  										};
  
  										var allOnHand;
  
  										availability.orderRows.forEach(function(orderRow) {
  												if (orderRow.orderRowId.toString() === elem._id.toString())
  														existedRow = orderRow;
  												else
  														allocated += orderRow.qty;
  										});
  
  										if (isFilled && elem.qty)
  												return cb();
  
  
  										allOnHand = availability.onHand + existedRow.qty;
  
  										if (!allOnHand)
  												return cb();
  
  
  										resultOnHand = allOnHand - lastSum;
  
  										if (resultOnHand < 0) {
  												lastSum = Math.abs(resultOnHand);
  												resultOnHand = 0;
  										} else
  												isFilled = true;
  
  
  										if (existedRow.orderRowId) {
  
  												if (!elem.qty) {
  														Availability.update({
  																_id: availability._id
  														}, {
  																$inc: {
  																		onHand: existedRow.qty
  																},
  																$pull: {
  																		orderRows: {
  																				orderRowId: existedRow.orderRowId
  																		}
  																}
  														}, function(err) {
  																if (err)
  																		return cb(err);
  
  																cb();
  														});
  												} else {
  														Availability.update({
  																_id: availability._id,
  																'orderRows.orderRowId': existedRow.orderRowId
  														}, {
  																'orderRows.$.qty': resultOnHand ? lastSum : allOnHand,
  																onHand: resultOnHand
  														}, function(err) {
  																if (err)
  																		return cb(err);
  
  																cb();
  														});
  												}
  
  										} else if (elem.qty) {
  												Availability.findByIdAndUpdate(availability._id, {
  														$addToSet: {
  																orderRows: {
  																		orderRowId: elem._id,
  																		qty: resultOnHand ? lastSum : allOnHand
  																}
  														},
  														onHand: resultOnHand
  												}, function(err) {
  														if (err)
  																return cb(err);
  
  														cb();
  												});
  										} else {
  												return cb();
  										}
  
  										setTimeout2('productInventory:' + availability.product.toString(), function() {
  												F.emit('inventory:update', {
  														userId: null,
  														product: {
  																_id: availability.product.toString()
  														}
  												});
  										}, 5000);
  
  								}, function(err) {
  										if (err)
  												return eachCb(err);
  
  										eachCb();
  								});
  						} else
  								eachCb();
  
  				});
  
  		}, callback);
  };
  
  orderCustomerSchema.methods.unsetAllocated = function(newRows, callback) {
  		const Availability = MODEL('productsAvailability').Schema;
  
  		async.eachSeries(newRows, function(elem, eachCb) {
  				Availability.update({
  								product: elem.product,
  								'orderRows.orderRowId': elem._id
  						}, {
  								$pull: {
  										"orderRows": {
  												"orderRowId": elem._id
  										}
  								}
  						}, {
  								multi: true,
  								upsert: false
  						},
  						eachCb);
  		}, callback);
  };
  
  
  var orderSupplierSchema = new Schema({
  		offer: {
  				type: ObjectId,
  				ref: 'order'
  		},
  
  		status: {
  				allocateStatus: {
  						type: String,
  						default: 'NOR',
  						enum: ['NOR', 'NOT', 'NOA', 'ALL']
  				},
  				fulfillStatus: {
  						type: String,
  						default: 'NOR',
  						enum: ['NOR', 'NOT', 'NOA', 'ALL']
  				},
  				shippingStatus: {
  						type: String,
  						default: 'NOR',
  						enum: ['NOR', 'NOT', 'NOA', 'ALL']
  				},
  				invoiceStatus: {
  						type: String,
  						default: 'NOR',
  						enum: ['NOR', 'NOT', 'NOA', 'ALL']
  				}
  		}
  });
  
  var quotationCustomerSchema = new Schema({
  		orders: [{
  				type: ObjectId,
  				ref: 'order'
  		}]
  });
  var quotationSupplierSchema = new Schema({
  		orders: [{
  				type: ObjectId,
  				ref: 'order'
  		}]
  });
  
  
  // Read Order
  /*baseSchema.statics.getById = function(id, callback) {
      var self = this;
      var OrderRowModel = MODEL('orderRows').Schema;
  
      //TODO Check ACL here
      var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
      var query = {};
  
      if (checkForHexRegExp.test(id))
          query = {
              _id: id
          };
      else
          query = {
              ref: id
          };
  
      //console.log(query);
  
      self.findOne(query, "-latex")
          .populate("contacts", "name phone email")
          .populate({
              path: "supplier",
              select: "name salesPurchases",
              populate: { path: "salesPurchases.priceList" }
          })
          .populate({
              path: "total_taxes.taxeId"
          })
          .populate("createdBy", "username")
          .populate("editedBy", "username")
          .populate("offer", "ref total_ht forSales")
          .populate("order", "ref total_ht forSales")
          .populate("orders", "ref total_ht forSales")
          .exec(function(err, order) {
              if (err)
                  return callback(err);
  
              if (!order.order)
                  order.order = { _id: order._id };
  
              OrderRowModel.find({ order: order.order._id, isDeleted: { $ne: true } })
                  .populate({
                      path: "product",
                      select: "taxes info weight units",
                      //populate: { path: "taxes.taxeId" }
                  })
                  .populate({
                      path: "total_taxes.taxeId"
                  })
                  .sort({ sequence: 1 })
                  .exec(function(err, rows) {
                      if (err)
                          return callback(err);
  
                      order = order.toObject();
                      order.lines = rows || [];
  
                      return callback(err, order);
                  });
          });
  };*/
  baseSchema.statics.getById = function(id, callback) {
  		var self = this;
  		var OrderRowModel = MODEL('orderRows').Schema;
  		var ObjectId = MODULE('utils').ObjectId;
  
  		//TODO Check ACL here
  		var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
  		var query = {};
  
  		if (checkForHexRegExp.test(id))
  				query = {
  						_id: id
  				};
  		else
  				query = {
  						ref: id
  				};
  
  		//console.log(query);
  
  		async.waterfall([
  						function(wCb) {
  								self.findOne(query, "-latex")
  										.populate("contacts", "name phones emails")
  										.populate({
  												path: "supplier",
  												select: "name salesPurchases",
  												populate: {
  														path: "salesPurchases.priceList"
  												}
  										})
  										.populate({
  												path: "total_taxes.taxeId"
  										})
  										.populate("createdBy", "username")
  										.populate("editedBy", "username")
  										.populate("offer", "ref total_ht forSales")
  										.populate("order", "ref total_ht forSales")
  										.populate("orders", "ref total_ht forSales")
  										.populate({
  												path: "orderRows.product",
  												select: "taxes info weight units",
  												populate: {
  														path: "info.productType"
  												} //For delivery stock
  										})
  										.populate("orderRows.locationsReceived.location", "_id name")
  										.populate('warehouse', 'name')
  										.populate('invoiceControl')
  										.populate('project', '_id name')
  										.populate('shippingMethod', '_id name')
  										.populate('logisticMethod', '_id name weight price')
  										.populate('workflow', '_id name status')
  										.exec(wCb);
  						},
  						function(order, wCb) {
  								if (!order)
  										return wCb(null, null);
  
  								if (!order.order)
  										order.order = {
  												_id: order._id
  										};
  
  								//return console.log(order.orderRows);
  
  								OrderRowModel.find({
  												order: order.order._id,
  												isDeleted: {
  														$ne: true
  												}
  										})
  										.populate({
  												path: "product",
  												select: "taxes info weight units sellFamily",
  												populate: {
  														path: "info.productType"
  												}
  										})
  										.populate({
  												path: "total_taxes.taxeId"
  										})
  										.populate('warehouse', 'name')
  										.sort({
  												sequence: 1
  										})
  										.lean()
  										.exec(function(err, rows) {
  												if (err)
  														return wCb(err);
  
  												//return console.log(rows);
  
  												OrderRowModel.getAvailableForRows(rows, order.forSales, function(err, rows, deliveries) {
  														//return console.log(rows);
  
  														rows = _.sortBy(rows, 'sequence');
  
  														order = order.toObject();
  
  														//Fix orderRowId to String
  														order.orderRows = _.map(order.orderRows, function(elem) {
  																//return console.log(elem);
  																elem.orderRowId = elem.orderRowId.toString();
  																return elem;
  														});
  														rows = _.map(rows, function(elem) {
  																//return console.log(elem);
  																elem._id = elem._id.toString();
  																return elem;
  														});
  
  														order.lines = rows || [];
  
  														//console.log(rows);
  
  														return wCb(err, order);
  												});
  										});
  						},
  						function(order, wCb) {
  								if (!order)
  										return wCb(null, null, null);
  
  								OrderRowModel.aggregate([{
  														$match: {
  																order: ObjectId(order.order._id),
  																isDeleted: {
  																		$ne: true
  																},
  																type: {
  																		$in: ['product', 'kit']
  																}
  														}
  												},
  												{
  														$lookup: {
  																from: 'Product',
  																localField: 'product',
  																foreignField: '_id',
  																as: 'product'
  														}
  												},
  												{
  														$unwind: {
  																path: '$product'
  														}
  												},
  												{
  														$lookup: {
  																from: 'productTypes',
  																localField: 'product.info.productType',
  																foreignField: '_id',
  																as: 'productType'
  														}
  												},
  												{
  														$unwind: {
  																path: '$productType'
  														}
  												},
  												{
  														$lookup: {
  																from: 'Orders',
  																localField: 'order',
  																foreignField: 'order',
  																as: 'deliveries'
  														}
  												},
  												{
  														$project: {
  																_id: 1,
  																isService: "$productType.isService",
  																'product._id': 1,
  																'product.info.SKU': 1,
  																'product.info.langs': 1,
  																'product.info.productType': 1,
  																'product.weight': 1,
  																'product.directCost': 1,
  																orderQty: "$qty",
  																order: 1,
  																sequence: 1,
  																"deliveries": {
  																		"$filter": {
  																				"input": "$deliveries",
  																				"as": "delivery",
  																				"cond": {
  																						$and: [{
  																								$ne: ["$$delivery.isremoved", true]
  																						}, {
  																								$or: [{
  																										$eq: ['$$delivery._type', 'GoodsOutNote']
  																								}, {
  																										$eq: ['$$delivery._type', 'GoodsInNote']
  																								}]
  																						}]
  																				}
  																		}
  																},
  																refProductSupplier: 1,
  																description: 1
  														}
  												},
  												{
  														$match: {
  																isService: {
  																		$ne: true
  																}
  														}
  												},
  												{
  														$project: {
  																_id: 1,
  																inventory: 1,
  																product: 1,
  																orderQty: 1,
  																order: 1,
  																sequence: 1,
  																"deliveries": {
  																		"$filter": {
  																				"input": "$deliveries",
  																				"as": "delivery",
  																				"cond": {
  																						$ne: ["$$delivery._id", ObjectId(order._id)]
  																				}
  																		}
  																},
  																refProductSupplier: 1,
  																description: 1
  														}
  												},
  												{
  														$unwind: {
  																path: '$deliveries',
  																preserveNullAndEmptyArrays: true
  														}
  												},
  												{
  														$project: {
  																_id: 1,
  																orderQty: 1,
  																order: 1,
  																product: 1,
  																sequence: 1,
  																'deliveries.ref': 1,
  																'deliveries._id': 1,
  																'deliveries.status': 1,
  																'deliveries.datedl': 1,
  																'deliveries.orderRows': {
  																		$filter: {
  																				input: "$deliveries.orderRows",
  																				as: "row",
  																				cond: {
  																						$eq: ["$$row.orderRowId", "$_id"]
  																				}
  																		}
  																},
  																refProductSupplier: 1,
  																description: 1
  														}
  												},
  												{
  														$unwind: {
  																path: '$deliveries.orderRows',
  																preserveNullAndEmptyArrays: true
  														}
  												},
  												{
  														$group: {
  																_id: "$_id",
  																orderQty: {
  																		$first: "$orderQty"
  																},
  																sequence: {
  																		$first: "$sequence"
  																},
  																product: {
  																		$first: "$product"
  																},
  																deliveryQty: {
  																		$sum: "$deliveries.orderRows.qty"
  																},
  																deliveries: {
  																		$addToSet: {
  																				_id: "$deliveries._id",
  																				ref: "$deliveries.ref",
  																				qty: "$deliveries.orderRows.qty",
  																				datedl: "$deliveries.datedl",
  																				status: "$deliveries.status"
  																		}
  																},
  																refProductSupplier: {
  																		$first: "$refProductSupplier"
  																},
  																description: {
  																		$first: "$description"
  																}
  														}
  												},
  												{
  														$project: {
  																_id: 0,
  																orderRowId: "$_id",
  																orderQty: 1,
  																qty: {
  																		$subtract: ["$orderQty", "$deliveryQty"]
  																},
  																sequence: 1,
  																product: 1,
  																cost: "$product.directCost",
  																deliveryQty: 1,
  																deliveries: 1,
  																refProductSupplier: 1,
  																description: 1
  														}
  												},
  												{
  														$sort: {
  																sequence: 1
  														}
  												}
  										],
  										function(err, result) {
  												wCb(err, order, result);
  										});
  						}
  				],
  				function(err, order, orderRows) {
  						if (err)
  								return callback(err);
  
  						if (!order)
  								return callback(null, null);
  
  						//return console.log(orderRows, order.orderRows);
  
  						//if (!order.orderRows.length)
  
  						let firstCreateDelivery = true;
  
  						if (order.orderRows.length > 0)
  								firstCreateDelivery = false;
  
  						order.orderRows = _.map(orderRows, function(item) {
  								if (!firstCreateDelivery)
  										delete item.qty;
  
  								//console.log(item.orderRowId, order.orderRows);
  								//console.log('test', _.find(order.orderRows, _.matchesProperty('orderRowId', item.orderRowId.toString())));
  
  								item = _.extend(item, _.find(order.orderRows, _.matchesProperty('orderRowId', item.orderRowId.toString())));
  								if (item.qty === undefined)
  										item.isDeleted = true;
  
  								return item;
  						});
  						//return console.log(order.orderRows);
  
  						//Add onHand in delivery lines
  						order.orderRows = _.map(order.orderRows, function(item) {
  								var data = _.find(order.lines, _.matchesProperty('_id', item.orderRowId));
  
  								if (!data || !data.onHand)
  										data = {
  												onHand: 0
  										};
  
  								//console.log(data);
  
  								if (order.Status == 'DRAFT' && item.qty > (data.onHand + data.allocated))
  										item.qty = data.onHand + data.allocated;
  
  								return _.extend(item, {
  										onHand: data.onHand,
  										fulfilled: data.fulfilled
  								});
  						});
  
  						//console.log(order.orderRows);
  						return callback(err, order);
  				});
  };
  
  //orderSupplierSchema.statics.getById = getById;
  
  /**
   * Methods
   */
  baseSchema.virtual('_status')
  		.get(function() {
  				return MODULE('utils').Status(this.Status, exports.Status);
  		});
  
  //Check if orderRow is attached to this main order
  baseSchema.virtual('_isOwn')
  		.get(function() {
  				if (!this.order)
  						return false;
  
  				return (this._id === this.order || this._id === this.order._id);
  		});
  
  
  
  const Order = mongoose.model('order', baseSchema);
  
  /**
   * Delivery
   */
  
  var goodsOutNoteSchema = new Schema({
  
  		orderRows: [{
  				_id: false,
  				orderRowId: {
  						type: ObjectId,
  						ref: 'orderRows',
  						default: null
  				},
  				product: {
  						type: ObjectId,
  						ref: 'product',
  						default: null
  				},
  				locationsDeliver: [{
  						type: ObjectId,
  						ref: 'location'
  				}],
  				cost: {
  						type: Number,
  						default: 0
  				},
  				qty: Number,
  
  				isDeleted: {
  						type: Boolean,
  						default: false
  				}
  		}],
  
  		tracking: String, //Tracking number
  
  		status: {
  				isPrinted: {
  						type: Date,
  						default: null
  				}, //Imprime
  				isPicked: {
  						type: Date,
  						default: null
  				}, //Prepare
  				isPacked: {
  						type: Date,
  						default: null
  				}, //Emballe
  				isShipped: {
  						type: Date,
  						default: null
  				}, //Expedier
  				isInventory: {
  						type: Date,
  						default: null
  				}, //Inventory done
  
  				pickedById: {
  						type: ObjectId,
  						ref: 'Users',
  						default: null
  				},
  				packedById: {
  						type: ObjectId,
  						ref: 'Users',
  						default: null
  				},
  				shippedById: {
  						type: ObjectId,
  						ref: 'Users',
  						default: null
  				},
  				printedById: {
  						type: ObjectId,
  						ref: 'Users',
  						default: null
  				}
  		},
  
  		logisticMethod: {
  				type: ObjectId,
  				ref: 'logisticMethod'
  		},
  		shippingCost: {
  				shipping: {
  						type: Number,
  						default: 0
  				},
  				logistic: {
  						type: Number,
  						default: 0
  				}
  		},
  
  		boxes: {
  				type: Number,
  				default: 1
  		},
  
  		archived: {
  				type: Boolean,
  				default: false
  		}
  });
  
  goodsOutNoteSchema.statics.cancelInventories = function(options, callback) {
  		var GoodsOutNoteModel = this;
  		const Availability = MODEL('productsAvailability').Schema;
  		var ids = options.ids || [];
  		var isMO = options.isManufacturing || false;
  		var orderType = 'order';
  
  		if (isMO)
  				orderType = 'manufacturingOrder';
  
  
  		async.each(ids, function(id, cb) {
  				GoodsOutNoteModel.findById(id).populate(orderType).exec(function(err, goodsNote) {
  						var options;
  
  						if (err)
  								return cb(err);
  
  						if (goodsNote && goodsNote[orderType]) {
  								async.each(goodsNote.orderRows, function(goodsOrderRow, callback) {
  
  										var query = goodsNote[orderType].project ? {
  												product: goodsOrderRow.product,
  												warehouse: goodsNote.warehouse
  										} : {
  												'goodsOutNotes.goodsNoteId': goodsNote._id,
  												product: goodsOrderRow.product,
  												warehouse: goodsNote.warehouse
  										};
  
  										Availability.updateByQuery({
  												query: query,
  
  												body: {
  														$inc: {
  																onHand: goodsOrderRow.qty
  														},
  
  														$pull: {
  																goodsOutNotes: {
  																		goodsNoteId: goodsNote._id
  																}
  														}
  												}
  										}, function(err) {
  												if (err)
  														return callback(err);
  
  
  												options = {
  														query: {
  																'sourceDocument.model': 'goodsOutNote',
  																'sourceDocument._id': id
  														}
  												};
  
  												//JournalEntryService.remove(options);
  
  												callback();
  										});
  								}, function(err) {
  										if (err)
  												return cb(err);
  
  										F.emit('order:recalculateStatus', {
  												order: {
  														_id: goodsNote[orderType]._id.toString()
  												}
  										});
  										cb();
  								});
  
  						} else {
  								cb();
  						}
  				});
  		}, function(err) {
  				if (err) {
  						return callback(err);
  				}
  
  				GoodsOutNoteModel.update({
  						_id: {
  								$in: ids
  						}
  				}, {
  						$set: {
  								Status: "DRAFT",
  								'status.isInventory': null
  						}
  				}, {
  						multi: true
  				}, function(err, result) {
  						if (err)
  								return callback(err);
  
  						callback();
  				});
  		});
  };
  
  goodsOutNoteSchema.statics.query = function(options, callback) {
  		const self = this;
  
  		var data = options.query;
  		var quickSearch = data.quickSearch;
  		const limit = options.limit;
  		const skip = options.skip;
  
  		const FilterMapper = MODULE('helper').filterMapper;
  		var filterMapper = new FilterMapper();
  
  		var accessRollSearcher;
  		var contentSearcher;
  		var waterfallTasks;
  		var contentType = data.contentType;
  		var sort = {};
  		var filter = data.filter && JSON.parse(data.filter) || {};
  		var key;
  		var filterObject = {
  				isremoved: {
  						$ne: true
  				}
  		};
  		var optionsObject = {};
  		var matchObject = {};
  		var regExp;
  		var pastDue = filter.pastDue;
  
  		if (quickSearch) {
  				regExp = new RegExp(quickSearch, 'ig');
  				matchObject['ref'] = {
  						$regex: regExp
  				};
  				filter = {};
  		}
  
  		//console.log(filter);
  
  		if (filter && filter.salesPerson && filter.salesPerson.value.length)
  				filter.Status.value = [];
  		if (filter && filter.supplier && filter.supplier.value.length)
  				filter.Status.value = [];
  
  		//TODO refresh Status on angular
  		if (filter && filter.Status && filter.Status.value[0] == "NEW") {
  				filter.Status.value = [];
  				filterObject.Status = {
  						$nin: ["SEND", "BILLED"]
  				};
  		}
  
  		if (filter && filter.Status && filter.Status.value[0] == "CLOSED") {
  				filter.Status.value[0] = "SEND";
  		}
  
  		filterObject.$and = [];
  
  		if (filter && typeof filter === 'object') {
  				filterObject.$and.push(filterMapper.mapFilter(filter, {
  						contentType: contentType
  				})); // caseFilter(filter);
  		}
  
  		//return console.log(filterObject);
  
  		if (options.query.sort) {
  				sort = JSON.parse(options.query.sort);
  				sort._id = 1;
  		} else
  				sort = {
  						datedl: 1,
  						_id: 1
  				};
  
  		/* if (contentType !== 'order' && contentType !== 'integrationUnlinkedOrders') {
  		     Order = MODEL('order').Schema.OrderSupplier;
  
  		     //queryObject.$and.push({ _type: 'purchaseOrders' });
  		 } else {
  		     //queryObject.$and.push({ _type: 'Order' });
  		 }*/
  
  		if (pastDue) {
  				optionsObject.$and.push({
  						expectedDate: {
  								$gt: new Date(filter.date.value[1])
  						}
  				}, {
  						'workflow.status': {
  								$ne: 'Done'
  						}
  				});
  		}
  
  		accessRollSearcher = function(cb) {
  				const accessRoll = MODULE('helper').accessRoll;
  
  				accessRoll(options.user, self, cb);
  		};
  
  		contentSearcher = function(ids, cb) {
  				var newQueryObj = {};
  				const ObjectId = MODULE('utils').ObjectId;
  
  				var salesManagerMatch = {
  						$and: [{
  										$eq: ['$$projectMember.projectPositionId', ObjectId("570e9a75785753b3f1d9c86e")]
  								}, //CONSTANTS.SALESMANAGER
  								{
  										$or: [{
  												$and: [{
  														$eq: ['$$projectMember.startDate', null]
  												}, {
  														$eq: ['$$projectMember.endDate', null]
  												}]
  										}, {
  												$and: [{
  														$lte: ['$$projectMember.startDate', '$datec']
  												}, {
  														$eq: ['$$projectMember.endDate', null]
  												}]
  										}, {
  												$and: [{
  														$eq: ['$$projectMember.startDate', null]
  												}, {
  														$gte: ['$$projectMember.endDate', '$datec']
  												}]
  										}, {
  												$and: [{
  														$lte: ['$$projectMember.startDate', '$datec']
  												}, {
  														$gte: ['$$projectMember.endDate', '$datec']
  												}]
  										}]
  								}
  						]
  				};
  
  				newQueryObj.$and = [];
  				//newQueryObj.$and.push(queryObject);
  				//console.log(JSON.stringify(filterObject));
  				newQueryObj.$and.push({
  						_id: {
  								$in: ids
  						}
  				});
  
  				//console.log(filterObject.$and[0]);
  				//console.log(newQueryObj.$and[0]);
  
  				var query = [{
  								$match: filterObject
  						},
  						{
  								$match: matchObject
  						},
  						{
  								$project: {
  										workflow: 1,
  										supplier: 1,
  										salesManagers: {
  												$filter: {
  														input: '$projectMembers',
  														as: 'projectMember',
  														cond: salesManagerMatch
  												}
  										},
  										channel: 1,
  										salesPerson: 1,
  										orderRows: 1,
  										datec: 1,
  										ref_client: 1,
  										datedl: 1,
  										Status: 1,
  										ID: 1,
  										ref: 1,
  										status: 1,
  										_type: 1,
  										forSales: 1,
  										logisticMethod: 1,
  										entity: 1,
  										weight: 1
  								}
  						},
  						{
  								$lookup: {
  										from: 'Customers',
  										localField: 'supplier',
  										foreignField: '_id',
  										as: 'supplier'
  								}
  						},
  						/*{
  						                   $lookup: {
  						                       from: 'workflows',
  						                       localField: 'workflow',
  						                       foreignField: '_id',
  						                       as: 'workflow'
  						                   }
  						               },*/
  						{
  								$lookup: {
  										from: 'Employees',
  										localField: 'salesPerson',
  										foreignField: '_id',
  										as: 'salesPerson'
  								}
  						},
  						{
  								$lookup: {
  										from: 'logisticMethod',
  										localField: 'logisticMethod',
  										foreignField: '_id',
  										as: 'logisticMethod'
  								}
  						},
  						/* {
  						                           $lookup: {
  						                               from: 'integrations',
  						                               localField: 'channel',
  						                               foreignField: '_id',
  						                               as: 'channel'
  						                           }
  						                       },*/
  						{
  								$project: {
  										workflow: {
  												$arrayElemAt: ['$workflow', 0]
  										},
  										supplier: {
  												$arrayElemAt: ['$supplier', 0]
  										},
  										salesManagers: 1,
  										channel: {
  												$arrayElemAt: ['$channel', 0]
  										},
  										salesPerson: {
  												$arrayElemAt: ['$salesPerson', 0]
  										},
  										logisticMethod: {
  												$arrayElemAt: ['$logisticMethod', 0]
  										},
  										orderRows: 1,
  										datec: 1,
  										ref_client: 1,
  										datedl: 1,
  										Status: 1,
  										ID: 1,
  										ref: 1,
  										status: 1,
  										_type: 1,
  										forSales: 1,
  										entity: 1,
  										weight: 1
  								}
  						}, {
  								$project: {
  										salesManager: {
  												$arrayElemAt: ['$salesManagers', 0]
  										},
  										supplier: {
  												_id: '$supplier._id',
  												fullName: {
  														$concat: ['$supplier.name.first', ' ', '$supplier.name.last']
  												}
  										},
  
  										workflow: {
  												_id: '$workflow._id',
  												status: '$workflow.status',
  												name: '$workflow.name'
  										},
  
  										tempWorkflow: {
  												_id: '$tempWorkflow._id',
  												status: '$tempWorkflow.status'
  										},
  
  										channel: {
  												_id: '$channel._id',
  												name: '$channel.channelName',
  												type: '$channel.type'
  										},
  
  										datec: 1,
  										ref_client: 1,
  										datedl: 1,
  										Status: 1,
  										ID: 1,
  										salesPerson: 1,
  										logisticMethod: "$logisticMethod.code",
  										ref: 1,
  										isOrder: 1,
  										proformaCounter: 1,
  										status: 1,
  										_type: 1,
  										forSales: 1,
  										orderRows: 1,
  										entity: 1,
  										weight: 1
  								}
  						}, {
  								$lookup: {
  										from: 'Employees',
  										localField: 'salesManager.employeeId',
  										foreignField: '_id',
  										as: 'salesManager'
  								}
  						}, {
  								$project: {
  										salesPerson: {
  												$ifNull: ['$salesPerson', {
  														$arrayElemAt: ['$salesManager', 0]
  												}]
  										},
  										workflow: 1,
  										tempWorkflow: 1,
  										supplier: 1,
  										datec: 1,
  										ref_client: 1,
  										logisticMethod: 1,
  										datedl: 1,
  										Status: 1,
  										ID: 1,
  										ref: 1,
  										status: 1,
  										_type: 1,
  										forSales: 1,
  										orderRows: 1,
  										channel: 1,
  										entity: 1,
  										weight: 1
  								}
  						}, {
  								$project: {
  										salesPerson: {
  												_id: '$salesPerson._id',
  												fullName: {
  														$concat: ['$salesPerson.name.first', ' ', '$salesPerson.name.last']
  												}
  										},
  										workflow: 1,
  										tempWorkflow: 1,
  										supplier: 1,
  										datec: 1,
  										ref_client: 1,
  										logisticMethod: 1,
  										datedl: 1,
  										Status: 1,
  										ID: 1,
  										ref: 1,
  										status: 1,
  										_type: 1,
  										forSales: 1,
  										entity: 1,
  										late: {
  												$cond: {
  														if: {
  																$and: [{
  																				$ne: ['$Status', 'SEND']
  																		},
  																		{
  																				$ne: ['$Status', 'BILLED']
  																		},
  																		{
  																				$lte: ['$datedl', moment().subtract(5, 'day').endOf('day').toDate()]
  																		}
  																]
  														},
  														then: 1,
  														else: 0
  												}
  										},
  										channel: 1,
  										removable: {
  												$cond: {
  														if: {
  																$or: [{
  																		$eq: ['$workflow.status', 'Done']
  																}, {
  																		$eq: ['$tempWorkflow.status', 'Done']
  																}, {
  																		$and: [{
  																				$ne: ['$status.fulfillStatus', 'NOR']
  																		}, {
  																				$ne: ['$status.fulfillStatus', 'NOT']
  																		}]
  																}]
  														},
  														then: false,
  														else: true
  												}
  										},
  										qty: {
  												$sum: "$orderRows.qty"
  										},
  										weight: 1
  								}
  						}, {
  								$match: newQueryObj
  						}, {
  								$group: {
  										_id: null,
  										total: {
  												$sum: 1
  										},
  										late: {
  												$sum: "$late"
  										},
  										root: {
  												$push: '$$ROOT'
  										}
  								}
  						}, {
  								$unwind: '$root'
  						}, {
  								$project: {
  										_id: '$root._id',
  										salesPerson: '$root.salesPerson',
  										workflow: '$root.workflow',
  										supplier: '$root.supplier',
  										datec: '$root.datec',
  										ref_client: '$root.ref_client',
  										logisticMethod: '$root.logisticMethod',
  										datedl: '$root.datedl',
  										qty: '$root.qty',
  										Status: '$root.Status',
  										ID: '$root.ID',
  										ref: '$root.ref',
  										status: '$root.status',
  										removable: '$root.removable',
  										channel: '$root.channel',
  										entity: '$root.entity',
  										weight: '$root.weight',
  										total: 1,
  										totalAll: {
  												count: "$total",
  												late: "$late"
  										}
  								}
  						}, {
  								$group: {
  										_id: "$Status",
  										total: {
  												$sum: 1
  										},
  										root: {
  												$push: '$$ROOT'
  										}
  								}
  						}, {
  								$unwind: '$root'
  						}, {
  								$group: {
  										_id: null,
  										Status: {
  												$addToSet: {
  														_id: "$_id",
  														total: "$total"
  												}
  										},
  										root: {
  												$push: '$root'
  										}
  								}
  						}, {
  								$unwind: '$root'
  						}, {
  								$project: {
  										_id: '$root._id',
  										salesPerson: '$root.salesPerson',
  										workflow: '$root.workflow',
  										supplier: '$root.supplier',
  										datec: '$root.datec',
  										ref_client: '$root.ref_client',
  										logisticMethod: '$root.logisticMethod',
  										datedl: '$root.datedl',
  										qty: '$root.qty',
  										Status: '$root.Status',
  										ID: '$root.ID',
  										ref: '$root.ref',
  										status: '$root.status',
  										removable: '$root.removable',
  										channel: '$root.channel',
  										entity: '$root.entity',
  										weight: '$root.weight',
  										total: "$root.total",
  										totalAll: {
  												count: "$root.totalAll.count",
  												late: "$root.totalAll.late",
  												Status: "$Status"
  										}
  								}
  						}, {
  								$project: {
  										salesPerson: 1,
  										workflow: 1,
  										supplier: 1,
  										datec: 1,
  										ref_client: 1,
  										logisticMethod: 1,
  										datedl: 1,
  										qty: 1,
  										Status: 1,
  										ID: 1,
  										ref: 1,
  										status: 1,
  										removable: 1,
  										channel: 1,
  										total: 1,
  										totalAll: 1,
  										entity: 1,
  										weight: 1
  								}
  						},
  						{
  								$sort: sort
  						}
  				];
  
  				if (skip)
  						query.push({
  								$skip: skip
  						});
  
  				if (limit)
  						query.push({
  								$limit: limit
  						});
  
  				if (options.exec == false) // No execute aggregate : juste return query
  						return cb(null, query);
  
  				self.aggregate(query, cb);
  		};
  
  		waterfallTasks = [accessRollSearcher, contentSearcher];
  
  		async.waterfall(waterfallTasks, callback);
  };
  
  var goodsInNoteSchema = new Schema({
  
  		status: {
  				isReceived: {
  						type: Date,
  						default: null
  				},
  				isInventory: {
  						type: Date,
  						default: null
  				}, //Inventory done
  
  				receivedById: {
  						type: ObjectId,
  						ref: 'Users',
  						default: null
  				}
  		},
  
  		description: {
  				type: String
  		},
  
  		boxes: {
  				type: Number,
  				default: 1
  		},
  
  		orderRows: [{
  				_id: false,
  				orderRowId: {
  						type: ObjectId,
  						ref: 'orderRows',
  						default: null
  				},
  				product: {
  						type: ObjectId,
  						ref: 'product',
  						default: null
  				},
  				cost: {
  						type: Number,
  						default: 0
  				},
  				locationsReceived: [{
  						_id: false,
  						location: {
  								type: ObjectId,
  								ref: 'location',
  								default: null
  						},
  						qty: Number
  				}],
  
  				isDeleted: {
  						type: Boolean,
  						default: false
  				},
  
  				qty: Number
  		}],
  
  		logisticMethod: {
  				type: ObjectId,
  				ref: 'logisticMethod'
  		},
  		shippingCost: {
  				shipping: {
  						type: Number,
  						default: 0
  				},
  				logistic: {
  						type: Number,
  						default: 0
  				}
  		}
  
  });
  
  goodsInNoteSchema.statics.query = function(options, callback) {
  		const self = this;
  
  		var data = options.query;
  		var quickSearch = data.quickSearch;
  		const limit = options.limit;
  		const skip = options.skip;
  
  		const FilterMapper = MODULE('helper').filterMapper;
  		var filterMapper = new FilterMapper();
  
  		var accessRollSearcher;
  		var contentSearcher;
  		var waterfallTasks;
  		var contentType = data.contentType;
  		var sort = {};
  		var filter = data.filter && JSON.parse(data.filter) || {};
  		var key;
  		var filterObject = {
  				isremoved: {
  						$ne: true
  				}
  		};
  		var optionsObject = {};
  		var matchObject = {};
  		var regExp;
  		var pastDue = filter.pastDue;
  
  		if (quickSearch) {
  				regExp = new RegExp(quickSearch, 'ig');
  				matchObject['ref'] = {
  						$regex: regExp
  				};
  				filter = {};
  		}
  
  		//console.log(filter);
  
  		if (filter && filter.salesPerson && filter.salesPerson.value.length)
  				filter.Status.value = [];
  		if (filter && filter.supplier && filter.supplier.value.length)
  				filter.Status.value = [];
  
  		//TODO refresh Status on angular
  		if (filter && filter.Status && filter.Status.value[0] == "NEW") {
  				filter.Status.value = [];
  				filterObject.Status = {
  						$nin: ["INSTOCK", "BILLED"]
  				};
  		}
  
  		if (filter && filter.Status && filter.Status.value[0] == "CLOSED") {
  				filter.Status.value[0] = "INSTOCK";
  		}
  
  		filterObject.$and = [];
  
  		if (filter && typeof filter === 'object') {
  				filterObject.$and.push(filterMapper.mapFilter(filter, {
  						contentType: contentType
  				})); // caseFilter(filter);
  		}
  
  		//return console.log(filterObject);
  
  		if (options.query.sort) {
  				sort = JSON.parse(options.query.sort);
  				sort._id = 1;
  		} else
  				sort = {
  						datedl: 1,
  						_id: 1
  				};
  
  		/* if (contentType !== 'order' && contentType !== 'integrationUnlinkedOrders') {
  		     Order = MODEL('order').Schema.OrderSupplier;
  
  		     //queryObject.$and.push({ _type: 'purchaseOrders' });
  		 } else {
  		     //queryObject.$and.push({ _type: 'Order' });
  		 }*/
  
  		if (pastDue) {
  				optionsObject.$and.push({
  						expectedDate: {
  								$gt: new Date(filter.date.value[1])
  						}
  				}, {
  						'workflow.status': {
  								$ne: 'Done'
  						}
  				});
  		}
  
  		accessRollSearcher = function(cb) {
  				const accessRoll = MODULE('helper').accessRoll;
  
  				accessRoll(options.user, self, cb);
  		};
  
  		contentSearcher = function(ids, cb) {
  				var newQueryObj = {};
  				const ObjectId = MODULE('utils').ObjectId;
  
  				var salesManagerMatch = {
  						$and: [{
  										$eq: ['$$projectMember.projectPositionId', ObjectId("570e9a75785753b3f1d9c86e")]
  								}, //CONSTANTS.SALESMANAGER
  								{
  										$or: [{
  												$and: [{
  														$eq: ['$$projectMember.startDate', null]
  												}, {
  														$eq: ['$$projectMember.endDate', null]
  												}]
  										}, {
  												$and: [{
  														$lte: ['$$projectMember.startDate', '$datec']
  												}, {
  														$eq: ['$$projectMember.endDate', null]
  												}]
  										}, {
  												$and: [{
  														$eq: ['$$projectMember.startDate', null]
  												}, {
  														$gte: ['$$projectMember.endDate', '$datec']
  												}]
  										}, {
  												$and: [{
  														$lte: ['$$projectMember.startDate', '$datec']
  												}, {
  														$gte: ['$$projectMember.endDate', '$datec']
  												}]
  										}]
  								}
  						]
  				};
  
  				newQueryObj.$and = [];
  				//newQueryObj.$and.push(queryObject);
  				//console.log(JSON.stringify(filterObject));
  				newQueryObj.$and.push({
  						_id: {
  								$in: ids
  						}
  				});
  
  				//console.log(filterObject.$and[0]);
  				console.log(newQueryObj.$and[0]);
  
  				var query = [{
  								$match: filterObject
  						},
  						{
  								$match: matchObject
  						},
  						{
  								$project: {
  										workflow: 1,
  										supplier: 1,
  										salesManagers: {
  												$filter: {
  														input: '$projectMembers',
  														as: 'projectMember',
  														cond: salesManagerMatch
  												}
  										},
  										channel: 1,
  										salesPerson: 1,
  										orderRows: 1,
  										datec: 1,
  										ref_client: 1,
  										datedl: 1,
  										Status: 1,
  										ID: 1,
  										ref: 1,
  										status: 1,
  										_type: 1,
  										forSales: 1,
  										entity: 1,
  										weight: 1
  								}
  						},
  						{
  								$lookup: {
  										from: 'Customers',
  										localField: 'supplier',
  										foreignField: '_id',
  										as: 'supplier'
  								}
  						},
  						/*{
  						                   $lookup: {
  						                       from: 'workflows',
  						                       localField: 'workflow',
  						                       foreignField: '_id',
  						                       as: 'workflow'
  						                   }
  						               },*/
  						{
  								$lookup: {
  										from: 'Employees',
  										localField: 'salesPerson',
  										foreignField: '_id',
  										as: 'salesPerson'
  								}
  						},
  						/* {
  						                           $lookup: {
  						                               from: 'integrations',
  						                               localField: 'channel',
  						                               foreignField: '_id',
  						                               as: 'channel'
  						                           }
  						                       },*/
  						{
  								$project: {
  										workflow: {
  												$arrayElemAt: ['$workflow', 0]
  										},
  										supplier: {
  												$arrayElemAt: ['$supplier', 0]
  										},
  										salesManagers: 1,
  										channel: {
  												$arrayElemAt: ['$channel', 0]
  										},
  										salesPerson: {
  												$arrayElemAt: ['$salesPerson', 0]
  										},
  										orderRows: 1,
  										datec: 1,
  										ref_client: 1,
  										datedl: 1,
  										Status: 1,
  										ID: 1,
  										ref: 1,
  										status: 1,
  										_type: 1,
  										forSales: 1,
  										entity: 1,
  										weight: 1
  								}
  						}, {
  								$project: {
  										salesManager: {
  												$arrayElemAt: ['$salesManagers', 0]
  										},
  										supplier: {
  												_id: '$supplier._id',
  												fullName: {
  														$concat: ['$supplier.name.first', ' ', '$supplier.name.last']
  												}
  										},
  
  										workflow: {
  												_id: '$workflow._id',
  												status: '$workflow.status',
  												name: '$workflow.name'
  										},
  
  										tempWorkflow: {
  												_id: '$tempWorkflow._id',
  												status: '$tempWorkflow.status'
  										},
  
  										channel: {
  												_id: '$channel._id',
  												name: '$channel.channelName',
  												type: '$channel.type'
  										},
  
  										datec: 1,
  										ref_client: 1,
  										datedl: 1,
  										Status: 1,
  										ID: 1,
  										salesPerson: 1,
  										ref: 1,
  										isOrder: 1,
  										proformaCounter: 1,
  										status: 1,
  										_type: 1,
  										forSales: 1,
  										orderRows: 1,
  										entity: 1,
  										weight: 1
  								}
  						}, {
  								$lookup: {
  										from: 'Employees',
  										localField: 'salesManager.employeeId',
  										foreignField: '_id',
  										as: 'salesManager'
  								}
  						}, {
  								$project: {
  										salesPerson: {
  												$ifNull: ['$salesPerson', {
  														$arrayElemAt: ['$salesManager', 0]
  												}]
  										},
  										workflow: 1,
  										tempWorkflow: 1,
  										supplier: 1,
  										datec: 1,
  										ref_client: 1,
  										datedl: 1,
  										Status: 1,
  										ID: 1,
  										ref: 1,
  										status: 1,
  										_type: 1,
  										forSales: 1,
  										orderRows: 1,
  										channel: 1,
  										entity: 1,
  										weight: 1
  								}
  						}, {
  								$project: {
  										salesPerson: {
  												_id: '$salesPerson._id',
  												fullName: {
  														$concat: ['$salesPerson.name.first', ' ', '$salesPerson.name.last']
  												}
  										},
  										workflow: 1,
  										tempWorkflow: 1,
  										supplier: 1,
  										datec: 1,
  										ref_client: 1,
  										datedl: 1,
  										Status: 1,
  										ID: 1,
  										ref: 1,
  										status: 1,
  										_type: 1,
  										forSales: 1,
  										entity: 1,
  										late: {
  												$cond: {
  														if: {
  																$and: [{
  																				$ne: ['$Status', 'SEND']
  																		},
  																		{
  																				$ne: ['$Status', 'BILLED']
  																		},
  																		{
  																				$lte: ['$datedl', moment().subtract(5, 'day').endOf('day').toDate()]
  																		}
  																]
  														},
  														then: 1,
  														else: 0
  												}
  										},
  										channel: 1,
  										removable: {
  												$cond: {
  														if: {
  																$or: [{
  																		$eq: ['$workflow.status', 'Done']
  																}, {
  																		$eq: ['$tempWorkflow.status', 'Done']
  																}, {
  																		$and: [{
  																				$ne: ['$status.fulfillStatus', 'NOR']
  																		}, {
  																				$ne: ['$status.fulfillStatus', 'NOT']
  																		}]
  																}]
  														},
  														then: false,
  														else: true
  												}
  										},
  										qty: {
  												$sum: "$orderRows.qty"
  										},
  										weight: 1
  								}
  						}, {
  								$match: newQueryObj
  						}, {
  								$group: {
  										_id: null,
  										total: {
  												$sum: 1
  										},
  										late: {
  												$sum: "$late"
  										},
  										root: {
  												$push: '$$ROOT'
  										}
  								}
  						}, {
  								$unwind: '$root'
  						}, {
  								$project: {
  										_id: '$root._id',
  										salesPerson: '$root.salesPerson',
  										workflow: '$root.workflow',
  										supplier: '$root.supplier',
  										datec: '$root.datec',
  										ref_client: '$root.ref_client',
  										datedl: '$root.datedl',
  										qty: '$root.qty',
  										Status: '$root.Status',
  										ID: '$root.ID',
  										ref: '$root.ref',
  										status: '$root.status',
  										removable: '$root.removable',
  										channel: '$root.channel',
  										entity: '$root.entity',
  										weight: '$root.weight',
  										total: 1,
  										totalAll: {
  												count: "$total",
  												late: "$late"
  										}
  								}
  						}, {
  								$group: {
  										_id: "$Status",
  										total: {
  												$sum: 1
  										},
  										root: {
  												$push: '$$ROOT'
  										}
  								}
  						}, {
  								$unwind: '$root'
  						}, {
  								$group: {
  										_id: null,
  										Status: {
  												$addToSet: {
  														_id: "$_id",
  														total: "$total"
  												}
  										},
  										root: {
  												$push: '$root'
  										}
  								}
  						}, {
  								$unwind: '$root'
  						}, {
  								$project: {
  										_id: '$root._id',
  										salesPerson: '$root.salesPerson',
  										workflow: '$root.workflow',
  										supplier: '$root.supplier',
  										datec: '$root.datec',
  										ref_client: '$root.ref_client',
  										datedl: '$root.datedl',
  										qty: '$root.qty',
  										Status: '$root.Status',
  										ID: '$root.ID',
  										ref: '$root.ref',
  										status: '$root.status',
  										removable: '$root.removable',
  										channel: '$root.channel',
  										entity: '$root.entity',
  										weight: '$root.weight',
  										total: "$root.total",
  										totalAll: {
  												count: "$root.totalAll.count",
  												late: "$root.totalAll.late",
  												Status: "$Status"
  										}
  								}
  						}, {
  								$project: {
  										salesPerson: 1,
  										workflow: 1,
  										supplier: 1,
  										datec: 1,
  										ref_client: 1,
  										datedl: 1,
  										qty: 1,
  										Status: 1,
  										ID: 1,
  										ref: 1,
  										status: 1,
  										removable: 1,
  										channel: 1,
  										total: 1,
  										totalAll: 1,
  										entity: 1,
  										weight: 1
  								}
  						},
  						{
  								$sort: sort
  						}
  				];
  
  				if (skip)
  						query.push({
  								$skip: skip
  						});
  
  				if (limit)
  						query.push({
  								$limit: limit
  						});
  
  				if (options.exec == false) // No execute aggregate : juste return query
  						return cb(null, query);
  
  				self.aggregate(query, cb);
  		};
  
  		waterfallTasks = [accessRollSearcher, contentSearcher];
  
  		async.waterfall(waterfallTasks, callback);
  };
  
  var stockCorrectionSchema = new Schema({
  		status: {
  				isReceived: {
  						type: Date,
  						default: null
  				},
  				isInventory: {
  						type: Date,
  						default: null
  				}, //Inventory done
  
  				receivedById: {
  						type: ObjectId,
  						ref: 'Users',
  						default: null
  				}
  		},
  
  		description: {
  				type: String
  		},
  
  		boxes: {
  				type: Number,
  				default: 1
  		},
  
  		location: {
  				type: ObjectId,
  				ref: 'location',
  				default: null
  		},
  
  		orderRows: [{
  				_id: false,
  				orderRowId: {
  						type: ObjectId,
  						ref: 'orderRows',
  						default: null
  				},
  				product: {
  						type: ObjectId,
  						ref: 'product',
  						default: null
  				},
  				cost: {
  						type: Number,
  						default: 0
  				},
  				locationsReceived: [{
  						_id: false,
  						location: {
  								type: ObjectId,
  								ref: 'location',
  								default: null
  						},
  						qty: Number
  				}],
  
  				qty: Number
  		}]
  });
  
  var stockReturnSchema = new Schema({
  		status: {
  				isReceived: {
  						type: Date,
  						default: null
  				},
  				isInventory: {
  						type: Date,
  						default: null
  				}, //Inventory done
  
  				receivedById: {
  						type: ObjectId,
  						ref: 'Users',
  						default: null
  				}
  		},
  
  		description: {
  				type: String
  		},
  
  		boxes: {
  				type: Number,
  				default: 1
  		},
  
  		journalEntrySources: [{
  				type: String,
  				default: ''
  		}],
  
  		orderRows: [{
  				_id: false,
  				orderRowId: {
  						type: ObjectId,
  						ref: 'orderRows',
  						default: null
  				},
  				product: {
  						type: ObjectId,
  						ref: 'product',
  						default: null
  				},
  				cost: {
  						type: Number,
  						default: 0
  				},
  				locationsReceived: [{
  						_id: false,
  						location: {
  								type: ObjectId,
  								ref: 'location',
  								default: null
  						},
  						qty: Number
  				}],
  
  				isDeleted: {
  						type: Boolean,
  						default: false
  				},
  
  				qty: Number
  				/*
  				        _id: false,
  				        goodsOutNote: { type: ObjectId, ref: 'GoodsOutNote', default: null },
  				        goodsInNote: { type: ObjectId, ref: 'GoodsInNote', default: null },
  				        product: { type: ObjectId, ref: 'product', default: null },
  				        cost: { type: Number, default: 0 },
  				        qty: Number,
  				        warehouse: { type: ObjectId, ref: 'warehouse', default: null }*/
  		}]
  });
  
  // OF ordre de fabrication
  var ordersFabSchema = new Schema({
  		status: {
  				isPrinted: {
  						type: Date,
  						default: null
  				}, //Imprime
  				isPicked: {
  						type: Date,
  						default: null
  				}, //Prepare
  				isPacked: {
  						type: Date,
  						default: null
  				}, //Emballe
  				isShipped: {
  						type: Date,
  						default: null
  				}, //Expedier
  				isInventory: {
  						type: Date,
  						default: null
  				}, //Inventory done
  
  				pickedById: {
  						type: ObjectId,
  						ref: 'Users',
  						default: null
  				},
  				packedById: {
  						type: ObjectId,
  						ref: 'Users',
  						default: null
  				},
  				shippedById: {
  						type: ObjectId,
  						ref: 'Users',
  						default: null
  				},
  				printedById: {
  						type: ObjectId,
  						ref: 'Users',
  						default: null
  				}
  		},
  
  		description: {
  				type: String
  		},
  
  		orderRows: [{
  				_id: false,
  				product: {
  						type: ObjectId,
  						ref: 'Product',
  						default: null
  				},
  				cost: {
  						type: Number,
  						default: 0
  				},
  				qty: Number,
  				warehouse: {
  						type: ObjectId,
  						ref: 'warehouse',
  						default: null
  				}
  		}]
  });
  
  var stockTransactionsSchema = new Schema({
  		warehouseTo: {
  				type: ObjectId,
  				ref: 'warehouse',
  				default: null
  		},
  		status: {
  				isPrinted: {
  						type: Date,
  						default: null
  				}, //Imprime
  				isPicked: {
  						type: Date,
  						default: null
  				}, //Prepare
  				isPacked: {
  						type: Date,
  						default: null
  				}, //Emballe
  				isShipped: {
  						type: Date,
  						default: null
  				}, //Expedier
  				isInventory: {
  						type: Date,
  						default: null
  				}, //Inventory done
  
  				pickedById: {
  						type: ObjectId,
  						ref: 'Users',
  						default: null
  				},
  				packedById: {
  						type: ObjectId,
  						ref: 'Users',
  						default: null
  				},
  				shippedById: {
  						type: ObjectId,
  						ref: 'Users',
  						default: null
  				},
  				printedById: {
  						type: ObjectId,
  						ref: 'Users',
  						default: null
  				}
  		},
  
  		boxes: {
  				type: Number,
  				default: 1
  		},
  
  		orderRows: [{
  				_id: false,
  				orderRowId: {
  						type: ObjectId,
  						ref: 'orderRows',
  						default: null
  				},
  				product: {
  						type: ObjectId,
  						ref: 'Product',
  						default: null
  				},
  				locationsDeliver: [{
  						type: ObjectId,
  						ref: 'location'
  				}],
  				batchesDeliver: [{
  						_id: false,
  						goodsNote: {
  								type: ObjectId,
  								ref: 'goodsInNotes',
  								default: null
  						},
  						qty: Number,
  						cost: Number
  				}],
  
  				locationsReceived: [{
  						_id: false,
  						location: {
  								type: ObjectId,
  								ref: 'location',
  								default: null
  						},
  						qty: Number
  				}],
  
  				cost: {
  						type: Number,
  						default: 0
  				},
  				qty: Number
  		}]
  
  });
  
  function saveOrder(next) {
  		var self = this;
  		const SeqModel = MODEL('Sequence').Schema;
  		const EntityModel = MODEL('entity').Schema;
  		const WarehouseModel = MODEL('warehouse').Schema;
  
  		if (this.isNew)
  				this.history = [];
  
  		async.waterfall([
  				function(wCb) {
  						if (self.warehouse && self.forSales == true)
  								return wCb();
  
  						if (self.warehouse && self.forSales == false) // Refresh shipping address
  								return WarehouseModel.findById(self.warehouse, "_id address", function(err, warehouse) {
  										if (warehouse && self.Status == "DRAFT")
  												self.shippingAddress = warehouse.address;
  
  										return wCb();
  								});
  
  						return WarehouseModel.findOne({
  								main: true
  						}, "_id", function(err, warehouse) {
  								if (warehouse) {
  										self.warehouse = warehouse._id;
  										self.shippingAddress = warehouse.address;
  								}
  
  
  								return wCb();
  						});
  				},
  				function(wCb) {
  						if (!self.isNew || self.ref)
  								return wCb();
  
  						return SeqModel.inc((self.forSales == true ? "CO" : "CF"), function(seq, number) {
  								//console.log(seq);
  								self.ID = number;
  								EntityModel.findOne({
  										_id: self.entity
  								}, "cptRef", function(err, entity) {
  										if (err)
  												console.log(err);
  
  										if (self.forSales == true)
  												if (entity && entity.cptRef)
  														self.ref = "CO" + entity.cptRef + seq;
  												else
  														self.ref = "CO" + seq;
  										else
  												self.ref = "CF" + seq;
  
  										wCb();
  								});
  						});
  				},
  				function(wCb) {
  						if (self.datec)
  								self.ref = F.functions.refreshSeq(self.ref, self.datec);
  
  						wCb();
  				}
  		], function(err) {
  				next();
  		});
  }
  
  function saveQuotation(next) {
  		var self = this;
  		var SeqModel = MODEL('Sequence').Schema;
  		var EntityModel = MODEL('entity').Schema;
  
  		if (this.isNew)
  				this.history = [];
  
  		if (self.isNew && !self.ref)
  				return SeqModel.inc((self.forSales == true ? "PC" : "DA"), function(seq, number) {
  						//console.log(seq);
  						self.ID = number;
  						EntityModel.findOne({
  								_id: self.entity
  						}, "cptRef", function(err, entity) {
  								if (err)
  										console.log(err);
  
  								if (self.forSales == true)
  										if (entity && entity.cptRef)
  												self.ref = "PC" + entity.cptRef + seq;
  										else
  												self.ref = "PC" + seq;
  								else
  										self.ref = "DA" + seq;
  
  								next();
  						});
  				});
  		self.ref = F.functions.refreshSeq(self.ref, self.datec);
  		next();
  }
  
  function setNameDelivery(next) {
  		var self = this;
  		var SeqModel = MODEL('Sequence').Schema;
  		var EntityModel = MODEL('entity').Schema;
  		var OrderModel = MODEL('order').Schema.Order;
  
  		if (self.isNew && self.order === self._id)
  				return SeqModel.inc((self.forSales == true ? "CO" : "CF"), function(seq, number) {
  						//console.log(seq);
  						self.ID = number;
  						EntityModel.findOne({
  								_id: self.entity
  						}, "cptRef", function(err, entity) {
  								if (err)
  										console.log(err);
  
  								if (self.forSales == true)
  										if (entity && entity.cptRef)
  												self.ref = "BL" + entity.cptRef + seq;
  										else
  												self.ref = "BL" + seq;
  								else
  										self.ref = "RE" + seq;
  
  								next();
  						});
  				});
  
  		if (self.isNew && !self.ref)
  				return OrderModel.findById(self.order, "ref ID", function(err, order) {
  						SeqModel.incCpt(order._id, function(number) {
  								//console.log(seq);
  
  								self.ref = (self.forSales == true ? "BL" : "RE") + order.ref.substring(2) + '/' + number;
  
  								next();
  						});
  				});
  
  		if (self.datedl)
  				self.ref = F.functions.refreshSeq(self.ref, self.datedl);
  		next();
  }
  
  function setNameTransfer(next) {
  		var transaction = this;
  		var db = transaction.db.db;
  		var prefix = 'TX';
  
  		db.collection('settings').findOneAndUpdate({
  				dbName: db.databaseName,
  				name: prefix
  		}, {
  				$inc: {
  						seq: 1
  				}
  		}, {
  				returnOriginal: false,
  				upsert: true
  		}, function(err, rate) {
  				if (err) {
  						return next(err);
  				}
  
  				transaction.name = prefix + '-' + rate.value.seq;
  
  				next();
  		});
  }
  
  function setNameReturns(next) {
  		var self = this;
  		var SeqModel = MODEL('Sequence').Schema;
  		var EntityModel = MODEL('entity').Schema;
  		var OrderModel = MODEL('order').Schema.Order;
  		var WarehouseModel = MODEL('warehouse').Schema;
  
  		async.waterfall([
  				function(wCb) {
  						if (self.warehouse) // Refresh shipping address
  								return WarehouseModel.findById(self.warehouse, "_id address", function(err, warehouse) {
  										if (warehouse && self.Status == "DRAFT")
  												self.shippingAddress = warehouse.address;
  
  										return wCb();
  								});
  
  						WarehouseModel.findOne({
  								main: true
  						}, "_id", function(err, warehouse) {
  								if (warehouse) {
  										self.warehouse = warehouse._id;
  										self.shippingAddress = warehouse.address;
  								}
  
  								return wCb();
  						});
  				},
  				function(wCb) {
  
  						if (self.isNew && !self.ref)
  								return OrderModel.findById(self.order, "ref ID", function(err, order) {
  										SeqModel.incCpt(order._id, function(number) {
  												//console.log(seq);
  
  												self.ref = "RT" + order.ref.substring(2) + '/' + number;
  
  												wCb();
  										});
  								});
  
  						wCb();
  				},
  				function(wCb) {
  						if (self.datedl)
  								self.ref = F.functions.refreshSeq(self.ref, self.datedl);
  
  						wCb();
  				}
  		], function(err) {
  				next();
  		});
  }
  
  function setNameOrdersFab(next) {
  		var self = this;
  		var SeqModel = MODEL('Sequence').Schema;
  		var EntityModel = MODEL('entity').Schema;
  
  		if (this.isNew)
  				this.history = [];
  
  		if (self.isNew && !self.ref)
  				return SeqModel.inc("OF", function(seq, number) {
  						//console.log(seq);
  						self.ID = number;
  
  						self.ref = "OF" + seq;
  						next();
  
  				});
  
  		self.ref = F.functions.refreshSeq(self.ref, self.datec);
  		next();
  }
  
  orderCustomerSchema.pre('save', saveOrder);
  orderSupplierSchema.pre('save', saveOrder);
  quotationCustomerSchema.pre('save', saveQuotation);
  quotationSupplierSchema.pre('save', saveQuotation);
  
  goodsOutNoteSchema.pre('save', setNameDelivery);
  stockTransactionsSchema.pre('save', setNameTransfer);
  goodsInNoteSchema.pre('save', setNameDelivery);
  stockReturnSchema.pre('save', setNameReturns);
  
  ordersFabSchema.pre('save', setNameOrdersFab);
  
  
  //goodsOutNoteSchema.statics.getById = getDeliveryById;
  //goodsInNoteSchema.statics.getById = getDeliveryById;
  
  const orderCustomer = Order.discriminator('orderCustomer', orderCustomerSchema);
  const orderSupplier = Order.discriminator('orderSupplier', orderSupplierSchema);
  const quotationCustomer = Order.discriminator('quotationCustomer', quotationCustomerSchema);
  const quotationSupplier = Order.discriminator('quotationSupplier', quotationSupplierSchema);
  
  const goodsOutNote = Order.discriminator('GoodsOutNote', goodsOutNoteSchema);
  const stockTransactions = Order.discriminator('stockTransactions', stockTransactionsSchema);
  const stockReturns = Order.discriminator('stockReturns', stockReturnSchema);
  const stockCorrection = Order.discriminator('stockCorrections', stockCorrectionSchema);
  const goodsInNote = Order.discriminator('GoodsInNote', goodsInNoteSchema);
  
  const ordersFab = Order.discriminator('OrdersFab', ordersFabSchema);
  
  exports.Schema = {
  		Order: Order, //Only for READING
  		OrderCustomer: orderCustomer,
  		OrderSupplier: orderSupplier,
  		QuotationCustomer: quotationCustomer,
  		QuotationSupplier: quotationSupplier,
  
  		GoodsOutNote: goodsOutNote,
  		GoodsInNote: goodsInNote,
  		StockCorrections: stockCorrection,
  		stockTransactions: stockTransactions,
  		stockReturns: stockReturns,
  
  		OrdersFab: ordersFab
  };
  
  exports.Status = {
  		"_id": "fk_order_status",
  		"lang": "orders",
  		"values": {
  				"DRAFT": {
  						"enable": true,
  						"label": "StatusOrderDraft",
  						"cssClass": "ribbon-color-default label-default",
  						"system": true
  				},
  				"VALIDATED": {
  						"enable": true,
  						"label": "StatusOrderValidated",
  						"cssClass": "ribbon-color-warning label-warning"
  				},
  				"CANCELED": {
  						"enable": true,
  						"label": "StatusOrderCanceled",
  						"cssClass": "ribbon-color-danger label-danger",
  						"system": true
  				},
  				"SEND": {
  						"enable": true,
  						"label": "StatusOrderSend",
  						"cssClass": "ribbon-color-success label-success"
  				},
  				"PROCESSING": {
  						"enable": true,
  						"label": "StatusOrderProcessing",
  						"cssClass": "ribbon-color-info label-info"
  				},
  				"SHIPPING": {
  						"label": "StatusOrderSending",
  						"enable": true,
  						"cssClass": "ribbon-color-success label-success"
  				},
  				"ADMISSION": {
  						"label": "StatusOrderOnProcess",
  						"enable": true,
  						"cssClass": "ribbon-color-success label-success"
  				},
  				"CLOSED": {
  						"enable": true,
  						"label": "StatusOrderClosed",
  						"cssClass": "ribbon-color-success label-success",
  						"system": true
  				},
  				"ERROR": {
  						"label": "StatusOrderError",
  						"cssClass": "ribbon-color-danger label-danger",
  						"system": true
  				},
  				"BILLING": {
  						"label": "StatusOrderToBill",
  						"cssClass": "ribbon-color-default label-default"
  				},
  				"BILLED": {
  						"enable": true,
  						"label": "StatusOrderToBill",
  						"cssClass": "ribbon-color-primary label-primary",
  						"system": true
  				},
  				"INSTOCK": {
  						"enable": true,
  						"label": "StatusDeliveryInStock",
  						"cssClass": "ribbon-color-success label-success",
  						"system": true
  				},
  				"NEW": {
  						"enable": true,
  						"label": "PropalStatusNew",
  						"cssClass": "ribbon-color-warning label-warning"
  				},
  				"SIGNED": {
  						"enable": true,
  						"label": "PropalStatusClosed",
  						"cssClass": "ribbon-color-info label-info",
  						"system": true
  				},
  				"NOTSIGNED": {
  						"enable": true,
  						"label": "PropalStatusNotSigned",
  						"cssClass": "ribbon-color-danger label-danger",
  						"system": true
  				}
  		}
  };
  
  exports.name = "order";
  
  
  // Refresh pack prices from directCost
  F.on('order:recalculateStatus', function(data, callback) {
  		const userId = data.userId;
  		const OrderRows = MODEL('orderRows').Schema;
  		const round = MODULE('utils').round;
  		var ObjectId = MODULE('utils').ObjectId;
  
  		//console.log(data);
  		console.log("Update emit order", data);
  
  		function getAvailableForRows(docs, cb) {
  				var Availability;
  				var OrderModel;
  
  				Availability = MODEL('productsAvailability').Schema;
  				OrderModel = exports.Schema.Order;
  
  				var stockStatus = {
  						allocateStatus: "NOR",
  						fulfillStatus: "NOR",
  						invoiceStatus: "NOT",
  						shippingStatus: "NOR"
  				};
  
  				if (docs && docs.length && docs[0].order.Status != "DRAFT" && docs[0].order.Status != "NEW") {
  						stockStatus.invoiceStatus = 'NOT'; //NO invoice
  						async.eachSeries(docs, function(elem, eahcCb) {
  										var product;
  
  										elem = elem.toJSON();
  										product = elem.product ? elem.product._id : null;
  
  										if (!elem.qty || elem.isDeleted)
  												return eahcCb();
  
  										if (elem.product.info.productType.isService == true)
  												return eahcCb();
  
  										Availability.aggregate([{
  												$match: {
  														product: ObjectId(product),
  														warehouse: elem.warehouse
  												}
  										}, {
  												$project: {
  														product: 1,
  														warehouse: 1,
  														onHand: 1,
  														filterRows: {
  																$filter: {
  																		input: '$orderRows',
  																		as: 'elem',
  																		cond: {
  																				$eq: ['$$elem.orderRowId', elem._id]
  																		}
  																}
  														},
  														orderRows: 1
  												}
  										}, {
  												$project: {
  														product: 1,
  														warehouse: 1,
  														onHand: 1,
  														allocated: {
  																$sum: '$filterRows.qty'
  														}
  												}
  										}, {
  												$project: {
  														product: 1,
  														warehouse: 1,
  														onHand: 1,
  														allocated: 1
  												}
  										}, {
  												$group: {
  														_id: '$warehouse',
  														allocated: {
  																$sum: '$allocated'
  														}
  												}
  										}], function(err, availability) {
  												if (err)
  														return eahcCb(err);
  
  												//console.log(availability);
  
  												OrderModel.aggregate([{
  														$match: {
  																'orderRows.orderRowId': elem._id,
  																_type: {
  																		$ne: 'stockReturns'
  																},
  																/*"status.isInventory": {
  																    $ne: null
  																},*/
  																Status: {
  																		$ne: 'CANCELED'
  																},
  																isremoved: {
  																		$ne: true
  																}
  														}
  												}, {
  														$project: {
  																ref: '$ref',
  																orderRow: {
  																		$filter: {
  																				input: '$orderRows',
  																				as: 'elem',
  																				cond: {
  																						$eq: ['$$elem.orderRowId', elem._id]
  																				}
  																		}
  																},
  
  																status: 1
  														}
  												}, {
  														$project: {
  																ref: '$ref',
  																orderRow: {
  																		$arrayElemAt: ['$orderRow', 0]
  																},
  																status: 1
  														}
  												}, {
  														$project: {
  																ref: '$ref',
  																orderRow: '$orderRow.orderRowId',
  																qty: '$orderRow.qty',
  																status: 1
  														}
  												}], function(err, docs) {
  														var fullfillOnRow = 0;
  														var shippedOnRow = 0;
  														var allocatedOnRow;
  														var shippedDocs;
  
  														console.log(docs);
  
  														if (err)
  																return eahcCb(err);
  
  														availability = availability && availability.length ? availability[0].allocated : 0;
  
  
  
  														if (!docs || !docs.length) {
  
  																if (!stockStatus.fulfillStatus)
  																		stockStatus.fulfillStatus = 'NOT';
  
  																stockStatus.fulfillStatus = (stockStatus.fulfillStatus === 'NOA') || (stockStatus.fulfillStatus === 'ALL') ? 'NOA' : 'NOT';
  																stockStatus.shippingStatus = (stockStatus.shippingStatus === 'NOA') || (stockStatus.shippingStatus === 'ALL') ? 'NOA' : 'NOT';
  
  														} else {
  
  																//console.log(stockStatus);
  
  																shippedDocs = _.filter(docs, function(el) {
  																		if (el.status && (el.status.isShipped || el.status.isReceived))
  																				return el;
  																});
  
  																if (shippedDocs.length) {
  																		shippedDocs.forEach(function(el) {
  																				if (el.qty)
  																						shippedOnRow += el.qty;
  																		});
  
  																		if (shippedOnRow !== elem.qty)
  																				stockStatus.shippingStatus = 'NOA';
  																		else
  																				stockStatus.shippingStatus = stockStatus.shippingStatus && (stockStatus.shippingStatus === 'NOA') ? 'NOA' : 'ALL';
  
  																} else
  																		stockStatus.shippingStatus = ((stockStatus.shippingStatus === 'NOA') || (stockStatus.shippingStatus === 'ALL')) ? 'NOA' : 'NOT';
  
  																console.log("stockStatus : ", stockStatus);
  
  
  																docs.forEach(function(el) {
  																		if (el.qty)
  																				fullfillOnRow += el.qty;
  																});
  
  																//console.log("test", fullfillOnRow, elem.qty);
  
  																if (fullfillOnRow !== elem.qty)
  																		stockStatus.fulfillStatus = (stockStatus.fulfillStatus === 'NOA') ? 'NOA' : 'NOT';
  																else if (!stockStatus.fulfillStatus || stockStatus.fulfillStatus == 'ALL')
  																		stockStatus.fulfillStatus = 'ALL';
  																else
  																		stockStatus.fulfillStatus = 'NOA';
  
  																//console.log(stockStatus);
  														}
  
  														allocatedOnRow = fullfillOnRow + availability;
  
  														console.log(availability);
  
  														if (!elem.product.info.productType.inventory) {
  																//Not IN STOCK Managment
  																// Allocated ALL
  
  																if (!stockStatus.allocateStatus)
  																		stockStatus.allocateStatus = "ALL";
  																//else
  																//    stockStatus.allocateStatus = ((stockStatus.allocateStatus === 'NOA') || (stockStatus.allocateStatus === 'NOT')) ? stockStatus.allocateStatus : 'ALL';
  																return eahcCb();
  														}
  
  														//console.log('allocated', allocatedOnRow);
  
  														if (!allocatedOnRow) {
  																// stockStatus.allocateStatus = stockStatus.allocateStatus || 'NOA';
  																stockStatus.allocateStatus = ((stockStatus.allocateStatus === 'NOA') || (stockStatus.allocateStatus === 'ALL')) ? 'NOA' : 'NOT';
  																return eahcCb();
  														}
  
  														if (!stockStatus.allocateStatus || stockStatus.allocateStatus == 'ALL') {
  																stockStatus.allocateStatus = 'ALL';
  																return eahcCb();
  														}
  
  														if (allocatedOnRow !== elem.qty) { // Before <
  																stockStatus.allocateStatus = 'NOA';
  																return eahcCb();
  														}
  
  														stockStatus.allocateStatus = 'NOA';
  														return eahcCb();
  
  												});
  										});
  
  								},
  								function(err) {
  										if (err)
  												return cb(err);
  
  										cb(null, stockStatus);
  
  								});
  				} else {
  						stockStatus.fulfillStatus = 'NOR';
  						stockStatus.allocateStatus = 'NOR';
  						stockStatus.shippingStatus = 'NOR';
  						stockStatus.invoiceStatus = 'NOR';
  
  						cb(null, stockStatus);
  				}
  
  		}
  
  		async.waterfall([
  				function(wCb) {
  						if (!data.order || !data.order._id)
  								return wCb("No Id order");
  
  						wCb(null);
  				},
  				function(wCb) {
  						const Order = exports.Schema.Order;
  						// Get Type ONLY CustomerOrder AND SupplierOrder type
  
  						Order.findById(data.order._id, "_type Status total_ht pdfs", function(err, doc) {
  								if (err)
  										return wCb(err);
  
  								if (!doc || doc.isremoved == true)
  										return wCb(null, null, null);
  
  								//if (doc.Status == 'CLOSED' || doc.Status == 'CANCELED')
  								//    return wCb(null, null, null); // No UPDATE IF CLOSED
  
  								if (doc._type == 'orderCustomer')
  										return wCb(null, exports.Schema.OrderCustomer, doc);
  
  								if (doc._type == 'orderSupplier')
  										return wCb(null, exports.Schema.OrderSupplier, doc);
  
  								return wCb(null, null, null);
  
  						});
  				},
  				function(OrderModel, order, wCb) {
  						if (!OrderModel)
  								return wCb();
  
  						async.parallel([
  								function(pCb) {
  										OrderRows.find({
  														order: data.order._id, //orderId
  														product: {
  																$ne: null
  														}
  												})
  												.populate({
  														path: "product",
  														select: "directCost info",
  														populate: {
  																path: "info.productType"
  														},
  												})
  												.populate('order', 'Status')
  												.exec(function(err, docs) {
  														if (err)
  																return pCb(err);
  
  														getAvailableForRows(docs, function(err, status) {
  																if (err)
  																		return pCb(err);
  
  																/* Update invoiceStatus */
  																const BillModel = MODEL('invoice').Schema;
  
  																BillModel.aggregate([{
  																		$match: {
  																				orders: ObjectId(data.order._id),
  																				isremoved: {
  																						$ne: true
  																				}
  																		}
  																}, {
  																		$project: {
  																				_id: 1,
  																				ref: 1,
  																				orders: 1,
  																				total_ht: 1
  																		}
  																}, {
  																		$group: {
  																				_id: null,
  																				total: {
  																						$sum: "$total_ht"
  																				},
  																				data: {
  																						$push: "$$ROOT"
  																				}
  																		}
  																}, {
  																		$unwind: "$data"
  																}, {
  																		$project: {
  																				_id: "$data._id",
  																				ref: "$data.ref",
  																				orders: "$data.orders",
  																				total: "$total"
  																		}
  																}, {
  																		$unwind: "$orders"
  																}, {
  																		$lookup: {
  																				from: 'Orders',
  																				localField: 'orders',
  																				foreignField: '_id',
  																				as: 'orders'
  																		}
  																}, {
  																		$unwind: {
  																				path: '$orders'
  																		}
  																}, {
  																		$project: {
  																				_id: 1,
  																				ref: 1,
  																				'orders._id': 1,
  																				'orders.ref': 1,
  																				'orders.total_ht': 1,
  																				total: 1
  																		}
  																}, {
  																		$group: {
  																				_id: null,
  																				total: {
  																						$sum: "$orders.total_ht"
  																				},
  																				data: {
  																						$push: "$$ROOT"
  																				}
  																		}
  																}, {
  																		$unwind: "$data"
  																}, {
  																		$project: {
  																				_id: "$data._id",
  																				ref: "$data.ref",
  																				orders: "$data.orders",
  																				total: {
  																						invoice: "$data.total",
  																						orders: "$total"
  																				}
  																		}
  																}, {
  																		$group: {
  																				_id: null,
  																				total: {
  																						$first: "$total"
  																				},
  																				data: {
  																						$push: "$$ROOT"
  																				}
  																		}
  																}], function(err, invoices) {
  																		if (err)
  																				return pCb(err);
  
  																		if (round(order.total_ht) == 0)
  																				status.invoiceStatus = 'NOR'; // No need bill
  
  																		//console.log("invoices: ", invoices);
  																		if (invoices && invoices.length) {
  																				let result = invoices[0].total;
  
  																				if (result.invoice >= result.orders)
  																						status.invoiceStatus = 'ALL'; // All billed
  																				else
  																						status.invoiceStatus = 'NOA'; // Not all billed
  																		}
  
  
  																		let query = {
  																				status: status
  																		};
  
  																		// Classify CLOSED
  																		if (status.fulfillStatus == 'ALL' && status.allocateStatus == 'ALL' && status.shippingStatus == 'ALL' && (status.invoiceStatus == 'ALL' || status.invoiceStatus == 'NOR'))
  																				query.Status = 'CLOSED';
  
  																		else if (status.fulfillStatus == 'NOR' && status.allocateStatus == 'NOR' && status.shippingStatus == 'NOR' && order.Status != 'DRAFT') {
  																				if (status.invoiceStatus == 'ALL')
  																						query.Status = 'CLOSED';
  																				else if (query.Status == 'CLOSED')
  																						query.Status = 'VALIDATED';
  																		}
  
  																		OrderModel.findByIdAndUpdate(data.order._id, query, {
  																				new: true
  																		}, function(err, el) {
  																				if (err)
  																						return pCb(err);
  
  																				F.emit('customer:recalculateStatus', {
  																						userId: null,
  																						supplier: {
  																								_id: el.supplier.toString()
  																						}
  																				});
  
  																				pCb(null, el);
  																		});
  																});
  														});
  												});
  								},
  								//Refresh PDF
  								function(pCb) {
  										if (!OrderModel)
  												return wCb();
  
  										if (order.Status != 'DRAFT')
  												return wCb();
  
  										if (!order.pdfs)
  												return wCb();
  
  										async.each(order.pdfs, function(elem, eCb) {
  												OrderModel.generatePdfById(data.order._id, elem.modelPdf, eCb)
  										}, pCb);
  								}
  						], function(err, el) {
  								if (err)
  										return wCb(err);
  
  								//console.log(status, el.status);
  
  								console.log('Status updated');
  								//Force reload order
  								//Force reload product
  
  								if (userId)
  										F.emit('notify:controllerAngular', {
  												userId: userId,
  												route: 'order',
  												_id: el[0]._id.toString(),
  												message: "Commande " + el[0].ref + ' modifie.'
  										});
  
  
  								setTimeout2('notifyorder:controllerAngular', function() {
  										F.emit('notify:controllerAngular', {
  												userId: null,
  												route: 'order',
  												//_id: el._id.toString(),
  												//message: "Commande " + el.ref + ' modifie.'
  										});
  								}, 60000);
  
  								wCb();
  						});
  				}
  		], function(err) {
  				if (err)
  						console.log(err);
  
  				if (callback)
  						callback(err);
  		});
  });
  
  F.on('order:sendDelivery', function(data) {
  		var userId = data.userId;
  		//const OrderRows = MODEL('orderRows').Schema;
  		var ObjectId = MODULE('utils').ObjectId;
  
  		const OrderModel = exports.Schema.OrderCustomer;
  
  		//console.log(data);
  		console.log("Update emit order sendFirstDelivery", data);
  		OrderModel.findOne({
  				_id: data.order._id,
  				isremoved: {
  						$ne: true
  				},
  				Status: "PROCESSING"
  		}, function(err, order) {
  				if (err || !order)
  						return;
  
  				var object = order.toObject();
  
  				if (order.forSales)
  						var DeliveryModel = exports.Schema.GoodsOutNote;
  				else
  						var DeliveryModel = exports.Schema.goodsInNotes;
  
  				DeliveryModel.findOne({
  						order: order._id
  				}, function(err, delivery) {
  						if (err || delivery)
  								return; //already exist
  
  						var id = object._id;
  						object.order = object._id;
  						delete object._id;
  						delete object.Status;
  						delete object.latex;
  						delete object.datec;
  						delete object.createdAt;
  						delete object.updatedAt;
  						delete object.ref;
  						delete object.history;
  						delete object._type;
  						delete object.status;
  
  						delivery = new DeliveryModel(object);
  
  						delivery.editedBy = userId;
  						delivery.createdBy = userId;
  
  						//return console.log(delivery);
  						delivery.save(function(err, doc) {
  								if (err)
  										return console.log(err);
  
  								F.emit('order:recalculateStatus', {
  										userId: userId,
  										order: {
  												_id: order._id.toString()
  										}
  								});
  						});
  				});
  		});
  });
  
  F.on('order:update', function(data) {
  
  		//console.log(data);
  		console.log("Update emit order !", data);
  });
  