|
@@ -11,6 +11,20 @@ let bill_detail = require("../facade/bill_facade");
|
|
|
let ration_glj = mongoose.model('ration_glj');
|
|
|
let ration_coe = mongoose.model('ration_coe');
|
|
|
let rationInstallationModel = mongoose.model('ration_installation');
|
|
|
+import fixedFlag from '../../common/const/bills_fixed';
|
|
|
+const uuidV1 = require('uuid/v1');
|
|
|
+const billType ={
|
|
|
+ DXFY:1,//大项费用
|
|
|
+ FB:2,//分部
|
|
|
+ FX:3,//分项
|
|
|
+ BILL:4,//清单
|
|
|
+ BX:5//补项
|
|
|
+};
|
|
|
+// 上传控件
|
|
|
+const multiparty = require("multiparty");
|
|
|
+const fs = require("fs");
|
|
|
+// excel解析
|
|
|
+const excel = require("node-xlsx");
|
|
|
//统一回调函数
|
|
|
var callback = function(req, res, err, message, data){
|
|
|
res.json({error: err, message: message, data: data});
|
|
@@ -147,10 +161,265 @@ module.exports = {
|
|
|
result.message = err.message;
|
|
|
}
|
|
|
res.json(result);
|
|
|
+ },
|
|
|
+ upload: async function(req, res){
|
|
|
+ let responseData = {
|
|
|
+ err: 0,
|
|
|
+ msg: '',
|
|
|
+ data: null
|
|
|
+ };
|
|
|
+ const allowHeader = ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];
|
|
|
+ const uploadOption = {
|
|
|
+ uploadDir: './public'
|
|
|
+ };
|
|
|
+ const form = new multiparty.Form(uploadOption);
|
|
|
+ form.parse(req, async function(err, fields, files) {
|
|
|
+ try{
|
|
|
+ const projectID = fields.projectID !== undefined && fields.projectID.length > 0 ?
|
|
|
+ parseInt(fields.projectID[0]) : 0;
|
|
|
+ if (projectID <= 0) {
|
|
|
+ throw '参数错误';
|
|
|
+ }
|
|
|
+ const file = files.file !== undefined ? files.file[0] : null;
|
|
|
+ if (err || file === null) {
|
|
|
+ throw '上传失败';
|
|
|
+ }
|
|
|
+ // 判断类型
|
|
|
+ if (file.headers['content-type'] === undefined || allowHeader.indexOf(file.headers['content-type']) < 0) {
|
|
|
+ throw '不支持该类型';
|
|
|
+ }
|
|
|
+ // 重命名文件名
|
|
|
+ const uploadFullName = uploadOption.uploadDir + '/' + file.originalFilename;
|
|
|
+ fs.renameSync(file.path, uploadFullName);
|
|
|
+
|
|
|
+ const sheet = excel.parse(uploadFullName);
|
|
|
+ if (sheet[0] === undefined || sheet[0].data === undefined) {
|
|
|
+ throw 'excel没有对应数据';
|
|
|
+ }
|
|
|
+ //导入的数据是否含有固定行(分部分项、施工技术措施项目、施工组织措施项目)
|
|
|
+ let flag = getImportFlag(file.originalFilename);
|
|
|
+ if(!flag){
|
|
|
+ throw 'excel数据错误';
|
|
|
+ }
|
|
|
+ let fixedBill = await billsData.model.findOne({projectID: projectID, 'flags.flag': flag, deleteInfo: null});
|
|
|
+ let insertFixedBill = null;
|
|
|
+ //导入xx措施项目,若不存在此固定清单,则先插入相关固定清单
|
|
|
+ if(!fixedBill){
|
|
|
+ //分部分项工程(不可删除)应存在
|
|
|
+ if(flag === fixedFlag.SUB_ENGINERRING){
|
|
|
+ throw '项目不存在分部分项工程'
|
|
|
+ }
|
|
|
+ //措施项目是否存在
|
|
|
+ let csxm = await billsData.model.findOne({projectID: projectID, 'flags.flag': fixedFlag.MEASURE, deleteInfo: null});
|
|
|
+ if(!csxm){
|
|
|
+ throw '项目不存在措施项目'
|
|
|
+ }
|
|
|
+ //插入清单固定行(施工技术措施项目、施工组织措施项目可删除)
|
|
|
+ insertFixedBill = {projectID: projectID, name: flag === fixedFlag.CONSTRUCTION_TECH ? '施工技术措施项目' : '施工组织措施项目', code: '1',
|
|
|
+ ID: uuidV1(), NextSiblingID: -1, ParentID: csxm.ID, flags: [{fieldName: 'fixed', flag: flag}], type: billType.BILL};
|
|
|
+ //更新前节点
|
|
|
+ let preDatas = await billsData.model.find({projectID: projectID, ParentID: csxm.ID, deleteInfo: null});
|
|
|
+ for(let preData of preDatas){
|
|
|
+ if(preData.NextSiblingID == -1){
|
|
|
+ await billsData.model.update({ID: preData.ID}, {$set: {NextSiblingID: insertFixedBill.ID}});
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ await billsData.model.create(insertFixedBill);
|
|
|
+ fixedBill = insertFixedBill;
|
|
|
+ }
|
|
|
+ //将excel数据转换成清单树结构数据
|
|
|
+ let insertDatas = parseToBillData(getValidImportData(sheet[0].data), getColMapping(sheet[0].data), fixedBill, projectID);
|
|
|
+ //删除相关数据
|
|
|
+ let deleteDatas = await billsData.deepDeleteBill([fixedBill], req.session.sessionUser.ssoId);
|
|
|
+ //新增清单数据
|
|
|
+ await billsData.importBills(insertDatas);
|
|
|
+ //返回数据以更新前端
|
|
|
+ if(insertFixedBill){
|
|
|
+ insertDatas.push(insertFixedBill);
|
|
|
+ }
|
|
|
+ responseData.data = {fixedBill: fixedBill, insert: {bill: insertDatas, ration: []}, remove: {bill: deleteDatas.bill, ration: deleteDatas.ration}};
|
|
|
+ //删除暂存文件
|
|
|
+ fs.unlink(uploadFullName);
|
|
|
+ res.json(responseData);
|
|
|
+ }
|
|
|
+ catch (error){
|
|
|
+ responseData.err = 1;
|
|
|
+ responseData.msg = error;
|
|
|
+ res.json(responseData);
|
|
|
+ }
|
|
|
+
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
+//提取excel表头列对应数据
|
|
|
+function getColMapping(sheetData){
|
|
|
+ //获取表头
|
|
|
+ let headRow = [];
|
|
|
+ for(let rData of sheetData) {
|
|
|
+ if (rData[0] && rData[0].toString().replace(/\s/g, '') === '序号') {
|
|
|
+ headRow = rData;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //获取表头列与列号对应关系
|
|
|
+ let colMapping = {};
|
|
|
+ for(let c = 0; c < headRow.length; c++){
|
|
|
+ if(headRow[c]){
|
|
|
+ headRow[c] = headRow[c].toString().replace(/\s/g, '');
|
|
|
+ switch(headRow[c]){
|
|
|
+ case '序号': colMapping.serialNo = c; break;
|
|
|
+ case '项目编码': colMapping.code = c; break;
|
|
|
+ case '项目名称': colMapping.name = c; break;
|
|
|
+ case '项目特征': colMapping.itemCharacterText = c; break;
|
|
|
+ case '计量单位': colMapping.unit = c; break;
|
|
|
+ case '工程量': colMapping.quantity = c; break;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return colMapping;
|
|
|
+}
|
|
|
+function rowExistData(rowData){
|
|
|
+ for(let cData of rowData){
|
|
|
+ if(cData !== undefined && cData !== ''){
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+//提取excel表数据中的有效数据(去表头表尾,提取其中的excel数据)
|
|
|
+function getValidImportData(sheetData){
|
|
|
+ let withingD = false;
|
|
|
+ let validData = [];
|
|
|
+ for(let r = 0; r < sheetData.length; r++){
|
|
|
+ let rData = sheetData[r];
|
|
|
+ if(rData[0]){
|
|
|
+ //首列去空格
|
|
|
+ rData[0] = rData[0].toString().replace(/\s/g, '');
|
|
|
+ //表头
|
|
|
+ if(rData[0] === '序号'){
|
|
|
+ withingD = true;
|
|
|
+ r++;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ //表尾
|
|
|
+ else if(rData[0] === '本页小计' || rData[0] === '合计'){
|
|
|
+ withingD = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(withingD && rowExistData(rData)){
|
|
|
+ validData.push(rData);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return validData;
|
|
|
+}
|
|
|
+
|
|
|
+function getImportFlag(sheetName){
|
|
|
+ const fixedItem = {'分部分项': fixedFlag.SUB_ENGINERRING, '施工技术措施项目': fixedFlag.CONSTRUCTION_TECH, '施工组织措施项目': fixedFlag.CONSTRUCTION_ORGANIZATION};
|
|
|
+ for(let flag in fixedItem){
|
|
|
+ if(sheetName.indexOf(flag) > 0){
|
|
|
+ return fixedItem[flag];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+}
|
|
|
+//excel数据转换成清单数据
|
|
|
+function parseToBillData(validData, colMapping, fixedBill, projectID){
|
|
|
+ let rst = [];
|
|
|
+ let billIdx = {};
|
|
|
+ let preRootID = -1,
|
|
|
+ preLeafID = -1,
|
|
|
+ preID = -1;
|
|
|
+ //合并了项目特征,且行有数据
|
|
|
+ function isRoot(rData){
|
|
|
+ return rData[colMapping.itemCharacterText] !== undefined && rData[colMapping.itemCharacterText] === '' && rowExistData(rData);
|
|
|
+ }
|
|
|
+ //不合并且有序号
|
|
|
+ function isLeaf(rData){
|
|
|
+ return (rData[colMapping] === undefined || rData[colMapping] !== '') && rData[colMapping.serialNo] && rData[colMapping.serialNo] !== '';
|
|
|
+ }
|
|
|
+ //续数据,上一行数据是有效节点且无序号
|
|
|
+ function isExtend(preData, rData){
|
|
|
+ return preData && (isRoot(preData) || isLeaf(preData)) && (rData[colMapping.serialNo] === undefined || rData[colMapping.serialNo] === '');
|
|
|
+ }
|
|
|
+ function getBillType(rData, flag){
|
|
|
+ if(flag === fixedFlag.CONSTRUCTION_TECH || flag === fixedFlag.CONSTRUCTION_ORGANIZATION){
|
|
|
+ return billType.BILL;
|
|
|
+ }
|
|
|
+ else if(flag === fixedFlag.SUB_ENGINERRING){
|
|
|
+ return isLeaf(rData) ? billType.FX : billType.FB;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ for(let r = 0; r < validData.length; r++){
|
|
|
+ let preData = validData[r-1],
|
|
|
+ rData = validData[r];
|
|
|
+ if(fixedBill.flags[0].flag == fixedFlag.CONSTRUCTION_TECH && rData[colMapping.name] === '施工技术措施项目'
|
|
|
+ || fixedBill.flags[0].flag == fixedFlag.CONSTRUCTION_ORGANIZATION && rData[colMapping.name] === '施工组织措施项目'){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ //过滤无效数据
|
|
|
+ if(!isRoot(rData) && !isLeaf(rData) && !isExtend(preData, rData)){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if(isExtend(preData, rData)){
|
|
|
+ let preBill = billIdx[preID];
|
|
|
+ //合并续数据
|
|
|
+ if(preBill){
|
|
|
+ preBill.code += rData[colMapping.code] ? rData[colMapping.code] : '';
|
|
|
+ preBill.name += rData[colMapping.name] ? rData[colMapping.name] : '';
|
|
|
+ preBill.itemCharacterText += rData[colMapping.itemCharacterText] ? rData[colMapping.itemCharacterText] : '';
|
|
|
+ preBill.unit += rData[colMapping.unit] ? rData[colMapping.unit] : '';
|
|
|
+ preBill.quantity += rData[colMapping.quantity] ? rData[colMapping.quantity] : '';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ let newID = uuidV1();
|
|
|
+ let pID = -1;
|
|
|
+ let preBill = null;
|
|
|
+ if(isRoot(rData)){
|
|
|
+ pID = fixedBill.ID;
|
|
|
+ preBill = billIdx[preRootID];
|
|
|
+ }
|
|
|
+ else if(isLeaf(rData)){
|
|
|
+ pID = preRootID !== -1 ? preRootID : fixedBill.ID;
|
|
|
+ preBill = billIdx[preLeafID];
|
|
|
+ }
|
|
|
+ //set bill data
|
|
|
+ billIdx[newID] = {
|
|
|
+ ID: newID, ParentID: pID, NextSiblingID: -1,
|
|
|
+ code: rData[colMapping.code] ? rData[colMapping.code] : '',
|
|
|
+ name: rData[colMapping.name] ? rData[colMapping.name] : '',
|
|
|
+ itemCharacterText: rData[colMapping.itemCharacterText] ? rData[colMapping.itemCharacterText] : '',
|
|
|
+ itemCharacter: [],
|
|
|
+ jobContentText: '',
|
|
|
+ jobContent: [],
|
|
|
+ unit: rData[colMapping.unit] ? rData[colMapping.unit] : '',
|
|
|
+ quantity: rData[colMapping.quantity] ? rData[colMapping.quantity] : '',
|
|
|
+ flags: fixedBill.flags[0].flag === fixedFlag.CONSTRUCTION_ORGANIZATION && rData[colMapping.name] === '安全文明施工专项费用' ?
|
|
|
+ [{fieldName: 'fixed', flag: fixedBill.flags[0].flag}] : [],
|
|
|
+ fees: [],
|
|
|
+ projectID: projectID,
|
|
|
+ type: getBillType(rData, fixedBill.flags[0].flag)};
|
|
|
+ //update preBill NextSibling
|
|
|
+ if(preBill){
|
|
|
+ preBill.NextSiblingID = newID;
|
|
|
+ }
|
|
|
+ //set new preID
|
|
|
+ preID = newID;
|
|
|
+ preRootID = isRoot(rData) ? newID : preRootID;
|
|
|
+ preLeafID = isLeaf(rData) ? newID : preLeafID;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for(let i in billIdx){
|
|
|
+ rst.push(billIdx[i]);
|
|
|
+ }
|
|
|
+ return rst;
|
|
|
+}
|
|
|
+
|
|
|
async function doBillsOrRationsDelete(data) {
|
|
|
let billTask = [];
|
|
|
let deleteBillIDs = [];
|
|
@@ -200,7 +469,7 @@ async function doBillsOrRationsDelete(data) {
|
|
|
await rationInstallationModel.deleteMany(sub_query);//删除安装增加费
|
|
|
}
|
|
|
if(rationTask.length>0){
|
|
|
- await ration_model.model.bulkWrite(rationTask);//删除定额
|
|
|
+ await ration_model.model.bulkWrite(rationTask);//删除定额
|
|
|
}
|
|
|
if(billTask.length>0){
|
|
|
await billsData.model.bulkWrite(billTask);//删除清单
|