| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660 | /** * Created by Mai on 2017/1/18. */import UnitPriceFileModel from "../../glj/models/unit_price_file_model";import moment from 'moment';import CompilationModel from "../../users/models/compilation_model";let mongoose = require('mongoose');let ProjectsData = require('../models/project_model').project;let labourCoe = require('../../main/facade/labour_coe_facade');let projType = require('../models/project_model').projType;let fileType = require('../models/project_model').fileType;const engineering = require("../../common/const/engineering");let EngineeringLibModel = require("../../users/models/engineering_lib_model");let fee_rate_facade = require("../../fee_rates/facade/fee_rates_facade");let billsModel = require('../../main/models/bills').model;let rationsModel = require('../../main/models/ration').model;let projectModel = mongoose.model('projects');let unitPriceFileModel = mongoose.model('unit_price_file');let feeRateFileModel = mongoose.model('fee_rate_file');let asyncTool = require('async');let pm_facade = require('../facade/pm_facade');const userModel = mongoose.model('user');let config = require("../../../config/config.js");const optionModel = mongoose.model('options');const stdBillsGuidanceLibModel = mongoose.model('std_billsGuidance_lib');const fs = require('fs');const _ = require('lodash');import SectionTreeDao from '../../complementary_ration_lib/models/sectionTreeModel';let sectionTreeDao = new SectionTreeDao();//统一回调函数let callback = function(req, res, err, message, data){    res.json({error: err, message: message, data: data});};module.exports = {    checkRight: function (req, res) {        if(typeof req.body.data === 'object'){            req.body.data = JSON.stringify(req.body.data);        }        let data = JSON.parse(req.body.data);        if (data.user_id) {            return data.user_id === req.session.sessionUser.id;        } else {            return false;        }    },    checkProjectRight: function (userId, projectId, callback) {        ProjectsData.getProject(projectId).then(async function (result) {            /**             * result._doc.userID(Number): MongoDB             * userId(String): Session.userID             */            let shareInfo = null;            //判断是否是打开分享的项目,分享项目shareInfo不为null            if(userId !== result.userID){                shareInfo = await pm_facade.getShareInfo(userId, result);            }            if ((userId === result.userID || shareInfo) && result._doc.projType === projType.tender) {                callback(true, result, shareInfo);            } else {                callback(false);            }        }).catch(function (err) {            callback(false);        });    },    getProjects: async function(req, res){         await ProjectsData.getUserProjects(req.session.sessionUser.id, req.session.sessionCompilation._id, function(err, message, projects){            if (projects) {                callback(req, res, err, message, projects);            } else {                callback(req, res, err, message, null);            }        });    },    updateProjects: async function (req, res) {        let data = JSON.parse(req.body.data);        await ProjectsData.updateUserProjects(req.session.sessionUser.id, req.session.sessionCompilation._id, req.session.sessionCompilation.name, data.updateData, function (err, message, data) {            if (err === 0) {                callback(req, res, err, message, data);            } else {                callback(req, res, err, message, null);            }        });    },    // CSL, 2017-12-14 该方法用于项目属性:提交保存混合型数据,这些数据来自不同的表,包括projects.property、ration、bills、labour_coes.    updateMixDatas: async function(req, res){        let datas = JSON.parse(req.body.data).mixDataArr;        let functions = [];        function updateFunc(model, cod, doc) {            return function (cb) {                model.update(cod, doc, cb);            }        };        function updateLC(){            return function (cb) {                datas.labourCoes.updateData.projectID = datas.projectID;                labourCoe.save(datas.labourCoes.updateData, cb);            }        };        // 项目属性        if (Object.keys(datas.properties).length > 0){            //基本信息特殊处理,更新建设项目            if(datas.properties['property.basicInformation']){                let constructionProject = await pm_facade.getConstructionProject(datas.projectID);                if(constructionProject){                    functions.push(updateFunc(projectModel, {ID: constructionProject.ID}, {'property.basicInformation': datas.properties['property.basicInformation']}));                }                delete datas.properties['property.basicInformation'];            }            functions.push(updateFunc(projectModel, {ID: datas.projectID}, datas.properties));        };        //选项        if(datas.options && datas.options.updateData){            functions.push(updateFunc(optionModel, {user_id: req.session.sessionUser.id, compilation_id: req.session.sessionCompilation._id}, {'options.GENERALOPTS': datas.options.updateData}));        }        // 人工系数        if (datas.labourCoes&&datas.labourCoes.updateData){            functions.push(updateLC());        };        // 清单:每文档doc只存储一条清单,每条清单都必须定位一次文档,无法合并处理        if (datas.bills.length > 0){            for (let bill of datas.bills){                functions.push(updateFunc(billsModel, {projectID: datas.projectID, ID: bill.ID, deleteInfo: null}, bill));            };        };        // 定额:每文档doc只存储一条定额,每条定额都必须定位一次文档,无法合并处理        if (datas.rations.length > 0){            for (let ration of datas.rations){                functions.push(updateFunc(rationsModel, {projectID: datas.projectID, ID: ration.ID, deleteInfo: null}, ration));            };        };        asyncTool.parallel(functions, function(err, result){            {                if (!err) {                    res.json({error: 0, message: err, data: result});                } else {                    res.json({error: 1, message: err, data: null});                }            }        });    },    updateFiles: async function(req, res){        let data = JSON.parse(req.body.data);        let updateDatas = data.updateDatas;        await ProjectsData.udpateUserFiles(req.session.sessionUser.id, updateDatas, function (err, message, data) {            callback(req, res, err, message, data);        });    },    defaultSettings: async function(req, res){        try{            let data = JSON.parse(req.body.data);            let projectID = data.projectID;            let defaultSettingSc = await ProjectsData.defaultSettings(req.session.sessionUser.id, req.session.sessionCompilation._id, projectID);            if(!defaultSettingSc){                throw '恢复失败';            }            res.json({error: 0, message: '恢复成功', data: null});        }        catch(error){            console.log(error);            res.json({error: 1, message: error, data: null});        }    }, /*   copyProjects: function (req, res) {        let data = JSON.parse(req.body.data);        ProjectsData.copyUserProjects(req.session.sessionUser.id, req.session.sessionCompilation._id, data.updateData, function (err, message, data) {            if (err === 0) {                callback(req, res, err, message, data);            } else {                callback(req, res, err, message, null);            }        });    },*/    rename: function (req, res) {        let data = JSON.parse(req.body.data);        ProjectsData.rename(req.session.sessionUser.id, req.session.sessionCompilation._id, data, function (err, message) {            callback(req, res, err, message, null);        });    },    getProject: function(req, res){        let data = JSON.parse(req.body.data);        let projectID = data.proj_id;        ProjectsData.getUserProject(req.session.sessionUser.id, data.proj_id, async function(err, message, data){            if (err === 0) {                let engineeringLibModel = new EngineeringLibModel();                let engineeringInfo = data !== null && data.property.engineering_id !== undefined ?                    await engineeringLibModel.getEngineering(data.property.engineering_id) : null;                let strData = JSON.stringify(data);                let projInfo = JSON.parse(strData);                if (engineeringInfo !== null) {                    if(engineeringInfo.billsGuidance_lib){                        for(let billsGuidanceLib of engineeringInfo.billsGuidance_lib){                            let stdBillsGuidanceLib = await stdBillsGuidanceLibModel.findOne({ID: billsGuidanceLib.id});                            if(stdBillsGuidanceLib){                                billsGuidanceLib.type = stdBillsGuidanceLib.type ? stdBillsGuidanceLib.type : 1;                            }                        }                    }                    projInfo.engineeringInfo = engineeringInfo;                }                //读取建设项目的基本信息                let basicInfo = await ProjectsData.getBasicInfo(projectID);                if(basicInfo !== null){                    projInfo.property.basicInformation = basicInfo;                }                //获取单位工程完整目录结构                let fullPath = await pm_facade.getFullPath(projectID);                projInfo.fullPath = fullPath;                callback(req, res, err, message, projInfo);            } else {                callback(req, res, err, message, null);            }        });    },    beforeOpenProject: function (req, res) {        let data = JSON.parse(req.body.data);        ProjectsData.beforeOpenProject(req.session.sessionUser.id, data.proj_id, data.updateData, function (err, message, data) {            callback(req, res, err, message, data);        });    },    getNewProjectID: function (req, res) {        let data = JSON.parse(req.body.data);        ProjectsData.getNewProjectID(data.count, function (err, message, data) {            callback(req, res, err, message, data);        });    },    // 项目管理首页    index: async function(request, response) {        // 获取编办信息        let sessionCompilation = request.session.sessionCompilation;        if (sessionCompilation === undefined ||sessionCompilation ===null) {            return response.redirect('/logout');        }        let compilationModel = new CompilationModel();        //更新编办信息        let compilationData = await compilationModel.getCompilationById(sessionCompilation._id);        request.session.sessionCompilation = compilationData;        sessionCompilation = request.session.sessionCompilation;        //更新用户的使用过的费用定额列表        let userData = await userModel.findOne({_id: mongoose.Types.ObjectId(request.session.sessionUser.id)}, '-_id used_list');        //是否第一次进入该费用定额        let isFirst = false;        if (userData) {            isFirst = !_.find(userData.used_list, function (o) {                return o.compilationId === compilationData._id.toString();            });;        }        // 清单计价        let billValuation = sessionCompilation.bill_valuation !== undefined ?            sessionCompilation.bill_valuation : [];        // 获取标准库数据        let engineeringLibModel = new EngineeringLibModel();        billValuation = await engineeringLibModel.getLib(billValuation);        // 定额计价        let rationValuation = sessionCompilation.ration_valuation !== undefined ?            sessionCompilation.ration_valuation : [];        rationValuation = await engineeringLibModel.getLib(rationValuation);        let absoluteUrl = compilationData.overWriteUrl ? request.app.locals.rootDir + compilationData.overWriteUrl : request.app.locals.rootDir;        let overWriteUrl = fs.existsSync(absoluteUrl) && fs.statSync(absoluteUrl).isFile()? compilationData.overWriteUrl : null;        let renderData = {            isFirst: isFirst,            userAccount: request.session.userAccount,            userID: request.session.sessionUser.id,            compilationData: JSON.stringify(sessionCompilation),            overWriteUrl: overWriteUrl,            billValuation: JSON.stringify(billValuation),            rationValuation: JSON.stringify(rationValuation),            engineeringList: JSON.stringify(engineering.List),            compilationName: sessionCompilation.name,            versionName: `纵横建筑云计价(${request.session.compilationVersion})`,            LicenseKey:config.getLicenseKey(process.env.NODE_ENV)        };        response.render('building_saas/pm/html/project-management.html', renderData);    },    //第一次进入该费用定额时准备的初始数据    prepareInitialData: async function(request, response) {        try {            let sessionCompilation = request.session.sessionCompilation;            await pm_facade.prepareInitialData(request.session.sessionUser.id, sessionCompilation._id, sessionCompilation.example);            callback(request, response, 0, 'success', null);        } catch(err) {            callback(request, response, 1, err, null);        }    },    // 获取单价文件列表    getUnitFileList: async function(request, response) {        let data = request.body.data;        try {            data = JSON.parse(data);            let projectId = data.parentID !== undefined ? data.parentID : 0;            if (isNaN(projectId) && projectId <= 0) {                throw {msg: 'id数据有误!', err: 1};            }            /*// 获取对应建设项目下所有的单位工程id            let idList = await ProjectsData.getTenderByProjectId(projectId);            if (idList.length <= 0) {                throw {msg: '不存在对应单位工程', err: 0};            }*/            // 获取对应的单价文件            let unitPriceFileModel = new UnitPriceFileModel();            let unitPriceFileData = await unitPriceFileModel.getDataByRootProject(projectId);            if (unitPriceFileData === null) {                throw {msg: '不存在对应单价文件', err: 0};            }            // 整理数据            let unitPriceFileList = [];            for (let unitPriceFile of unitPriceFileData) {                let tmp = {                    name: unitPriceFile.name,                    id: unitPriceFile.id                };                unitPriceFileList.push(tmp);            }            callback(request, response, 0, '', unitPriceFileList);        } catch (error) {            console.log(error);            let responseData = error.err === 1 ? null : [];            callback(request, response, error.err, error.msg, responseData);        }    },    getFeeRateFileList:async function(request, response) {        let data = request.body.data;        try {            data = JSON.parse(data);            let projectId = data.parentID !== undefined ? data.parentID : 0;            if (isNaN(projectId) && projectId <= 0) {                throw {msg: 'id数据有误!', err: 1};            }            // 获取对应建设项目下所有的单位工程id            let feeRateFileList = await fee_rate_facade.getFeeRatesByProject(projectId);            callback(request, response, 0, '',feeRateFileList );        } catch (error) {            console.log(error);            let responseData = error.err === 1 ? null : [];            callback(request, response, error.err, error.msg, responseData);        }    },    getGCDatas: async function(request, response) {        let userID = request.session.sessionUser.id;        let compilatoinId = request.session.sessionCompilation._id;        let rst = [];        let _projs = Object.create(null), _engs = Object.create(null), prefix = 'ID_';        try{            let gc_unitPriceFiles = await ProjectsData.getGCFiles(fileType.unitPriceFile, userID);            let gc_feeRateFiles = await ProjectsData.getGCFiles(fileType.feeRateFile, userID);            let gc_tenderFiles = await ProjectsData.getGCFiles(projType.tender, userID);            for(let i = 0, len = gc_unitPriceFiles.length; i < len; i++){                let gc_uf = gc_unitPriceFiles[i];                let theProj = _projs[prefix + gc_uf.root_project_id] || null;                if(!theProj){                    let tempProj = await ProjectsData.getProjectsByIds(userID, compilatoinId, [gc_uf.root_project_id]);                    if(tempProj.length > 0 && tempProj[0].projType !== projType.folder){                        theProj = _projs[prefix + gc_uf.root_project_id] = tempProj[0]._doc;                        buildProj(theProj);                    }                }                if(theProj){                    theProj.unitPriceFiles.push(gc_uf);                }            }            for(let i = 0, len = gc_feeRateFiles.length; i < len; i++){                let gc_ff = gc_feeRateFiles[i];                let theProj = _projs[prefix + gc_ff.rootProjectID] || null;                if(!theProj){                    let tempProj = await ProjectsData.getProjectsByIds(userID, compilatoinId, [gc_ff.rootProjectID]);                    if(tempProj.length > 0 && tempProj[0].projType !== projType.folder){                        theProj = _projs[prefix + gc_ff.rootProjectID] = tempProj[0]._doc;                        buildProj(theProj);                    }                }                if(theProj) {                    theProj.feeRateFiles.push(gc_ff);                }            }            if(gc_tenderFiles.length > 0){                for(let i = 0, len = gc_tenderFiles.length; i < len; i++){                    let gc_t = gc_tenderFiles[i];                    let theEng = _engs[prefix + gc_t.ParentID] || null;                    if(!theEng){                        let tempEngs = await ProjectsData.getProjectsByIds(userID, compilatoinId, [gc_t.ParentID]);                        if(tempEngs.length > 0 && tempEngs[0].projType === projType.engineering){                            theEng = _engs[prefix + gc_t.ParentID] = tempEngs[0]._doc;                            theEng.children = [];                        }                    }                    if(theEng) {                        theEng.children.push(gc_t);                        let theProj = _projs[prefix + theEng.ParentID] || null;                        if(!theProj){                            let tempProj = await ProjectsData.getProjectsByIds(userID, compilatoinId, [theEng.ParentID]);                            if(tempProj.length > 0 && tempProj[0].projType === projType.project){                                theProj = _projs[prefix + theEng.ParentID] = tempProj[0]._doc;                                buildProj(theProj);                            }                        }                        if(theProj) {                            let isExist = false;                            for(let j = 0, jLen = theProj.children.length; j < jLen; j++){                                if(theProj.children[j].ID === theEng.ID){                                    isExist = true;                                    break;                                }                            }                            if(!isExist){                                theProj.children.push(theEng);                            }                        }                    }                }            }            for(let i in _projs){                rst.push(_projs[i]);            }            function buildProj(proj){                proj.children = [];                proj.unitPriceFiles = [];                proj.feeRateFiles = [];            }            callback(request, response, 0, 'success', rst);        }        catch (error){            callback(request, response, true, error, null);        }    },    recGC: function(request, response){        let userID = request.session.sessionUser.id;        let data = JSON.parse(request.body.data);        let nodes = data.nodes;        ProjectsData.recGC(userID, nodes, function (err, msg, data) {            callback(request, response, err, msg, data);        });   },    delGC: async function(request, response){        let data = JSON.parse(request.body.data);        let delDatas = data.delDatas;        let bulkProjs = [], bulkUFs = [], bulkFFs = [];        try{            for(let data of delDatas){                if(data.updateType === 'Project'){                    bulkProjs.push({updateOne: {filter: {ID: data.ID}, update: {'deleteInfo.completeDeleted': true}}});                }                else if(data.updateType === fileType.unitPriceFile){                    bulkUFs.push({updateOne: {filter: {id: data.ID}, update: {'deleteInfo.completeDeleted': true}}});                }                else{                    bulkFFs.push({updateOne: {filter: {ID: data.ID}, update: {'deleteInfo.completeDeleted': true}}});                }            }            if(bulkProjs.length > 0){                await projectModel.bulkWrite(bulkProjs);            }            if(bulkUFs.length > 0){                await unitPriceFileModel.bulkWrite(bulkUFs);            }            if(bulkFFs.length > 0){                await feeRateFileModel.bulkWrite(bulkFFs);            }            callback(request, response, 0, 'success', null);        } catch(err){            callback(request, response, 1, err, null);        }    },    moveProject:async function(req,res){        let result={            error:0        };        try {            let data = req.body.data;            let rdata= await pm_facade.moveProject(data);            result.data= rdata;        }catch (err){            console.log(err);            result.error=1;            result.message = err.message;        }        res.json(result);    },    copyProjects:async function (req, res) {        let result={            error:0        };        try {            let data = JSON.parse(req.body.data);            result.data = await pm_facade.copyProject(req.session.sessionUser.id, req.session.sessionCompilation._id,data);        }catch (err){            console.log(err);            result.error=1;            result.message = err.message;        }        res.json(result);    },    projectShareInfo: async function(req, res){        try{            let data = JSON.parse(req.body.data);            let shareInfo = await projectModel.findOne({ID: data.projectID, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]}, '-_id shareInfo');            callback(req, res, 0, 'success', shareInfo);        }        catch (err){            callback(req, res, 1, err, null);        }    },    share: async function(req, res){        try{            let data = JSON.parse(req.body.data);            let shareDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss'),                shareUserIDs = [];            for (let data of data.shareData) {                shareUserIDs.push(data.userID);                data.shareDate = shareDate;            }            //添加分享            if(data.type === 'create'){                //新增                for (let sData of data.shareData) {                    await projectModel.update({ID: data.projectID, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]}, {$addToSet: {shareInfo: sData}});                }            } else if (data.type === 'update') {                await projectModel.update({ID: data.projectID, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]}, {$set: {shareInfo: data.shareData}});            }            //取消分享            else {                await projectModel.update({ID: data.projectID, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]}, {$pull: {shareInfo: {userID: {$in: shareUserIDs}}}});            }            callback(req, res, 0, 'success', data.shareData);        }        catch (err){            callback(req, res, 1, err, null);        }    },    receiveProjects: async function(req, res) {        try {            let rst = {grouped: [], ungrouped: [], summaryInfo: null};            let userID = req.session.sessionUser.id;            let receiveProjects = await projectModel.find({                $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}], compilation: req.session.sessionCompilation._id, 'shareInfo.userID': userID}, '-_id');            //设置原项目用户信息            if(receiveProjects.length > 0){                let orgUserIDs = [];                for(let proj of receiveProjects){                    orgUserIDs.push(proj.userID);                    if (proj.projType === projType.tender) {                        //设置工程专业                        proj._doc.feeStandardName = proj.property.feeStandardName || '';                    }                    delete proj._doc.property;                }                orgUserIDs = Array.from(new Set(orgUserIDs));                let userObjIDs = [];                for(let uID of orgUserIDs){                    userObjIDs.push(mongoose.Types.ObjectId(uID));                }                let orgUsersInfo = await userModel.find({_id: {$in : userObjIDs}});                //建设项目                let consProjIDs = [],                    ungroupedTenders = [];                for(let proj of receiveProjects){                    if (proj.projType === projType.project) {                        consProjIDs.push(proj.ID);                    }                    //获取分享项目子项                    if (proj.projType !== projType.tender) {                        proj._doc.children = await pm_facade.getPosterityProjects([proj.ID]);                        for (let projC of proj._doc.children) {                            if (projC.projType === projType.project) {                                consProjIDs.push(projC.ID);                            } else if (projC.projType === projType.tender) {                                //设置工程专业                                projC._doc.feeStandardName = projC.property.feeStandardName || '';                                if (proj.projType === projType.engineering) {                                    ungroupedTenders.push(projC._doc);                                }                            }                            delete projC._doc.property;                        }                    } else {//未分类的单位工程不进行汇总,只取价格信息                        ungroupedTenders.push(proj._doc);                    }                    //设置分组,单位工程及单项工程分到未分组那                    if (proj.projType === projType.tender || proj.projType === projType.engineering) {                        rst.ungrouped.push(proj);                    } else {                        rst.grouped.push(proj);                    }                    //设置项目类型为来自别人分享                    proj._doc.shareType = 'receive';                    for(let userData of orgUsersInfo){                        if(proj.userID == userData._id.toString()){                            let userInfo = {name: userData.real_name, mobile: userData.mobile, company: userData.company, email: userData.email};                            proj._doc.userInfo = userInfo;                        }                    }                }                consProjIDs = Array.from(new Set(consProjIDs));                let summaryInfo = await pm_facade.getSummaryInfo(consProjIDs);                let tendersFeeInfo = await pm_facade.getTendersFeeInfo(ungroupedTenders);                rst.summaryInfo = {grouped: summaryInfo, ungrouped: tendersFeeInfo};            }            callback(req, res, 0, 'success', rst);        }        catch (err){            console.log(err);            callback(req, res, 1, err, null);        }    },    getProjectsByQuery: async function (req, res) {        try{            let data = JSON.parse(req.body.data);            let compilation = req.session.sessionCompilation._id;            let query = data.query;            query.compilation = compilation;            let options = data.options;            let projects = await projectModel.find(query, options);            callback(req, res, 0, 'success', projects);        }        catch (err){            callback(req, res, 1, err, null);        }    },    getSummaryInfo: async function(req, res){        try{            let data = JSON.parse(req.body.data);            let summaryInfo = await pm_facade.getSummaryInfo(data.projectIDs);            callback(req, res, 0, 'success', summaryInfo);        }        catch (err){            callback(req, res, 1, err, null);        }    },    changeFile:async function(req,res){        try{            let data = JSON.parse(req.body.data);            console.log(data);           // let summaryInfo = await pm_facade.getSummaryInfo(data.projectIDs);            callback(req, res, 0, 'success', []);        }        catch (err){            callback(req, res, 1, err, null);        }    }};
 |