Quellcode durchsuchen

Merge branch 'master' into 养护调价

chenshilong vor 5 Jahren
Ursprung
Commit
b1a3b96426

+ 2 - 1
modules/all_models/user.js

@@ -29,7 +29,8 @@ let upgrade = mongoose.Schema({
     compilationID:String,//编办ID
     upgrade_time:Number,
     isUpgrade:Boolean,
-    remark:String//描述:广东办刘飞 2018-06-17 启用/关闭
+    remark:String, //描述:广东办刘飞 2018-06-17 启用/关闭
+    deadline: String,
 }, { _id: false })
 
 const userdList = mongoose.Schema({

+ 4 - 0
modules/import/controllers/import_controller.js

@@ -11,6 +11,10 @@ let controller = {
         let data = req.body;
         return await pm_facade.downLoadProjectFile(data);
     },
+    copyConstructionProject:async function(req){
+      let data = req.body;
+      return await pm_facade.copyConstructionProject(data);
+    },
     prepareInitialData: async function(req) {
         const data = req.body;
         return await pm_facade.prepareInitialData(data.userID, data.compilationID, data.example);

+ 1 - 0
modules/import/routes/import_route.js

@@ -9,6 +9,7 @@ module.exports = function (app) {
     importRouter.post('/importProject',importController.action);
     importRouter.post('/exportProject',importController.action);
     importRouter.post('/copyProject',importController.action);
+    importRouter.post('/copyConstructionProject',importController.action);
     importRouter.post('/prepareInitialData',importController.action);
     importRouter.get('/test',function (req,res) {
         res.json("hello word");

+ 5 - 7
modules/main/facade/ration_facade.js

@@ -308,10 +308,9 @@ async function insertNewRation(newData,defaultLibID,std,calQuantity) {//插入
         }
         newData.prefix = '';
         newData.from = std.type === 'complementary' ? 'cpt' : 'std';
-        if(defaultLibID !== std.rationRepId){//借
+        if(newData.from === 'std' && defaultLibID !== std.rationRepId){//借
             newData.prefix = '借';
-        }
-        else if(std.rationRepId === defaultLibID && newData.from === 'cpt') {
+        } else if(newData.from === 'cpt') {
             newData.prefix = '补';
         }
         if(std.feeType == undefined || std.feeType == null || std.feeType ==''){//定额取费专业为空的情况下,取项目属性中的定额取费专业ID
@@ -591,7 +590,7 @@ async function addRationGLJ(std,newRation,compilation,isMaterial,connect_key) {
         if(isMaterial == true){//材料计算添加时要先检查
             for(let sub of std.rationGljList){
                 let t_g = getStdGlj(sub,stdGLJMap,cptGLJMap,{},ext);
-                if(t_g && connect_key == gljUtil.getIndex(t_g)) throw `本定额中包含工料机${t_g.code},与当前工料机编号相同,添加定额失败`;
+                if(t_g && connect_key == gljUtil.getIndex(t_g,['code','name','specs','unit','gljType'])) throw `本定额中包含工料机[${t_g.code}]${t_g.name},与当前工料机编号相同,添加定额失败`;
             }
         }
         for(let sub of std.rationGljList){
@@ -1169,10 +1168,9 @@ async function  updateRation(std,defaultLibID,rationID,billsItemID,projectID,cal
     //定额前缀 none:0, complementary:1, borrow: 2
     ration.prefix = '';
     //借用优先级比补充高
-    if(std.rationRepId !== parseInt(defaultLibID)){//借用
+    if(ration.from === ' std' && std.rationRepId !== parseInt(defaultLibID)){//借用
         ration.prefix = '借';
-    }
-    else if(std.rationRepId === defaultLibID && ration.from === 'cpt') {
+    } else if(ration.from === 'cpt') {
         ration.prefix = '补';
     }
     ration.rationAssList =await createRationAss(std);//生成辅助定额

+ 4 - 1
modules/main/routes/main_route.js

@@ -6,6 +6,7 @@
 import BaseController from "../../common/base/base_controller";
 const projectModel = require("../../pm/models/project_model");
 const pmFacade = require('../../pm/facade/pm_facade');
+const systemSettingModel = require('../../system_setting/model/index');
 import OptionsDao from '../../options/models/optionsModel';
 import optionSetting from '../../options/models/optionTypes';
 let config = require("../../../config/config.js");
@@ -31,6 +32,7 @@ module.exports =function (app) {
                     options = await optionsDao.saveOptions(req.session.sessionUser.id, req.session.sessionCompilation._id, optionSetting);
                 }
                 const markReadProjectIDs = isOpenShareProject ? await pmFacade.markShareItemsRead(projectID, req.session.sessionUser.id) : [];
+                const version = await systemSettingModel.getVersion();
                 res.render('building_saas/main/html/main.html',
                     {
                         userAccount: req.session.userAccount,
@@ -43,7 +45,8 @@ module.exports =function (app) {
                         LicenseKey:config.getLicenseKey(process.env.NODE_ENV),
                         options:JSON.stringify(options),
                         overWriteUrl:req.session.sessionCompilation.overWriteUrl,
-                        markReadProjectIDs: JSON.stringify(markReadProjectIDs)
+                        markReadProjectIDs: JSON.stringify(markReadProjectIDs),
+                        version
                     });
             } else {
                 res.redirect('/pm');

+ 15 - 0
modules/pm/controllers/pm_controller.js

@@ -870,6 +870,21 @@ module.exports = {
         }
         res.json(result);
     },
+    copyConstructionProject: async function (req, res) {
+      let data = JSON.parse(req.body.data);
+      let result = {
+          error: 0
+      };
+      try {
+          data.session = req.session;
+          result.data = await redirectToImportServer(data, "copyConstructionProject", req);
+      } catch (err) {
+          console.log(err);
+          result.error = 1;
+          result.message = err.message;
+      }
+      res.json(result);
+    },
     importProcessChecking:async function (req,res) {
         let result={
             error:0

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

@@ -48,6 +48,7 @@ module.exports={
     uploadToken:uploadToken,
     downLoadProjectFile:downLoadProjectFile,
     importProcessChecking:importProcessChecking,
+    copyConstructionProject,
     isTenderOverrun,
     getWelcomeInfo:getWelcomeInfo
 };
@@ -417,7 +418,7 @@ async function getShareInfoAfterChangePermission(permissionType, receiver, proje
 
 //拷贝例题项目
 //@param {String}userID {Array}projIDs拷贝的例题项目ID(建设项目、文件夹)@return {Boolean}
-async function copyExample(userID, compilation, projIDs){
+async function copyExample(userID, compilation, projIDs,nameMap){
     let allProjs = [],
         IDMapping = {},
         projMapping = {};
@@ -437,6 +438,7 @@ async function copyExample(userID, compilation, projIDs){
         if (i === parentExample.length - 1) {
             data.NextSiblingID = -1;
         }
+        if(nameMap && nameMap[data.ID]) data.name = nameMap[data.ID];
     }
     //获取所有的子项目
     let posterityProjs = await getPosterityProjects(projIDs);
@@ -1696,6 +1698,39 @@ async function importProcessChecking(data){
 
 }
 
+async function copyConstructionProject(data){
+  let log_data = {
+    key:data.key,
+    content: '正在复制建设项目,请稍候……',
+    userID:data.user_id,
+    compilationID: data.session.sessionCompilation._id,
+    status:"start",
+    create_time:+new Date()
+  };
+
+  await importLogsModel.create(log_data);
+  doCopy(data.user_id,data.session.sessionCompilation._id,data.projectID,data.newName,data.key);
+
+  return "start copy";
+  async function doCopy(user_id,compilationId,projectID,newName,key) {
+      let doc = {status:"finish"};
+      try {
+          let nameMap={};
+          nameMap[projectID] = newName
+          let r = await copyExample(user_id,compilationId,[projectID],nameMap);
+          if(r == false){
+              doc.errorMsg = "复制失败,请检查项目是否存在!";
+              doc.status = "error";
+          }
+      }catch (error){
+          console.log(error);
+          doc.errorMsg = "复制失败,请检查项目是否存在!";
+          doc.status = "error";
+      }finally {
+          await importLogsModel.update({key:key},doc);
+      }
+  }
+}
 
 async function importProject(data,req,updateData) {
     let result = {error:0};

+ 1 - 0
modules/pm/routes/pm_route.js

@@ -65,6 +65,7 @@ module.exports = function (app) {
     pmRouter.post('/changeFile', pmController.changeFile);
     pmRouter.post('/exportProject', pmController.exportProject);
     pmRouter.post('/importProject', pmController.importProject);
+    pmRouter.post('/copyConstructionProject',systemMiddleware.tenderNumberChecking,pmController.copyConstructionProject);
     pmRouter.post('/importProcessChecking', pmController.importProcessChecking);
 
 

+ 6 - 2
modules/reports/rpt_component/jpc_cross_tab.js

@@ -238,8 +238,12 @@ JpcCrossTabSrv.prototype.createNew = function(){
                     for (let di = 0; di < data_fields.length; di++) {
                         rowGrandTotal.push(0.0);
                         for (let k = 0; k < me.sortedRowSequence[i][j].length; k++) {
-                            //3. start to sum
-                            rowGrandTotal[di] = rowGrandTotal[di] + 1.0 * JpcFieldHelper.getValue(data_fields[di], me.sortedRowSequence[i][j][k]);
+                            // 3. start to sum
+                            let vTtl = parseFloat(JpcFieldHelper.getValue(data_fields[di], me.sortedRowSequence[i][j][k]));
+                            if (isNaN(vTtl)) {
+                                vTtl = 0;
+                            }
+                            rowGrandTotal[di] = rowGrandTotal[di] + vTtl;
                         }
                     }
                     me.col_sum_fields_value_total[i].push(rowGrandTotal);

+ 11 - 0
modules/system_setting/model/index.js

@@ -0,0 +1,11 @@
+module.exports = {
+    getVersion,
+};
+
+const mongoose = require('mongoose');
+const sysModel = mongoose.model('system_setting');
+
+async function getVersion() {
+    const data = await sysModel.findOne({}, '-_id version').lean();
+    return data && data.version || '';
+}

+ 67 - 4
modules/users/controllers/cld_controller.js

@@ -11,7 +11,9 @@
 import CLDModel from "../models/cld_model";
 import UserModel from "../models/user_model"
 import CompilationModel from "../models/compilation_model";
-let online_facade = require('../facade/online_facade')
+let online_facade = require('../facade/online_facade');
+const SMS = require('../models/sms');
+const moment = require('moment');
 
 class CLDController {
 
@@ -106,6 +108,9 @@ class CLDController {
     async setUsersUpgrade(request, response) {
         let ssoID = request.body.ssoId;
         let compilationID = request.body.cid;
+        let deadline = request.body.deadline || '';
+        let status = parseInt(request.body.status);
+        let smssend = parseInt(request.body.smssend);
         try {
 
             let userModel = new UserModel();
@@ -114,7 +119,7 @@ class CLDController {
 
             let compilationModel = new CompilationModel();
 
-            let compilationData = compilationModel.getCompilationById(compilationID);
+            let compilationData = await compilationModel.getCompilationById(compilationID);
 
             if (compilationData === null || compilationData === undefined) {
                 throw '不存在该编办或者编办未发布';
@@ -129,8 +134,9 @@ class CLDController {
             let upgradeInfo = {
                 compilationID:compilationID,//编办ID
                 upgrade_time:new Date().getTime(),
-                isUpgrade:true,
-                remark: ''
+                isUpgrade: status !== 2,
+                remark: '',
+                deadline: deadline,
             };
 
             if (upgradeIndex === -1) {
@@ -143,6 +149,12 @@ class CLDController {
             let result = await userModel.updateUser(condition, {upgrade_list: upgrade_list});
 
             if (result) {
+                // 短信发送
+                if (smssend) {
+                    // 发送短信
+                    const Sms = new SMS();
+                    await Sms.sendProductMsg(userData.mobile, status, userData.real_name, compilationData.name, deadline);
+                }
                 response.json({error: 0, msg: 'success'});
             } else {
                 throw '更新失败';
@@ -301,6 +313,57 @@ class CLDController {
         }
         response.json(responseData);
     }
+
+    async checkUserCompilationStatus(request, response) {
+        try {
+            let today = moment(new Date()-86400*1000).format('YYYY-MM-DD');
+            let userModel = new UserModel();
+            let userList = await userModel.getDeadlineList({upgrade_list: {$elemMatch:{ deadline: today }}});
+            if (userList.length > 0) {
+                for (let user of userList) {
+                    for (let cul of user.upgrade_list) {
+                        if (cul.deadline === today) {
+                            // cul.deadline = '';
+                            cul.isUpgrade = false;
+                        }
+                    }
+                    let condition = {ssoId: user.ssoId};
+                    await userModel.updateUser(condition, {upgrade_list: user.upgrade_list});
+                }
+            }
+            response.json({error: 0, msg: 'success', data: userList});
+        } catch (error) {
+            response.json({error: 1, msg: error});
+        }
+    }
+
+    async sendCompilationStatusSms(request, response) {
+        try {
+            let today = moment(new Date()-86400*1000).format('YYYY-MM-DD');
+            let userModel = new UserModel();
+            let userList = await userModel.getDeadlineList({upgrade_list: {$elemMatch:{ deadline: today }}});
+            if (userList.length > 0) {
+                let compilationModel = new CompilationModel();
+                const Sms = new SMS();
+                for (let user of userList) {
+                    for (let cul of user.upgrade_list) {
+                        if (cul.deadline === today) {
+                            cul.deadline = '';
+                            // cul.isUpgrade = false;
+                            // 发送短信
+                            let compilationData = await compilationModel.getCompilationById(cul.compilationID);
+                            await Sms.sendProductMsg(user.mobile, 2, user.real_name, compilationData.name, '');
+                        }
+                    }
+                    let condition = {ssoId: user.ssoId};
+                    await userModel.updateUser(condition, {upgrade_list: user.upgrade_list});
+                }
+            }
+            response.json({error: 0, msg: 'success', data: userList});
+        } catch (error) {
+            response.json({error: 1, msg: error});
+        }
+    }
 }
 
 export default CLDController;

+ 11 - 8
modules/users/models/log_model.js

@@ -59,10 +59,10 @@ class LogModel extends BaseModel {
      * @return {Promise}
      */
     async addLoginLog(userId, request) {
-        let ip = request.connection.remoteAddress;
-        ip = ip?ip.split(':'):[];
-        ip = ip[3] === undefined ? '' : ip[3];
-
+        //let ip = request.connection.remoteAddress;
+        // ip = ip?ip.split(':'):[];
+        // ip = ip[3] === undefined ? '' : ip[3];
+        let ip = request.headers["x-real-ip"]? request.headers["x-real-ip"]:"";
         // let ipInfo = '127.0.0.1';//await this.getIpInfoFromApi(ip);
         let ipInfo = await this.getIpInfoFromApi(ip);
 
@@ -119,15 +119,18 @@ class LogModel extends BaseModel {
         if (ip === '127.0.0.1') {
             return '服务器本机访问';
         }
-        let getData = {
-            url: 'http://ip.taobao.com/service/getIpInfo.php?ip=' + ip,
+        let option = {
+            url: 'https://api01.aliyun.venuscn.com/ip?ip=' + ip,
             encoding: 'utf8',
-            timeout:2000
+            timeout:2000,
+            headers: {
+                Authorization: 'APPCODE ' + '85c64bffe70445c4af9df7ae31c7bfcc',
+            }
         };
         return new Promise(function (resolve, reject) {
             try {
                 // 请求接口
-                Request.get(getData, function (err, getResponse, body) {
+                Request(option, function (err, getResponse, body) {
                     if (err) {
                         //throw '请求错误';
                         resolve("请求错误");

+ 45 - 0
modules/users/models/sms.js

@@ -111,6 +111,51 @@ class SMS {
         }
     }
 
+    async sendProductMsg(mobile, status, name, product, deadline) {
+        try {
+            let templateId = 0;
+            switch (status) {
+                case 1: templateId = 746380;break;// 产品升级通知
+                case 2: templateId = 746381;break;// 产品降级通知
+                case 3: templateId = 746379;break;// 产品延期通知
+            }
+            const formData = {
+                smsUser: this.smsUser,
+                templateId: templateId,
+                msgType: 0,
+                phone: mobile,
+            };
+            formData.vars = '{"%name%": "' + name + '", "%product%": "' + product + '"' + (status !== 2 ? ', "%deadline%": "' + deadline + '"' : '') +'}';
+            const signature = await this.getSignature(this.sortDict(formData), this.smskey);
+            formData.signature = signature;
+
+            let postData = {
+                url: this.url,
+                form: formData,
+                encoding: 'utf8'
+            };
+
+            return new Promise(function (resolve, reject) {
+                try {
+                    // 请求接口
+                    Request.post(postData, function (err, postResponse, body) {
+                        if (err) {
+                            throw '请求错误';
+                        }
+                        if (postResponse.statusCode !== 200) {
+                            throw '短信发送失败!';
+                        }
+                        resolve(body);
+                    });
+                } catch (error) {
+                    reject([]);
+                }
+            });
+        } catch (error) {
+            console.log(error);
+        }
+    }
+
     md5(data) {
         var str = data;
         return crypto.createHash("md5").update(str).digest("hex");

+ 17 - 1
modules/users/models/user_model.js

@@ -384,7 +384,7 @@ class UserModel extends BaseModel {
     /**
      * 从session中判断用户是否是免费版
      * @param {String} sessionVersion
-     * @return {Boolean} 
+     * @return {Boolean}
      */
     isFreeFromSession(sessionVersion) {
         if (!sessionVersion) {
@@ -522,6 +522,22 @@ class UserModel extends BaseModel {
         return this.dayMsg[index];
     }
 
+    /**
+     * 获取usercompilation到期列表
+     *
+     * @param {object} condition
+     * @param {number} page
+     * @param {Number} pageSize
+     * @return {promise}
+     */
+    async getDeadlineList(condition = null) {
+
+        let userList = await this.db.find(condition);
+        userList = userList.length > 0 ? userList : [];
+
+        return userList;
+    }
+
 }
 
 export default UserModel;

+ 4 - 0
modules/users/routes/cld_route.js

@@ -30,5 +30,9 @@ module.exports = function (app) {
 
     router.post('/getUserOnlineInfo', cldController.getUserOnlineInfo);
 
+    router.get('/checkUserCompilationStatus', cldController.checkUserCompilationStatus);
+
+    router.get('/sendCompilationStatusSms', cldController.sendCompilationStatusSms);
+
     app.use('/cld',router)
 };

+ 31 - 5
public/common_util.js

@@ -51,12 +51,38 @@
     // 通过F11打开全屏后,没有办法通过代码退出全屏,只能通过F11退出:
     // https://stackoverflow.com/questions/51114885/combining-requestfullscreen-and-f11; https://stackoverflow.com/questions/43392583/fullscreen-api-not-working-if-triggered-with-f11/44368592#44368592;
     function handleFullscreen() {
-        const isFullscreen = window.innerHeight === window.screen.height;
-        if (isFullscreen) {
-            const p = document.exitFullscreen();
-            p.catch(() => alert('按F11即可退出全屏模式'));
+        if (isFullscreen()) {
+            const p = exitFullscreen();
+            if (Object.prototype.toString.call(p) === '[object Promise]') {
+                p.catch(() => alert('按F11即可退出全屏模式'));
+            }
         } else {
-            document.documentElement.requestFullscreen();
+            fullscreen(document.documentElement);
+        }
+    }
+    function isFullscreen() {
+        return window.innerHeight === window.screen.height;
+    }
+    function fullscreen(ele) {
+        if (ele.requestFullscreen) {
+            ele.requestFullscreen();
+        } else if (ele.mozRequestFullscreen) {
+            ele.mozRequestFullScreen();
+        } else if (ele.webkitRequestFullscreen) {
+            ele.webkitRequestFullscreen();
+        } else if (ele.msRequestFullscreen) {
+            ele.msRequestFullscreen();
+        }
+    }
+    function exitFullscreen() {
+        if(document.exitFullscreen) {
+            return document.exitFullscreen();
+        } else if(document.mozCancelFullscreen) {
+            return document.mozCancelFullscreen();
+        } else if(document.webkitExitFullscreen) {
+            return document.webkitExitFullscreen();
+        } else if(document.msExitFullscreen) {
+            return document.msExitFullscreen();
         }
     }
 

+ 27 - 12
public/web/gljUtil.js

@@ -12,7 +12,7 @@ let gljUtil = {
         let rationGljGroup = _.groupBy(rationGLJDatas,'projectGLJID');
         let IDarray =  this.getSubdivisionAndTechBillsLeavesID(billsDatas);//分别取分部分项和技术措施项目的所有叶子清单ID
         let billIDs = IDarray[0],tech_billIDS = IDarray[1];
-
+        let materialCalcMap={};
         //养护没有分部分项消耗量这一说的所以不用管
 
         let qField = isTender==true?"tenderQuantity":"quantity";
@@ -30,6 +30,7 @@ let gljUtil = {
             pglj[qField] = result.quantity;
             quantityMap[pg_index] = pglj;
             if(pglj.code == gljUtil.getElecCode() && pglj.name == '电' && pglj.unit == "kW·h") elecIndex = pg_index;
+            if(pglj.unit_price.calcMaterial == 1) materialCalcMap[pg_index] = true;
         }
         //计算做为组成物的消耗量
         for(let pkey in mixRatioMap){
@@ -45,9 +46,25 @@ let gljUtil = {
             }
         }
 
+        //计算经过场外运输损耗后的总消耗量
+        for(let pglj of project_gljs ){
+          let offSiteTransportLossRate = this.getOffSiteTransportLossRate(pglj);
+          pglj.offSiteTransportLossRate = parseFloat(offSiteTransportLossRate);//scMathUtil.roundForObj(offSiteTransportLossRate,getDecimal("glj.unitPrice")) ;这里再取小数位数太麻烦了,暂时用保存的吧
+          offSiteTransportLossRate = offSiteTransportLossRate/100;
+          pglj.transportLossQuantity =  scMathUtil.roundForObj(pglj.quantity*offSiteTransportLossRate,q_decimal);
+          pglj.quantity = scMathUtil.roundForObj(pglj.quantity +  pglj.transportLossQuantity ,q_decimal);
+        }
         //材料计算中,工料机的消耗量
-        if(projectGLJDatas.freightList) this.setMaterialCalcQuantity(quantityMap,projectGLJDatas.freightList,q_decimal,_,scMathUtil,isTender); //运费计算中工料机的消耗量
-        if(projectGLJDatas.originalList) this.setMaterialCalcQuantity(quantityMap,projectGLJDatas.originalList,q_decimal,_,scMathUtil,isTender);//原价计算中工料机的消耗量
+
+        //材料计算中,有可能A包含B,B包含C。。。。。等情况,为避免循环计算,只多计算一次,不做再深的嵌套处理
+        // 1.先计算做为父工料机的材料计算消耗量
+        if(projectGLJDatas.freightList) this.setMaterialCalcQuantity(quantityMap,materialCalcMap,true,projectGLJDatas.freightList,q_decimal,_,scMathUtil,isTender); //运费计算中工料机的消耗量
+        if(projectGLJDatas.originalList) this.setMaterialCalcQuantity(quantityMap,materialCalcMap,true,projectGLJDatas.originalList,q_decimal,_,scMathUtil,isTender);//原价计算中工料机的消耗量
+
+        // 2.再计算做为子工料机的材料计算消耗量
+        if(projectGLJDatas.freightList) this.setMaterialCalcQuantity(quantityMap,materialCalcMap,false,projectGLJDatas.freightList,q_decimal,_,scMathUtil,isTender); //运费计算中工料机的消耗量
+        if(projectGLJDatas.originalList) this.setMaterialCalcQuantity(quantityMap,materialCalcMap,false,projectGLJDatas.originalList,q_decimal,_,scMathUtil,isTender);//原价计算中工料机的消耗量
+
 
         //因为材料计算里自采材料产生的消耗量和进行材料计算的父工料机的消耗量有关,所以材料计算中的组成物的消耗量要在上一步的组成物计算完后,再计算自采材料里组成物的消耗量,所认两个组成物计算不能合并,而且乘的值也不同
         for(let pkey in mixRatioMap){
@@ -91,16 +108,9 @@ let gljUtil = {
                 }
             }
         }
-        //计算经过场外运输损耗后的总消耗量
-        for(let pglj of project_gljs ){
-            let offSiteTransportLossRate = this.getOffSiteTransportLossRate(pglj);
-            pglj.offSiteTransportLossRate = parseFloat(offSiteTransportLossRate);//scMathUtil.roundForObj(offSiteTransportLossRate,getDecimal("glj.unitPrice")) ;这里再取小数位数太麻烦了,暂时用保存的吧
-            offSiteTransportLossRate = offSiteTransportLossRate/100;
-            pglj.transportLossQuantity =  scMathUtil.roundForObj(pglj.quantity*offSiteTransportLossRate,q_decimal);
-            pglj.quantity = scMathUtil.roundForObj(pglj.quantity +  pglj.transportLossQuantity ,q_decimal);
-        }
+        
     },
-    setMaterialCalcQuantity:function (quantityMap,calcList,q_decimal,_,scMathUtil,isTender) {
+    setMaterialCalcQuantity:function (quantityMap,materialCalcMap,isParent,calcList,q_decimal,_,scMathUtil,isTender) {
         let qField = isTender==true?"tenderQuantity":"quantity";
         for(let t of calcList){
             if(quantityMap[t.connect_key] && quantityMap[t.connect_key][qField] > 0){
@@ -110,6 +120,11 @@ let gljUtil = {
                         let rIndex = this.getIndex(rg);
                         let pglj = quantityMap[rIndex];
                         let ration = rationIDMap[rg.rationID];
+                        if(isParent == true) {//当计算做为材料计算父消耗量时,忽略子消耗量
+                          if(!materialCalcMap[rIndex]) continue
+                        }else{//当计算做为材料计算子消耗量时,忽略父消耗量
+                          if(materialCalcMap[rIndex]) continue
+                        }
                         if(pglj && ration){
                             let rg_quantity = scMathUtil.roundForObj(rg.quantity,q_decimal);
                             let r_quantity = scMathUtil.roundForObj(ration.quantity,q_decimal);

+ 5 - 2
public/web/syntax-detection.js

@@ -151,8 +151,11 @@ function checkSyntax() {
         }
 
         // DOM
-        if (typeof document.documentElement.requestFullscreen !== 'function') {
-            throw new TypeError('document.documentElement.requestFullscreen is not a function');
+        if (typeof document.documentElement.requestFullscreen !== 'function' && 
+            typeof document.documentElement.mozRequestFullscreen !== 'function' &&
+            typeof document.documentElement.webkitRequestFullscreen !== 'function' &&
+            typeof document.documentElement.msRequestFullscreen !== 'function') {
+            throw new TypeError('fullscreen is not a function');
         }
 
     } catch (err) {

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

@@ -45,6 +45,7 @@
         // const G_SHOW_BLOCK_LIB = true;
         const G_SHOW_BLOCK_LIB = false;
         const markReadProjectIDs = JSON.parse('<%- markReadProjectIDs %>');
+        const VERSION = '<%- version %>';
     </script>
 </head>
 

+ 1 - 0
web/building_saas/main/js/views/material_calc_view.js

@@ -1034,6 +1034,7 @@ materialCalcObj = {
 $(function () {
     $("#calc-nav").on('shown.bs.tab', function () {
         materialCalcObj.initTabWidth();
+        materialCalcObj.showDatas();
         projectGljObject.refreshSubViews();
     });
     $("#calTab ul li a").on('shown.bs.tab', function () {

+ 24 - 40
web/building_saas/main/js/views/project_glj_view.js

@@ -269,29 +269,13 @@ projectGljObject={
       $.bootstrapLoading.end();
       return result;
     },
-    getSelectedProjectGLJ:function () {
-        let me = this,data = null;
-        let sheet = me.projectGljSpread.getActiveSheet();
-        if(sheet.name() == 'projectGljSheet'){//projectGljSheet/materialSheet 工料机汇总和三材汇总表
-            let sel = me.projectGljSheet.getSelections()[0];
-            let srow = sel.row == -1||sel.row == ""?0:sel.row;
-            if(me.projectGljSheetData.length>srow){
-                data = me.projectGljSheetData[srow];
-            }
-        }else if(sheet.name() == 'materialTreeSheet'){
-            if(this.materialTree.selected){
-                data = this.materialTree.selected.data;
-            }
-        }
-        return data;
-    },
 
     showMixRatioData:function () {
         let me = this,gljId = null,gljType = null;
         if(!$('#mixRatio-nav').hasClass('active')) return;
         me.mixRatioSpread.setActiveSheetIndex(0);
         let oldSel = me.mixRatioSheet.getSelections()[0];
-        let projectGLJData = me.getSelectedProjectGLJ();
+        let projectGLJData = me.getProjectGLJSelected();
         if(projectGLJData){
             gljId = projectGLJData.id;
             gljType = projectGLJData.type;
@@ -316,10 +300,10 @@ projectGljObject={
         })
     },
     showRelatedRationDatas:function(){
-      let me = this,gljId = null,gljType = null;
+      let me = this;
       if(!$('#ration-nav').hasClass('active')) return;
       me.mixRatioSpread.setActiveSheetIndex(1);
-      let projectGLJData = me.getSelectedProjectGLJ();
+      let projectGLJData = me.getProjectGLJSelected();
       let rationIDMap = {};
       let rations = [];
       if(projectGLJData){
@@ -327,9 +311,9 @@ projectGljObject={
             if(rg.projectGLJID == projectGLJData.id) rationIDMap[rg.rationID] = true;
          } 
          for(let r of projectObj.project.Ration.datas){
-            if(rationIDMap[r.ID] || (r.subType == rationType.gljRation && r.projectGLJID== projectGLJData.id)){
+            if(rationIDMap[r.ID] || (r.type == rationType.gljRation && r.projectGLJID== projectGLJData.id)){
               rations.push(r);
-            }
+            } 
          } 
       }
       this.relatedRationSheetData = rations;
@@ -656,7 +640,7 @@ projectGljObject={
         }
        // parentBasePrice = scMathUtil.roundForObj(parentBasePrice,getDecimal("glj.unitPrice"));
         parentMarketPrice = scMathUtil.roundForObj(parentMarketPrice,getDecimal("glj.unitPrice"));
-        let parentData = me.getSelectedProjectGLJ();
+        let parentData = me.getProjectGLJSelected();
         if(parentData){  //计算受影响的综合电价
             let ext = {};
             ext[parentData.id] = {marketPrice:parentMarketPrice};
@@ -964,7 +948,7 @@ projectGljObject={
         let me = this, deleteRecode = me.mixRatioData[row];
         let consumption = deleteRecode.consumption;
         let [parentMarketPrice, parentBasePrice] = me.getCompositionSumPrice('delete', row);
-        let prowData =  me.getSelectedProjectGLJ();
+        let prowData =  me.getProjectGLJSelected();
         let updateData = {id: deleteRecode.mix_ratio_id, field: 'mix_ratio.consumption' , value: 0, market_price: parentMarketPrice, base_price: parentBasePrice};
         let ext = {};
         ext[prowData.id] = {marketPrice:parentMarketPrice};
@@ -1104,21 +1088,21 @@ projectGljObject={
         return sheetCommonObj.checkData(col,setting, value);
     },
     getProjectGLJSelected:function () {
-        let me = projectGljObject;
-        let sheet = me.projectGljSpread.getActiveSheet();
-        let selectedProjectGLJ = null;
-        if(sheet.name() == 'projectGljSheet'){//projectGljSheet/materialSheet 工料机汇总和三材汇总表
-            let sel = me.projectGljSheet.getSelections()[0];
-            if(sel.row != -1 && me.projectGljSheetData.length>sel.row){
-                selectedProjectGLJ = me.projectGljSheetData[sel.row]
-            }
-        }else if(sheet.name() == 'materialTreeSheet' ){
-            if(me.materialTree.selected){
-                selectedProjectGLJ = me.materialTree.selected.data;
-            }
-        }
-        return selectedProjectGLJ;
-    },
+      let me = this,data = null;
+      let sheet = me.projectGljSpread.getActiveSheet();
+      if(sheet.name() == 'projectGljSheet'){//projectGljSheet/materialSheet 工料机汇总和三材汇总表
+          let sel = me.projectGljSheet.getSelections()[0];
+          let srow = sel.row == -1||sel.row == ""?0:sel.row;
+          if(me.projectGljSheetData.length>srow){
+              data = me.projectGljSheetData[srow];
+          }
+      }else if(sheet.name() == 'materialTreeSheet'){
+          if(this.materialTree.selected){
+              data = this.materialTree.selected.data;
+          }
+      }
+      return data;
+  },
     initProjectGljRightClick:function(){
         //如果当前行是无组成物的“普通材料”、“绿化苗木”、“外购砼构件”、“商品混凝土”、“商品砂浆”,则右键“添加计算材料”按钮有效。
         let me = this;
@@ -1304,6 +1288,7 @@ function getProjectResizeEles() {
 
 
 function loadProjectGljSize() {
+    if(!$('#project-glj-main').is(':visible')) return;
     let me = projectGljObject;
     let pojGljResizeEles = getProjectResizeEles();
     SlideResize.loadVerticalHeight(pojGljResizeEles.eleObj.module, pojGljResizeEles.eleObj, pojGljResizeEles.limit, function () {
@@ -1583,8 +1568,7 @@ $(function () {
     $("#ration-nav").on('shown.bs.tab', function () {
       projectGljObject.mixRatioSpread.refresh();
       projectGljObject.showRelatedRationDatas();
-      console.log('hehe');
-  });
+    });
 });
 
 

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

@@ -445,7 +445,7 @@ const pmShare = (function () {
             let node = tree.items[options.row];
             // Draw Text
             GC.Spread.Sheets.CellTypes.Text.prototype.paint.apply(this, arguments);
-            if (node && node.data.projType === projectType.tender) {
+            if (node && node.data.projType === projectType.tender||node.data.projType === projectType.project) {
                 let text = options.sheet.getText(options.row, options.col);
                 let acStyle = options.sheet.getActualStyle(options.row, options.col),
                     zoom = options.sheet.zoom();
@@ -732,6 +732,7 @@ const pmShare = (function () {
         //data.allowCopy与shareInfo里allowCopy的区别:
         //data.allowCopy为该单位实际的权限(跟着最新的分享信息走,可能随着父项)
         for (let data of datas) {
+            if(data.projType == projectType.project) data.allowCopy = isAllowCopy(userID, data); //20200715 专业版要求能复制建设项目
             if (data.projType === projectType.tender) {
                 data.allowCopy = isAllowCopy(userID, data);
                 data.allowCooperate = isAllowCoop(userID, data);
@@ -782,12 +783,27 @@ const pmShare = (function () {
                     icon: 'fa-copy',
                     disabled: function () {
                         let selected = tree.selected;
-                        return !(selected && selected.data.allowCopy);
+                        return !(selected && selected.data.allowCopy && selected.data.projType === projectType.tender);
                     },
                     callback: function (key, opt) {
                         $('#copyShare').modal('show');
                     }
                 },
+                "copyProject": {
+                  name: "拷贝建设项目",
+                  icon: 'fa-copy',
+                  disabled: function () {
+                      let selected = tree.selected;
+                      return !(selected && selected.data.allowCopy && selected.data.projType === projectType.project);
+                  },
+                  callback: function (key, opt) {
+                      if($(".p-title").text().includes('免费')){
+                        hintBox.versionBox('此功能仅在专业版中提供,免费公用版可选择单个分段进行拷贝');
+                        return;
+                      }
+                      copyContructionProject(tree.selected);
+                  }
+                },
                 "cancel": {
                     name: "清除",
                     icon: 'fa-remove',
@@ -928,6 +944,31 @@ const pmShare = (function () {
             alert(err);
         }
     }
+    //拷贝分享的建设项目
+    //@param {Object}selected 
+    async function copyContructionProject(selected){
+      try {
+        let newName = getCopyName(selected);
+        //获取单项工程的单位工程
+        let projectQuery = {$or: [{deleteInfo: null}, {'deleteInfo.deleted': false}], userID: userID,projType: "Project"};
+        const rstData = await ajaxPost('/pm/api/getProjectsByQuery', {user_id: userID, query: projectQuery, options: '-_id -property'}, false, 10000);
+        for(let project of rstData){
+            if(project.name === newName){
+                alert("已存在此建设项目");
+                return;
+            }
+        }
+        let tenderCount = 0;
+        if(selected.children) tenderCount = selected.children.length;
+        let key = uuid.v1();
+        $.bootstrapLoading.progressStart('拷贝建设项目', true);
+        $("#progress_modal_body").text('正在拷贝建设项目,请稍候……');
+        await ajaxPost('/pm/api/copyConstructionProject', {user_id: userID, tenderCount: tenderCount,key:key,projectID:selected.data.actualTreeInfo.ID,newName:newName});
+        importProcessChecking(key, null, projTreeObj.emitTreeChange);
+      } catch (error) {
+        console.log(error);
+      }
+    }
     //获取拷贝后的名称
     //@param {Object}node @return {String}
     function getCopyName(node) {

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

@@ -36,9 +36,6 @@
                     <a class="dropdown-item" href="/logout">退出登录</a>
                 </div>
             </li>
-            <li class="nav-item">
-                <a id="fullscreen-a" href="javascript:void(0);" class="nav-link" onclick="commonUtil.handleFullscreen()"><span><i class="fa fa-window-maximize "></i> 全屏</span></a>
-            </li>
             <% if (!versionName.includes('免费')) {%>
             <li class="nav-item">
               <a href="user/buy" target="_blank"><img src="/web/building_saas/img/vip.png" data-toggle="tooltip" data-placement="bottom" data-original-title="专业版用户"></a>
@@ -69,6 +66,9 @@
                     <!--  <a class="dropdown-item" href="#">关于</a>-->
                 </div>
             </li>
+            <li class="nav-item">
+                <a id="fullscreen-a" href="javascript:void(0);" class="nav-link" onclick="commonUtil.handleFullscreen()"><span><i class="fa fa-window-maximize "></i> 全屏</span></a>
+            </li>
             <!-- <li class="nav-item">
                  <a class="nav-link new-msg" data-toggle="modal" data-target="#msg" href="javacript:void(0);">
                      <i class="fa fa-envelope-o" aria-hidden="true"></i>&nbsp;2

+ 105 - 4
web/over_write/hunan_2020.js

@@ -1,3 +1,81 @@
+/**
+ * Created by CSL on 2020/07/13.  湖南计算程序、基数 等覆盖。
+ */
+
+let isHN2020 = true;
+
+// 一般计税取不含税市场价、不含税定额价。简易计税取含税市场价、含税定额价。打开项目时,4个价格根据计税类型只载入其二,所以这里可不作区分。
+function overwriteRationCalcBases (taxType){
+    if (typeof rationCalcBases == 'undefined') return;
+    for (let key in rationCalcBases) delete rationCalcBases[key];
+    // let isJY = taxType == '2';
+
+    rationCalcBases['人工费'] = function (node, isTender) {
+        return calcTools.rationBaseFee(node, [gljType.LABOUR], priceTypes.ptMarketPrice, isTender);
+    };
+    rationCalcBases['材料费'] = function (node, isTender) {
+        return calcTools.rationBaseFee(node, baseMaterialTypes, priceTypes.ptMarketPrice, isTender);
+    };
+    rationCalcBases['施工机械使用费'] = function (node, isTender) {
+        return calcTools.rationBaseFee(node, baseMachineTypes, priceTypes.ptMarketPrice, isTender);
+    };
+    rationCalcBases['施工机械人工费'] = function (node, isTender) {
+        return calcTools.machineDetailFee(node, node.data.gljList, [], gljType.MACHINE_LABOUR, isTender);
+    };
+    rationCalcBases['设备购置费'] = function (node, isTender) {
+        return calcTools.rationBaseFee(node, [gljType.EQUIPMENT], priceTypes.ptMarketPrice, isTender);
+    };
+    rationCalcBases['定额人工费'] = function (node, isTender) {
+        return calcTools.rationBaseFee(node, [gljType.LABOUR], priceTypes.ptBasePrice, isTender);
+    };
+    rationCalcBases['定额材料费'] = function (node, isTender) {
+        return calcTools.rationBaseFee(node, baseMaterialTypes, priceTypes.ptBasePrice, isTender);
+    };
+    rationCalcBases['定额施工机械使用费'] = function (node, isTender) {
+        return calcTools.rationBaseFee(node, baseMachineTypes, priceTypes.ptBasePrice, isTender);
+    };
+    rationCalcBases['定额商品砼费'] = function (node, isTender) {
+        return calcTools.rationBaseFee(node, [gljType.COMMERCIAL_CONCRETE, gljType.COMMERCIAL_MORTAR], priceTypes.ptBasePrice, isTender);
+    };
+    rationCalcBases['定额设备费'] = function (node, isTender) {
+        return calcTools.rationBaseFee(node, [gljType.EQUIPMENT], priceTypes.ptBasePrice, isTender);
+    };
+    rationCalcBases['定额外购砼构件费'] = function (node, isTender) {
+        return calcTools.rationBaseFee(node, [gljType.PURCHASE_COMPONENT], priceTypes.ptBasePrice, isTender);
+    };
+    rationCalcBases['定额绿化苗木费'] = function (node, isTender) {
+        return calcTools.rationBaseFee(node, [gljType.GREEN_SEEDLING], priceTypes.ptBasePrice, isTender);
+    }
+};
+
+(function overwriteFeeTypes() {
+    if (typeof cpFeeTypes == 'undefined') return;
+    cpFeeTypes = [
+        {type: 'marketLabour', name: '人工费'},
+        {type: 'marketMaterial', name: '材料费'},
+        {type: 'marketMachine', name: '施工机械使用费'},
+        {type: 'marketMachineLabour', name: '施工机械人工费'},
+        {type: 'marketEquipment', name: '设备购置费'},
+        {type: 'marketDirect', name: '直接费'},
+
+        {type: 'labour', name: '定额人工费'},
+        {type: 'material', name: '定额材料费'},
+        {type: 'machine', name: '定额施工机械使用费'},
+        {type: 'equipment', name: '定额设备费'},
+        {type: 'direct', name: '定额直接费'},
+
+        {type: 'measure', name: '措施费'},
+        {type: 'measure1', name: '措施费I'},
+        {type: 'measure2', name: '措施费II'},
+        {type: 'manage', name: '企业管理费'},
+        {type: 'force', name: '规费'},
+        {type: 'profit', name: '利润'},
+        {type: 'tax', name: '税金'},
+        {type: 'common', name: '建安费'},
+        {type: 'rationCommon', name: '定额建安费'}
+    ];
+})();
+
 // 清单基数
 const progression = ['施工场地建设费', '养护单位项目管理费', '养护单位项目管理费(未单独设置项目部)', '工程监理费', '工程监理费(未实行社会监理)', '设计文件审查费', '工程设计费'];
 const deficiency = {};
@@ -159,9 +237,24 @@ if (typeof baseFigureTemplate !== 'undefined') {
         YHGCFBHSC(tender) {
             return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, [fixedFlag.SAFE_COST], tender, 'common');
         },
-        // 养护工程费(不含设备费) 算法:取清单固定类别是“建筑安装工程费”的金额,扣除设备的金额。
+        // 养护工程费(不含设备费) 算法:取清单固定类别是“建筑安装工程费”的金额,扣除设备的金额(所有设备类型的人材机型定额)
         YHGCFBHSB(tender) {
-            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, [fixedFlag.EQUIPMENT_ACQUISITION_FEE], tender, 'common');
+            let baseFee = this['YHGCF'](tender);
+            const fixedNode = projectObj.project.mainTree.roots.find(node => node.getFlag() === fixedFlag.CONSTRUCTION_INSTALL_FEE);
+            const allSubNodes = [];
+            projectObj.project.mainTree.getAllSubNode(fixedNode, allSubNodes);
+            const equipmentNodes = allSubNodes.filter(node => node.data.type === rationType.gljRation && node.data.subType === gljType.EQUIPMENT);
+            const feeField = 'common';
+            const subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
+            for (const node of equipmentNodes) {
+                const data = node.data;
+                if (cbTools.isUnDef(data.feesIndex) || _.isEmpty(data.feesIndex) ||
+                    cbTools.isUnDef(data.feesIndex[feeField]) || cbTools.isUnDef(data.feesIndex[feeField][subFeeField])) {
+                    continue;
+                }
+                baseFee -= data.feesIndex[feeField][subFeeField];
+            }
+            return baseFee.toDecimal(decimalObj.bills.totalPrice);
         },
         // 定额养护工程费 取清单固定类别是“建筑安装工程费”的定额建安费。
         DEYHGCF(tender) {
@@ -243,7 +336,7 @@ if (typeof baseFigureTemplate !== 'undefined') {
         */
         JCYBF(tender) {
             //建筑安装工程费作为基数
-            const installFee = this['JZAZGCF'](tender);
+            const installFee = this['YHGCF'](tender);
             //年造价增涨
             const costGrowthRate = calcBase.project.property.costGrowthRate
                 ? calcBase.project.property.costGrowthRate
@@ -311,10 +404,18 @@ if (typeof baseFigureTemplate !== 'undefined') {
     };
 }
 
+if (typeof projectObj !== 'undefined') {
+  projectObj.isInsertEquipmentVisable = function (selected) {
+      return true;   //湖南不管是预算或者工程量清单,都显示   
+  }
+}
+
 // CommonJS module
 if (typeof module !== 'undefined' && !module.nodeType) { // 防止module是前端的一个html标签
     module.exports = {
         progression,
         deficiency
     };
-}
+}
+
+