Kaynağa Gözat

Merge branch '1.0.0_online' of http://192.168.1.41:3000/SmartCost/ConstructionCost into 1.0.0_online

zhangweicheng 6 yıl önce
ebeveyn
işleme
430d1e25a3

+ 0 - 0
.tgitconfig


+ 1 - 0
config/gulpConfig.js

@@ -49,6 +49,7 @@ module.exports = {
         'lib/JSExpressionEval_src/Date.js',
         'web/building_saas/glj/js/socket.io.slim.js',
         'public/web/socket/connection.js',
+        'public/billsUtil.js',
         'web/building_saas/main/js/models/importStandardInterface.js',
         'web/building_saas/pm/js/**/*.js',
         'lib/ztree/*.js',

+ 11 - 3
modules/glj/models/glj_list_model.js

@@ -783,12 +783,14 @@ class GLJListModel extends BaseModel {
             mixRatioData[m_index]=tmp;
         }
         // 查找对应的项目工料机数据
-        let gcondition = {project_id: glj.project_id,code: {"$in": codeList}, name: {"$in": nameList},specs:{"$in": specsList},type:{"$in": typeList},unit:{"$in": unitList} };
+        //let gcondition = {project_id: glj.project_id,code: {"$in": codeList}, name: {"$in": nameList},specs:{"$in": specsList},type:{"$in": typeList},unit:{"$in": unitList} };
+        let gcondition = {project_id: glj.project_id,code: {"$in": codeList}, name: {"$in": nameList},type:{"$in": typeList},unit:{"$in": unitList} };
         let gljData = await this.findDataByCondition(gcondition, {_id: 0}, false);
 
         // 查找对应的单价数据
         let unitPriceModel = new UnitPriceModel();
-        let ucondition = { unit_price_file_id: unitPriceFileId,code: {"$in": codeList}, name: {"$in": nameList},specs:{"$in": specsList},type:{"$in": typeList},unit:{"$in": unitList}};
+        //let ucondition = { unit_price_file_id: unitPriceFileId,code: {"$in": codeList}, name: {"$in": nameList},specs:{"$in": specsList},type:{"$in": typeList},unit:{"$in": unitList}};
+        let ucondition = { unit_price_file_id: unitPriceFileId,code: {"$in": codeList}, name: {"$in": nameList},type:{"$in": typeList},unit:{"$in": unitList}};
         let unitPriceList = await unitPriceModel.findDataByCondition(ucondition, {_id: 0}, false);
 
 
@@ -796,7 +798,9 @@ class GLJListModel extends BaseModel {
         let unitPriceData = {};
         for(let tmp of unitPriceList) {
             let u_index = this.getIndex(tmp,['code','name','specs','unit','type']);
-            unitPriceData[u_index] = tmp;
+            if (mixRatioData[u_index]) {
+                unitPriceData[u_index] = tmp;
+            }
         }
         return [gljData,mixRatioData,unitPriceData];
 
@@ -825,6 +829,10 @@ class GLJListModel extends BaseModel {
             specs:projectGLJData.specs,
             type:projectGLJData.type,
             unit:projectGLJData.unit
+        };
+        if (!condition.specs) {
+            delete condition.specs;
+            condition.$or = [{specs: null}, {specs: ''}];
         }
         let unitPrice = await unitPriceModel.findDataByCondition(condition);
 

+ 17 - 0
modules/main/facade/project_facade.js

@@ -176,6 +176,23 @@ async function updateNodes(datas){
 
         }
     }
+    //test------------------------
+    if (billTasks.length) {
+        console.log(`billTasks===============================`);
+        billTasks.forEach(task => {
+            if (task.updateOne.update.name === '分部分项工程') {
+                console.log(`task.updateOne.filter`);
+                console.log(task.updateOne.filter);
+                console.log(`task.updateOne.update`);
+                console.log(task.updateOne.update);
+            }
+            /*console.log(`task.updateOne.filter`);
+            console.log(task.updateOne.filter);
+            console.log(`task.updateOne.update`);
+            console.log(task.updateOne.update);*/
+        });
+    }
+    //test------------------------
     rationTasks.length>0?asyncTasks.push(ration_model.model.bulkWrite(rationTasks)):'';
     billTasks.length>0?asyncTasks.push(bill_model.model.bulkWrite(billTasks)):"";
     rationGLJTasks.length>0?asyncTasks.push(ration_glj_model.bulkWrite(rationGLJTasks)):"";

+ 6 - 0
modules/pm/controllers/new_proj_controller.js

@@ -9,6 +9,7 @@ let async = require('async');
 const uuidV1 = require('uuid/v1');
 const mongoose = require('mongoose');
 let mainColLibModel = mongoose.model('std_main_col_lib');
+const billsUtil = require('../../../public/billsUtil');
 
 import BillsTemplateModel from "../models/templates/bills_template_model";
 import EngineeringLibModel from "../../users/models/engineering_lib_model";
@@ -27,11 +28,16 @@ module.exports = {
                 for(let bill of billsDatas){
                     uuidMaping[bill.ID] = uuidV1();
                 }
+                const reg = /@\d+/;
                 billsDatas.forEach(function (template) {
                     template.projectID = newProjID;
                     template.ID = uuidMaping[template.ID] ? uuidMaping[template.ID] : -1;
                     template.ParentID = uuidMaping[template.ParentID] ? uuidMaping[template.ParentID] : -1;
                     template.NextSiblingID = uuidMaping[template.NextSiblingID] ? uuidMaping[template.NextSiblingID] : -1;
+                    const needToParseCalcBase = template.calcBase && reg.test(template.calcBase);
+                    if (needToParseCalcBase) {
+                        template.calcBase = billsUtil.parseCalcBase(template.calcBase, uuidMaping);
+                    }
                 });
                 billsData.insertData(billsDatas, callback);
             },

+ 3 - 0
modules/pm/facade/pm_facade.js

@@ -1580,6 +1580,9 @@ async function setupStdData(tenderData) {
             r.content = stdRation.jobContent;
             if (stdRation.feeType) {
                 r.programID = stdRation.feeType;
+            } else {
+                // 是标准定额,但是该标准定额取费专业为空,取定额取费专业(后台配置项)
+                r.programID = tenderData.tender.property.engineering;
             }
         } else {
             r.from = 'cpt';

+ 9 - 1
modules/templates/models/bills_template.js

@@ -8,6 +8,7 @@ let BillsTemplates = mongoose.model('temp_bills');
 let BillsTemplateDAO = function(){};
 import BillsTemplateModel from "../../pm/models/templates/bills_template_model";
 const uuidV1 = require('uuid/v1');
+const billsUtil = require('../../../public/billsUtil');
 
 BillsTemplateDAO.prototype.getTemplate = function (type, callback) {
     if (callback) {
@@ -82,7 +83,9 @@ BillsTemplateDAO.prototype.getNeedfulTemplate = async function (templateLibID) {
     }
     //过滤掉不含清单固定类别的模板数据 (导入接口只包含必要的清单模板数据)
     let needfulDatas = billsDatas.filter(data => getFlag(data));
-    sortToTreeData(needfulDatas);
+    sortSeqToNextSibling(needfulDatas);
+    //sortToTreeData(needfulDatas);
+    billsUtil.resetTreeData(needfulDatas, uuidV1);
     return needfulDatas;
 };
 
@@ -152,10 +155,15 @@ function sortToTreeData(needfulData) {
     for(let bill of needfulData){
         uuidMaping[bill.ID] = uuidV1();
     }
+    const reg = /@\d+/;
     needfulData.forEach(function (template) {
         template.ID = uuidMaping[template.ID] ? uuidMaping[template.ID] : -1;
         template.ParentID = uuidMaping[template.ParentID] ? uuidMaping[template.ParentID] : -1;
         template.NextSiblingID = uuidMaping[template.NextSiblingID] ? uuidMaping[template.NextSiblingID] : -1;
+        const needToParseCalcBase = template.calcBase && reg.test(template.calcBase);
+        if (needToParseCalcBase) {
+            template.calcBase = billsUtil.parseCalcBase(template.calcBase, uuidMaping);
+        }
     });
 }
 

+ 60 - 0
public/billsUtil.js

@@ -0,0 +1,60 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2019/11/1
+ * @version
+ */
+
+((factory) => {
+    if (typeof module !== 'undefined') {
+        module.exports = factory();
+    } else {
+        window.BILLS_UTIL = factory();
+    }
+})(() => {
+    // 清单模板各清单重设ID时,重新转换清单基数的ID引用
+    function parseCalcBase(calcBase, uuidMapping) {
+        const orgIDRefs = [...new Set(calcBase.match(/@\d+/g))];
+        orgIDRefs.forEach(orgRef => {
+            const orgID = orgRef.match(/\d+/)[0];
+            const newID = uuidMapping[orgID] || null;
+            // ID匹配不上则不转换这个引用
+            if (!newID) {
+                return;
+            }
+            const replaceStr = `@${newID}`;
+            calcBase = calcBase.replace(new RegExp(`${orgRef}\\b`, 'g'), replaceStr);
+        });
+        return calcBase;
+    }
+
+    /*
+     * @param {Array}billsList (完整的清单树结构数据)
+     * @param {Function}idFactory 生成ID的方法
+     * @return {void}
+     * */
+    function resetTreeData(billsList, idFactory) {
+        const idMapping = {};
+        idMapping['-1'] = -1;
+        // 建立新ID-旧ID映射
+        billsList.forEach(bills => idMapping[bills.ID] = idFactory());
+        const reg = /@\d+/;
+        billsList.forEach(function (bills) {
+            bills.ID = idMapping[bills.ID] ? idMapping[bills.ID] : -1;
+            bills.ParentID = idMapping[bills.ParentID] ? idMapping[bills.ParentID] : -1;
+            bills.NextSiblingID = idMapping[bills.NextSiblingID] ? idMapping[bills.NextSiblingID] : -1;
+            const needToParseCalcBase = bills.calcBase && reg.test(bills.calcBase);
+            if (needToParseCalcBase) {
+                bills.calcBase = parseCalcBase(bills.calcBase, idMapping);
+            }
+        });
+    }
+
+    return {
+        parseCalcBase,
+        resetTreeData
+    };
+});

+ 8 - 4
web/building_saas/js/global.js

@@ -142,10 +142,14 @@ function getFeeIndex(fees) {
     let feesIndex = {};
     if (fees) {
         for(let fee of fees){
-            fee.unitFee = parseFloat(fee.unitFee);
-            fee.totalFee = parseFloat(fee.totalFee);
-            fee.tenderUnitFee = parseFloat(fee.tenderUnitFee);
-            fee.tenderTotalFee = parseFloat(fee.tenderTotalFee);
+            const parsedUnitFee = parseFloat(fee.unitFee);
+            const parsedTotalFee = parseFloat(fee.totalFee);
+            const parsedTenderUnitFee = parseFloat(fee.tenderUnitFee);
+            const parsedTenderTotalFee = parseFloat(fee.tenderTotalFee);
+            fee.unitFee = parsedUnitFee || 0;
+            fee.totalFee = parsedTotalFee || 0;
+            fee.tenderUnitFee = parsedTenderUnitFee || 0;
+            fee.tenderTotalFee = parsedTenderTotalFee || 0;
             feesIndex[fee.fieldName] = fee;
         }
     }

+ 2 - 1
web/building_saas/main/html/main.html

@@ -75,7 +75,8 @@
                 <li class="nav-item"><a data-toggle="tab" href="#project_glj" id="tab_project_glj" data-name="tab_project_glj" role="tab">人材机汇总</a></li>
                 <li class="nav-item"><a data-toggle="tab" href="#fee_rates" id="tab_fee_rate" role="tab" >费率</a></li>
                 <li class="nav-item"><a data-toggle="tab" href="#calc_program_manage" id="tab_calc_program_manage" role="tab" style="display:none">总计算程序</a></li>
-                <li class="nav-item"><a data-toggle="tab" href="#tender_price" id="tab_tender_price" role="tab" style="display:none">调价</a></li>
+                <!--<li class="nav-item"><a data-toggle="tab" href="#tender_price" id="tab_tender_price" role="tab" style="display:none">调价</a></li>-->
+                <li class="nav-item"><a data-toggle="tab" href="#tender_price" id="tab_tender_price" role="tab">调价</a></li>
                 <li class="nav-item"><a data-toggle="tab" href="#reports" role="tab" id="tab_report" onclick="rptTplObj.iniPage();">报表</a></li>
                 <li class="nav-item"><a data-toggle="tab" href="#index" id="tab_index" role="tab" style="display:none">指标信息</a></li>
             </ul>

+ 1 - 1
web/building_saas/main/js/controllers/block_controller.js

@@ -599,7 +599,7 @@ let BlockController = {
             let newID = uuid.v1(); //新的清单ID
             billsIDMap[temData.ID] = newID;
             temData.ID = newID; //新的清单ID
-            if(temData.code.length == 12){//是从清单库来的  //temData.billsLibId && temData.billsLibId!=""    2019-09-03  修改需求,不做清单库ID判断
+            if(temData.code && temData.code.length == 12){//是从清单库来的  //temData.billsLibId && temData.billsLibId!=""    2019-09-03  修改需求,不做清单库ID判断
                 let value = temData.code.substr(0,9);
                 if (value&&value.length === 9) {//&& /^[\d]+$/.test(value) 去掉全数字判断    07-31 zhang
                     temData.code = me.newFormatCode(value);

+ 0 - 1
web/building_saas/main/js/models/calc_base.js

@@ -1589,7 +1589,6 @@ let calcBase = {
             if(!cbTools.isDef(exp)){
                 throw '表达式不正确';
             }
-
             //输入式转换表达式
             let compileExp = $CBP.toCompileExpr(exp);
             //计算

+ 4 - 3
web/building_saas/main/js/models/calc_program.js

@@ -796,6 +796,7 @@ let calcTools = {
             quantity: treeNode.data.quantity,
             calcBase: treeNode.data.calcBase,
             calcBaseValue: treeNode.data.calcBaseValue,
+            tenderCalcBaseValue: treeNode.data.tenderCalcBaseValue,
             programID: treeNode.data.programID,
             marketUnitFee: treeNode.data.marketUnitFee,
             marketTotalFee: treeNode.data.marketTotalFee,
@@ -840,7 +841,7 @@ let calcTools = {
         if (this.isBill(treeNode))                 // 清单只有一个工程量,没有调整后工程量。
             return this.uiNodeQty(treeNode)
         else{
-            let qCoe = (treeNode.data.rationQuantityCoe == undefined || treeNode.data.rationQuantityCoe == 0) ? 1 : treeNode.data.rationQuantityCoe;
+            let qCoe = (treeNode.data.rationQuantityCoe == undefined || treeNode.data.rationQuantityCoe == null || treeNode.data.rationQuantityCoe == 0) ? 1 : treeNode.data.rationQuantityCoe;
             treeNode.data.tenderQuantity = (this.uiNodeQty(treeNode) * qCoe).toDecimal(decimalObj.decimal("quantity", treeNode));
             return treeNode.data.tenderQuantity;
         }
@@ -1902,11 +1903,11 @@ class CalcProgram {
                     calcItem.unitFee = (eval(calcItem.compiledExpr) * feeRate * 0.01).toDecimal(decimalObj.decimal('unitPrice', treeNode));
                     calcItem.totalFee = (calcItem.unitFee * calcTools.uiNodeQty(treeNode)).toDecimal(decimalObj.decimal('totalPrice', treeNode));
 
-                    if (tender == tenderTypes.ttCalc) {
+                    // if (tender == tenderTypes.ttCalc) {
                         let tExpr = analyzer.getCompiledTenderExpr(calcItem.compiledExpr);
                         calcItem.tenderUnitFee = (eval(tExpr) * feeRate * 0.01).toDecimal(decimalObj.decimal('unitPrice', treeNode));
                         calcItem.tenderTotalFee = (calcItem.tenderUnitFee * treeNode.data.tenderQuantity).toDecimal(decimalObj.decimal('totalPrice', treeNode));
-                    };
+                    // };
 
                     if (calcItem.fieldName) {
                         fnArr.push(calcItem.fieldName);

+ 14 - 7
web/building_saas/main/js/models/exportStandardInterface.js

@@ -173,7 +173,7 @@ const XMLStandard = (function () {
             this.constraints = {
                 engCode: [],    //单项工程编号
                 tenderCode: [], //单位工程编号
-                billsCode: [],  //清单项目项目编码(修改成了建设项目下唯一)
+                billsCode: [],  //清单项目项目编码(修改成了建设项目下唯一,xsd中没体现,通过跟pm沟通得知)
             };
             XML_EXPORT_BASE.Element.call(this, '标段', attrs);
         }
@@ -301,14 +301,14 @@ const XMLStandard = (function () {
                     '土建工程', '装饰工程', '安装工程', '市政工程', '园林绿化工程',
                     '仿古建筑工程', '房修工程', '轨道工程', '构筑物工程', '机械(爆破)土石方',
                     '围墙工程', '幕墙工程', '市政安装工程', '城市轨道交通安装', '人工土石方',
-                    '房屋安装修缮工程', '房屋修缮单拆除'
+                    '房屋安装修缮工程', '房屋修缮单拆除', '城市地下综合管廊', '城市管线迁改'
                 ]},
                 {name: '金额', value: exportKind === _config.EXPORT_KIND.Tender
                     ? source.summaryInfo.engineeringCost
                     : '0', type: _config.TYPE.NUM2},
                 {name: '定额库编码', value: source.defaultRationLibCode, enumeration: [
                     'CQ18TJ', 'CQ18ZS', 'CQ18AZ', 'CQ18SZ', 'CQ18YL', 'CQ18FG', 'CQ18GD', 'CQ18FS',
-                    'CQ18LS', 'CQ18GZW', 'CQ18BP', 'CQ18ZP'
+                    'CQ18LS', 'CQ18GZW', 'CQ18BP', 'CQ18ZP', 'CQ18GL', 'CQ18GX'
                 ]},
                 {name: '计税方法', value: TAX[source.taxType], required: true, enumeration: ['营业税', '一般计税', '简易计税']},
             ];
@@ -1140,9 +1140,12 @@ const XMLStandard = (function () {
             if (gljSumarryInfo) {
                 //投标导出承包人材料相关
                 if (exportKind === _config.EXPORT_KIND.Tender) {
+                    let projectGLJList = tenderDetail.projectGLJ.datas.gljList;
+                    let contractorList = tenderDetail.contractor_list.datas;
+                    let decimalObj = tenderDetail.projectInfo.property.decimal;
                     let adjustType = tenderData.property.gljAdjustType || _config.ADJUST_TYPE.info;
                     //承包人材料差额法表
-                    let diffGljs = materialAdjustObj.getPirceInfoDatas(tenderDetail.projectGLJ.datas.gljList, tenderDetail.contractor_list.datas);
+                    let diffGljs = materialAdjustObj.getPirceInfoDatas(projectGLJList, contractorList, decimalObj);
                     if (diffGljs.length) {
                         let diffP = new DifferentiaGlj();
                         diffGljs.forEach(data => {
@@ -1156,7 +1159,7 @@ const XMLStandard = (function () {
                     let engineeringCostNode = tenderDetail.Bills.tree.roots.find(node => node.getFlag() === fixedFlag.ENGINEERINGCOST);
                     if (engineeringCostNode) {
                         let ecTotalFee = _util.getFee(engineeringCostNode.data.fees, 'common.totalFee');
-                        let exponentialGljs = materialAdjustObj.getPriceCoeDatas(tenderDetail.projectGLJ.datas.gljList, tenderDetail.contractor_list.datas, ecTotalFee);
+                        let exponentialGljs = materialAdjustObj.getPriceCoeDatas(projectGLJList, contractorList, ecTotalFee, decimalObj);
                         if (exponentialGljs.length) {
                             // 变值权重B累加
                             let totalVarWeight = exponentialGljs.reduce((acc, cur) => acc += cur.varWeight || 0, 0);
@@ -2001,8 +2004,12 @@ const XMLStandard = (function () {
                     }
                     gljSummary.children.push(gljEle);
                 }
+                const projectGLJList = detail.projectGLJ.datas.gljList;
+                const bidEvaluationList = detail.bid_evaluation_list.datas;
+                const evaluateList = detail.evaluate_list.datas;
+                const decimalObj = detail.projectInfo.property.decimal;
                 // 评标材料
-                const bidEvaluationDetail = loadMaterialDetail(configMaterialObj.getBidMaterialDatas());
+                const bidEvaluationDetail = loadMaterialDetail(configMaterialObj.getBidMaterialDatas(projectGLJList, bidEvaluationList, decimalObj));
                 evalBidMaterial.children.push(...bidEvaluationDetail);
                 // 给建设项目下的评审材料汇总设置明细数据 //code name specs unit marketPrice
                 const appraisalSummaryDetail = bidEvaluationDetail.map(ele => {
@@ -2018,7 +2025,7 @@ const XMLStandard = (function () {
                 });
                 appraisalSummary.children.push(...appraisalSummaryDetail);
                 // 暂估材料
-                const evaluationDetail = loadMaterialDetail(configMaterialObj.getEvaluateMaterialDatas());
+                const evaluationDetail = loadMaterialDetail(configMaterialObj.getEvaluateMaterialDatas(projectGLJList, evaluateList, decimalObj));
                 evalEstimateMaterial.children.push(...evaluationDetail);
                 return {gljSummary, evalBidMaterial, evalEstimateMaterial};
             }

+ 129 - 63
web/building_saas/main/js/models/importStandardInterface.js

@@ -155,6 +155,30 @@ const ImportXML = (() => {
         '&quot;': 'escape{quot}',
         '&apos;': 'escape{apos}'
     };
+    // 第x册定额名称包含字符与计算程序ID的匹配映射
+    const NameProgramMapping = {
+        '机械设备': 25,
+        '热力设备': 26,
+        '静置设备与工艺金属结构制作': 27,
+        '电气设备': 28,
+        '建筑智能化': 29,
+        '自动化控制仪表': 30,
+        '通风空调': 31,
+        '工业管道': 32,
+        '消防': 33,
+        '给排水、采暖、燃气': 34,
+        '刷油、防腐蚀、绝热': 35
+    };
+    // 获取定额取费专业,根据名称匹配有无固定映射,若没有则取单位工程取费专业(后台配置)
+    function getProgramID(name, mapping, projectEngineering) {
+        for (let mapName in mapping) {
+            const reg = new RegExp(`第.{1,2}册\\s*${mapName}(安装)?工程`);
+            if (name.match(reg)) {
+                return mapping[mapName];
+            }
+        }
+        return projectEngineering;
+    }
     // 避免字符实体进行转义。原文本中含有xml字符实体,转换为其他字符。
     function escapeXMLEntity(str) {
         for (let [key, value] of Object.entries(XML_ENTITY)) {
@@ -382,7 +406,8 @@ const ImportXML = (() => {
                 let obj = {
                     rowCode: getValue(itemSrc, ['_行代号']),
                     name: getValue(itemSrc, ['_项目名称']),
-                    calcBase: getValue(itemSrc, ['_计算基础表达式'])
+                    calcBase: getValue(itemSrc, ['_计算基础表达式']),
+                    feeType: getValue(itemSrc, ['_费用类别'])
                 };
                 if (importFileKind === FileKind.tender) {
                     // obj.calcBase = getValue(itemSrc, ['_计算基础表达式']);
@@ -853,19 +878,16 @@ const ImportXML = (() => {
         }
         //根据费用类别、配比类比获取人材机类别(后端匹配不到标准人材机的时候用)
         function getGljTypeData(feeType, ratioType, name) {
-            let map = {
-                '-': {type: 201, shortName: '材'},
-                '1-': {type: 1, shortName: '人'},
-                '2-': {type: 201, shortName: '材'},
-                '2-1': {type: 202, shortName: '砼'},
-                '2-2': {type: 205, shortName: '商砼'},
-                '2-3': {type: 203, shortName: '浆'},
-                '2-4': {type: 206, shortName: '商浆'},
-                '2-5': {type: 204, shortName: '配比'},
-                '3-': {type: 301, shortName: '机'},
-                '4-': {type: 4, shortName: '主'},
+            // 因为这份文件不太标准,各家也不统一,没有一个靠谱的方法能准备获取人材机类型,所以有些地方需要特殊处理
+            // 有的人材机类型根据人材机名称特殊获取
+            const specialMap = {
+                '定额管理费': {type: 6, shortName: '管'}
             };
-            let nameMap = {
+            if (specialMap[name]) {
+                return specialMap[name];
+            }
+            // 一些需要特殊处理的动力材料
+            const powerNameMap = {
                 '柴油': {type: 305, shortName: '动'},
                 '柴油(机械用)': {type: 305, shortName: '动'},
                 '柴油(机械用)': {type: 305, shortName: '动'},
@@ -877,12 +899,26 @@ const ImportXML = (() => {
                 '电(机械用)': {type: 305, shortName: '动'},
                 '机上人工': {type: 303, shortName: '机人'},
             };
-            if (feeType === '3' && nameMap[name]) {
-                return nameMap[name] || null;
+            // 人、机不需要匹配配比类别
+            if (feeType === '1') {
+                return {type: 1, shortName: '人'};
+            } else if (feeType === '3') {
+                // 特殊处理动力材料
+                return powerNameMap[name] || {type: 301, shortName: '机'};
             }
+            const map = {
+                '-': {type: 201, shortName: '材'},
+                '2-': {type: 201, shortName: '材'},
+                '2-1': {type: 202, shortName: '砼'},
+                '2-2': {type: 205, shortName: '商砼'},
+                '2-3': {type: 203, shortName: '浆'},
+                '2-4': {type: 206, shortName: '商浆'},
+                '2-5': {type: 204, shortName: '配比'},
+                '4-': {type: 4, shortName: '主'},
+            };
             return map[`${feeType}-${ratioType}`] || null;
         }
-        //主要材料类别-三材类别映射、、
+        //主要材料类别-三材类别映射
         const MaterialMap = {
             '100': 1,   //钢材钢筋认为钢材,因为文件没细分
             '200': 4,   //水泥
@@ -1029,26 +1065,29 @@ const ImportXML = (() => {
             );
             return isCoe ? AdjustType.coe : AdjustType.info;
         }
-        
-        let toMatches = [
-            {reg: /分部分项/, flag: fixedFlag.SUB_ENGINERRING},
-            {reg: /^措施项目/, flag: fixedFlag.MEASURE},
-            {reg: /技术措施/, flag: fixedFlag.CONSTRUCTION_TECH},
-            {reg: /组织措施/, flag: fixedFlag.CONSTRUCTION_ORGANIZATION},
-            {reg: /组织措施/, flag: fixedFlag.CONSTRUCTION_ORGANIZATION},
-            {reg: /安全文明/, flag: fixedFlag.SAFETY_CONSTRUCTION},
-            {reg: /其他项目/, flag: fixedFlag.OTHER},
-            {reg: /暂列金额/, flag: fixedFlag.PROVISIONAL},
-            {reg: /暂估价/, flag: fixedFlag.ESTIMATE},
-            {reg: /计日工/, flag: fixedFlag.DAYWORK},
-            {reg: /总承包服务/, flag: fixedFlag.TURN_KEY_CONTRACT},
-            {reg: /索赔(?:及|与|和)现场签证/, flag: fixedFlag.TURN_KEY_CONTRACT},
-            {reg: /规费/, flag: fixedFlag.CHARGE},
-            {reg: /税金/, flag: fixedFlag.TAX},
-            {reg: /增值税/, flag: fixedFlag.ADDED_VALUE_TAX},
-            {reg: /^附加税/, flag: fixedFlag.ADDITIONAL_TAX},
-            {reg: /环境保护税/, flag: fixedFlag.ENVIRONMENTAL_PROTECTION_TAX},
-            {reg: /(?:合\s*价)|(?:工程造价)/, flag: fixedFlag.ENGINEERINGCOST},
+
+        // 个软件公司确定了对应关系的其他项目、税金,使用费用类别进行匹配,其他按照名称关键字匹配
+        const matchRegs = [
+            {field: 'feeType', reg: /^1300$/, flag: fixedFlag.OTHER},
+            {field: 'feeType', reg: /^900$/, flag: fixedFlag.TAX},
+            {field: 'name', reg: /分部分项/, flag: fixedFlag.SUB_ENGINERRING},
+            {field: 'name', reg: /^措施项目/, flag: fixedFlag.MEASURE},
+            {field: 'name', reg: /技术措施/, flag: fixedFlag.CONSTRUCTION_TECH},
+            {field: 'name', reg: /组织措施/, flag: fixedFlag.CONSTRUCTION_ORGANIZATION},
+            {field: 'name', reg: /组织措施/, flag: fixedFlag.CONSTRUCTION_ORGANIZATION},
+            {field: 'name', reg: /安全文明/, flag: fixedFlag.SAFETY_CONSTRUCTION},
+           // {reg: /其他项目/, flag: fixedFlag.OTHER},
+            {field: 'name', reg: /暂列金额/, flag: fixedFlag.PROVISIONAL},
+            {field: 'name', reg: /暂估价/, flag: fixedFlag.ESTIMATE},
+            {field: 'name', reg: /计日工/, flag: fixedFlag.DAYWORK},
+            {field: 'name', reg: /总承包服务/, flag: fixedFlag.TURN_KEY_CONTRACT},
+            {field: 'name', reg: /索赔(?:及|与|和)现场签证/, flag: fixedFlag.TURN_KEY_CONTRACT},
+            {field: 'name', reg: /规费/, flag: fixedFlag.CHARGE},
+          //  {reg: /税金/, flag: fixedFlag.TAX},
+            {field: 'name', reg: /增值税/, flag: fixedFlag.ADDED_VALUE_TAX},
+            {field: 'name', reg: /^附加税/, flag: fixedFlag.ADDITIONAL_TAX},
+            {field: 'name', reg: /环境保护税/, flag: fixedFlag.ENVIRONMENTAL_PROTECTION_TAX},
+            {field: 'name', reg: /(?:合\s*价)|(?:工程造价)/, flag: fixedFlag.ENGINEERINGCOST}
         ];
         //处理单位工程费用汇总的清单,这一部分没有靠谱的规则,特殊处理并添加相关清单
         function setupFeeSummary(feeSummary, needfulTemplate) {
@@ -1084,8 +1123,8 @@ const ImportXML = (() => {
                 } else {
                     let isMatched = false;
                     //匹配固定项
-                    for (let match of toMatches) {
-                        if (!match.reg.test(feeBills.name)) {
+                    for (let match of matchRegs) {
+                        if (!match.reg.test(feeBills[match.field])) {
                             continue;
                         }
                         let findBills = needfulTemplate.find(nData => getFlag(nData) === match.flag);
@@ -1352,7 +1391,7 @@ const ImportXML = (() => {
             JSCS_JXF: '{技术措施项目定额施工机具使用费}',
             JSCS_ZCF: '{技术措施项目主材费}',
             JSCS_GR: '{技术措施项目人工工日}',
-            JZMJ: '{建筑面积}',
+            //JZMJ: '{建筑面积}',
             RCJJC: '{人材机价差}',
             RGJC: '{人工价差}',
             CLJC: '{材料价差}',
@@ -1374,10 +1413,33 @@ const ImportXML = (() => {
             SJHJ: '{税金}',
             SQGCZJ: '{税前工程造价}'
         };
+        // 检查固定清单引用的基数造成自身循环,比如分部分项部分引用了FBFXHJ
+        // 有文件的单位工程费汇总中,含有技术措施项目费清单,含有基数,且无子项。
+        function isCalcBaseCycle(bills) {
+            const flag = getFlag(bills);
+            if (!flag) {
+                return false;
+            }
+            // 跟固定清单直接相关联的基数,若相关固定清单引用了其下的基数,则造成自身循环
+            const CycleMap = {
+                [fixedFlag.SUB_ENGINERRING]: ['FBFXHJ', 'RGF', 'CLF', 'JXF', 'ZCF', 'GR'],
+                [fixedFlag.MEASURE]: ['CSXMHJ'],
+                [fixedFlag.CONSTRUCTION_ORGANIZATION]: ['ZZCSF'],
+                [fixedFlag.CONSTRUCTION_TECH]: ['JSCSF', 'JSCS_RGF', 'JSCS_CLF', 'JSCS_JXF', 'JSCS_ZCF', 'JSCS_GR'],
+                [fixedFlag.CHARGE]: ['GF'],
+                [fixedFlag.TAX]: ['SJ', 'SJHJ']
+            };
+            const match = CycleMap[flag];
+            if (!match) {
+                return false;
+            }
+            return match.some(item => bills.calcBase.match(new RegExp(`\\b${item}\\b`)));
+        }
         //转换计算基数
         //1.有子项数据,则清空基数
-        //2.行代号引用转换为ID引用
-        //3.对应字典代号转换,对应字典里找不到则设置成金额
+        //2.引用的基数造成自身循环,比如分部分项部分引用了FBFXHJ
+        //3.行代号引用转换为ID引用
+        //4.对应字典代号转换,对应字典里找不到则设置成金额
         function transformCalcBase(billsData) {
             //行代号 - ID映射
             let rowCodeMap = {};
@@ -1400,6 +1462,12 @@ const ImportXML = (() => {
                 if (typeof bills.calcBase !== 'string') {
                     bills.calcBase = String(bills.calcBase);
                 }
+                // 引用的基数造成自循环,清空基数
+                const isCycle = isCalcBaseCycle(bills);
+                if (isCycle) {
+                    bills.calcBase = '';
+                    continue;
+                }
                 //提取基数
                 bills.calcBase = bills.calcBase.replace(/\s/g, '');
                 let bases = bills.calcBase.split(/[\+\-\*\/]/g);
@@ -1526,6 +1594,8 @@ const ImportXML = (() => {
                     }
                     //提取详细数据
                     let needfulTemplate = _.cloneDeep(templateMapping[curTender.property.templateLibID]);
+                    // 重设模板树结构数据
+                    BILLS_UTIL.resetTreeData(needfulTemplate, uuid.v1);
                     let postTenderData = await transformTender(curTender, IDPlaceholder, needfulTemplate);
                     postTenderData.tender.property.rootProjectID = postConstructData.ID;
                     postEngData.tenders.push(postTenderData);
@@ -1589,6 +1659,13 @@ const ImportXML = (() => {
             // 项目人材机customCode、code-原始数据映射,有的导入数据中,有一部分关联自生成材料号CX,有的关联代码...
             // 因此customCode、code都需要跟原始数据映射,通过customCode取不到数据的时候,通过orgCode获取
             let projectGLJMap = {};
+            // 代码映射
+            let customCodeMap = {};
+            // 原始代码映射
+            let originCodeMap = {};
+            function getGLJByMap(code) {
+                return customCodeMap[code] || originCodeMap[code] || null;
+            }
             //投标文件才需要导入定额等数据
             if (importFileKind === FileKind.tender) {
                 tenderData.gljSummary.forEach(pGLJ => {
@@ -1598,14 +1675,17 @@ const ImportXML = (() => {
                     pGLJ.type = pGLJ.type || 201;
                     pGLJ.shortName = pGLJ.shortName || '材';
                     //gljCodeMap[pGLJ.code] = pGLJ;
-                    projectGLJMap[pGLJ.customCode] = pGLJ;
-                    projectGLJMap[pGLJ.code] = pGLJ;
+                    //projectGLJMap[pGLJ.customCode] = pGLJ;
+                    //projectGLJMap[pGLJ.code] = pGLJ;
+                    customCodeMap[pGLJ.customCode] = pGLJ;
+                    originCodeMap[pGLJ.code] = pGLJ;
                 });
                 //处理项目人材机数据
                 tenderData.gljSummary.forEach(pGLJ => {
                     //组成物数据
                     pGLJ.ratios.forEach(ratio => {
-                        let matchData = projectGLJMap[ratio.code];
+                        //let matchData = projectGLJMap[ratio.code];
+                        let matchData = getGLJByMap(ratio.code);
                         ratio.code = matchData.code;
                         ratio.projectGLJID = pGLJ.id; //后端查找标准数据后,方便更新组成物数据
                         ratio.id = IDPlaceholder.ratio++;
@@ -1648,7 +1728,8 @@ const ImportXML = (() => {
                     if (typeof data.seq === 'undefined') {
                         data.seq = data.code;
                     }
-                    const projectGLJ = projectGLJMap[data.code];
+                    //const projectGLJ = projectGLJMap[data.code];
+                    const projectGLJ = getGLJByMap(data.code);
                     if (projectGLJ) {
                         projectGLJ[relatedType] = 1;
                         data.is_related = 1;
@@ -1695,11 +1776,7 @@ const ImportXML = (() => {
                 billsHasRations.forEach(bills => {
                     //处理定额
                     bills.rations.forEach(ration => {
-                        // 定额的取费专业在定额库默认为空的话,取定额取费专业
-                        // 如果是量价、与定额同级的人材机,则取单位工程取费专业
-                        ration.programID = ration.type === RationType.ration
-                            ? tenderData.property.engineering
-                            : tenderData.property.projectEngineering;
+                        ration.programID = getProgramID(ration.name, NameProgramMapping, tenderData.property.projectEngineering);
                         ration.ID = uuid.v1();
                         ration.projectID = tenderData.ID;
                         ration.billsItemID = bills.ID;
@@ -1710,21 +1787,10 @@ const ImportXML = (() => {
                             let tempV = ration.quantity / bills.quantity;
                             ration.contain = isFinite(tempV) ? scMathUtil.roundForObj(tempV, 6) : '0';
                         }
-                        //工程量表达式:工程量 * 单位前的量
-                        /*if (!ration.unit) {
-                            ration.quantityEXP = ration.quantity;
-                        } else {
-                            let unitNum = ration.unit.match(/^\d+/);
-                            //工程量小数位数
-                            let qDecimal = ration.quantity.match(/\.\d+/);
-                            ration.quantityEXP = unitNum
-                                ? scMathUtil.roundForObj(ration.quantity * unitNum[0], qDecimal ? qDecimal[0] - 1 : 0)
-                                : ration.quantity;
-                        }*/
-                        // 问题
                         //处理定额人材机,添加需要的数据
                         ration.rationGljs.forEach(rGLJ => {
-                            let matchGLJ = projectGLJMap[rGLJ.code];
+                            //let matchGLJ = projectGLJMap[rGLJ.code];
+                            let matchGLJ = getGLJByMap(rGLJ.code);
                             if (matchGLJ) {
                                 rGLJ.projectGLJID = matchGLJ.id;
                                 rGLJ.code = matchGLJ.code;

+ 14 - 8
web/building_saas/main/js/views/config_material_view.js

@@ -223,10 +223,13 @@ let configMaterialObj = {
         }
         return _.sortByAll(datas,'code');
     },
-    getEvaluateMaterialDatas:function () {
+    getEvaluateMaterialDatas:function (projectGLJList, evaluateList, decimalObj) {
+        projectGLJList = projectGLJList ? projectGLJList : projectObj.project.projectGLJ.datas.gljList;
+        evaluateList = evaluateList ? evaluateList : projectObj.project.evaluate_list.datas;
+        const billsDecimal = decimalObj ? decimalObj.bills.totalPrice : getDecimal('bills.totalPrice');
         let datas = [];
-        let gljMap = _.indexBy(projectObj.project.projectGLJ.datas.gljList,'id');
-        for(let e of projectObj.project.evaluate_list.datas){
+        let gljMap = _.indexBy(projectGLJList,'id');
+        for(let e of evaluateList){
             let t = {
                 ID:e.ID,
                 projectID: e.projectID,
@@ -255,16 +258,19 @@ let configMaterialObj = {
                 t.remark = pglj.remark;
                 t.quantity = pglj.quantity;
             }
-            t.totalPrice = scMathUtil.roundForObj(parseFloat(t.quantity) * parseFloat(t.marketPrice) ,getDecimal('bills.totalPrice'));
+            t.totalPrice = scMathUtil.roundForObj(parseFloat(t.quantity) * parseFloat(t.marketPrice) ,billsDecimal);
             datas.push(t);
         }
         return _.sortByAll(datas,'code');
     },
 
-    getBidMaterialDatas:function () {
+    getBidMaterialDatas:function (projectGLJList, bidEvaluationList, decimalObj) {
+        projectGLJList = projectGLJList ? projectGLJList : projectObj.project.projectGLJ.datas.gljList;
+        bidEvaluationList = bidEvaluationList ? bidEvaluationList : projectObj.project.bid_evaluation_list.datas;
+        const billsDecimal = decimalObj ? decimalObj.bills.totalPrice : getDecimal('bills.totalPrice');
         let datas = [];
-        let gljMap = _.indexBy(projectObj.project.projectGLJ.datas.gljList,'id');
-        for(let e of projectObj.project.bid_evaluation_list.datas){
+        let gljMap = _.indexBy(projectGLJList,'id');
+        for(let e of bidEvaluationList){
             let t = {
                 ID:e.ID,
                 projectID: e.projectID,
@@ -293,7 +299,7 @@ let configMaterialObj = {
                 t.quantity = pglj.quantity;
                 t.is_evaluate = pglj.is_evaluate;
             }
-            t.totalPrice = scMathUtil.roundForObj(parseFloat(t.quantity) * parseFloat(t.marketPrice) ,getDecimal('bills.totalPrice'));
+            t.totalPrice = scMathUtil.roundForObj(parseFloat(t.quantity) * parseFloat(t.marketPrice) ,billsDecimal);
             datas.push(t);
         }
         return _.sortByAll(datas,'code');

+ 6 - 4
web/building_saas/main/js/views/material_adjust_view.js

@@ -172,10 +172,11 @@ let materialAdjustObj = {
         sheetCommonObj.showData(this.priceCoeSheet, this.priceCoeSetting,this.priceCoeDatas);
         this.priceCoeSheet.setRowCount(this.priceCoeDatas.length);
     },
-    getPirceInfoDatas:function(projectGLJList,contractor_list){
+    getPirceInfoDatas:function(projectGLJList,contractor_list,decimalObj){
         let datas = [];
         let pgljList =   projectGLJList?projectGLJList:projectObj.project.projectGLJ.datas.gljList;
         let contractorList = contractor_list?contractor_list:projectObj.project.contractor_list.datas;
+        let billsDecimal = decimalObj?decimalObj.bills.totalPrice:getDecimal('bills.totalPrice');
         let gljMap = _.indexBy(pgljList,'id');
         for(let e of contractorList){
             let t = {
@@ -205,15 +206,16 @@ let materialAdjustObj = {
                 t.is_evaluate = pglj.is_evaluate;
                 t.quantity = pglj.quantity;
             }
-            t.totalPrice = scMathUtil.roundForObj(parseFloat(t.quantity) * parseFloat(t.marketPrice) ,getDecimal('bills.totalPrice'));
+            t.totalPrice = scMathUtil.roundForObj(parseFloat(t.quantity) * parseFloat(t.marketPrice) ,billsDecimal);
             datas.push(t);
         }
         return _.sortByAll(datas,'code');
     },
-    getPriceCoeDatas:function (projectGLJList,contractor_list,totalFee) {
+    getPriceCoeDatas:function (projectGLJList,contractor_list,totalFee,decimalObj) {
         let datas = [];
         let pgljList =   projectGLJList?projectGLJList:projectObj.project.projectGLJ.datas.gljList;
         let contractorList = contractor_list?contractor_list:projectObj.project.contractor_list.datas;
+        let billsDecimal = decimalObj?decimalObj.bills.totalPrice:getDecimal('bills.totalPrice');
         let gljMap = _.indexBy(pgljList,'id');
         if(!gljUtil.isDef(totalFee)){
             let node =  projectObj.project.Bills.getEngineeringCostNode(projectObj.mainController);
@@ -247,7 +249,7 @@ let materialAdjustObj = {
                 t.quantity = pglj.quantity;
                 t.is_evaluate = pglj.is_evaluate;
             }
-            t.totalPrice = scMathUtil.roundForObj(parseFloat(t.quantity) * parseFloat(t.marketPrice) ,getDecimal('bills.totalPrice'));
+            t.totalPrice = scMathUtil.roundForObj(parseFloat(t.quantity) * parseFloat(t.marketPrice) ,billsDecimal);
             if(totalFee !==0) t.varWeight = scMathUtil.roundForObj(t.totalPrice/totalFee,2);
             datas.push(t);
         }

+ 1 - 0
web/building_saas/pm/html/project-management.html

@@ -951,6 +951,7 @@
 <script src="/lib/JSExpressionEval_src/Date.js"></script>
 <script src="/web/building_saas/glj/js/socket.io.slim.js"></script>
 <script src="/public/web/socket/connection.js"></script>
+<script src="/public/billsUtil.js"></script>
 <script src="/web/building_saas/main/js/models/importStandardInterface.js"></script>
 <script src="/web/building_saas/pm/js/pm_ajax.js"></script>
 <script src="/web/building_saas/pm/js/pm_newMain.js"></script>

+ 4 - 0
web/building_saas/pm/js/pm_import.js

@@ -443,6 +443,10 @@ const importView = (() => {
                     xmlObj.name += `(${moment(Date.now()).format('YYYY-MM-DD HH:mm:ss')})`;
                 }
                 $('#importInterface').modal('hide');
+                //TEST======================
+                /*let importData = await importXML.transformData(xmlObj);
+                console.log(importData);*/
+                //TEST======================
                 pr.start('导入文件', '正在生成文件,请稍候……');
                 let importData = await importXML.transformData(xmlObj);
                 let blob = new Blob([JSON.stringify(importData)], {type: 'text/plain;charset=utf-8'});