/**
 * Created by vian on 2017/3/20.
 */
const mongoose = require('mongoose');
let counter = require("../../../public/counter/counter");
let async = require("async");
let StdBillsLib = mongoose.model('std_bills_lib_list');
let Bills = mongoose.model('std_bills_lib_bills');
let JobContent = mongoose.model('std_bills_lib_jobContent');
let ItemCharacter = mongoose.model('std_bills_lib_itemCharacter');
let moment = require("moment");
let billsGuidanceLib = mongoose.model('std_billsGuidance_lib');
const engLibModel = mongoose.model('engineering_lib');
let uuid = require('uuid');
let billsLibDao = function(){};

billsLibDao.prototype.copyLib = async function (userName, libName, fromLibId) {
    const libData = await this.createStdBillsLibSync(userName, libName);
    const newLibId = libData.billsLibId;
    //插入工作内容
    let orgJobs = await JobContent.find({billsLibId: fromLibId}).lean();
    let jobCounter = await counter.counterDAO.getIDAfterCount(counter.moduleName.billsLib_jobs, orgJobs.length);
    let maxJobId = jobCounter.sequence_value;
    let jobTask = [],
        orgNewJobMapping = {};
    for (let i = 0; i < orgJobs.length; i++) {
        let jobData = orgJobs[i];
        delete jobData._id;
        let newID = (maxJobId - (orgJobs.length - 1) + i);
        orgNewJobMapping[jobData.id] = newID;
        jobData.id = newID;
        jobData.billsLibId = newLibId;
        jobTask.push({insertOne: {document: jobData}});
    }
    if (jobTask.length > 0) {
        await JobContent.bulkWrite(jobTask);
    }
    //插入项目特征
    let orgItems = await ItemCharacter.find({billsLibId: fromLibId}).lean();
    let itemCounter = await counter.counterDAO.getIDAfterCount(counter.moduleName.billsLib_items, orgItems.length);
    let maxItemId = itemCounter.sequence_value;
    let itemTask = [],
        orgNewItemMapping = {};
    for (let i = 0; i < orgItems.length; i++) {
        let itemData = orgItems[i];
        delete itemData._id;
        let newID = (maxItemId - (orgItems.length - 1) + i);
        orgNewItemMapping[itemData.id] = newID;
        itemData.id = newID;
        itemData.billsLibId = newLibId;
        itemTask.push({insertOne: {document: itemData}});
    }
    if (itemTask.length > 0) {
        await ItemCharacter.bulkWrite(itemTask);
    }
    //插入清单
    let billsTask = [];
    let bills = await Bills.find({billsLibId: fromLibId}).lean();
    let IDMapping = {},
        billsDatas = [];
    for (let b of bills) {
        let bData = b;
        delete bData._id;
        bData.billsLibId = newLibId;
        billsDatas.push(bData);
        IDMapping[bData.ID] = uuid.v1();
    }
    for (let bD of billsDatas) {
        bD.ID = IDMapping[bD.ID];
        if (IDMapping[bD.NextSiblingID]) {
            bD.NextSiblingID = IDMapping[bD.NextSiblingID];
        }
        if (IDMapping[bD.ParentID]) {
            bD.ParentID = IDMapping[bD.ParentID];
        }
        // 更新sectionInfo数据
        if (bD.sectionInfo) {
            const first = IDMapping[bD.sectionInfo.first];
            const second = IDMapping[bD.sectionInfo.second];
            const third = IDMapping[bD.sectionInfo.third];
            bD.sectionInfo.first = first || null;
            bD.sectionInfo.second = second || null;
            bD.sectionInfo.third = third || null;
        }
        for (let subJob of bD.jobs) {
            if (orgNewJobMapping[subJob.id]) {
                subJob.id = orgNewJobMapping[subJob.id];
            }
        }
        for (let subItem of bD.items) {
            if (orgNewItemMapping[subItem.id]) {
                subItem.id = orgNewItemMapping[subItem.id];
            }
        }
        billsTask.push({insertOne: {document: bD}});
    }
    if (billsTask.length > 0) {
        await Bills.bulkWrite(billsTask);
    }
    return libData;
};

billsLibDao.prototype.getMaxNumber = function(gData, callback){
    let billsLibId = gData.billsLibId;
    let field = gData.field;
    if(field === 'jobs'){
        JobContent.find({billsLibId: billsLibId, deleted: false}).sort({code: -1}).limit(1).exec(function(err, result){
            if(err){
                callback(1, 'Error', null);
            }
            else {
                callback(0, '', result);
            }
        });
    }
    else if(field === 'items'){
        ItemCharacter.find({billsLibId: billsLibId, deleted: false}).sort({code: -1}).limit(1).exec(function(err, result){
            if(err){
                callback(1, 'Error', null);
            }
            else {
                callback(0, '', result);
            }
        });
    }
};
//----------StdBillsLib-------------------
billsLibDao.prototype.getABillsLib = function(data, callback){
    let billsLibId = data.billsLibId;
    StdBillsLib.find({billsLibId: billsLibId, deleted: false}, function(err, data){
        if(err){
            callback(1, 'Error', null);
        }
        else {
            callback(0, '', data);
        }
    });
};


billsLibDao.prototype.getStdBillsLib = function(callback){
   // let userId = data.userId;
    StdBillsLib.find({deleted: false}, "-_id", function(err, data){
        if(err){
            callback(1, "Error", null);
        }
        else{
            callback(0, "", data);
        }
    });
};

billsLibDao.prototype.createStdBillsLibSync = async function (userName, libName) {
    const counterRst = await counter.counterDAO.getIDAfterCount(counter.moduleName.billsLib, 1);
    const dateStr = moment().format('YYYY-MM-DD HH:mm:ss');
    const libData = {
        creator: userName,
        createDate: dateStr,
        recentOpr: [{operator: userName, operateDate: dateStr}],
        billsLibId: counterRst.sequence_value,
        billsLibName: libName,
        deleted: false
    };
    await StdBillsLib.create(libData);
    return libData;
};

billsLibDao.prototype.createStdBillsLib = function(clibData, callback){
    counter.counterDAO.getIDAfterCount(counter.moduleName.billsLib, 1, function(err, result){
        let billsLibId = result.sequence_value;
        let userAccount = clibData.userAccount;
        let billsLibName = clibData.name;
        let dateStr = moment().format('YYYY-MM-DD HH:mm:ss');
        let newStdBillsLib = {
            creator: userAccount,
            createDate: dateStr,
            recentOpr: [{operator: userAccount, operateDate: dateStr}],
            billsLibId: billsLibId,
            billsLibName: billsLibName,
            deleted: false
        };
        StdBillsLib.create(newStdBillsLib, function(err){
            if(err){
                callback(1, "Error", null);
            }
            else {
                StdBillsLib.find({billsLibId: billsLibId}, function(err, data){
                    if(err){
                        callback(1, "Error", null)
                    }
                    else {
                        callback(0, "", data);
                    }
                });
            }
        });
    });
};

function getNewOprArr(recentOprArr, oprtor, oprDate){
    let isExist = false;
    for(let i =0 ; i<recentOprArr.length; i++){
        if(recentOprArr[i].operator === oprtor){
           recentOprArr[i].operateDate = oprDate;
            isExist = true;
        }
    }
    if(!isExist){
        if(recentOprArr.length < 5){
            recentOprArr.push({operator: oprtor, operateDate: oprDate});
        }
        else if(recentOprArr.length === 5){
            recentOprArr.sort(function (a, b) {
                if(a.operateDate > b.operateDate){
                    return -1;
                }else {
                    return 1;
                }
                return 0;
            });
            recentOprArr.splice(recentOprArr.length -1, 1);
            recentOprArr.splice(0, 1, {operator: oprtor, operateDate: oprDate});
        }
    }
    return recentOprArr;
}

billsLibDao.prototype.deleteStdBillsLib = function(data, callback){
    let billsLibId = data.billsLibId, lastOperator = data.lastOperator,
        lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    async.parallel([
        function(cb){
            StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                if(err){
                    cb(err);
                }
                else{
                    if(result.length > 0 ){
                        let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                        StdBillsLib.remove({billsLibId: billsLibId}, function(err){
                            if(err){
                                cb(err);
                            }
                            else{
                                cb(null);
                            }
                        });
                    }
                }
            });
        },
        function(cb){
            Bills.remove({billsLibId: billsLibId}, function(err){
                cb(null);
            });
        },
        function(cb){
            JobContent.remove({billsLibId: billsLibId}, function(err, result){
                cb(null);
            });
        },
        function(cb){
            ItemCharacter.remove({billsLibId: billsLibId}, function(err){
                cb(null);
            })
        }
    ], function(err){
        if(err){
            callback(1, 'Error');
        }
        else {
            callback(0, '');
        }
    });
};

billsLibDao.prototype.renameStdBillsLib = async function(renameData, callback){
    let billsLibId = renameData.id;
    let renameVal = renameData.value;
    let lastOperator = renameData.lastOperator;
    let lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    try{
        let billsLib = await StdBillsLib.findOne({billsLibId: billsLibId});
        if(billsLib){
            let newRecentOpr = getNewOprArr(billsLib.recentOpr, lastOperator, lastOperateDate);
            await StdBillsLib.update({billsLibId: billsLibId}, {$set:{billsLibName: renameVal, recentOpr: newRecentOpr}});
            await billsGuidanceLib.update({billsLibId: billsLibId}, {$set: {billsLibName: renameVal}}, {multi: true});
            await engLibModel.update({'bill_lib.id': billsLibId}, {$set: {'bill_lib.$.name': renameVal}}, {multi: true});
            callback(0, '');
        }
    }
    catch (err){
        callback(1, err);
    }
};

billsLibDao.prototype.getStdBillsLibName = function(billsLibId, callback){
    StdBillsLib.find({billsLibId: billsLibId, deleted: false}, "-_id", function(err,  data){
        if(err){
            callback(1, "Error", null);
        }
        else {
            callback(0, "", data);
        }
    });
};

billsLibDao.prototype.getCurrentUniqId = function(callback){
    counter.counterDAO.getCurrentID(counter.moduleName.billsLib, function(result, err){
        let id = result.sequence_value;
        if(err){
            callback(1, 'Error', null);
        }
        else{
            callback(0, '', id);
        }
    });
}
//----------------------------Bills---------------------
billsLibDao.prototype.getBills = function (billsLibId, callback) {
    Bills.find({billsLibId: billsLibId, deleted: false}, "-_id", {lean: true}, function(err, billsData){
        if(err){
            callback(1, "Error", null);
        }
        else{
            callback(0, "", billsData);
        }
    });
}

billsLibDao.prototype.createBills = function(cbillsData, callback){
    let newId = cbillsData.newId;
    let pid = cbillsData.ParentID;
    let nid = cbillsData.NextSiblingID;
    let billsLibId = cbillsData.billsLibId;
    let updatePreData = cbillsData.updatePreData;
    let sectionInfo = cbillsData.sectionInfo;
    let lastOperator = cbillsData.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    let newBills = {
        ID: newId,
        ParentID: pid,
        NextSiblingID: nid,
        billsLibId: billsLibId,
        code: '',
        name: '',
        unit: '',
        ruleText: '',
        Expression: '',
        recharge:'',
        sectionInfo: sectionInfo,
        deleted: false
    };
    async.parallel([
        function(cb){
            Bills.create(newBills, function(err){
                if(err){
                    cb(err);
                }
                else {
                    cb(null);
                }
            });
        },
        function(cb){
            if(updatePreData){
                Bills.findOneAndUpdate({billsLibId: billsLibId, ID: updatePreData.ID, deleted: false}, {$set: {NextSiblingID: updatePreData.NextSiblingID}}, function(err, result){
                    if(err){
                        cb(err);
                    }
                    else {
                        cb(null)
                    }
                });
            }
            else {
                cb(null);
            }
        },
        function (cb) {
            StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                if(err){
                    cb(err);
                }
                else{
                    if(result.length > 0 ){
                        let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                        StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                            if(err){
                                cb(err);
                            }
                            else{
                                cb(null);
                            }
                        });
                    }
                }
            });
        }
    ], function(err){
        if(err){
            callback(1, 'Error');
        }
        else {
            callback(0, '');
        }
    });
};

billsLibDao.prototype.upMove = function(data, callback){
    let billsLibId = data.billsLibId,
        updateDatas = data.updateDatas,
        lastOperator = data.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    async.parallel([
        function (cb) {
            async.each(updateDatas, function(updateData, ecb){
                Bills.findOneAndUpdate({billsLibId: billsLibId, ID: updateData.ID, deleted: false}, {$set: {NextSiblingID: updateData.NextSiblingID}}, function(err){
                    if(err){
                        ecb(err);
                    }
                    else {
                        ecb(null);
                    }
                });
            }, function(err){
                if(err){
                    cb(err);
                }
                else{
                    cb(null);
                }
            });
        },
        function (cb) {
            StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                if(err){
                    cb(err);
                }
                else{
                    if(result.length > 0 ){
                        let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                        StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                            if(err){
                                cb(err);
                            }
                            else{
                                cb(null);
                            }
                        });
                    }
                }
            });
        }
    ], function (err) {
        if(err){
            callback(1, 'Error');
        }
        else{
            callback(0, '');
        }
    });
};

billsLibDao.prototype.downMove = function (data, callbck) {
    let billsLibId = data.billsLibId,
        updateDatas = data.updateDatas,
        functions = [], lastOperator = data.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    let parallelFucs = {
        updateLastOpr: function () {
            return function (cb) {
                StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                    if(err){
                        cb(err);
                    }
                    else{
                        if(result.length > 0 ){
                            let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                            StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    cb(null);
                                }
                            });
                        }
                    }
                });
            }
        },
        nextSiblingNode: function (data) {
            return function (cb) {
                Bills.update({billsLibId: billsLibId, ID: data.ID, deleted: false}, {$set: {NextSiblingID: data.NextSiblingID}}, function (err) {
                    if(err){
                        cb(err);
                    }
                    else{
                        cb(null);
                    }
                });
            };
        },
        oprNode: function (data) {
            return function (cb) {
                Bills.update({billsLibId: billsLibId, ID: data.ID, deleted: false}, {$set: {NextSiblingID: data.NextSiblingID}}, function (err) {
                    if(err){
                        cb(err);
                    }
                    else {
                        cb(null);
                    }
                })
            }
        },
        preSiblingNode: function (data) {
            return function (cb) {
                Bills.update({billsLibId: billsLibId, ID: data.ID, deleted: false}, {$set: {NextSiblingID: data.NextSiblingID}}, function (err) {
                    if(err){
                        cb(err);
                    }
                    else{
                        cb(null);
                    }
                });
            };
        }
    };
    for(let i =0; i< updateDatas.length; i++){
        if(updateDatas[i].type === 'nextSiblingNode'){
            functions.push(parallelFucs.nextSiblingNode(updateDatas[i]));
        }
        else if(updateDatas[i].type === 'oprNode'){
            functions.push(parallelFucs.oprNode(updateDatas[i]));
        }
        else {
            functions.push(parallelFucs.preSiblingNode(updateDatas[i]));
        }
    }
    functions.push(parallelFucs.updateLastOpr());
    async.parallel(functions, function (err) {
        if(err){
            callbck(1, 'Error');
        }
        else{
            callbck(0, '');
        }
    })
}

billsLibDao.prototype.upLevel = function(data, callback){
    let billsLibId = data.billsLibId,
        updateDatas = data.updateDatas, functions = [],
        lastOperator = data.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    let parallelFucs = {
        updateLastOpr: function () {
            return function (cb) {
                StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                    if(err){
                        cb(err);
                    }
                    else{
                        if(result.length > 0 ){
                            let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                            StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    cb(null);
                                }
                            });
                        }
                    }
                });
            }
        },
        oprNode: function (data) {
            return function (cb){
                Bills.update({billsLibId: billsLibId, ID: data.ID, deleted: false}, {$set: {ParentID: data.ParentID, NextSiblingID: data.NextSiblingID, sectionInfo: data.sectionInfo?data.sectionInfo:null}}, function(err){
                    if(err){
                        cb(err);
                    }
                    else {
                        cb(null);
                    }
                });
            };
        },
        parentNode: function (data) {
            return function(cb){
                Bills.update({billsLibId: billsLibId, ID: data.ID, deleted: false}, {$set: {NextSiblingID: data.NextSiblingID}}, function (err) {
                    if(err){
                        cb(err);
                    }
                    else {
                        cb(null);
                    }
                })
            };
        },
        nextSiblingNode: function (data) {
            return function (cb) {
                Bills.update({billsLibId: billsLibId, ID: data.ID, deleted: false}, {$set: {ParentID: data.ParentID, sectionInfo: data.sectionInfo}}, function (err) {
                    if(err){
                        cb(err);
                    }
                    else{
                        cb(null);
                    }
                });
            };
        },
        preSiblingNode: function(data){
            return function (cb) {
                Bills.update({billsLibId: billsLibId, ID: data.ID, deleted: false}, {$set: {NextSiblingID: data.NextSiblingID}}, function (err) {
                    if(err){
                        cb(err);
                    }
                    else {
                        cb(null);
                    }
                });
            };
        }
    }
    for(let i=0; i< updateDatas.length; i++){
        if(updateDatas[i].type === 'oprNode'){
            functions.push(parallelFucs.oprNode(updateDatas[i]));
        }
        else if(updateDatas[i].type === 'parentNode'){
            functions.push(parallelFucs.parentNode(updateDatas[i]));
        }
        else if(updateDatas[i].type === 'nextSiblingNode'){
            updateDatas[i].ID.forEach(function (id) {
                let obj = {ID: id, ParentID: updateDatas[i].ParentID, sectionInfo: updateDatas[i].sectionInfo ? updateDatas[i].sectionInfo : null };
                functions.push(parallelFucs.nextSiblingNode(obj));
            });
        }
        else {
            functions.push(parallelFucs.preSiblingNode(updateDatas[i]));
        }
    }
    functions.push(parallelFucs.updateLastOpr());
    async.parallel(functions, function(err){
        if(err){
            callback(1, 'Error');
        }
        else{
            callback(0, '');
        }
    });
};

billsLibDao.prototype.downLevel = function (data, callback) {
    let billsLibId = data.billsLibId,
        updateDatas = data.updateDatas,
        functions = [], lastOperator = data.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    let parallelFucs = {
        updateLastOpr: function () {
            return function (cb) {
                StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                    if(err){
                        cb(err);
                    }
                    else{
                        if(result.length > 0 ){
                            let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                            StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    cb(null);
                                }
                            });
                        }
                    }
                });
            }
        },
        preSiblingNode: function (data) {
            return function (cb) {
                Bills.update({billsLibId: billsLibId, ID: data.ID, deleted: false}, {$set: {NextSiblingID: data.NextSiblingID}}, function (err) {
                    if(err){
                        cb(err);
                    }
                    else {
                        cb(null);
                    }
                });
            };
        },
        oprNode: function (data) {
            return function (cb) {
                Bills.update({billsLibId: billsLibId, ID: data.ID, deleted: false}, {$set: {ParentID: data.ParentID, NextSiblingID: data.NextSiblingID, sectionInfo: data.sectionInfo?data.sectionInfo:null}}, function (err) {
                    if(err){
                        cb(err);
                    }
                    else{
                        cb(null);
                    }
                });
            };
        },
        preChildren: function (data) {
            return function (cb) {
                Bills.update({billsLibId: billsLibId, ID: data.ID, deleted: false}, {$set: {NextSiblingID: data.NextSiblingID}}, function (err) {
                    if(err){
                        cb(err);
                    }
                    else{
                        cb(null);
                    }
                });
            };
        },
        oprChildren: function (data) {
            return function (cb) {
                Bills.update({billsLibId: billsLibId, ID: data.ID, deleted: false}, {$set: {sectionInfo: data.sectionInfo?data.sectionInfo:null}}, function (err) {
                    if(err){
                        cb(err);
                    }
                    else{
                        cb(null);
                    }
                });
            };
        }
    }
    for(let i=0; i<updateDatas.length; i++){
        if(updateDatas[i].type === 'preSiblingNode'){
            functions.push(parallelFucs.preSiblingNode(updateDatas[i]));
        }
        else if(updateDatas[i].type === 'oprNode'){
            functions.push(parallelFucs.oprNode(updateDatas[i]));
        }
        else if(updateDatas[i].type === 'oprChildren'){
            functions.push(parallelFucs.oprChildren(updateDatas[i]));
        }
        else {
            functions.push(parallelFucs.preChildren(updateDatas[i]));
        }
    }
    functions.push(parallelFucs.updateLastOpr());
    async.parallel(functions, function (err) {
        if(err){
            callback(1, 'Error');
        }
        else{
            callback(0, '');
        }
    })
};

billsLibDao.prototype.updatePNId= function(upData, callback){
    let billsLibId = upData.billsLibId;
    let updateDatas = upData.updateData;
    let eachDatas = [];
        for(let i=0; i< updateDatas.length; i++){
            if(updateDatas[i].ID.length > 1){
                let ids = updateDatas[i].ID;
                for(let j=0; j<ids.length; j++){
                    let eachObj = {
                        id: ids[j],
                        data: updateDatas[i]
                    }
                    eachDatas.push(eachObj);
                }
            }
            else {
                eachDatas.push({id: updateDatas[i].ID, data: updateDatas[i]});
            }
        }

        async.each(eachDatas, function(eachObj, cb){
            let id = eachObj.id, data = eachObj.data;
            if(data.ParentID && data.NextSiblingID){
                Bills.update({billsLibId: billsLibId, ID: id, deleted: false}, {$set: {ParentID: data.ParentID, NextSiblingID: data.NextSiblingID}}, function(err){
                    if(err){
                        cb(err);
                    }else {
                        cb(null);
                    }
                });
            }
            else if(data.ParentID && !data.NextSiblingID){
                Bills.update({billsLibId: billsLibId, ID: id, deleted: false}, {$set: {ParentID: data.ParentID}}, function(err){
                    if(err){
                        if(err){
                            cb(err);
                        }else {
                            cb(null);
                        }
                    }
                });
            }
            else if(!data.ParentID && data.NextSiblingID){
                Bills.update({billsLibId: billsLibId, ID: id, deleted: false}, {$set: {NextSiblingID: data.NextSiblingID}}, function(err){
                    if(err){
                        cb(err);
                    }else {
                        cb(null);
                    }
                });
            }
        }, function(err){
            if(err){
                callback(1, 'Error');
            }
            else {
                callback(0, '');
            }
        });
}

billsLibDao.prototype.updateSectionInfo = function (data, callback) {
    let functions = [];
    let updateData = data.updateData;
    for(let ud of updateData){
        functions.push((function(udData){
            return function(cb){
                Bills.update({ID: udData.ID}, {$set: udData}, function(err){
                    if(err){
                        cb(err);
                    }
                    else{
                        cb(null);
                    }
                });
            };
        })(ud));
    }
    async.parallel(functions, function(err, results) {
        let errCode = 0,
            errMsg = '';
        if(err){
            errCode = 1;
            errMsg = 'Error'
        }
        callback(errCode, errMsg);
    });
};

billsLibDao.prototype.updateBills = function(ubillsData, callback){
    let billsLibId = ubillsData.billsLibId;
    let updateId = ubillsData.updateId;
    let updateField = ubillsData.field;
    let updateData = ubillsData.data, lastOperator = ubillsData.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    let needSet = {};
    needSet[updateField] = updateData;
    async.parallel([
        function (cb) {
            if(updateField !== 'jobs' && updateField !== 'items'){
                Bills.update({billsLibId: billsLibId, ID: updateId, deleted: false}, {$set: needSet}, function (err) {
                    if(err){
                        cb(err);
                    }
                    else{
                        cb(null);
                    }
                })
            }
            else{
                Bills.update({billsLibId: billsLibId, ID: updateId, deleted: false}, {$addToSet: needSet}, function (err) {
                    if(err){
                        cb(err);
                    }
                    else{
                        cb(null);
                    }
                });
            }
        },
        function (cb) {
            StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                if(err){
                    cb(err);
                }
                else{
                    if(result.length > 0 ){
                        let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                        StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                            if(err){
                                cb(err);
                            }
                            else{
                                cb(null);
                            }
                        });
                    }
                }
            });
        }
    ], function (err) {
        if(err){
            callback(1, 'Error');
        }
        else{
            callback(0, '');
        }
    });
}

billsLibDao.prototype.removeTotal = function (data, callback) {
    let billsLibId = data.billsLibId, billsIds = data.billsIds,
        delIds = data.delIds, field = data.field, functions = [], delArr = [],
        lastOperator = data.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    if(billsIds.length > 0){
        billsIds.forEach(function (updateId) {
            delIds.forEach(function (delId) {
                delArr.push({updateID: updateId, delId: delId});
            });
        });
    }
    let parallelFucs = {
        updateLastOpr: function () {
            return function (cb) {
                StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                    if(err){
                        cb(err);
                    }
                    else{
                        if(result.length > 0 ){
                            let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                            StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    cb(null);
                                }
                            });
                        }
                    }
                });
            }
        },
        delJobs: function () {
            return function (cb) {
                async.each(delIds, function (delJobId, ecb) {
                    JobContent.remove({billsLibId: billsLibId, id: delJobId}, function (err, result) {
                        if(err){
                            ecb(err);
                        }
                        else {
                            ecb(null);
                        }
                    });
                }, function (err) {
                    if(err){
                        cb(err);
                    }
                    else{
                        cb(null);
                    }
                });
            };
        },
        delJobsArr: function () {
            return function (cb) {
                async.each(delArr, function (delObj, ecb) {
                    Bills.update({billsLibId: billsLibId, ID: delObj.updateID, deleted: false}, {$pull: {jobs: {id: delObj.delId}}}, function (err) {
                        if(err){
                            ecb(err);
                        }else{
                            ecb(null);
                        }
                    });
                }, function (err) {
                    if(err){
                        cb(err);
                    }
                    else{
                        cb(null);
                    }
                })
            };
        },
        delItems: function () {
            return function (cb) {
                async.each(delIds, function (delItemId, ecb) {
                    ItemCharacter.remove({billsLibId: billsLibId, id: delItemId}, function (err, result) {
                        if(err){
                            ecb(err);
                        }
                        else {
                            ecb(null);
                        }
                    });
                }, function (err) {
                    if(err){
                        cb(err);
                    }
                    else{
                        cb(null);
                    }
                });
            };
        },
        delItemsArr: function () {
            return function (cb) {
                async.each(delArr, function (delObj, ecb) {
                    Bills.update({billsLibId: billsLibId, ID: delObj.updateID, deleted:false}, {$pull: {items: {id: delObj.delId}}}, function(err){
                        if(err){
                            ecb(err);
                        }
                        else{
                            ecb(null);
                        }
                    });
                }, function (err) {
                    if(err){
                        cb(err);
                    }
                    else{
                        cb(null);
                    }
                });
            }
        }
    };
    if(field === 'jobs'){
        functions.push(parallelFucs.delJobs());
        functions.push(parallelFucs.delJobsArr());
    }
    else{
        functions.push(parallelFucs.delItems());
        functions.push(parallelFucs.delItemsArr());
    }
    functions.push(parallelFucs.updateLastOpr());
    async.parallel(functions, function (err) {
        if(err){
            callback(1, 'Error');
        }
        else{
            callback(0, '');
        }
    });
};

billsLibDao.prototype.updateSerialNo = function (data, callback) {
    let billsLibId = data.billsLibId, billsId = data.billsId,
        updateArr = data.updateArr, field = data.field,
        lastOperator = data.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    if(field === 'jobs'){
        async.parallel([
            function (cb) {
                async.each(updateArr, function (updateObj, ecb) {
                    Bills.update({billsLibId: billsLibId, ID: billsId, deleted: false, 'jobs.id': updateObj.id}, {$set: {'jobs.$.serialNo': updateObj.serialNo}}, function (err) {
                        if(err){
                            ecb(err);
                        }
                        else{
                            ecb(null);
                        }
                    });
                }, function (err) {
                    if(err){
                        cb(err);
                    }
                    else{
                        cb(null);
                    }
                });
            },
            function (cb) {
                StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                    if(err){
                        cb(err);
                    }
                    else{
                        if(result.length > 0 ){
                            let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                            StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    cb(null);
                                }
                            });
                        }
                    }
                });
            }
        ], function (err) {
            if(err){
                callback(1, 'Error');
            }
            else{
                callback(0, '');
            }
        });
    }
    else{
        async.parallel([
            function (cb) {
                async.each(updateArr, function (updateObj, ecb) {
                    Bills.update({billsLibId: billsLibId, ID: billsId, deleted: false, 'items.id': updateObj.id}, {$set: {'items.$.serialNo': updateObj.serialNo}}, function (err) {
                        if(err){
                            ecb(err);
                        }
                        else{
                            ecb(null);
                        }
                    });
                }, function (err) {
                    if(err){
                        cb(err);
                    }
                    else{
                        cb(null);
                    }
                });
            },
            function (cb) {
                StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                    if(err){
                        cb(err);
                    }
                    else{
                        if(result.length > 0 ){
                            let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                            StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    cb(null);
                                }
                            });
                        }
                    }
                });
            }
        ], function (err) {
            if(err){
                callback(1, 'Error');
            }
            else{
                callback(0, '');
            }
        });
    }
};

billsLibDao.prototype.updateBillsArr = function(updateData, callback){
    let billsLibId = updateData.billsLibId;
    let updateId = updateData.updateId;
    let orgId = updateData.orgId;
    let newId = updateData.newId;
    let classify = updateData.classify;
    let type = updateData.type;
    let lastOperator = updateData.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    if(classify === 'jobs'){
        if(orgId && newId && type === 'update'){
            async.parallel([
                function (cb) {
                    Bills.update({billsLibId: billsLibId, ID: updateId, deleted: false, 'jobs.id': orgId}, {$set: {'jobs.$.id': newId}}, function(err){
                        if(err){
                            cb(err);
                        }
                        else {
                            cb(null);
                        }
                    });
                },
                function (cb) {
                    StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                        if(err){
                            cb(err);
                        }
                        else{
                            if(result.length > 0 ){
                                let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                                StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                    if(err){
                                        cb(err);
                                    }
                                    else{
                                        cb(null);
                                    }
                                });
                            }
                        }
                    });
                }
            ], function (err) {
                if(err){
                    callback(1, 'Error');
                }
                else{
                    callback(0, '');
                }
            });
        }
        if(orgId && !newId && type === 'delete'){
            async.parallel([
                function (cb) {
                    async.each(orgId, function(oid, ecb){
                        Bills.update({billsLibId: billsLibId, ID: updateId, deleted: false}, {$pull: {jobs: {id: oid}}}, function(err){
                            if(err){
                                ecb(err);
                            }else {
                                ecb(null);
                            }
                        });
                    }, function(err){
                        if(err){
                            cb(err);
                        }
                        else{
                            cb(null);
                        }
                    });
                },
                function (cb) {
                    StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                        if(err){
                            cb(err);
                        }
                        else{
                            if(result.length > 0 ){
                                let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                                StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                    if(err){
                                        cb(err);
                                    }
                                    else{
                                        cb(null);
                                    }
                                });
                            }
                        }
                    });
                }
            ], function (err) {
                if(err){
                    callback(1, 'Error');
                }
                else{
                    callback(0, '');
                }
            })
        }

        if(orgId && !newId && type === 'deleteAll'){
            let delArr = [];
            updateId.forEach(function(uid){
                orgId.forEach(function(oid){
                    delArr.push({updateId: uid, delId: oid});
                });
            });
            async.parallel([
                function (cb) {
                    async.each(delArr, function(delObj, ecb){
                        Bills.update({billsLibId: billsLibId, ID: delObj.updateId, deleted: false}, {$pull: {jobs: {id: delObj.delId}}}, function(err){
                            if(err){
                                ecb(err);
                            }else {
                                ecb(null);
                            }
                        });
                    }, function(err){
                        if(err){
                            cb(err);
                        }else {
                            cb(null);
                        }
                    });
                },
                function (cb) {
                    StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                        if(err){
                            cb(err);
                        }
                        else{
                            if(result.length > 0 ){
                                let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                                StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                    if(err){
                                        cb(err);
                                    }
                                    else{
                                        cb(null);
                                    }
                                });
                            }
                        }
                    });
                }
            ], function (err) {
                if(err){
                    callback(1, 'Error');
                }
                else{
                    callback(0, '');
                }
            });
        }
    }
    else if(classify === 'items'){
        if(orgId && newId && type === 'update'){
            async.parallel([
                function (cb) {
                    Bills.update({billsLibId: billsLibId, ID: updateId, deleted: false, 'items.id': orgId}, {$set: {'items.$.id': newId}}, function(err){
                        if(err){
                            cb(err);
                        }
                        else {
                            cb(null);
                        }
                    });
                },
                function (cb) {
                    StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                        if(err){
                            cb(err);
                        }
                        else{
                            if(result.length > 0 ){
                                let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                                StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                    if(err){
                                        cb(err);
                                    }
                                    else{
                                        cb(null);
                                    }
                                });
                            }
                        }
                    });
                }
            ], function (err) {
                if(err){
                    callback(1, 'Error');
                }
                else{
                    callback(0, '');
                }
            });
        }
        if(orgId && !newId && type === 'delete'){
            async.parallel([
                function (cb) {
                    async.each(orgId, function(oid, ecb){
                        Bills.update({billsLibId: billsLibId, ID: updateId, deleted: false}, {$pull: {items: {id: oid}}}, function(err){
                            if(err){
                                ecb(err);
                            }else {
                                ecb(null);
                            }
                        });
                    }, function(err){
                        if(err){
                            cb(err);
                        }else {
                            cb(null);
                        }
                    });
                },
                function (cb) {
                    StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                        if(err){
                            cb(err);
                        }
                        else{
                            if(result.length > 0 ){
                                let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                                StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                    if(err){
                                        cb(err);
                                    }
                                    else{
                                        cb(null);
                                    }
                                });
                            }
                        }
                    });
                }
            ], function (err) {
                if(err){
                    callback(1, 'Error');
                }
                else{
                    callback(0, '');
                }
            })
        }
        if(orgId && !newId &&  type === 'deleteAll'){
            let delArr = [];
            updateId.forEach(function(uid){
                orgId.forEach(function(oid){
                    delArr.push({updateId: uid, delId: oid});
                });
            });
            async.parallel([
                function (cb) {
                    async.each(delArr, function(delObj, ecb){
                        Bills.update({billsLibId: billsLibId, ID: delObj.updateId, deleted: false}, {$pull: {items: {id: delObj.delId}}}, function(err){
                            if(err){
                                ecb(err);
                            }else {
                                ecb(null);
                            }
                        });
                    }, function(err){
                        if(err){
                            cb(err);
                        }else {
                            cb(err);
                        }
                    });
                },
                function (cb) {
                    StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                        if(err){
                            cb(err);
                        }
                        else{
                            if(result.length > 0 ){
                                let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                                StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                    if(err){
                                        cb(err);
                                    }
                                    else{
                                        cb(null);
                                    }
                                });
                            }
                        }
                    });
                }
            ], function (err) {
                if(err){
                    callback(1, 'Error');
                }
                else{
                    callback(0, '');
                }
            });
        }
    }
};

billsLibDao.prototype.pasteBills = function(pData, callback){
    let datas = pData.datas,
        billsLibId = pData.billsLibId, lastOperator = pData.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    async.parallel([
        function (cb) {
            async.each(datas, function(data, ecb){
                let needSet = {};
                for(let i in data){
                    needSet[i] = data[i];
                }
                Bills.update({billsLibId: billsLibId, ID: data.ID, deleted: false}, {$set: needSet}, function (err) {
                    if(err){
                        ecb(err);
                    }
                    else {
                        ecb(null);
                    }
                });
            }, function(err){
                if(err){
                    cb(err);
                }else {
                    cb(null);
                }
            });
        },
        function (cb) {
            StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                if(err){
                    cb(err);
                }
                else{
                    if(result.length > 0 ){
                        let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                        StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                            if(err){
                                cb(err);
                            }
                            else{
                                cb(null);
                            }
                        });
                    }
                }
            });
        }
    ], function (err) {
        if(err){
            callback(1, 'Error');
        }
        else{
            callback(0, '');
        }
    });
}

billsLibDao.prototype.updateRecharge = function(uData, callback){
    let billsLibId = uData.billsLibId;
    let updateId = uData.updateId;
    let data = uData.data, lastOperator = uData.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    async.parallel([
        function (cb) {
            Bills.update({billsLibId: billsLibId, ID: updateId, deleted: false}, {$set: {recharge: data}}, function(err){
                if(err){
                    cb(err);
                }
                else {
                    cb(null);
                }
            });
        },
        function (cb) {
            StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                if(err){
                    cb(err);
                }
                else{
                    if(result.length > 0 ){
                        let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                        StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                            if(err){
                                cb(err);
                            }
                            else{
                                cb(null);
                            }
                        });
                    }
                }
            });
        }
    ], function (err) {
        if(err){
            callback(1, 'Error');
        }
        else{
            callback(0, '');
        }
    });
};

billsLibDao.prototype.pasteRel = function (data, callback) {
    const UpdateExist = 'updateExist', UpdateNew = 'updateNew', CreateExist = 'createExist', CreateNew = 'createNew';
    const Jobs = 'jobs', Items = 'items';
    let field = data.field;
    let updateDatas = data.updateDatas;
    let createDatas = data.createDatas;
    let functions = [], lastOperator = data.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    let parallelFucs = {
        updateLastOpr: function () {
            return function (cb) {
                StdBillsLib.find({billsLibId: data.billsLibId}, function (err, result) {
                    if(err){
                        cb(err);
                    }
                    else{
                        if(result.length > 0 ){
                            let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                            StdBillsLib.update({billsLibId: data.billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    cb(null);
                                }
                            });
                        }
                    }
                });
            }
        },
        updateExJobs: function(data){
            let orgJobId = data.orgJobId, newJobId = data.newJobId,
                nodeId = data.nodeId, billsLibId = data.billsLibId;
            return function (cb){
                if(orgJobId && newJobId && nodeId && billsLibId){
                    Bills.update({billsLibId: billsLibId, ID: nodeId, deleted: false, 'jobs.id': orgJobId}, {$set: {'jobs.$.id': newJobId}}, function(err){
                        if(err){
                            cb(err);
                        }
                        else{
                            cb(null, data)
                        }
                    });
                }
            }
        },
        updateNeJobs: function(data){
            let billsLibId = data.billsLibId, nodeId = data.nodeId, newData = data.newData,
                orgJobId = data.orgJobId, code = data.code;
            return function (cb){
                if(billsLibId && nodeId && newData && orgJobId && code){
                    counter.counterDAO.getIDAfterCount(counter.moduleName.billsLib_jobs, 1, function(err, result){
                        if(err){
                            cb(err);
                        }
                        else {
                            let newJobId = result.sequence_value;
                            let newJobContent = {
                                id: newJobId,
                                billsLibId: billsLibId,
                                code: code,
                                content: newData,
                                deleted: false
                            };
                            JobContent.create(newJobContent, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    Bills.update({billsLibId: billsLibId, ID: nodeId, deleted: false, 'jobs.id': orgJobId}, {$set: {'jobs.$.id': newJobId}}, function(err){
                                        if(err){
                                            cb(err);
                                        }
                                        else{
                                            data.newJobId = newJobId;
                                            cb(null, data);
                                        }
                                    });
                                }
                            });

                        }
                    });
                }
            }

        },
        createExJobs: function(data){
            let billsLibId = data.billsLibId, newData = data.newData,
                nodeId = data.nodeId, jobId = data.jobId, serialNo = data.serialNo;
            return function (cb){
                if(billsLibId && newData && nodeId && jobId){
                    Bills.update({billsLibId: billsLibId, ID: nodeId, deleted: false}, {$addToSet: {jobs: {id: jobId, serialNo: serialNo}}}, function(err){
                        if(err){
                            cb(err);
                        }
                        else {
                            cb(null, data);
                        }
                    });
                }
            }
        },
        createNeJobs: function(data){
            let billsLibId = data.billsLibId, newData = data.newData,
                code = data.code, nodeId = data.nodeId, serialNo = data.serialNo;
            return function (cb){
                if(billsLibId && newData && code){
                    counter.counterDAO.getIDAfterCount(counter.moduleName.billsLib_jobs, 1, function(err, result){
                        if(err){
                            cb(err);
                        }
                        else {
                            let newJobId = result.sequence_value;
                            let newJobContent = {
                                id: newJobId,
                                billsLibId: billsLibId,
                                code: code,
                                content: newData,
                                deleted: false
                            };
                            JobContent.create(newJobContent, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    Bills.update({billsLibId: billsLibId, ID: nodeId, deleted: false}, {$addToSet: {jobs: {id: newJobId, serialNo: serialNo}}}, function(err){
                                        if(err){
                                            cb(err);
                                        }
                                        else {
                                            data.newJobId = newJobId;
                                            cb(null, data);
                                        }
                                    });
                                }
                            });
                        }
                    });
                }
            }
        },
        updateExItems: function(data){
            let orgItemId = data.orgItemId, newItemId = data.newItemId,
                nodeId = data.nodeId, billsLibId = data.billsLibId;
            return function (cb){
                if(orgItemId && newItemId && nodeId && billsLibId){
                    Bills.update({billsLibId: billsLibId, ID: nodeId, deleted: false, 'items.id': orgItemId}, {$set: {'items.$.id': newItemId}}, function(err){
                        if(err){
                            cb(err);
                        }
                        else{
                            cb(null, data);
                        }
                    });
                }
            }
        },
        updateNeItems: function(data){
            let billsLibId = data.billsLibId, nodeId = data.nodeId, newData = data.newData,
                orgItemId = data.orgItemId, code = data.code;
            return function (cb){
                if(billsLibId && nodeId && newData && orgItemId && code){
                    counter.counterDAO.getIDAfterCount(counter.moduleName.billsLib_items, 1, function(err, result){
                        if(err){
                            cb(err);
                        }
                        else {
                            let newItemId = result.sequence_value;
                            let newItemContent = {
                                id: newItemId,
                                billsLibId: billsLibId,
                                code: code,
                                content: newData,
                                deleted: false
                            };
                            ItemCharacter.create(newItemContent, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    Bills.update({billsLibId: billsLibId, ID: nodeId, deleted: false, 'items.id': orgItemId}, {$set: {'items.$.id': newItemId}}, function(err){
                                        if(err){
                                            cb(err);
                                        }
                                        else{
                                            data.newItemId = newItemId;
                                            cb(null, data);
                                        }
                                    });
                                }
                            });
                        }
                    });
                }
            }
        },
        createExItems: function(data){
            let billsLibId = data.billsLibId, newData = data.newData,
                nodeId = data.nodeId, itemId = data.itemId, serialNo = data.serialNo;
            return function (cb){
                if(billsLibId && newData && nodeId && itemId){
                    Bills.update({billsLibId: billsLibId, ID: nodeId, deleted: false}, {$addToSet: {items: {id: itemId, serialNo: serialNo}}}, function(err){
                        if(err){
                            cb(err);
                        }
                        else {
                            cb(null, data);
                        }
                    });
                }
            }
        },
        createNeItems: function(data){
            let billsLibId = data.billsLibId, newData = data.newData,
                code = data.code, nodeId = data.nodeId, serialNo = data.serialNo;
            return function (cb){
                if(billsLibId && newData && code){
                    counter.counterDAO.getIDAfterCount(counter.moduleName.billsLib_items, 1, function(err, result){
                        if(err){
                            cb(err);
                        }
                        else {
                            let newItemId = result.sequence_value;
                            let newItemContent = {
                                id: newItemId,
                                billsLibId: billsLibId,
                                code: code,
                                content: newData,
                                deleted: false
                            };
                            ItemCharacter.create(newItemContent, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    Bills.update({billsLibId: billsLibId, ID: nodeId, deleted: false}, {$addToSet: {items: {id: newItemId, serialNo: serialNo}}}, function(err){
                                        if(err){
                                            cb(err);
                                        }
                                        else {
                                            data.newItemId = newItemId;
                                            cb(null, data);
                                        }
                                    });
                                }
                            });
                        }
                    });
                }
            }
        }
    };
    if(field === Jobs){
        if(updateDatas.length > 0){
            for(let i=0; i<updateDatas.length; i++){
                if(updateDatas[i].type === UpdateExist){
                    functions.push(parallelFucs.updateExJobs(updateDatas[i]));
                }
                if(updateDatas[i].type === UpdateNew){
                    functions.push(parallelFucs.updateNeJobs(updateDatas[i]));
                }
            }
        }
        if(createDatas.length > 0){
            for(let i=0; i<createDatas.length; i++){
                if(createDatas[i].type === CreateExist){
                    functions.push(parallelFucs.createExJobs(createDatas[i]));
                }
                if(createDatas[i].type === CreateNew){
                    functions.push(parallelFucs.createNeJobs(createDatas[i]));
                }
            }
        }
    }
    else {
        if(updateDatas.length > 0){
            for(let i=0; i<updateDatas.length; i++){
                if(updateDatas[i].type === UpdateExist){
                    functions.push(parallelFucs.updateExItems(updateDatas[i]));
                }
                if(updateDatas[i].type === UpdateNew){
                    functions.push(parallelFucs.updateNeItems(updateDatas[i]));
                }
            }
        }
        if(createDatas.length > 0){
            for(let i=0; i<createDatas.length; i++){
                if(createDatas[i].type === CreateExist){
                    functions.push(parallelFucs.createExItems(createDatas[i]));
                }
                if(createDatas[i].type === CreateNew){
                    functions.push(parallelFucs.createNeItems(createDatas[i]));
                }
            }
        }

    }
    functions.push(parallelFucs.updateLastOpr());
    async.parallel(functions, function(err, datas){
        if(err){
            callback(1, 'Error', null);
        }else {
            callback(0, '', datas);
        }
    });
};

billsLibDao.prototype.deleteBills = function(delData, callback){
    let deleteIds = delData.deleteIds;
    let billsLibId = delData.billsLibId;
    let updateNode = delData.updateNode,
        lastOperator = delData.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    if(deleteIds){
        async.parallel([
            function(cb){
                async.each(deleteIds, function(delId, ecb){
                    Bills.remove({billsLibId: billsLibId, ID: delId}, function(err){
                        if(err){
                            ecb(err);
                        }
                        else {
                            ecb(null);
                        }
                    });
                }, function(err){
                    if(err){
                        cb(err);
                    }
                    else {
                        cb(null);
                    }
                });
            },
            function(cb){
                if(updateNode){
                    Bills.update({billsLibId: billsLibId, ID: updateNode.ID, deleted: false}, {$set: {NextSiblingID: updateNode.NextSiblingID}}, function(err){
                        if(err){
                            cb(err);
                        }
                        else {
                            cb(null);
                        }
                    });
                }else {
                    cb(null);
                }
            },
            function (cb) {
                StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                    if(err){
                        cb(err);
                    }
                    else{
                        if(result.length > 0 ){
                            let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                            StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    cb(null);
                                }
                            });
                        }
                    }
                });
            }
        ], function(err){
            if(err){
                callback(1, 'Error');
            }else {
                callback(0, '');
            }
        });
    }
}

billsLibDao.prototype.isUsed = function (data, callback) {
    if(data.field === 'jobs'){
        Bills.findOne({$or: [{deleted: null}, {deleted: false}], 'jobs.id': {$in: data.delIds}}, function (err, result) {
            if(err){
                callback(1, 'error', null);
            }
            else {
                callback(0, '', {isUsed: result ? true : false});
            }
        });
    }
    else {
        Bills.findOne({$or: [{deleted: null}, {deleted: false}], 'items.id': {$in: data.delIds}}, function (err, result) {
            if(err){
                callback(1, 'error', null);
            }
            else {
                callback(0, '', {isUsed: result ? true : false});
            }
        });
    }
};

//--------------JobContent------------------

billsLibDao.prototype.getJobContent = function(gJobData, callback){
    let billsLibId = gJobData.billsLibId;
    JobContent.find({billsLibId: billsLibId, deleted: false}, '-_id').sort({code: 1}).exec(function(err, result){
        if(err){
            console.log(err);
            callback(1, 'Error', null);
        }
        else {
            callback(0, '', result);
        }
    });
}

billsLibDao.prototype.createJobContent = function(cJobData, callback){
    let data = cJobData.data;
    let billsLibId = cJobData.billsLibId;
    let serialNo = cJobData.serialNo;
    let newJobContent, lastOperator = cJobData.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    async.parallel([
        function (cb) {
            counter.counterDAO.getIDAfterCount(counter.moduleName.billsLib_jobs, 1, function(err, result){
                if(err){
                    cb(err)
                }
                else {
                    let newJobId = result.sequence_value;
                    newJobContent = {
                        id: newJobId,
                        billsLibId: billsLibId,
                        code: serialNo,
                        content: data,
                        deleted: false
                    };
                    JobContent.create(newJobContent, function(err){
                        if(err){
                            cb(err);
                        }
                        else{
                            cb(null, newJobId);
                        }
                    });
                }
            });
        },
        function (cb) {
            StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                if(err){
                    cb(err);
                }
                else{
                    if(result.length > 0 ){
                        let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                        StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                            if(err){
                                cb(err);
                            }
                            else{
                                cb(null);
                            }
                        });
                    }
                }
            });
        }
    ], function (err, result) {
        if(err){
            callback(1, 'Error', null);
        }
        else{
            callback(0, '', result[0])
        }
    })
};

billsLibDao.prototype.updateJobContent = function(uJobData, callback){
    let billsLibId = uJobData.billsLibId;
    let field = uJobData.field;
    let updateData = uJobData.data;
    let updateid = uJobData.updateId, lastOperator = uJobData.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss'),
        needSet = {};
    needSet[field] = updateData;
    async.parallel([
        function (cb) {
            JobContent.update({billsLibId: billsLibId, id: updateid}, {$set: needSet}, function (err) {
                if(err){
                    cb(err);
                }
                else{
                    cb(null);
                }
            });
        },
        function (cb) {
            StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                if(err){
                    cb(err);
                }
                else{
                    if(result.length > 0 ){
                        let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                        StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                            if(err){
                                cb(err);
                            }
                            else{
                                cb(null);
                            }
                        });
                    }
                }
            });
        }
    ], function (err) {
        if(err){
            callback(1, 'Error');
        }
        else{
            callback(0, '');
        }
    });
};

billsLibDao.prototype.deleteJobContent = function(data, callback){
    let delIds = data.ids;
    let billsLibId = data.billsLibId, lastOperator = data.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    if(delIds){
        async.parallel([
            function (cb) {
                async.each(delIds, function(id, ecb){
                    JobContent.update({billsLibId: billsLibId, id: id}, {$set: {deleted: true}}, function(err){
                        if(err){
                            ecb(err);
                        }
                        else {
                            ecb(null);
                        }
                    });
                }, function(err){
                    if(err){
                        cb(err);
                    }else {
                        cb(null);
                    }
                });
            },
            function (cb) {
                StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                    if(err){
                        cb(err);
                    }
                    else{
                        if(result.length > 0 ){
                            let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                            StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    cb(null);
                                }
                            });
                        }
                    }
                });
            }
        ], function (err) {
            if(err){
                callback(1, 'Error');
            }
            else{
                callback(0, '');
            }
        })
    }
}

billsLibDao.prototype.pasteJobs = function (data, callback) {
    let pasteDatas = data.pasteDatas;
    let updateDatas = pasteDatas.updateDatas,
        createDatas = pasteDatas.createDatas;
    let functions = [], lastOperator = data.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    let parallelFucs = {
        updateLastOpr: function () {
            return function (cb) {
                StdBillsLib.find({billsLibId: data.billsLibId}, function (err, result) {
                    if(err){
                        cb(err);
                    }
                    else{
                        if(result.length > 0 ){
                            let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                            StdBillsLib.update({billsLibId: data.billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    cb(null);
                                }
                            });
                        }
                    }
                });
            }
        },
        updateFuc: function(data){
            let updateId = data.orgId,
                field = data.field,
                updateData = data.data, billsLibId = data.billsLibId;
            if(field === 'code'){
                return function (cb){
                    JobContent.update({billsLibId: billsLibId, id: updateId}, {$set: {code: updateData}}, function(err){
                        if(err){
                            cb(err);
                        }else {
                            cb(null, data);
                        }
                    });
                };
            }else {
                return function (cb){
                    JobContent.update({billsLibId: billsLibId, id: updateId}, {$set: {content: updateData}}, function(err){
                        if(err){
                            cb(err);
                        }else {
                            cb(null, data);
                        }
                    });
                }
            }
        },
        createFuc: function(data){
            let billsLibId = data.billsLibId,
                code;
            let jobObj;
            if(data.type === 'CreateT'){
                jobObj = {
                    billsLibId: billsLibId,
                    code: data.code,
                    content: data.content,
                    deleted: false
                }
            }
            else{
                if(data.field === 'code'){
                    jobObj = {
                        billsLibId: billsLibId,
                        code: data.data,
                        content: '',
                        deleted: false
                    }
                }
                else {
                    jobObj = {
                        billsLibId: billsLibId,
                        code: data.code,
                        content: data.data,
                        deleted: false
                    }
                }
            }
            return function (cb){
                counter.counterDAO.getIDAfterCount(counter.moduleName.billsLib_jobs, 1, function(err, result){
                    if(err){
                        cb(err);
                    }
                    else {
                        let newJobId = result.sequence_value;
                        jobObj.id = newJobId;
                        data.newJobId = newJobId
                        JobContent.create(jobObj, function(err){
                            if(err){
                                cb(err);
                            }else {
                                cb(null, data);
                            }
                        });
                    }
                });
            }
        }
    };
    if(updateDatas.length > 0){
        for(let i=0; i<updateDatas.length; i++){
            functions.push(parallelFucs.updateFuc(updateDatas[i]));
        }
    }
    if(createDatas.length > 0){
        for(let i =0; i<createDatas.length; i++){
            functions.push(parallelFucs.createFuc(createDatas[i]));
        }
    }
    functions.push(parallelFucs.updateLastOpr());
    async.parallel(functions, function(err, datas){
        if(err){
            callback(1, 'Error', null);
        }else {
            callback(0, '', datas);
        }
    });
};

billsLibDao.prototype.edCreateJob = function(data, callback){
    let billsLibId = data.billsLibId,
        code = data.code,
        billsId = data.billsId,
        content = data.data,
        serialNo = data.serialNo, lastOperator = data.lastOperator, lastOperateDate =  moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    async.waterfall([
        function(cb){
            counter.counterDAO.getIDAfterCount(counter.moduleName.billsLib_jobs, 1, function(err, result){
                if(err){
                    cb(err, null);
                }
                else {
                    let newJobId = result.sequence_value;
                    cb(null, newJobId);
                }
            })
        },
        function(newJobId, cb){
            async.parallel([
                function(pcb){
                    JobContent.create({id: newJobId, billsLibId: billsLibId, code: code, content: content, deleted: false}, function(err){
                        if(err){
                            pcb(err, null);
                        }
                        else {
                            pcb(null, newJobId);
                        }
                    });
                },
                function(pcb){
                    Bills.update({billsLibId: billsLibId, ID: billsId, deleted: false}, {$addToSet: {jobs: {id: newJobId, serialNo: serialNo}}}, function(err){
                        if(err){
                            pcb(err, null);
                        }
                        else {
                            pcb(null, newJobId);
                        }
                    });
                },
                function (pcb) {
                    StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                        if(err){
                            pcb(err);
                        }
                        else{
                            if(result.length > 0 ){
                                let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                                StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                    if(err){
                                        pcb(err);
                                    }
                                    else{
                                        pcb(null);
                                    }
                                });
                            }
                        }
                    });
                }
            ], function(err, result){
                if(err){
                    cb(err, null);
                }
                else {
                    cb(null, result[0]);
                }
            });
        }
    ], function(err, reslut){
        if(err){
            callback(1, "Error", null);
        }
        else {
            callback(0, '', reslut);
        }
    });
};

billsLibDao.prototype.edUpdateJob = function(data, callback){
    let billsLibId = data.billsLibId,
        billsId = data.billsId,
        content = data.content,
        code = data.code,
        orgJobId = data.orgJobId, lastOperator = data.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    async.waterfall([
        function(cb){
            counter.counterDAO.getIDAfterCount(counter.moduleName.billsLib_jobs, 1, function(err, result){
                if(err){
                    cb(err, null);
                }
                else {
                    let newJobId = result.sequence_value;
                    cb(null, newJobId);
                }
            })
        },
        function(newJobId, cb){
            async.parallel([
                function(pcb){
                    JobContent.create({id: newJobId, billsLibId: billsLibId, code: code, content: content, deleted: false}, function(err){
                        if(err){
                            pcb(err, null);
                        }
                        else {
                            pcb(null, newJobId);
                        }
                    });
                },
                function(pcb){
                    Bills.update({billsLibId: billsLibId, ID: billsId, deleted: false, 'jobs.id': orgJobId}, {$set: {'jobs.$.id': newJobId}}, function(err){
                        if(err){
                            pcb(err, null);
                        }
                        else {
                            pcb(null, newJobId);
                        }
                    });
                },
                function (pcb) {
                    StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                        if(err){
                            pcb(err);
                        }
                        else{
                            if(result.length > 0 ){
                                let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                                StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                    if(err){
                                        pcb(err);
                                    }
                                    else{
                                        pcb(null);
                                    }
                                });
                            }
                        }
                    });
                }
            ], function(err, result){
                if(err){
                    cb(err, null);
                }
                else {
                    cb(null, result[0]);
                }
            });
        }
    ], function(err, reslut){
        if(err){
            callback(1, "Error", null);
        }
        else {
            callback(0, '', reslut);
        }
    });
};
//----------------------ItemCharacter---------------------
billsLibDao.prototype.getItemCharacter = function(gdata, callback){
    let billsLibId = gdata.billsLibId;
    ItemCharacter.find({billsLibId: billsLibId, deleted: false}, '-_id').sort({code: 1}).exec(function(err, result){
        if(err){
            callback(1, 'Error', null);
        }
        else {
            callback(0, '', result);
        }
    });
}

billsLibDao.prototype.createItemCharacter = function(cItemData, callback){
    let data = cItemData.data;
    let billsLibId = cItemData.billsLibId;
    let code = cItemData.code;
    let newItemCharacter, lastOperator = cItemData.lastOperator, lastOperateDate =  moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    async.parallel([
        function (cb) {
            counter.counterDAO.getIDAfterCount(counter.moduleName.billsLib_items, 1, function (err, result) {
                if(err){
                    cb(err);
                }
                else{
                    let newItemId = result.sequence_value;
                    newItemCharacter = {
                        id: newItemId,
                        billsLibId: billsLibId,
                        content: data,
                        code: code,
                        deleted: false
                    }
                    ItemCharacter.create(newItemCharacter, function(err){
                        if(err){
                            cb(err);
                        }
                        else{
                            cb(null, newItemId);
                        }
                    });
                }
            });
        },
        function (cb) {
            StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                if(err){
                    cb(err);
                }
                else{
                    if(result.length > 0 ){
                        let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                        StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                            if(err){
                                cb(err);
                            }
                            else{
                                cb(null);
                            }
                        });
                    }
                }
            });
        }
    ], function (err, result) {
        if(err){
            callback(1, 'Error', null);
        }
        else{
            callback(0, '', result[0]);
        }
    });
};

billsLibDao.prototype.updateItemCharacter = function(uItemData, callback){
    let field = uItemData.field;
    let updateData = uItemData.data;
    let id = uItemData.updateId;
    let billsLibId = uItemData.billsLibId, needSet = {},
        lastOperator = uItemData.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    needSet[field] = updateData;
    async.parallel([
        function (cb) {
            ItemCharacter.update({billsLibId: billsLibId, id: id}, {$set: needSet}, function(err){
                if(err){
                    cb(err);
                }
                else{
                    cb(null);
                }
            });
        },
        function (cb) {
            StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                if(err){
                    cb(err);
                }
                else{
                    if(result.length > 0 ){
                        let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                        StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                            if(err){
                                cb(err);
                            }
                            else{
                                cb(null);
                            }
                        });
                    }
                }
            });
        }
    ], function (err) {
        if(err){
            callback(1, 'Error');
        }
        else{
            callback(0, '');
        }
    });
};

billsLibDao.prototype.updateValue = function(uData, callback){
    let type = uData.type;
    let updateId = uData.updateId;
    let data = uData.data;
    let deleteCodes = uData.deleteCodes;
    let billsLibId = uData.billsLibId, lastOperator = uData.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    if(type === 'create'){
        async.parallel([
            function (cb) {
                ItemCharacter.update({billsLibId: billsLibId, id: updateId}, {$addToSet: {itemValue: data}}, function(err){
                    if(err){
                        cb(err);
                    }
                    else{
                        cb(null);
                    }
                });
            },
            function (cb) {
                StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                    if(err){
                        cb(err);
                    }
                    else{
                        if(result.length > 0 ){
                            let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                            StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    cb(null);
                                }
                            });
                        }
                    }
                });
            }
        ], function (err) {
            if(err){
                callback(1, 'Error');
            }
            else{
                callback(0, '');
            }
        })
    }
    else if(type === 'update'){
        if(data.field === 'code'){
            async.parallel([
                function (cb) {
                    ItemCharacter.update({billsLibId: billsLibId, id: updateId,  'itemValue.code': data.code}, {$set: {'itemValue.$.code': data.newData}}, function(err){
                        if(err){
                            cb(err);
                        }
                        else{
                            cb(null);
                        }
                    });
                },
                function (cb) {
                    StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                        if(err){
                            cb(err);
                        }
                        else{
                            if(result.length > 0 ){
                                let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                                StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                    if(err){
                                        cb(err);
                                    }
                                    else{
                                        cb(null);
                                    }
                                });
                            }
                        }
                    });
                }
            ], function (err) {
                if(err){
                    callback(1, 'Error');
                }
                else{
                    callback(0, '');
                }
            });
        }
        else {
            async.parallel([
                function (cb) {
                    ItemCharacter.update({billsLibId: billsLibId, id: updateId,  'itemValue.code': data.code}, {$set: {'itemValue.$.value': data.newData}}, function(err,result){
                        if(err){
                            cb(err);
                        }
                        else{
                            cb(null);
                        }
                    });
                },
                function (cb) {
                    StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                        if(err){
                            cb(err);
                        }
                        else{
                            if(result.length > 0 ){
                                let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                                StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                    if(err){
                                        cb(err);
                                    }
                                    else{
                                        cb(null);
                                    }
                                });
                            }
                        }
                    });
                }
            ], function (err) {
                if(err){
                    callback(1, 'Error');
                }
                else{
                    callback(0, '');
                }
            })
        }
    }
    //delete
    else {
        if(deleteCodes.length > 0){
            async.parallel([
                function (cb) {
                    async.each(deleteCodes, function(code, ecb){
                        ItemCharacter.update({billsLibId: billsLibId, id: updateId}, {$pull: {itemValue: {code: code}}}, function(err){
                            if(err){
                                ecb(err);
                            }else {
                                ecb(null);
                            }
                        });
                    }, function(err){
                        if(err){
                            cb(err);
                        }else {
                            cb(null);
                        }
                    });
                },
                function (cb) {
                    StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                        if(err){
                            cb(err);
                        }
                        else{
                            if(result.length > 0 ){
                                let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                                StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                    if(err){
                                        cb(err);
                                    }
                                    else{
                                        cb(null);
                                    }
                                });
                            }
                        }
                    });
                }
            ], function (err) {
                if(err){
                    callback(1, 'Error');
                }
                else{
                    callback(0, '');
                }
            });
        }
    }
};

billsLibDao.prototype.deleteItemCharacter = function(data, callback){
    let delIds = data.ids;
    let billsLibId = data.billsLibId, lastOperator = data.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    if(delIds){
        async.parallel([
            function (cb) {
                async.each(delIds, function(id, ecb){
                    ItemCharacter.update({billsLibId: billsLibId, id: id}, {$set: {deleted: true}}, function(err){
                        if(err){
                            ecb(err);
                        }else {
                            ecb(null);
                        }
                    }, function(err){
                        if(err){
                            cb(err);
                        }else {
                            cb(null);
                        }
                    });
                });
            },
            function (cb) {
                StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                    if(err){
                        cb(err);
                    }
                    else{
                        if(result.length > 0 ){
                            let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                            StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    cb(null);
                                }
                            });
                        }
                    }
                });
            }
        ], function (err) {
            if(err){
                callback(1, 'Error');
            }
            else{
                callback(0, '');
            }
        });
    }
};


billsLibDao.prototype.pasteItems = function (data, callback) {
    let pasteDatas = data.pasteDatas;
    let updateDatas = pasteDatas.updateDatas,
        createDatas = pasteDatas.createDatas;
    let functions = [], lastOperator = data.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    let parallelFucs = {
        updateLastOpr: function () {
            return function (cb) {
                StdBillsLib.find({billsLibId: data.billsLibId}, function (err, result) {
                    if(err){
                        cb(err);
                    }
                    else{
                        if(result.length > 0 ){
                            let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                            StdBillsLib.update({billsLibId: data.billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    cb(null);
                                }
                            });
                        }
                    }
                });
            }
        },
        updateFuc: function(data){
            let updateId = data.orgId,
                field = data.field,
                updateData = data.data, billsLibId = data.billsLibId;
            if(field === 'code'){
                return function (cb){
                    ItemCharacter.update({billsLibId: billsLibId, id: updateId}, {$set: {code: updateData}}, function(err){
                        if(err){
                            cb(err);
                        }else {
                            cb(null, data);
                        }
                    });
                };
            }
            else{
                return function(cb){
                    ItemCharacter.update({billsLibId: billsLibId, id: updateId}, {$set: {content: updateData}}, function(err){
                        if(err){
                            cb(err);
                        }else {
                            cb(null, data);
                        }
                    });
                }
            }
        },
        createFuc: function(data){
            let billsLibId = data.billsLibId,
                code;
            let itemObj;
            if(data.type === 'CreateT'){
                itemObj = {
                    billsLibId: billsLibId,
                    code: data.code,
                    content: data.content,
                    deleted: false
                }
            }
            else{
                if(data.field === 'code'){
                    itemObj = {
                        billsLibId: billsLibId,
                        code: data.data,
                        content: '',
                        deleted: false
                    }
                }
                else {
                    itemObj = {
                        billsLibId: billsLibId,
                        code: data.code,
                        content: data.data,
                        deleted: false
                    }
                }
            }
            return function (cb){
                counter.counterDAO.getIDAfterCount(counter.moduleName.billsLib_items, 1, function(err, result){
                    if(err){
                        cb(err);
                    }
                    else{
                        let newItemId = result.sequence_value;
                        itemObj.id = newItemId;
                        data.newItemId = newItemId;
                        ItemCharacter.create(itemObj, function(err){
                            if(err){
                                cb(err);
                            }else {
                                cb(null, data);
                            }
                        });
                    }
                });
            }
        }
    };
    if(updateDatas.length > 0){
        for(let i=0; i<updateDatas.length; i++){
            functions.push(parallelFucs.updateFuc(updateDatas[i]));
        }
    }
    if(createDatas.length > 0){
        for(let i =0; i<createDatas.length; i++){
            functions.push(parallelFucs.createFuc(createDatas[i]));
        }
    }
    functions.push(parallelFucs.updateLastOpr());
    async.parallel(functions, function(err, datas){
        if(err){
            callback(1, 'Error', null);
        }else {
            callback(0, '', datas);
        }
    });

};

billsLibDao.prototype.pasteValues = function(data, callback) {
    let pasteDatas = data.pasteDatas;
    let updateDatas = pasteDatas.updateDatas, createDatas = pasteDatas.createDatas;
    let functions = [], lastOperator = data.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    let parallelFucs = {
        updateLastOpr: function () {
            return function (cb) {
                StdBillsLib.find({billsLibId: data.billsLibId}, function (err, result) {
                    if(err){
                        cb(err);
                    }
                    else{
                        if(result.length > 0 ){
                            let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                            StdBillsLib.update({billsLibId: data.billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                if(err){
                                    cb(err);
                                }
                                else{
                                    cb(null);
                                }
                            });
                        }
                    }
                });
            }
        },
        updateFuc: function(data){
            let billsLibId = data.billsLibId, itemId = data.itemId, orgCode = data.orgId;
            if (data.type === 'UpdateT') {
                let valueObj = {code: data.code, value: data.content};
                return function (cb){
                    ItemCharacter.update({billsLibId: billsLibId, id: itemId}, {$pull: {itemValue: {code: orgCode}}}, function (err) {
                        if (err) {
                            cb(err);
                        }
                        else {
                            ItemCharacter.update({billsLibId: billsLibId, id: itemId}, {$addToSet: {itemValue: valueObj}}, function (err) {
                                if (err) {
                                    cb(err);
                                }
                                else {
                                    cb(null, data);
                                }
                            });
                        }
                    });
                };
            }
            else if (data.type === 'Update' && data.field === 'code') {
                return function (cb) {
                    ItemCharacter.update({billsLibId: billsLibId, id: itemId, 'itemValue.code': orgCode},
                        {$set: {'itemValue.$.code': data.data}}, function (err) {
                            if(err){
                                cb(err);
                            }
                            else {
                                cb(null, data);
                            }
                        });
                }
            }
            else if(data.type === 'Update' && data.field === 'value') {
                return function (cb){
                    ItemCharacter.update({billsLibId: billsLibId, id: itemId, 'itemValue.code': orgCode}, {$set: {'itemValue.$.value': data.data}}, function(err){
                        if(err){
                            cb(err);
                        }
                        else {
                            cb(null, data);
                        }
                    });
                };
            }
        },

        createFuc: function(data){
            let valueObj;
            if(data.type === 'CreateT'){
                valueObj = {code: data.code, value: data.content};
                return function (cb){
                    ItemCharacter.update({billsLibId: data.billsLibId, id: data.itemId}, {$addToSet: {itemValue: valueObj}}, function (err) {
                        if (err) {
                            cb(err);
                        }
                        else {
                            cb(null, data);
                        }
                    });
                };
            }
            else if(data.type === 'Create' && data.field === 'code'){
                valueObj = {code: data.data, value: ''};
                return function (cb){
                    ItemCharacter.update({billsLibId: data.billsLibId, id: data.itemId}, {$addToSet: {itemValue: valueObj}}, function (err) {
                        if (err) {
                            cb(err);
                        }
                        else {
                            cb(null, data);
                        }
                    });
                };
            }
            else {
                valueObj = {code: data.code, value: data.data};
                return function(cb){
                    ItemCharacter.update({billsLibId: data.billsLibId, id: data.itemId}, {$addToSet: {itemValue: valueObj}}, function (err) {
                        if (err) {
                            cb(err);
                        }
                        else {
                            cb(null, data);
                        }
                    });
                };
            }
        }
    };
    if (updateDatas.length > 0) {
        for (let i = 0; i < updateDatas.length; i++) {
            functions.push(parallelFucs.updateFuc(updateDatas[i]));
        }
    }
    if(createDatas.length >0){
        for(let i=0; i< createDatas.length; i++){
            functions.push(parallelFucs.createFuc(createDatas[i]));
        }
    }
    functions.push(parallelFucs.updateLastOpr());
    async.parallel(functions, function(err, datas){
        if(err){
            callback(1, 'Error', null);
        }
        else {
            callback(0, '', datas);
        }
    });
};

billsLibDao.prototype.edCreateItem = function(data, callback){
    let billsLibId = data.billsLibId,
        code = data.code,
        billsId = data.billsId,
        content = data.data,
        serialNo = data.serialNo, lastOperator = data.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    async.waterfall([
        function(cb){
            counter.counterDAO.getIDAfterCount(counter.moduleName.billsLib_items, 1, function(err, result){
                if(err){
                    cb(err, null);
                }
                else {
                    let newItemId = result.sequence_value;
                    cb(null, newItemId);
                }
            })
        },
        function(newItemId, cb){
            async.parallel([
                function(pcb){
                    ItemCharacter.create({id: newItemId, billsLibId: billsLibId, code: code, content: content, deleted: false}, function(err){
                        if(err){
                            pcb(err, null);
                        }
                        else {
                            pcb(null, newItemId);
                        }
                    });
                },
                function(pcb){
                    Bills.update({billsLibId: billsLibId, ID: billsId, deleted: false}, {$addToSet: {items: {id: newItemId, serialNo: serialNo}}}, function(err){
                        if(err){
                            pcb(err, null);
                        }
                        else {
                            pcb(null, newItemId);
                        }
                    });
                },
                function (pcb) {
                    StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                        if(err){
                            pcb(err);
                        }
                        else{
                            if(result.length > 0 ){
                                let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                                StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                    if(err){
                                        pcb(err);
                                    }
                                    else{
                                        pcb(null);
                                    }
                                });
                            }
                        }
                    });
                }
            ], function(err, result){
                if(err){
                    cb(err, null);
                }
                else {
                    cb(null, result[0]);
                }
            });
        }
    ], function(err, reslut){
        if(err){
            callback(1, "Error", null);
        }
        else {
            callback(0, '', reslut);
        }
    });
};

billsLibDao.prototype.edUpdateItem = function(data, callback){
    let billsLibId = data.billsLibId,
        billsId = data.billsId,
        content = data.content,
        code = data.code,
        orgItemId = data.orgItemId, lastOperator = data.lastOperator, lastOperateDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
    async.waterfall([
        function(cb){
            counter.counterDAO.getIDAfterCount(counter.moduleName.billsLib_items, 1, function(err, result){
                if(err){
                    cb(err, null);
                }
                else {
                    let newItemId = result.sequence_value;
                    cb(null, newItemId);
                }
            })
        },
        function(newItemId, cb){
            async.parallel([
                function(pcb){
                    ItemCharacter.create({id: newItemId, billsLibId: billsLibId, code: code, content: content, deleted: false}, function(err){
                        if(err){
                            pcb(err, null);
                        }
                        else {
                            pcb(null, newItemId);
                        }
                    });
                },
                function(pcb){
                    Bills.update({billsLibId: billsLibId, ID: billsId, deleted: false, 'items.id': orgItemId}, {$set: {'items.$.id': newItemId}}, function(err){
                        if(err){
                            pcb(err, null);
                        }
                        else {
                            pcb(null, newItemId);
                        }
                    });
                },
                function (pcb) {
                    StdBillsLib.find({billsLibId: billsLibId}, function (err, result) {
                        if(err){
                            pcb(err);
                        }
                        else{
                            if(result.length > 0 ){
                                let newRecentOpr = getNewOprArr(result[0].recentOpr, lastOperator, lastOperateDate);
                                StdBillsLib.update({billsLibId: billsLibId}, {$set: {recentOpr: newRecentOpr}}, function(err){
                                    if(err){
                                        pcb(err);
                                    }
                                    else{
                                        pcb(null);
                                    }
                                });
                            }
                        }
                    });
                }
            ], function(err, result){
                if(err){
                    cb(err, null);
                }
                else {
                    cb(null, result[0]);
                }
            });
        }
    ], function(err, reslut){
        if(err){
            callback(1, "Error", null);
        }
        else {
            callback(0, '', reslut);
        }
    });
};

billsLibDao.prototype.hasData = async function (billsLibId) {
    let bills = await Bills.findOne({billsLibId: billsLibId});
    if (bills) {
        return true;
    }
    return false;
};

billsLibDao.prototype.importBills = async function (billsLibId, sheetData) {
    //实际节点深度数组,下标等于深度，元素为该为上一个该深度清单数据, 数组长度-1等于最大深度
    let depthArr = [];
    const deepest = 10000;
    //深度的值，并不是真正的深度，只是三种类型节点(顶节点、中间节点、底节点)的深度值，节点根据深度值与深度数组最大深度的比较，对深度数组进行更新，
    //并且确定父子上下关系
    function getDepthValue(code){
        let midReg = /\.{1,}/g;
        //root
        if (code.length === 1) {
            return 0;
        }
        if (midReg.test(code)) {
            return code.match(midReg).length;
        }
        return deepest;
    }
    let billsDatas = [],
        jobMapping = {},
        itemMapping = {};
    function getDivideData(data){
        let rst = [];
        let divideReg = /[\n,\r]/g,
            prefixReg = /\d+\.{1}/g; //前缀 1. 2.
        let divideArr = data.split(divideReg);
        for (let dData of divideArr) {
            if (dData !== '') {
                rst.push(dData.replace(prefixReg, ''));
            }
        }
        return rst;
    }
    //设置总工作内容/项目特征映射
    function setBillsSubMapping(subData, mapping){
        for (let data of subData) {
            if (!mapping[data]) {
                let code = Object.keys(mapping).length + 1;
                mapping[data] = {billsLibId: billsLibId, code: code, content: data, deleted: false};
            }
        }
    }
    let row = -1;
    //从表格中获取清单数据
    for (let rowData of sheetData) {
        row++;
        if (row === 0) {
            continue;
        }
        let bills = {
            deleted: false,
            billsLibId: billsLibId,
            parent: null,
            ID: uuid.v1(),
            ParentID: -1,
            NextSiblingID: -1,
            code: typeof rowData[0] !== 'undefined' ? rowData[0] : '',
            name: typeof rowData[1] !== 'undefined' ? rowData[1] : '',
            unit: typeof rowData[2] !== 'undefined' ? rowData[2] : '',
            ruleText: typeof rowData[3] !== 'undefined' ? rowData[3] : '',
            recharge: '',
        };
        let jobData = typeof rowData[4] !== 'undefined' ? getDivideData(rowData[4]) : [],
            itemData = typeof rowData[5] !== 'undefined' ? getDivideData(rowData[5]) :[];
        bills.jobData = jobData;
        bills.itemData = itemData;
        setBillsSubMapping(jobData, jobMapping);
        setBillsSubMapping(itemData, itemMapping);
        billsDatas.push(bills);
    }
    //设置工作内容、项目特征id
    async function setSubId(moduleName, subMapping) {
        let idx = 0,
            subCount = Object.keys(subMapping).length;
        let subCounter = await counter.counterDAO.getIDAfterCount(moduleName, subCount);
        for (let mapping in subMapping) {
            let id = subCounter.sequence_value - (subCount - 1) + idx;
            let sub = subMapping[mapping];
            sub.id = id;
            idx++;
        }
    }
    await setSubId(counter.moduleName.billsLib_jobs, jobMapping);
    await setSubId(counter.moduleName.billsLib_items, itemMapping);
    //获得清单、工作内容/项目特征关联数组
    //subDatas为清单工作内容/项目特征的字符串数组 eg: ['场内运输', '开挖'];
    //mapping为总工作内容/项目特征字符串映射 eg: {'场内运输': {content: '场内运输', id: 1, code: 1, deleted: false}}
    function getBillsSubRef(subDatas, mapping) {
        let rst = [];
        for (let i = 0; i < subDatas.length; i++) {
            let data = subDatas[i];
            if (mapping[data]) {
                rst.push({id: mapping[data].id, serialNo: i + 1});
            }
        }
        return rst;
    }
    //设置完整的清单数据(树结构、工作内容/项目特征)
    for (let i = 0; i < billsDatas.length; i++) {
        let bills = billsDatas[i],
            preBills = billsDatas[i - 1];
        //树结构相关设置
        let maxDepth = depthArr.length - 1;
        let depthV = getDepthValue(bills.code);
        //前节点为最底层节点，且这个节点为最底层节点，则深度相同
        let preIsDeepest = preBills && getDepthValue(preBills.code) === deepest ? true : false;
        if (preIsDeepest && depthV === deepest) {
            depthV = maxDepth;
        }
        if (depthV > maxDepth) {
            let parent = depthArr[depthArr.length - 1];
            if (parent) {
                bills.ParentID = parent.ID;
                bills.parent = parent;
            }
            depthArr.push(bills);
        } else {
            let parent = depthArr[depthV -1];
            if (parent) {
                bills.ParentID = parent.ID;
                bills.parent = parent;
            }
            depthArr[depthV].NextSiblingID = bills.ID;
            depthArr.splice(depthV, 1);
            if (depthV < maxDepth) { //切割
                depthArr = depthArr.slice(0, depthV);
            }
            depthArr.push(bills);
        }
        //工作内容项目特征
        bills.jobs = getBillsSubRef(bills.jobData, jobMapping);
        bills.items = getBillsSubRef(bills.itemData, itemMapping);
    }
    function getSectionInfo(bills){
        let parentIDs = [];
        let sectionInfo = {first: null, second: null, third: null};
        getParent(bills);
        if(parentIDs[parentIDs.length - 1]){
            sectionInfo.first = parentIDs[parentIDs.length - 1];
        }
        if(parentIDs[parentIDs.length - 2]){
            sectionInfo.second = parentIDs[parentIDs.length - 2];
        }
        if(parentIDs[parentIDs.length - 3]){
            sectionInfo.third = parentIDs[parentIDs.length - 3];
        }
        return sectionInfo;
        function getParent(bills){
            if(bills.parent){
                parentIDs.push(bills.parent.ID);
                getParent(bills.parent);
            }
        }
    }
    //设置sectionInfo
    for (let bills of billsDatas) {
        bills.sectionInfo = getSectionInfo(bills);
    }
    //插入清单
    let bulks = [];
    for (let bills of billsDatas) {
        delete bills.parent;
        bulks.push({insertOne: {document: bills}});
    }
    if (bulks.length > 0) {
        await Bills.bulkWrite(bulks);
    }
    //清除工作内容、项目特征
    await JobContent.remove({billsLibId: billsLibId});
    await ItemCharacter.remove({billsLibId: billsLibId});
    function getSubBulks(subMapping) {
        let rst = [];
        for (let mapping in subMapping) {
            let sub = subMapping[mapping];
            rst.push({insertOne: {document: sub}});
        }
        return rst;
    }
    //插入工作内容
    let jobBulks = getSubBulks(jobMapping);
    if (jobBulks.length > 0) {
        await JobContent.bulkWrite(jobBulks);
    }
    //插入项目特征
    let itemBulks = getSubBulks(itemMapping);
    if (itemBulks.length > 0) {
        await ItemCharacter.bulkWrite(itemBulks);
    }
};


module.exports = new billsLibDao();
