Kaynağa Gözat

Merge branch 'master' of http://192.168.1.41:3000/SmartCost/ConstructionCost

chenshilong 5 yıl önce
ebeveyn
işleme
f555536a4a

+ 1 - 1
modules/import/controllers/import_controller.js

@@ -33,7 +33,7 @@ let controller = {
         const result = {
             error: 0
         };
-        result.data = await pm_facade.importInterface(req.body.key, req.body.session.sessionUser.id, req.body.session.sessionCompilation._id);
+        result.data = await pm_facade.importInterface(req.body.key, req.body.session);
         return result;
     },
     async getDataForInterface (req) {

+ 25 - 2
modules/main/facade/project_facade.js

@@ -495,11 +495,34 @@ async function loadSEIProjectData(projectID) {
     };
     return{bills:bills,rations:rations,ration_gljs:ration_gljs,projectGLJs:projectGLJs}
 }
+//从建设项目中取工程信息默认值
+async function setDefaultInfo(rootProjectID,engineerInfos){
+    let rootProject = await projectsModel.findOne({ID:rootProjectID}).lean();
+    if(rootProject){
+        let baseInfo = _.find(rootProject.property.basicInformation,{key:"basicInfo"});
+        if(!baseInfo) return;
+        let baseMap = {};
+        let attrs = ["工程地点","开工日期","竣工日期","编制日期"];
+        for(let item of baseInfo.items){
+            baseMap[item.dispName] = item;
+        }
+        for(let en of engineerInfos){
+            if(attrs.indexOf(en.dispName) != -1){
+                let key = en.dispName;
+                if(key == "工程地点") key = "工程所在地";
+                en.value = baseMap[key].value;
+            }
+        }
+    }
+}
 
 async function setSEILibData(property){
     if(property.engineerInfoLibID){//工程信息指标
-        let engineerInfo = await engineerInfoLib.findOne({'ID':property.engineerInfoLibID});
-        if(engineerInfo) property.engineerInfos = engineerInfo.info;
+        let engineerInfo = await engineerInfoLib.findOne({'ID':property.engineerInfoLibID}).lean();
+        if(engineerInfo){
+            property.engineerInfos = engineerInfo.info;
+            await setDefaultInfo(property.rootProjectID,property.engineerInfos);
+        }
     }
     if(property.engineerFeatureLibID){//工程特征指标
         let engineerFeature = await engineerFeatureLib.findOne({'ID':property.engineerFeatureLibID});

+ 26 - 1
modules/main/middleware/system_setting.js

@@ -3,11 +3,20 @@
  */
 
 module.exports={
-    rationNumberChecking:rationNumberChecking
+    getSystemSetting,
+    rationNumberChecking:rationNumberChecking,
+    tenderNumberChecking
 };
 
 let mongoose = require("mongoose");
 let rationModel = mongoose.model("ration");
+const systemSettingModel = mongoose.model('system_setting');
+const pmFacade = require('../../pm/facade/pm_facade');
+
+// 获取系统设置,这个系统设置正常情况下有存在session中
+async function getSystemSetting() {
+    return await systemSettingModel.findOne({}).lean();
+}
 
 async function rationNumberChecking(req, res, next) {
     if(req.session.systemSetting){
@@ -25,4 +34,20 @@ async function rationNumberChecking(req, res, next) {
         }
     }
     next();
+}
+
+async function tenderNumberChecking(req, res, next) {
+    const data = JSON.parse(req.body.data);
+    const tenderCount = data.tenderCount;
+    if (tenderCount) {
+        const tenderOverrun = await pmFacade.isTenderOverrun(tenderCount, req.session);
+        if (tenderOverrun) {
+            return res.json({
+                error: 1,
+                message: '您创建的项目个数超限,请联系我们的客服人员,或者导出建设项目保存到本地备份,删除云上数据。'
+            });
+        }
+    }
+    next();
+
 }

+ 39 - 1
modules/pm/facade/pm_facade.js

@@ -37,6 +37,7 @@ module.exports={
     downLoadProjectFile:downLoadProjectFile,
     importProcessChecking:importProcessChecking,
     importInterface,
+    isTenderOverrun
 };
 
 
@@ -109,6 +110,7 @@ let qiniu = require("qiniu");
 let fs = require("fs");
 let path = require("path");
 let request = require("request");
+const systemSettingMiddleware = require('../../main/middleware/system_setting');
 
 let qiniu_config = {
     "AccessKey": "_gR1ed4vi1vT2G2YITGSf4_H0fJu_nRS9Tzk3T4z",
@@ -1839,7 +1841,7 @@ async function downloadFileSync(key) {
 }
 
 // 导入接口
-async function importInterface(key, userID, compilationID) {
+async function importInterface(key, session) {
     // 源文件内容文本
     let downloadFilePath = '';
     try {
@@ -1848,7 +1850,18 @@ async function importInterface(key, userID, compilationID) {
         if (!srcData) {
             throw '无有效数据';
         }
+        const userID = session.sessionUser.id;
+        const compilationID = session.sessionCompilation._id;
         const importData = JSON.parse(srcData);
+        let  tenderCount = 0;
+        importData.engs.forEach(eng => {
+            eng.tenders.forEach(tender => {
+                tenderCount += 1;
+            });
+        });
+        if (await isTenderOverrun(tenderCount, session)) {
+            throw '您创建的项目个数超限,请联系我们的客服人员,或者导出建设项目保存到本地备份,删除云上数据。';
+        }
         const projectData = await importProject(importData, userID, compilationID);
         return projectData;
     } catch (err) {
@@ -1877,6 +1890,12 @@ async function importProjects(data,req,updateData) {
             result.error = 1;
             result.msg = `导入失败:您要导入的文件是由“${fileCompilationName}”导出,当前软件是“${curCompilationName}”,请选择正确的费用定额再进行操作!`;
         }else {
+            const tenders = mainData.projects.filter(item => item.projType === projectType.tender);
+            const tenderOverrun = await isTenderOverrun(tenders.length, req.session);
+            if (tenderOverrun) {
+                result.error = 1;
+                result.msg = `您创建的项目个数超限,请联系我们的客服人员,或者导出建设项目保存到本地备份,删除云上数据。`;
+            }
             let [projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap] = await handleMainProjectDatas(mainData,updateData,req.session.sessionUser.id);
             if(datas.length > 1 ){
                 for(let i = 1;i<datas.length;i++){
@@ -2147,4 +2166,23 @@ function uploadToken() {
         domain: qiniu_config.Domain
     }
     return result
+}
+
+// 有些方法无法通过中间件就检查单位工程数量是否超限
+// 需要到具体的业务代码中进行判断
+// 这个方法就是具体业务代码中,需要检查单位工程数量是否超限用
+async function isTenderOverrun(tenderCount, session) {
+    const userID = session.sessionUser.id;
+    const compilation = session.sessionCompilation._id;
+    const compilationVersion = session.compilationVersion || '免费';
+    let systemSetting = session.systemSetting;
+    // 这种情况只有在刚上线此功能时会出现,不考虑时间差
+    if (!systemSetting) {
+        systemSetting = await systemSettingMiddleware.getSystemSetting();
+        session.systemSetting = systemSetting;
+    }
+    const type = compilationVersion.includes('免费') ? 'normal' : 'professional';
+    const limit = systemSetting[type].project;
+    const curTenderCount = await projectModel.count({userID, compilation, projType: 'Tender', '$or':[{deleteInfo: null}, {'deleteInfo.completeDeleted': false}]});
+    return tenderCount + curTenderCount > limit;
 }

+ 3 - 2
modules/pm/routes/pm_route.js

@@ -6,6 +6,7 @@ import BaseController from "../../common/base/base_controller";
 let express = require('express');
 let pmController = require('./../controllers/pm_controller');
 const baseController = new BaseController();
+const systemMiddleware = require('../../main/middleware/system_setting');
 
 module.exports = function (app) {
 
@@ -34,7 +35,7 @@ module.exports = function (app) {
      req.body = {data: '{user_id: user_id, updateData: [{updateType, updateData}]}'}
      data.updateData.updateType: 1 of ['new', 'update', 'delete']
      */
-    pmRouter.post('/updateProjects', pmController.updateProjects);
+    pmRouter.post('/updateProjects', systemMiddleware.tenderNumberChecking, pmController.updateProjects);
     pmRouter.post('/updateMixDatas', pmController.updateMixDatas);
     pmRouter.post('/moveProject', pmController.moveProject);
 
@@ -43,7 +44,7 @@ module.exports = function (app) {
      data.updateData.updateType: 1 of ['update', 'copy']
      */
     pmRouter.post('/getProjectsByQuery', pmController.getProjectsByQuery);
-    pmRouter.post('/copyProjects', pmController.copyProjects);
+    pmRouter.post('/copyProjects', systemMiddleware.tenderNumberChecking, pmController.copyProjects);
     pmRouter.post('/renameProject', pmController.rename);
     pmRouter.post('/beforeOpenProject', pmController.beforeOpenProject);
     pmRouter.post('/getProject', pmController.getProject);

+ 1 - 0
public/scHintBox.html

@@ -180,6 +180,7 @@
     };
 
     $('#hintBox_form').on('hide.bs.modal', function() {
+        $('#hintBox_caption').removeClass('export-check');
         if($.bootstrapLoading) $.bootstrapLoading.end();
         return;
     });

+ 2 - 2
public/web/common_ajax.js

@@ -6,14 +6,14 @@ var CommonAjax = {
     get:function (url,data,cb,dataType) {
         $.get(url,data,cb,dataType)
     },
-    post: function (url, data, successCallback, errorCallback) {
+    post: function (url, data, successCallback, errorCallback, timeout = 50000) {
         $.ajax({
             type:"POST",
             url: url,
             data: {'data': JSON.stringify(data)},
             dataType: 'json',
             cache: false,
-            timeout: 50000,
+            timeout: timeout,
             success: function(result){
                 if (result.error === 0) {
                     if (successCallback) {

+ 3 - 0
public/web/sheet/sheet_common.js

@@ -378,6 +378,9 @@ var sheetCommonObj = {
                 val = setting.getText[setting.header[col].getText](data[row],val)
             }
             sheet.setValue(row, col, val, ch);
+            if(data[row].foreColor && data[row].styleCol == col){
+                sheet.setStyle(row, col, {foreColor: "#ff2a23"});
+            }
         }
         this.setRowStyle(row,sheet,data[row].bgColour);
         if(setting.autoFit==true){//设置自动行高

+ 4 - 1
web/building_saas/css/custom.css

@@ -374,6 +374,9 @@ input.text-right{
     color:#ECE0F5 !important;
     -webkit-text-stroke:.5px #ced4da;
 }
+.z-index-3000 {
+    z-index: 3000;
+}
 .progress-bar {
     position: relative;
     width: 100%;
@@ -452,7 +455,7 @@ input.text-right{
     height: 500px;
 }
 
-@media screen and (max-width: 1366px) {
+@media screen and (max-width: 1366px), (max-height: 768px) {
     .middle-modal-width {
         max-width: 500px;
     }

+ 69 - 14
web/building_saas/main/js/models/exportSEIInterface.js

@@ -29,24 +29,41 @@ let exportUtil = {
 async function exportSEI(projectID) {
 
     let tenderProjects = [];
+    let errorMap = {};
     let result = await ajaxPost("/project/getSEIProjects",{projectID:projectID});
     let pr = new SCComponent.InitProgressBar();
     pr.start('导出数据接口', '正在导出文件,请稍候……');
-    console.log(result);
     let project = getProject(result);
-
     await prepareTenderDatas(tenderProjects,project);
 
 
+    if(!_.isEmpty(errorMap)){
+        showError(errorMap);
+        return pr.end();
+    }
     toXml(project);
 
     pr.end();
 
-
-
+    
+    function showError(errorMap) {
+        let infoList = [];
+        for(let name in errorMap){
+            infoList.push(`<span style="font-weight: bold">单位工程“${name}”下:</span>`);
+            for(let err of errorMap[name]){
+                infoList.push(err);
+            }
+        }
+        if(infoList.length > 20){
+            $('#hintBox_caption').addClass('export-check');
+        }
+        let msg = infoList.join(`<br>`);
+        alert(msg);
+    }
 
     function getProject(orignal) {//取建设项目信息
         let basicInformation = getBaseInfo(orignal);
+        let textTpye =  orignal.property.taxType == 1 ?"一般计税":"简易计税";
         let project = {
             name: "建设项目",
             attrs: [
@@ -54,7 +71,8 @@ async function exportSEI(projectID) {
                 {name:"建设单位",value:getItemValueBykey(basicInformation.items,"constructionUnit")},
                 {name:"施工单位",value:getItemValueBykey(basicInformation.items,"buildingUnit")},
                 {name: "标准名称", value: "重庆市建设工程造价指标采集标准(清单计价)"},
-                {name: "标准版本号", value: "1.1"}
+                {name: "标准版本号", value: "1.1"},
+                {name: "计税模式", value: textTpye}
             ],
             basicInformation:basicInformation,
             children:[]
@@ -93,7 +111,8 @@ async function exportSEI(projectID) {
             children:[],
             valuationType:source.property.valuationType,
             taxType:source.property.taxType,
-            property:source.property
+            property:source.property,
+            name:source.name
         };
         return tender;
     }
@@ -141,6 +160,7 @@ async function exportSEI(projectID) {
             if(projectData) gljUtil.calcProjectGLJQuantity(projectData.projectGLJs,projectData.ration_gljs,projectData.rations,projectData.bills,getDecimal("glj.quantity"),_,scMathUtil);
             let materials = indexObj.getMainMaterialDatas(tender.property.materials,projectData.projectGLJs,tender.property.calcOptions,tender.property.decimal,false,_,scMathUtil);
             for(let m of materials){
+                if(m.name == "其他材料") continue;//2020-01-08  其他材料 不输出字段(不管是否有值)。
                 mainIndex.children.push(getMaterial(m))
             }
             let index = {
@@ -151,8 +171,9 @@ async function exportSEI(projectID) {
             return index;
 
             function getMaterial(m) {
+                let mname = m.exportName?m.exportName:m.name;
                 let material = {
-                    name:m.name,
+                    name:mname,
                     attrs:[
                         {name:"综合单价",value:scMathUtil.roundToString(m.unitPrice,2)},
                         {name:"数量",value:scMathUtil.roundToString(m.quantity,2)},
@@ -184,8 +205,9 @@ async function exportSEI(projectID) {
             return index;
 
             function getQuantity(q) {
+                let qname = q.exportName?q.exportName:q.name;
                 let quantity = {
-                    name:q.name,
+                    name:qname,
                     attrs:[
                         {name:"工程量指标",value:scMathUtil.roundToString(q.quantity,3)},
                         {name:"单位",value:q.quantityIndexUnit}
@@ -213,8 +235,9 @@ async function exportSEI(projectID) {
             return index;
 
             function getEco(e) {
+                let ename = e.exportName?e.exportName:e.name;
                 let eco = {
-                  name:e.name,
+                  name:ename,
                   attrs:[
                       {name:"综合合价",value:scMathUtil.roundToString(e.cost,2)},
                       {name:"单方指标",value:scMathUtil.roundToString(e.unitCost,2)},
@@ -264,13 +287,35 @@ async function exportSEI(projectID) {
                 {name: "备注", value: source.remark}
             ]
         };
+        handleXMLEntity(g.attrs);
         return g;
     }
-
+    // 对每个元素的所有属性值进行特殊字符处理
+    function handleXMLEntity(attrs) {
+        let _xmlEntity = {
+            '&': '&amp;',
+            '\n': '&#xA;',
+            '"': '&quot;',
+            '\'': '&apos;',
+            '<': '&lt;',
+            '>': '&gt;'
+        };
+        for (let attr of attrs) {
+            if (!attr.value) {
+                continue;
+            }
+            for (let [key, value] of Object.entries(_xmlEntity)) {
+                attr.value = attr.value+"";
+                attr.value = attr.value.replace(new RegExp(key, 'g'), value);
+            }
+        }
+    }
 
 
     function setEngineerInfo(tender) {//设置工程信息
         let infos = tender.property.engineerInfos?tender.property.engineerInfos:[];
+        let errors =  infoRequireChecking(infos);
+        if(errors.length > 0) errorMap[tender.name] = errors;
         let info = {
             name:"工程信息",
             attrs:[
@@ -289,8 +334,7 @@ async function exportSEI(projectID) {
                 {name:"计价方式及依据",value:getItemValueByDispName(infos,"计价方式及依据")},
                 {name:"工程类别",value:getItemValueByDispName(infos,"工程类别")},
                 {name:"编制日期",value:getItemValueByDispName(infos,"编制日期")},
-                {name:"审查日期",value:getItemValueByDispName(infos,"审查日期")}
-
+                {name:"审查日期",value:getItemValueByDispName(infos,"审查日期"),required:false}
             ]
         };
         initAtts(info.attrs);
@@ -348,7 +392,7 @@ async function exportSEI(projectID) {
 
         function getValue(node) {
             let value = node.value;
-            if(node.required == true){//必填项的值为空时导出0
+            if(node.required == true || node.cellType == "number"){//必填项的值为空时导出0
                 if(value == undefined || value == null || value == "") value = "0";
             }
             return value;
@@ -367,6 +411,15 @@ async function exportSEI(projectID) {
     }
 
 
+    function infoRequireChecking(items) {
+        let error = [];
+        for(let i of items){
+            if(i.required == true && _.isEmpty(i.value)) error.push(`工程信息 - ${i.dispName}不能为空。`);
+        }
+        return error;
+    }
+
+
     function getItemValueBykey(items,key) {
         let item = _.find(items,{"key":key});
         if(item) return item.value;
@@ -374,7 +427,9 @@ async function exportSEI(projectID) {
     }
     function getItemValueByDispName(items,dispName) {
         let item = _.find(items,{"dispName":dispName});
-        if(item) return item.value;
+        if(item){
+            return item.value;
+        }
         return ""
     }
 

+ 1 - 1
web/building_saas/main/js/views/export_view.js

@@ -231,7 +231,7 @@ const ExportView = (() => {
             STATE.exporting = false;
             STATE.confirming = false;
             //恢复设置提示弹窗 因为是共用的alert
-            $('#hintBox_caption').removeClass('export-check');
+            //$('#hintBox_caption').removeClass('export-check');  已经统一在info_box关闭时删除了
             $('#export input[type="checkbox"]:eq(0)').prop('checked', true);
             if (spread) {
                 spread.destroy();

+ 4 - 0
web/building_saas/main/js/views/index_view.js

@@ -224,6 +224,10 @@ let indexObj= {
                         options:info.options,
                         cellType:info.cellType
                     };
+                    if(info.required == true){
+                        d.foreColor = "#ff2a23";
+                        d.styleCol = 0;
+                    }
                     if(info.cellType)d.dateCol = 1;
                     datas.push(d);
                 }

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

@@ -491,7 +491,7 @@
             <div class="modal-footer">
                 <a href="javascript:void(0);" class="btn btn-primary" id="add-tender-prev" style="display: none">上一步</a>
                 <a href="javascript:void(0);" class="btn btn-primary" id="add-tender-next">下一步</a>
-                <a href="javascript:void(0);" class="btn btn-primary" style="display: none" id="add-tender-confirm">确定</a>
+                <a href="javascript:void(0);" class="btn btn-primary" data-dismiss="modal" style="display: none" id="add-tender-confirm">确定</a>
                 <button type="button" class="btn btn-secondary" data-dismiss="modal" id="add-tender-cancel" style="display: none;">取消</button>
             </div>
         </div>

+ 3 - 2
web/building_saas/pm/js/pm_ajax.js

@@ -25,10 +25,11 @@ var GetAllProjectData = function (callback) {
 }
 // 更新数据到服务器
 var UpdateProjectData = function (updateData, callback, errCB) {
+    const tenderCount = updateData.filter(item => item.updateType === 'new' && item.updateData.projType === projectType.tender).length;
     $.ajax({
         type:"POST",
         url: '/pm/api/updateProjects',
-        data: {'data': JSON.stringify({"user_id": userID, "updateData": updateData})},
+        data: {'data': JSON.stringify({"user_id": userID, "updateData": updateData, tenderCount})},
         dataType: 'json',
         cache: false,
         timeout: 50000,
@@ -39,7 +40,7 @@ var UpdateProjectData = function (updateData, callback, errCB) {
                 if (errCB) {
                     errCB();
                 }
-                alert('error: ' + result.message);
+                alert(result.message);
             }
         },
         error: function(jqXHR, textStatus, errorThrown){

+ 9 - 2
web/building_saas/pm/js/pm_newMain.js

@@ -1699,6 +1699,13 @@ function setupRequiredWarn(compilation) {
     }
 }
 $(document).ready(function() {
+    // 单位工程超限提示后,弹出客服列表
+    $('#hintBox_form').on('hide.bs.modal', function () {
+        const text = $('#hintBox_caption').text();
+        if (text === '您创建的项目个数超限,请联系我们的客服人员,或者导出建设项目保存到本地备份,删除云上数据。') {
+            $('#customerService').click();
+        } 
+    });
     setupRequiredWarn(compilationData);
     $('#add-project-dialog').find('.modal-body').append();
     //绑定项目管理的升降级、上下移按钮事件
@@ -2621,12 +2628,12 @@ $(document).ready(function() {
         projectMap['copy'] = {document:projectData};
         $("#copy-to-dialog").modal('hide');
         $.bootstrapLoading.start();
-        CommonAjax.post('/pm/api/copyProjects',{projectMap:projectMap,user_id: userID},function (result) {
+        CommonAjax.post('/pm/api/copyProjects',{projectMap:projectMap,user_id: userID, tenderCount: 1},function (result) {
             let newNode = projTreeObj.insert(result['copy'].document,parent,next);
             let refreshNodes = projTreeObj.calEngineeringCost(newNode);
             projTreeObj.refreshNodeData(refreshNodes);
             $.bootstrapLoading.end();
-        })
+        }, null, 1000 * 60 * 5);
 
     });
     $('#selectSameTypeProject').click(function(){

+ 2 - 2
web/building_saas/pm/js/pm_share.js

@@ -946,11 +946,11 @@ const pmShare = (function () {
             copyMap.copy = {document: copyData};
             $('#copyShare').modal('hide');
             $.bootstrapLoading.start();
-            CommonAjax.post('/pm/api/copyProjects', {projectMap: copyMap, user_id: userID}, function (rstData) {
+            CommonAjax.post('/pm/api/copyProjects', {projectMap: copyMap, user_id: userID, tenderCount: 1}, function (rstData) {
                 $.bootstrapLoading.end();
             }, function () {
                 $.bootstrapLoading.end();
-            });
+            }, 1000 * 60 * 5);
         });
 
     }

+ 1 - 1
web/common/html/header.html

@@ -183,7 +183,7 @@
 </div>
 <!--激活产品 & 售后服务 & 联系客服-->
 <!--办事处客服列表-->
-<div class="modal fade" id="activ" data-backdrop="static" style="display: none;" aria-hidden="true">
+<div class="modal fade z-index-3000" id="activ" data-backdrop="static" style="display: none;" aria-hidden="true">
     <div class="modal-dialog modal-lg" role="document">
         <div class="modal-content">
             <div class="modal-header">