浏览代码

Merge branch 'dev' of http://192.168.1.41:3000/maixinrong/Calculation into dev

Tony Kang 3 年之前
父节点
当前提交
955bb1ef66
共有 64 个文件被更改,包括 2311 次插入493 次删除
  1. 2 2
      app/base/base_service.js
  2. 11 6
      app/controller/change_controller.js
  3. 93 3
      app/controller/material_controller.js
  4. 1 1
      app/controller/report_controller.js
  5. 2 0
      app/controller/revise_controller.js
  6. 172 0
      app/controller/setting_controller.js
  7. 44 30
      app/controller/stage_controller.js
  8. 2 0
      app/controller/tender_controller.js
  9. 0 1
      app/extend/helper.js
  10. 6 0
      app/lib/stage_im.js
  11. 38 25
      app/lib/sum_load.js
  12. 42 0
      app/middleware/auto_finish_logger.js
  13. 6 0
      app/middleware/auto_logger.js
  14. 1 1
      app/public/js/change_information.js
  15. 9 8
      app/public/js/change_information_set.js
  16. 94 24
      app/public/js/change_plan_information.js
  17. 1 1
      app/public/js/gcl_gather.js
  18. 3 1
      app/public/js/ledger.js
  19. 5 5
      app/public/js/material.js
  20. 16 5
      app/public/js/material_checklist.js
  21. 55 4
      app/public/js/material_list.js
  22. 130 2
      app/public/js/measure_material.js
  23. 55 59
      app/public/js/revise.js
  24. 236 8
      app/public/js/setting.js
  25. 3 3
      app/public/js/shares/cs_tools.js
  26. 2 1
      app/public/js/shares/gcl_gather_compare.js
  27. 1 1
      app/public/js/shares/tender_select.js
  28. 2 2
      app/public/js/spreadjs_rela/spreadjs_zh.js
  29. 65 38
      app/public/js/stage.js
  30. 56 15
      app/public/js/stage_change.js
  31. 26 18
      app/public/js/stage_im.js
  32. 8 2
      app/router.js
  33. 1 0
      app/service/change.js
  34. 117 0
      app/service/construction_unit.js
  35. 6 1
      app/service/material.js
  36. 30 0
      app/service/material_list.js
  37. 68 0
      app/service/material_list_gcl.js
  38. 8 0
      app/service/project_account.js
  39. 2 0
      app/service/report_memory.js
  40. 47 0
      app/service/revise_pos.js
  41. 14 2
      app/service/stage.js
  42. 7 2
      app/service/stage_bills.js
  43. 17 5
      app/service/stage_change.js
  44. 2 3
      app/service/stage_change_final.js
  45. 220 0
      app/service/stage_import_change.js
  46. 2 2
      app/service/stage_rela.js
  47. 2 0
      app/view/change/information.ejs
  48. 4 0
      app/view/change/plan_information.ejs
  49. 1 1
      app/view/layout/layout.ejs
  50. 6 0
      app/view/material/index.ejs
  51. 1 0
      app/view/material/list.ejs
  52. 19 1
      app/view/material/modal.ejs
  53. 1 1
      app/view/revise/index.ejs
  54. 55 40
      app/view/setting/user.ejs
  55. 34 20
      app/view/setting/user_modal.ejs
  56. 54 40
      app/view/setting/user_permission.ejs
  57. 16 10
      app/view/setting/user_permission_modal.ejs
  58. 139 0
      app/view/setting/user_unit.ejs
  59. 137 0
      app/view/setting/user_unit_modal.ejs
  60. 7 1
      config/config.default.js
  61. 0 2
      db_script/depart-database-table.js
  62. 2 95
      sql/update.sql
  63. 105 0
      sql/update20220402.sql
  64. 0 1
      test/app/lib/rpt_data_analysis.test.js

+ 2 - 2
app/base/base_service.js

@@ -139,7 +139,7 @@ class BaseService extends Service {
      * @param {Object} condition - 更新的条件筛选
      * @return {Boolean} - 返回更新结果
      */
-    async update(data, condition) {
+    async update(data, condition, transaction = false) {
         if (Object.keys(data).length <= 0 || Object.keys(condition).length <= 0) {
             return false;
         }
@@ -159,7 +159,7 @@ class BaseService extends Service {
         const whereString = where.join(' AND ');
 
         const sql = 'UPDATE ' + this.tableName + ' SET ' + paramString + ' WHERE ' + whereString;
-        const result = await this.db.query(sql, sqlParam);
+        const result = transaction ? await transaction.query(sql, sqlParam) : await this.db.query(sql, sqlParam);
         return result.affectedRows > 0;
     }
 

+ 11 - 6
app/controller/change_controller.js

@@ -79,7 +79,7 @@ module.exports = app => {
                             changeAudit = await ctx.service.changeAudit.getLastUser(c.cid, c.times - 1, status);
                             auditStatus = c.uid === ctx.session.sessionUser.accountId ? 1 : 0;
                             const back_changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, c.cid);
-                            c.stageChangeNum = this.ctx.helper.sum(back_changeUsedData.map(x => { return Math.abs(x.used_qty); }));
+                            c.stageChangeNum = this.ctx.helper.sum(back_changeUsedData.map(x => { return Math.abs(x.qty); }));
                             break;
                         case 6:
                             changeAudit = await ctx.service.changeAudit.getLastBackUser(c.cid, c.times);
@@ -89,7 +89,7 @@ module.exports = app => {
                         case 9:
                             auditStatus = 9;
                             const changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, c.cid);
-                            c.stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.used_qty); }));
+                            c.stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.qty); }));
                             break;
                         default:
                             break;
@@ -563,7 +563,7 @@ module.exports = app => {
 
                 // 获取是否已存在调用变更令
                 const changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, change.cid);
-                renderData.stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.used_qty); }));
+                renderData.stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.qty); }));
                 await this.layout('change/info.ejs', renderData, 'change/info_modal.ejs');
             } catch (err) {
                 this.log(err);
@@ -760,7 +760,7 @@ module.exports = app => {
                 // 获取是否已存在调用变更令
                 let changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, change.cid);
                 changeUsedData = ctx.helper._.filter(changeUsedData, function(item) {
-                    return item.used_qty !== null;
+                    return item.qty !== null;
                 })
                 renderData.changeUsedData = changeUsedData;
                 renderData.stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.used_qty); }));
@@ -1151,6 +1151,11 @@ module.exports = app => {
                     fileData.id = result.insertId;
                     fileData.orginpath = ctx.app.config.fujianOssPath + filepath;
                     delete fileData.filepath;
+                    if (!ctx.helper.canPreview(fileData.fileext)) {
+                        fileData.filepath = `/change/download/file/${fileData.id}`;
+                    } else {
+                        fileData.filepath = ctx.app.config.fujianOssPath + filepath;
+                    }
                     files.push(fileData);
                     ++index;
                 }
@@ -1395,7 +1400,7 @@ module.exports = app => {
 
                 // 获取是否已存在调用变更令
                 const changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, changeData.cid);
-                const stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.used_qty); }));
+                const stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.qty); }));
                 if (stageChangeNum !== 0) {
                     throw '该变更令已被调用,无法重新审批';
                 }
@@ -1451,7 +1456,7 @@ module.exports = app => {
 
                 // 获取是否已存在调用变更令
                 // const changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, changeData.cid);
-                // const stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.used_qty); }));
+                // const stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.qty); }));
                 // if (stageChangeNum !== 0) {
                 //     throw '该变更令已被调用,无法重新审批';
                 // }

+ 93 - 3
app/controller/material_controller.js

@@ -81,6 +81,11 @@ module.exports = app => {
                     }
                     s.decimal = s.decimal ? JSON.parse(s.decimal) : materialConst.decimal;
                 }
+                if (lastMaterial && lastMaterial.status === auditConst.status.checked && ctx.session.sessionUser.accountId === ctx.tender.data.user_id && stages.length > 0) {
+                    renderData.lastMaterialList = lastMaterial.is_new ? await ctx.service.materialListGcl.getAllDataByCondition({ where: { tid: ctx.tender.id } }) : await ctx.service.materialList.getMaterialData(ctx.tender.id, lastMaterial.id);
+                } else {
+                    renderData.lastMaterialList = null;
+                }
                 renderData.stages = stages;
                 renderData.openMaterialTax = openMaterialTax;
                 renderData.allMaterialTax = allMaterialTax;
@@ -166,7 +171,7 @@ module.exports = app => {
                 if (ctx.session.sessionUser.accountId !== ctx.tender.data.user_id) {
                     throw '您无权创建材料调差期';
                 }
-                const data = ctx.request.body;
+                const data = JSON.parse(ctx.request.body.data);
                 if (data.s_order === '') {
                     throw '没有选中计量期';
                 }
@@ -179,10 +184,12 @@ module.exports = app => {
                 if (!newMaterial) {
                     throw '新增材料调差期失败,请重试';
                 }
-                ctx.redirect('/tender/' + ctx.tender.id + '/measure/material/' + newMaterial.order);
+                // ctx.redirect('/tender/' + ctx.tender.id + '/measure/material/' + newMaterial.order);
+                ctx.body = { err: 0, msg: '', data: newMaterial };
             } catch (err) {
                 this.log(err);
-                ctx.redirect(ctx.request.header.referer);
+                // ctx.redirect(ctx.request.header.referer);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
 
@@ -479,6 +486,89 @@ module.exports = app => {
                 responseData.data.materialChecklistData = materialChecklistData.sort(function(a, b) {
                     return ctx.helper.compareCode(a.b_code, b.b_code);
                 });
+                const lastMaterial = await ctx.service.material.getLastestMaterial(ctx.tender.id, true);
+                if (!lastMaterial.is_new) {
+                    const materialListData = await ctx.service.materialList.getMaterialData(ctx.tender.id, lastMaterial.id);
+                    const hadBillsList = [];
+                    for (const ml of materialListData) {
+                        const hb = _.find(hadBillsList, { gcl_id: ml.gcl_id, mb_id: ml.mb_id });
+                        if (!hb) {
+                            hadBillsList.push({
+                                gcl_id: ml.gcl_id,
+                                mb_id: ml.mb_id,
+                                order: ml.order,
+                                quantity: ml.quantity,
+                                expr: ml.expr,
+                            });
+                        }
+                    }
+                    // 批量插入并修改is_new值
+                    await ctx.service.materialListGcl.setData(lastMaterial.id, hadBillsList);
+                    // responseData.data.gclList = hadBillsList;
+                } else {
+                    const hadBillsList = await ctx.service.materialListGcl.getAllDataByCondition({ where: { tid: ctx.tender.id } });
+                    // responseData.data.gclList = hadBillsList;
+                }
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
+        /**
+         * 调差清单新建台账数据获取 (Post)
+         * @param {Object} ctx - egg全局变量
+         * @return {Promise<void>}
+         */
+        async loadGclData(ctx) {
+            try {
+                // const data = JSON.parse(ctx.request.body.data);
+                // const filter = data.filter.split(';');
+                const responseData = { err: 0, msg: '', data: {} };
+                const data = JSON.parse(ctx.request.body.data);
+                if (data.stage_id === '') {
+                    throw '没有选中计量期';
+                }
+                // 取所有已被调用的工料清单表
+                // responseData.data.materialListData = await ctx.service.materialList.getMaterialData(ctx.tender.id, ctx.material.id);
+                // responseData.data.materialNotJoinListData = await ctx.service.materialListNotjoin.getAllDataByCondition({ where: { tid: ctx.tender.id, mid: ctx.material.id } });
+                // 获取清单数据
+                responseData.data.ledger = await ctx.service.ledger.getData(ctx.tender.id);
+                responseData.data.pos = await ctx.service.pos.getPosData({ tid: ctx.tender.id });
+                // 获取所选期数据并合并相加同类清单项
+                responseData.data.curLedgerData = await ctx.service.stageBills.getStagesData(ctx.tender.id, data.stage_id.join(','));
+                responseData.data.curPosData = await ctx.service.stagePos.getStagesData(ctx.tender.id, data.stage_id.join(','), 'list');
+
+                // 获取gclidlist值
+                const lastMaterial = await ctx.service.material.getLastestCompleteMaterial(ctx.tender.id);
+                // responseData.data.materialListData = await ctx.service.materialList.getMaterialData(ctx.tender.id, lastMaterial.id);
+                if (!lastMaterial.is_new) {
+                    const materialListData = await ctx.service.materialList.getMaterialData(ctx.tender.id, lastMaterial.id);
+                    const hadBillsList = [];
+                    for (const ml of materialListData) {
+                        const hb = _.find(hadBillsList, { gcl_id: ml.gcl_id, mb_id: ml.mb_id });
+                        if (!hb) {
+                            hadBillsList.push({
+                                gcl_id: ml.gcl_id,
+                                mb_id: ml.mb_id,
+                                order: ml.order,
+                                quantity: ml.quantity,
+                                expr: ml.expr,
+                            });
+                        }
+                    }
+                    // 批量插入并修改is_new值
+                    await ctx.service.materialListGcl.setData(lastMaterial.id, hadBillsList);
+                    // responseData.data.gclList = hadBillsList;
+                }
+                const hadBillsList = await ctx.service.materialListGcl.getAllDataByCondition({ where: { tid: ctx.tender.id } });
+                responseData.data.gclList = hadBillsList;
+                // 获取清单设置已选清单
+                // const materialChecklistData = await ctx.service.materialChecklist.getAllDataByCondition({ where: { tid: ctx.tender.id } });
+                // responseData.data.materialChecklistData = materialChecklistData.sort(function(a, b) {
+                //     return ctx.helper.compareCode(a.b_code, b.b_code);
+                // });
                 ctx.body = responseData;
             } catch (err) {
                 this.log(err);

+ 1 - 1
app/controller/report_controller.js

@@ -434,7 +434,7 @@ module.exports = app => {
                 signatureRelInfo: roleRel,
                 stageAudit: stgAudit,
                 stageAuditOrg: stgAuditForOrg,
-                debugInfo: ctx.app.config.is_debug ? ctx.debugInfo : null,
+                debugInfo: ctx.session.sessionUser.loginStatus ? ctx.debugInfo : null,
                 customDefine: rptTpl[JV.NODE_CUSTOM_DEFINE],
                 stageFlow,
                 customSelect,

+ 2 - 0
app/controller/revise_controller.js

@@ -647,6 +647,8 @@ module.exports = app => {
                     return await this.ctx.service.revisePos.deletePos(revise.tid, data.postData);
                 case 'paste':
                     return await this.ctx.service.revisePos.pastePosData(revise.tid, revise.id, data.postData);
+                case 'insert':
+                    return await this.ctx.service.revisePos.insertPos(revise.tid, revise.id, data.postData);
                 default:
                     throw '未知操作';
             }

+ 172 - 0
app/controller/setting_controller.js

@@ -17,6 +17,8 @@ const projectLog = require('../const/project_log');
 const imType = require('../const/tender').imType;
 const S2b = require('../lib/s2b');
 const measureType = require('../const/tender').measureType;
+const sendToWormhole = require('stream-wormhole');
+const path = require('path');
 
 module.exports = app => {
 
@@ -107,6 +109,10 @@ module.exports = app => {
                 if (ctx.query.keyword) {
                     keyword = ctx.query.keyword;
                 }
+                let company = '';
+                if (ctx.query.company) {
+                    company = ctx.query.company;
+                }
 
                 const page = ctx.page;
                 const pageSize = ctx.pageSize;
@@ -146,6 +152,8 @@ module.exports = app => {
                     queryData: JSON.stringify(ctx.urlInfo.query),
                 };
 
+                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: projectId } });
+
                 const renderData = {
                     projectData,
                     accountData,
@@ -154,6 +162,8 @@ module.exports = app => {
                     pageInfo,
                     keyword,
                     user_total,
+                    unitList,
+                    company,
                     // rule: JSON.stringify(frontRule),
                 };
                 await this.layout('setting/user.ejs', renderData, 'setting/user_modal.ejs');
@@ -214,6 +224,11 @@ module.exports = app => {
                     keyword = ctx.query.keyword;
                 }
 
+                let company = '';
+                if (ctx.query.company) {
+                    company = ctx.query.company;
+                }
+
                 // 获取数据规则
                 const page = ctx.page;
                 const pageSize = ctx.pageSize;
@@ -238,6 +253,8 @@ module.exports = app => {
                     queryData: JSON.stringify(ctx.urlInfo.query),
                 };
 
+                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: projectId } });
+
                 const renderData = {
                     projectData,
                     accountData,
@@ -247,6 +264,8 @@ module.exports = app => {
                     permissionStr: JSON.stringify(permission),
                     pageInfo,
                     user_total,
+                    unitList,
+                    company,
                     // rule: JSON.stringify(frontRule),
                 };
                 await this.layout('setting/user_permission.ejs', renderData, 'setting/user_permission_modal.ejs');
@@ -257,6 +276,159 @@ module.exports = app => {
         }
 
         /**
+         * 项目权限 -- 参建单位(Get)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async userUnit(ctx) {
+            try {
+                // 获取项目数据
+                const projectId = ctx.session.sessionProject.id;
+                const projectData = await ctx.service.project.getDataById(projectId);
+                if (projectData === null) {
+                    throw '没有对应的项目数据';
+                }
+                if (ctx.session.sessionUser.is_admin === 0) {
+                    throw '没有访问权限';
+                }
+
+                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: projectId } });
+                for (const u of unitList) {
+                    const accountList = await ctx.service.projectAccount.getAllDataByCondition({ where: { project_id: projectId, company: u.name } });
+                    u.account_num = accountList.length;
+                }
+
+                // 获取账号个数
+                const user_total = await ctx.service.projectAccount.count({ project_id: projectId });
+
+                const renderData = {
+                    projectData,
+                    accountGroup,
+                    unitList,
+                    user_total,
+                    fujianOssPath: ctx.app.config.fujianOssPath,
+                };
+                await this.layout('setting/user_unit.ejs', renderData, 'setting/user_unit_modal.ejs');
+            } catch (error) {
+                console.log(error);
+                ctx.redirect('/dashboard');
+            }
+        }
+
+        /**
+         * 项目设置 -- 单位新增设置(Post)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async userUnitAdd(ctx) {
+            const projectData = ctx.session.sessionProject;
+            try {
+                // 验证数据
+                if (projectData.id === undefined) {
+                    throw '不存在对应的项目数据';
+                }
+
+                // 获取验证规则
+                const rule = ctx.service.constructionUnit.rule('add');
+                ctx.validate(rule);
+                ctx.request.body.pid = projectData.id;
+                const result = await ctx.service.constructionUnit.save(ctx.request.body);
+                if (!result) {
+                    throw '保存单位失败';
+                }
+
+                this.setMessage('保存单位成功', this.messageType.SUCCESS);
+                ctx.redirect('/' + ctx.controllerName + '/user/unit');
+            } catch (error) {
+                console.log(error);
+                this.setMessage(error.toString(), this.messageType.ERROR);
+                ctx.redirect(ctx.request.header.referer);
+            }
+        }
+
+        /**
+         * 项目设置 -- 单位编辑(Post)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async userUnitSave(ctx) {
+            const projectData = ctx.session.sessionProject;
+            const responseData = {
+                err: 0, msg: '', data: null,
+            };
+            try {
+                // 验证数据
+                if (projectData.id === undefined) {
+                    throw '不存在对应的项目数据';
+                }
+                const data = JSON.parse(ctx.request.body.data);
+                switch (data.type) {
+                    case 'update':
+                        const post_data = {
+                            pid: projectData.id,
+                            id: data.id,
+                        };
+                        post_data[data.val_name] = data.val;
+                        if (data.name) post_data.name = data.name;
+                        const result = await ctx.service.constructionUnit.save(post_data);
+                        if (!result) {
+                            throw '保存单位失败';
+                        }
+                        if (post_data.name) {
+                            const accountList = await ctx.service.projectAccount.getAllDataByCondition({ where: { project_id: projectData.id, company: post_data.name } });
+                            responseData.data = { account_num: accountList.length };
+                        }
+                        break;
+                    case 'delete':
+                        const accountList = await ctx.service.projectAccount.getAllDataByCondition({ where: { project_id: projectData.id, company: data.name } });
+                        if (accountList.length > 0) {
+                            throw '该单位下还存在账号,无法删除';
+                        }
+                        await ctx.service.constructionUnit.del(data.id);
+                        break;
+                    case 'del-sign':
+                        const info = await ctx.service.constructionUnit.getDataById(data.id);
+                        if (info.sign_path) {
+                            await ctx.app.fujianOss.delete(ctx.app.config.fujianOssFolder + info.sign_path);
+                        }
+                        await ctx.service.constructionUnit.update({ sign_path: null }, { id: info.id });
+                        break;
+                    default:
+                        break;
+                }
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
+        async userUnitUpload(ctx) {
+            const responseData = {
+                err: 0, msg: '', data: null,
+            };
+            try {
+                const stream = await ctx.getFileStream();
+                const create_time = Date.parse(new Date()) / 1000;
+                const fileInfo = path.parse(stream.filename);
+                const filepath = `app/public/upload/sign/unit/qianzhang_${create_time + fileInfo.ext}`;
+                await ctx.app.fujianOss.put(ctx.app.config.fujianOssFolder + filepath, stream);
+                await sendToWormhole(stream);
+                const result = await ctx.service.constructionUnit.update({ sign_path: filepath }, { id: stream.fields.id });
+                if (result) {
+                    responseData.data = { sign_path: filepath };
+                } else {
+                    throw '添加数据库失败';
+                }
+            } catch (err) {
+                this.log(err);
+                responseData.err = 1;
+                responseData.msg = err;
+            }
+            ctx.body = responseData;
+        }
+
+        /**
          * 项目设置 -- 账号启用和停用设置(Post)
          * @param ctx
          * @return {Promise<void>}

+ 44 - 30
app/controller/stage_controller.js

@@ -252,12 +252,8 @@ module.exports = app => {
                 ? await ctx.service.ledger.loadDataFromOss(ctx.tender.id, ctx.stage.ledgerHis.bills_file)
                 : await ctx.service.ledger.getAllDataByCondition({ columns: ledgerColumn, where: { tender_id: ctx.tender.id } });
             const dgnData = await ctx.service.stageBillsDgn.getDgnData(ctx.tender.id);
-            for (const d of dgnData) {
-                const l = ctx.app._.find(ledgerData, { id: d.id });
-                ctx.app._.assignIn(l, d);
-            }
-            let curStageData,
-                preStageData;
+            const importData = await ctx.service.stageImportChange.getImportLid(ctx.stage.id);
+            let curStageData;
             // 当前操作人查看最新数据,其他人查看历史数据
             if (ctx.stage.readOnly) {
                 curStageData = await ctx.service.stageBills.getAuditorStageData2(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder);
@@ -269,25 +265,21 @@ module.exports = app => {
                 }
             }
             // 查询截止上期数据
-            if (ctx.stage.order > 1) {
-                preStageData = await ctx.service.stageBillsFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1);
-            } else {
-                preStageData = [];
-            }
+            const preStageData = ctx.stage.order > 1 ? await ctx.service.stageBillsFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1) : [];
             this.ctx.helper.assignRelaData(ledgerData, [
+                { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id'},
+                { data: importData, fields: ['is_import'], prefix: '', relaId: 'lid'},
                 { data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid' },
                 { data: preStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'used'], prefix: 'pre_', relaId: 'lid' },
             ]);
             return ledgerData;
         }
         async _getStagePosData(ctx, posColumn) {
-            let curStageData,
-                preStageData;
+            let curStageData;
             const posData =  ctx.stage.ledgerHis
                 ? await ctx.service.ledger.loadDataFromOss(ctx.tender.id, ctx.stage.ledgerHis.pos_file)
                 : await ctx.service.pos.getAllDataByCondition({ columns: posColumn, where: { tid: ctx.tender.id } });
             // 根据当前人,或指定对象查询数据
-            // console.time('cur');
             if (ctx.stage.readOnly) {
                 curStageData = await ctx.service.stagePos.getAuditorStageData2(ctx.tender.id,
                     ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder);
@@ -299,18 +291,8 @@ module.exports = app => {
                     await ctx.service.stagePos.deleteById(surplus);
                 }
             }
-            // console.timeEnd('cur');
             // 查询截止上期数据
-            // console.time('pre');
-            if (ctx.stage.order > 1) {
-                preStageData = await ctx.service.stagePosFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1);
-            } else {
-                preStageData = [];
-            }
-            // console.timeEnd('pre');
-            // console.time('assign');
-            // console.log('cur: ' + curStageData.length);
-            // console.log('pre: ' + preStageData.length);
+            const preStageData = ctx.stage.order > 1 ? await ctx.service.stagePosFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1) : [];
             this.ctx.helper.assignRelaData(posData, [
                 { data: curStageData, fields: ['contract_qty', 'contract_expr', 'qc_qty', 'postil'], prefix: '', relaId: 'pid' },
                 { data: preStageData, fields: ['contract_qty', 'qc_qty'], prefix: 'pre_', relaId: 'pid' },
@@ -364,6 +346,9 @@ module.exports = app => {
                         case 'change':
                             responseData.data.changeData = await this._getStageChangeData(ctx);
                             break;
+                        case 'import_change':
+                            responseData.data.import_change = await this.ctx.service.stageImportChange.getStageImportData(this.ctx.stage);
+                            break;
                         case 'dealBills':
                             responseData.data.dealBills = await ctx.service.dealBills.getAllDataByCondition({
                                 where: { tender_id: this.ctx.tender.id },
@@ -576,7 +561,8 @@ module.exports = app => {
                 if (!data.cid) {
                     throw '查询数据错误';
                 }
-                const detailData = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, data.cid);
+                console.log(data);
+                const detailData = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, data.cid, data.is_import);
                 ctx.body = { err: 0, msg: '', data: detailData };
             } catch (err) {
                 this.log(err);
@@ -1055,12 +1041,17 @@ module.exports = app => {
          * @return {Promise<{}>}
          * @private
          */
-        async _getChangeDetailData(tid, sid, cid) {
+        async _getChangeDetailData(tid, sid, cid, isImport) {
             const data = {};
             data.attachments = await this.ctx.service.changeAtt.getChangeAttachment(cid);
             data.bills = await this.ctx.service.changeAuditList.getAllDataByCondition({ where: { cid } });
-            data.addUsedBills = await this.ctx.service.stageChange.getUsedData(tid, cid);
-            data.curUsedBills = await this.ctx.service.stageChange.getStageUsedData(sid, cid);
+            if (isImport) {
+                data.endUsedBills = await this.ctx.service.stageImportChange.getChangeEndUsedData(tid, cid);
+                data.curUsedBills = await this.ctx.service.stageImportChange.getChangeUsedData(sid, cid);
+            } else {
+                data.endUsedBills = await this.ctx.service.stageChange.getUsedData(tid, cid);
+                data.curUsedBills = await this.ctx.service.stageChange.getStageUsedData(sid, cid);
+            }
             return data;
         }
         /**
@@ -1077,14 +1068,37 @@ module.exports = app => {
                 data.changes = await ctx.service.stageChange.getChangeWithUsedInfo(ctx.stage);
                 if (data.changes.length > 0) {
                     const change = data.changes[0];
-                    change.detail = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, change.cid);
+                    change.detail = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, change.cid, false);
                 }
+                data.import_changes = await ctx.service.stageImportChange.getChangeWithUsedInfo(ctx.stage);
+                if (data.import_changes.length > 0) {
+                    const change = data.import_changes[0];
+                    change.detail = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, change.cid, true);
+                }
+                data.used_import_cid = await ctx.service.stageImportChange.getStageUsedChangeId(ctx.stage.id);
                 ctx.body = { err: 0, msg: '', data };
             } catch (err) {
                 this.log(err);
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
+
+        async getImportChangeData(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.import_lid) throw '查询数据错误';
+                const result = {};
+                result.import_changes = await ctx.service.stageImportChange.getChangeWithUsedInfo(ctx.stage, data.import_lid);
+                if (result.import_changes.length > 0) {
+                    const change = result.import_changes[0];
+                    change.detail = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, change.cid, true);
+                }
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
         /**
          * 变更令 (Get)
          * @param ctx

+ 2 - 0
app/controller/tender_controller.js

@@ -1306,6 +1306,8 @@ module.exports = app => {
                         break;
                     case 'stage':
                         const stageData = await this.ctx.service.stageBills.sumLoad(data.lid, data.tenders, data.cover);
+                        stageData.import_change = { target: { import_lid: data.lid } };
+                        stageData.import_change.data = await this.ctx.service.stageImportChange.getLeafXmjStageImportData(this.ctx.stage, data.lid);
                         ctx.body = {err: 0, msg: '', data: stageData};
                         break;
                     default:

+ 0 - 1
app/extend/helper.js

@@ -282,7 +282,6 @@ module.exports = {
                 data,
                 dataType,
             }) : await this.ctx.curl(url);
-            // console.log(response);
             if (response.status !== 200) {
                 if (count > 0) {
                     count = count - 1;

+ 6 - 0
app/lib/stage_im.js

@@ -120,6 +120,7 @@ class StageIm {
         } else {
             this.changes = await this.ctx.service.stageChange.getLastestAllStageData(this.ctx.tender.id, this.ctx.stage.id);
         }
+        this.import_changes = await this.ctx.service.stageImportChange.getStageImportData(this.ctx.stage);
     }
 
     // 检查汇总节点
@@ -497,6 +498,7 @@ class StageIm {
                         im.changes.push(c);
                     }
                 }
+                this.import_changes.forEach(x => { if (x.lid === p.id) im.changes.push(x) });
             } else {
                 for (const pp of posRange) {
                     if ((!pp.qc_qty || pp.qc_qty === 0)) {
@@ -728,6 +730,7 @@ class StageIm {
                         imDefault.changes.push(c);
                     }
                 }
+                this.import_changes.forEach(x => { if (x.lid === p.id) imDefault.changes.push(x) });
 
                 imDefault.contract_jl = this.ctx.helper.add(imDefault.contract_jl, p.contract_qty);
                 imDefault.qc_jl = this.ctx.helper.add(imDefault.qc_jl, p.qc_qty);
@@ -810,6 +813,7 @@ class StageIm {
                     im.changes.push(c);
                 }
             }
+            this.import_changes.forEach(x => { if (x.lid === node.id) im.changes.push(x) });
         } else {
             for (const p of posRange) {
                 if ((!p.qc_qty || p.qc_qty === 0)) {
@@ -984,6 +988,7 @@ class StageIm {
                             im.changes.push(c);
                         }
                     }
+                    this.import_changes.forEach(x => { if (x.lid === p.id) im.changes.push(x) });
                 }
             }
         }
@@ -1061,6 +1066,7 @@ class StageIm {
         this.stage = stage;
         this.details = details;
         this.changes = changes;
+        this.import_changes = [];
 
         const self = this;
         // 初始化

+ 38 - 25
app/lib/sum_load.js

@@ -8,6 +8,7 @@
  * @version
  */
 const Ledger = require('../lib/ledger');
+const auditConst = require('../const/audit');
 
 class loadGclBaseTree {
     /**
@@ -340,9 +341,13 @@ class gatherStageGclTree extends loadGclBaseTree {
                 unit_price: d.unit_price,
                 org_contract_qty: this.cover ? d.contract_qty || 0 : 0,
                 org_contract_tp: this.cover ? d.contract_tp || 0 : 0,
+                org_qc_qty: d.is_import ? 0 : d.qc_qty || 0,
+                org_qc_tp: d.is_import ? 0 : d.qc_tp || 0,
                 org_order: d.order,
                 contract_qty: 0,
                 contract_tp: 0,
+                qc_qty: 0,
+                qc_tp: 0,
                 is_tp: d.is_tp,
                 hasPos: relaPos.length > 0,
             };
@@ -357,12 +362,7 @@ class gatherStageGclTree extends loadGclBaseTree {
         if (!node.change_detail) node.change_detail = [];
         for (const cd of source.change_detail) {
             if (!cd.qty) continue;
-            let ncd = node.change_detail.find(x => { return x.cid === cd.cid; });
-            if (!ncd) {
-                ncd = { cid: cd.cid, c_code: cd.c_code };
-                node.change_detail.push(ncd);
-            }
-            ncd.qty = this.ctx.helper.add(ncd.qty, cd.qty);
+            node.change_detail.push(cd);
         }
     }
     gather(source, parent) {
@@ -376,36 +376,44 @@ class gatherStageGclTree extends loadGclBaseTree {
         } else {
             node.contract_qty = this.ctx.helper.add(node.contract_qty, source.contract_qty);
             node.contract_tp = this.ctx.helper.mul(node.unit_price, node.contract_qty, this.ctx.tender.info.decimal.tp);
+
+            node.qc_qty = this.ctx.helper.add(node.qc_qty, source.qc_qty);
+            node.qc_tp = this.ctx.helper.mul(node.unit_price, node.qc_qty, this.ctx.tender.info.decimal.tp);
         }
         this._gatherChange(node, source);
         return node;
     }
     getUpdateData() {
-        const result = {update: [], errors: []};
+        const result = { update: [], errors: [], qc_detail: [] };
         for (const bn of this.baseNodes) {
-            if (bn.contract_qty !== bn.org_contract_qty || bn.contract_tp !== bn.org_contract_tp) {
-                result.update.push({lid: bn.id, contract_qty: bn.contract_qty, contract_tp: bn.contract_tp });
+            if (!bn.is_import && bn.org_qc_qty !== 0 && bn.qc_qty !== 0) {
+                result.errors.push({ b_code: bn.b_code, name: bn.name, unit: bn.unit, qc_qty: bn.qc_qty, ledger_id: bn.ledger_id, type: 'qc-conflict'});
+                if (bn.contract_qty !== bn.org_contract_qty || bn.contract_tp !== bn.org_contract_tp) {
+                    result.update.push({ lid: bn.id, contract_qty: bn.contract_qty, contract_tp: bn.contract_tp, qc_qty: bn.org_qc_qty, qc_tp: bn.org_qc_tp });
+                }
+                continue;
+            }
+            if (bn.contract_qty !== bn.org_contract_qty || bn.contract_tp !== bn.org_contract_tp || bn.qc_qty !== bn.org_qc_qty) {
+                let data = { lid: bn.id, contract_qty: bn.contract_qty, contract_tp: bn.contract_tp };
+                if (!bn.is_import && bn.org_qc_qty) {
+                    data.qc_qty = bn.org_qc_qty;
+                    data.qc_tp = bn.org_qc_tp;
+                } else {
+                    data.qc_qty = bn.qc_qty;
+                    data.qc_tp = bn.qc_tp;
+                }
+                result.update.push(data);
             }
             if (bn.change_detail && bn.change_detail.length > 0) {
                 for (const cd of bn.change_detail) {
-                    result.errors.push({
-                        ledger_id: bn.ledger_id,
-                        b_code: bn.b_code, name: bn.name, unit: bn.unit,
-                        c_code: cd.c_code, qty: cd.qty, type: 'qc',
+                    result.qc_detail.push({
+                        lid: bn.id, rela_tid: cd.tid, rela_sid: cd.sid, rela_lid: cd.lid, rela_cid: cd.cid, rela_cbid: cd.cbid, rela_qty: cd.qty
                     });
                 }
             }
         }
         for (const i of this.items) {
-            result.errors.push({ b_code: i.b_code, name: i.name, unit: i.unit, qty: i.contract_qty, type: 'miss' });
-            if (i.change_detail && i.change_detail.length > 0) {
-                for (const cd of i.change_detail) {
-                    result.errors.push({
-                        b_code: i.b_code, name: i.name, unit: i.unit,
-                        c_code: cd.c_code, qty: cd.qty, type: 'miss-qc',
-                    });
-                }
-            }
+            result.errors.push({ b_code: i.b_code, name: i.name, unit: i.unit, qty: i.contract_qty, qc_qty: i.qc_qty, type: 'miss' });
         }
         return result;
     }
@@ -493,6 +501,7 @@ class sumLoad {
             if (!b) continue;
             b.contract_qty = csb.contract_qty;
             b.contract_tp = csb.contract_tp;
+            b.qc_qty = csb.qc_qty;
         }
         for (const csc of curStageChange) {
             if (!csc.qty) continue;
@@ -500,9 +509,9 @@ class sumLoad {
             const b = billsIndex[csc.lid];
             if (!b) continue;
             if (!b.change_detail) b.change_detail = [];
-            let c = b.change_detail.find(x => { return x.cid === csc.cid });
+            let c = b.change_detail.find(x => { return x.cbid === csc.cbid });
             if (!c) {
-                c = { cid: csc.cid };
+                c = { tid: csc.tid, sid: csc.sid, cid: csc.cid, cbid: csc.cbid, lid: csc.lid, c_code: csc.c_code };
                 b.change_detail.push(c);
             }
             c.qty = this.ctx.helper.add(c.qty, csc.qty);
@@ -516,7 +525,9 @@ class sumLoad {
         });
         const posterity = await this.ctx.service.ledger.getPosterityByParentId(this.ctx.tender.id, select.ledger_id);
         const stageBills = await this.ctx.service.stageBills.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id);
+        const importLid = await this.ctx.service.stageImportChange.getLeafXmjImportLid(this.ctx.stage.id, select.id);
         this.ctx.helper.assignRelaData(posterity, [
+            { data: importLid, fields: [ 'is_import' ], prefix: '', relaId: 'lid' },
             { data: stageBills, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp' ], prefix: '', relaId: 'lid' },
         ]);
         const pos = await this.ctx.service.revisePos.getData(this.ctx.tender.id);
@@ -528,7 +539,9 @@ class sumLoad {
             const stage = await this.ctx.service.stage.getDataByCondition({tid: tender.tid, order: tender.stage});
             if (!stage) throw '选择的期不存在';
             const curStageData = await this.ctx.service.stageBills.getLastestStageData2(tender.tid, stage.id);
-            const curStageChange = await this.ctx.service.stageChangeFinal.getSumLoadFinalData(stage.id);
+            const curStageChange = stage.status === auditConst.stage.status.checked
+                ? await this.ctx.service.stageChangeFinal.getSumLoadFinalData(stage.id)
+                : await this.ctx.service.stageChange.getSumLoadFinalData(stage.id);
             this._loadCurStageAndChange(billsData, curStageData, curStageChange);
             const billsTree = new Ledger.billsTree(this.ctx, {
                 id: 'ledger_id',

+ 42 - 0
app/middleware/auto_finish_logger.js

@@ -0,0 +1,42 @@
+'use strict';
+
+/**
+ * 自动记录日志
+ *
+ * @author CaiAoLin
+ * @date 2017/10/30
+ * @version
+ */
+const os = require('os');
+
+module.exports = options => {
+    return async function autoFinishLogger(ctx, next) {
+        await next();
+
+        if (ctx.url.indexOf('/public/') === 0) return;
+
+        const responseTime = new Date();
+        const runTime = responseTime.getTime() - ctx.logInfo.time.getTime();
+        let logData;
+        if (ctx.session && ctx.session.sessionUser) {
+            const cpus = os.cpus();
+            const endCpus = cpus.map(x => {
+                return `${((1-x.times.idle/(x.times.idle+x.times.user+x.times.nice+x.times.sys+x.times.irq))*100).toFixed(2)}%`
+            });
+            logData = {
+                requestTime: ctx.logInfo.time, responseTime, runTime,
+                beginCpus: ctx.logInfo.cpus, endCpus,
+                user: ctx.session.sessionUser.accountId, userName: ctx.session.sessionUser.name, loginType: ctx.session.sessionUser.loginType,
+                pCode: ctx.session.sessionProject.code,
+                data: ctx.request.body,
+            };
+        } else {
+            logData = {
+                requestTime: ctx.logTime, responseTime, runTime,
+                data: ctx.body,
+            };
+        }
+        const bLogger = runTime > 500 ? ctx.getLogger('warning') : ctx.getLogger('finish');
+        bLogger.info(JSON.stringify(logData));
+    };
+};

+ 6 - 0
app/middleware/auto_logger.js

@@ -7,6 +7,7 @@
  * @date 2017/10/30
  * @version
  */
+const os = require('os');
 
 module.exports = options => {
     return function* autoLogger(next) {
@@ -36,6 +37,11 @@ module.exports = options => {
             };
             bLogger.info(JSON.stringify(logData));
         }
+        this.logInfo = { time: new Date() };
+        const cpus = os.cpus();
+        this.logInfo.cpus = cpus.map(x => {
+            return `${((1-x.times.idle/(x.times.idle+x.times.user+x.times.nice+x.times.sys+x.times.irq))*100).toFixed(2)}%`
+        });
 
         // 自动记录log的action
         // const autoLogAction = ['save', 'delete'];

+ 1 - 1
app/public/js/change_information.js

@@ -326,7 +326,7 @@ function getAllList(currPageNum = 1) {
         html += `<tr>
         <td width="25"><input type="checkbox" class="check-file" file-id=${att.id}></td>
         <td>${((currPageNum-1)*pageCount)+index+1}</td>
-        <td><a href="javascript:void(0)" class="pl-0 col-11 att-file-name" file-id=${att.id}>${att.filename}${att.fileext}</a></td>
+        <td><a href="${att.filepath}" target="_blank" class="pl-0 col-11 att-file-name" file-id=${att.id}>${att.filename}${att.fileext}</a></td>
         <td>${moment(att.in_time * 1000).format('YYYY-MM-DD')}<br>${bytesToSize(att.filesize)}</td>
         <td>
             <a href="/change/download/file/${att.id}" class="mr-2" title="下载"><span class="fa fa-download text-primary"></span></a>`

+ 9 - 8
app/public/js/change_information_set.js

@@ -154,7 +154,7 @@ $(document).ready(() => {
                 return ZhCalc.round(data.camount, findDecimal(data.unit));
             },
             del_list: function (data) {
-                return !_.find(changeUsedData, { id: data.id }) ? '移除' : '';
+                return !_.find(changeUsedData, { cbid: data.id }) ? '移除' : '';
             }
         },
         readOnly: {
@@ -162,7 +162,7 @@ $(document).ready(() => {
                 return !readOnly && data.lid != 0;
             },
             isEdit2: function (data) {
-                return !readOnly && (data.lid != 0 || (data.lid == 0 && _.findIndex(changeUsedData, { id: data.id }) !== -1));
+                return !readOnly && (data.lid != 0 || (data.lid == 0 && _.findIndex(changeUsedData, { cbid: data.id }) !== -1));
             },
         },
     };
@@ -211,7 +211,7 @@ $(document).ready(() => {
         del: function () {
             const select = SpreadJsObj.getSelectObject(changeSpreadSheet);
             const index = changeList.indexOf(select);
-            if (index > -1 && !_.find(changeUsedData, { id: select.id })) {
+            if (index > -1 && !_.find(changeUsedData, { cbid: select.id })) {
                 postData(window.location.pathname + '/save', {type: 'del', id: select.id}, function (result) {
                     changeList.splice(index, 1);
                     changeSpreadSheet.deleteRows(index, 1);
@@ -240,7 +240,7 @@ $(document).ready(() => {
             const sel = info.sheet.getSelections()[0];
             const col = info.sheet.zh_setting.cols[sel.col];
             const data = SpreadJsObj.getSelectObject(info.sheet);
-            if (col && col.field === 'del_list' && !_.find(changeUsedData, { id: data.id })) {
+            if (col && col.field === 'del_list' && !_.find(changeUsedData, { cbid: data.id })) {
                 changeSpreadObj.del();
             }
             changeSpreadObj.resetXmjSpread(data);
@@ -281,7 +281,7 @@ $(document).ready(() => {
                 }
                 if(col.field === 'camount') {
                     // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
-                    const usedInfo = _.find(changeUsedData, { id: select.id });
+                    const usedInfo = _.find(changeUsedData, { cbid: select.id });
                     if (usedInfo && usedInfo.used_qty >= 0 && validText < usedInfo.used_qty) {
                         toastr.error('清单变更数值必须大于等于已调用值 ' + usedInfo.used_qty);
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -371,7 +371,7 @@ $(document).ready(() => {
                         }
                         if(colSetting.field === 'camount') {
                             // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
-                            const usedInfo = _.find(changeUsedData, { id: sortData[curRow].id });
+                            const usedInfo = _.find(changeUsedData, { cbid: sortData[curRow].id });
                             if (usedInfo && usedInfo.used_qty >= 0 && validText < usedInfo.used_qty) {
                                 toastr.error(hintRow ? '清单' + (hintRow+1) + '行变更数值必须大于等于已调用值 ' + usedInfo.used_qty : '清单变更数值必须大于等于已调用值 ' + usedInfo.used_qty);
                                 bPaste = false;
@@ -619,7 +619,7 @@ $(document).ready(() => {
                         const sel = changeSpreadSheet.getSelections()[0];
                         changeSpreadObj.resetXmjSpread(select);
                         // console.log(select, sel);
-                        if (!readOnly && select && sel.row !== changeSpreadSheet.getRowCount() - 1 && !_.find(changeUsedData, { id: select.id })) {
+                        if (!readOnly && select && sel.row !== changeSpreadSheet.getRowCount() - 1 && !_.find(changeUsedData, { cbid: select.id })) {
                             return false;
                         } else {
                             return true;
@@ -659,7 +659,8 @@ $(document).ready(() => {
                     + '!_!' + (leaf.gcl_id ? leaf.gcl_id : '0') + '!_!' + (leaf.mx_id ? leaf.mx_id : '') + '!_!' +
                     (bwmx !== undefined ? bwmx : leaf.jldy ? leaf.jldy : '') + '*;*' + quantity) !== -1 && isCheck ?
                     'checked' : '';
-                const isUsed = _.find(changeUsedData, { gcl_id: leaf.gcl_id, bwmx: (bwmx ? bwmx : leaf.jldy ? leaf.jldy : ''), oamount: leaf.quantity });
+                const existGcl = _.find(changeList, {gcl_id: leaf.gcl_id, bwmx: (bwmx ? bwmx : leaf.jldy ? leaf.jldy : ''), oamount: leaf.quantity});
+                const isUsed = existGcl ? _.find(changeUsedData, { cbid: existGcl.id }) : null;
                 const isDisabled = isUsed ? 'disabled ' : '';
                 codeHtml += '<tr quantity="' + quantity + '" gcl_id="' + gcl_id + '" mx_id="' + mx_id + '">' +
                     '<td class="text-center">' + (index+1) + (leaf.cid ? '<i class="text-danger" style="font-weight: 900">*</i>' : '') + '</td>' +

+ 94 - 24
app/public/js/change_plan_information.js

@@ -180,6 +180,7 @@ $(document).ready(() => {
 
     const changeSpreadSetting = {
         cols: [
+            {title: '新增单价', colSpan: '1', rowSpan: '2', field: 'new_up', hAlign: 1, width: 35, cellType: 'checkbox', readOnly: 'readOnly.isEdit'},
             {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 110, formatter: '@', readOnly: 'readOnly.isEdit'},
             {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 130, formatter: '@', readOnly: 'readOnly.isEdit'},
             {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: 'readOnly.isEdit', cellType: 'unit', comboItems: changeUnits, comboEdit: true},
@@ -226,6 +227,8 @@ $(document).ready(() => {
             changeSpreadSetting.cols.push(newColTp);
         }
     }
+    changeSpreadSetting.cols.push({title: '备注1', colSpan: '1', rowSpan: '2', field: 'ex_memo1', hAlign: 0, width: 110, formatter: '@', readOnly: 'readOnly.isEdit2'});
+    changeSpreadSetting.cols.push({title: '备注2', colSpan: '1', rowSpan: '2', field: 'ex_memo2', hAlign: 0, width: 110, formatter: '@', readOnly: 'readOnly.isEdit2'});
 
     const changeCol = {
         getValue: {
@@ -255,13 +258,18 @@ $(document).ready(() => {
             isEdit: function (data) {
                 return readOnly;
             },
+            isEdit2: function (data) {
+                return !(!readOnly || change.shenpiPower);
+            },
         },
     };
     const changeSpreadObj = {
         makeSjsFooter: function() {
             // 增加汇总行并设为锁定禁止编辑状态
             changeSpreadSheet.addRows(changeSpreadSheet.getRowCount(), 1);
-            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 0, '合计');
+            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 1, '合计');
+            const cellType1 = new GC.Spread.Sheets.CellTypes.Text();
+            changeSpreadSheet.getCell(changeSpreadSheet.getRowCount() - 1, 0).cellType(cellType1);
             changeSpreadSheet.setStyle(changeSpreadSheet.getRowCount() - 1, -1, style1);
             changeSpreadObj.countSum();
         },
@@ -275,11 +283,15 @@ $(document).ready(() => {
         },
         setRowValueAndSum: function (data, row, col) {
             for (const j in change.listAudits) {
-                const sum = ZhCalc.round(ZhCalc.mul(ZhCalc.round(data.unit_price, unitPriceUnit), parseFloat(changeSpreadSheet.getValue(row, 10 + parseInt(j)*2))), totalPriceUnit);
-                changeSpreadSheet.setValue(row, 11 + j*2, sum !== 0 ? sum : null);
+                const sum = ZhCalc.round(ZhCalc.mul(ZhCalc.round(data.unit_price, unitPriceUnit), parseFloat(changeSpreadSheet.getValue(row, 11 + parseInt(j)*2))), totalPriceUnit);
+                changeSpreadSheet.setValue(row, 12 + j*2, sum !== 0 ? sum : null);
             }
             // const sum = ZhCalc.round(ZhCalc.mul(data.unit_price, data.spamount), totalPriceUnit);
             // changeSpreadSheet.setValue(row, col+1, sum !== 0 ? sum : null);
+            const count = changeSpreadSetting.cols.length;
+            if (col+1 >= count - 2) {
+                return;
+            }
             const rowCount = changeSpreadSheet.getRowCount();
             // 用户的数据合计
             let audit_sum = 0;
@@ -294,20 +306,20 @@ $(document).ready(() => {
                 cSum = 0,
                 sSum = 0;
             for (let i = 0; i < rowCount - 1; i++) {
-                oSum = ZhCalc.add(oSum, changeSpreadSheet.getValue(i, 5));
-                cSum = ZhCalc.add(cSum, changeSpreadSheet.getValue(i, 7));
-                sSum = ZhCalc.add(sSum, changeSpreadSheet.getValue(i, 9));
+                oSum = ZhCalc.add(oSum, changeSpreadSheet.getValue(i, 6));
+                cSum = ZhCalc.add(cSum, changeSpreadSheet.getValue(i, 8));
+                sSum = ZhCalc.add(sSum, changeSpreadSheet.getValue(i, 10));
             }
-            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 5, oSum !== 0 ? oSum : null);
-            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 7, cSum !== 0 ? cSum : null);
-            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 9, sSum !== 0 ? sSum : null);
+            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 6, oSum !== 0 ? oSum : null);
+            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 8, cSum !== 0 ? cSum : null);
+            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 10, sSum !== 0 ? sSum : null);
             // 用户的数据合计
             for (const j in change.listAudits) {
                 let audit_sum = 0;
                 for(let i = 0; i < rowCount - 1; i++){
-                    audit_sum = ZhCalc.add(audit_sum, changeSpreadSheet.getValue(i, 11 + j*2));
+                    audit_sum = ZhCalc.add(audit_sum, changeSpreadSheet.getValue(i, 12 + j*2));
                 }
-                changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 11 + j*2, audit_sum !== 0 ? audit_sum : null);
+                changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 12 + j*2, audit_sum !== 0 ? audit_sum : null);
             }
         },
         deletePress: function (sheet) {
@@ -315,11 +327,13 @@ $(document).ready(() => {
         },
         showHideAudit: function (show = false) {
             const count = changeSpreadSetting.cols.length;
-            for (let i = 10; i < count; i++) {
+            for (let i = 11; i < count; i++) {
                 changeSpreadSheet.setColumnVisible(i, show, GC.Spread.Sheets.SheetArea.viewport);
             }
-            changeSpreadSheet.setColumnVisible(8, !show, GC.Spread.Sheets.SheetArea.viewport);
+            changeSpreadSheet.setColumnVisible(count - 1, true, GC.Spread.Sheets.SheetArea.viewport);
+            changeSpreadSheet.setColumnVisible(count - 2, true, GC.Spread.Sheets.SheetArea.viewport);
             changeSpreadSheet.setColumnVisible(9, !show, GC.Spread.Sheets.SheetArea.viewport);
+            changeSpreadSheet.setColumnVisible(10, !show, GC.Spread.Sheets.SheetArea.viewport);
         },
         valueChanged: function (e, info) {
             // 防止ctrl+z撤销数据
@@ -365,6 +379,9 @@ $(document).ready(() => {
                 const type = SpreadJsObj.getSelectObject(info.sheet) ? 'update' : 'add';
                 const select = type === 'update' ? SpreadJsObj.getSelectObject(info.sheet) : {unit: ''};
                 const col = info.sheet.zh_setting.cols[info.col];
+                if (col.field === 'new_up') {
+                    return;
+                }
                 // 未改变值则不提交
                 let validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : '');
                 const orgValue = type === 'update' ? select[col.field] : '';
@@ -546,12 +563,61 @@ $(document).ready(() => {
                 });
             }
         };
-        // changeSpread.bind(spreadNS.Events.CellChanged, changeSpreadObj.cellChanged);
+        changeSpreadObj.buttonClicked = function (e, info) {
+            if (info.sheet.zh_setting) {
+                const select = SpreadJsObj.getSelectObject(info.sheet);
+                const col = info.sheet.zh_setting.cols[info.col];
+                if(!select) {
+                    toastr.error('请添加清单编号再勾选');
+                    if (info.sheet.isEditing()) {
+                        info.sheet.endEdit(true);
+                    }
+                    return;
+                } else if (col.field === 'new_up') {
+                    if (info.sheet.isEditing()) {
+                        info.sheet.endEdit(true);
+                    }
+                    select.new_up = info.sheet.getValue(info.row, info.col) ? 0 : 1;
+                    postData(preUrl + '/list/save', { type: 'update', updateData: { id: select.id, new_up: select.new_up } }, function (result) {
+                        changeList.splice(info.row, 1, select);
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        changeSpreadObj.countSum();
+                    }, function () {
+                        select.new_up = info.sheet.getValue(info.row, info.col) ? 1 : 0;
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    });
+                }
+                // if (materialCol.readOnly.isEdit(select)) {
+                //     return;
+                // }
+                // if (col.field === 'new_up') {
+                //     if (info.sheet.isEditing()) {
+                //         info.sheet.endEdit(true);
+                //     }
+                //     select.is_summary = info.sheet.getValue(info.row, info.col) ? 1 : 0;
+                //     delete select.waitingLoading;
+                //     // 更新至服务器
+                //     postData(window.location.pathname + '/save', { type:'update', updateData: select }, function (result) {
+                //         m_tp = result.m_tp;
+                //         if (materialTax) {
+                //             m_tax_tp = result.m_tax_tp;
+                //         }
+                //         resetTpTable();
+                //         SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                //     }, function () {
+                //         select.is_summary = info.sheet.getValue(info.row, info.col) ? 0 : 1;
+                //         SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                //     });
+                // }
+            }
+        };
         changeSpread.bind(spreadNS.Events.EditEnded, changeSpreadObj.editEnded);
+        changeSpread.bind(spreadNS.Events.ButtonClicked, changeSpreadObj.buttonClicked);
         changeSpread.bind(spreadNS.Events.ClipboardPasted, changeSpreadObj.clipboardPasted);
         changeSpread.bind(spreadNS.Events.ValueChanged, changeSpreadObj.valueChanged);
         SpreadJsObj.addDeleteBind(changeSpread, changeSpreadObj.deletePress);
 
+
         let batchInsertObj;
         $.contextMenu.types.batchInsert = function (item, opt, root) {
             const self = this;
@@ -706,14 +772,19 @@ $(document).ready(() => {
                     }
                     validText = ZhCalc.round(validText, findDecimal(select.unit)) || 0;
                     // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
+                    select.spamount = ZhCalc.round(validText, findDecimal(select.unit)) || 0;
                 }
                 select[col.field] = validText;
-                select.spamount = ZhCalc.round(validText, findDecimal(select.unit)) || 0;
 
                 const data = {
                     id: select.id,
-                    spamount: select.spamount,
+                    // spamount: select.spamount,
                 };
+                if (col.field === 'ex_memo1' || col.field === 'ex_memo2') {
+                    data[col.field] = select[col.field];
+                } else {
+                    data.spamount = select.spamount;
+                }
                 console.log(data);
 
                 // 更新至服务器
@@ -723,7 +794,9 @@ $(document).ready(() => {
                     changeSpreadObj.setRowValueAndSum(select, info.row, info.col);
                 }, function () {
                     select[col.field] = orgValue;
-                    select.spamount = orgValue;
+                    if (col.field !== 'ex_memo1' && col.field !== 'ex_memo2') {
+                        select.spamount = orgValue;
+                    }
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
                     changeSpreadObj.setRowValueAndSum(select, info.row, info.col);
                 });
@@ -861,14 +934,11 @@ $(document).ready(() => {
             //     postData(preUrl + '/list/save', {type: 'update_tp', updateData: new_tp}, function (result) {
             //     });
             // }
-            if (change.status === auditConst.status.checked) {
-                changeSpreadObj.showHideAudit(false);
-            } else {
-                changeSpreadObj.showHideAudit(true);
-            }
+            changeSpreadObj.showHideAudit(true);
+        }
+        if (change.status === auditConst.status.checked) {
+            changeSpreadObj.showHideAudit(false);
         }
-
-
     });
 
     // 审批流程展示与隐藏

+ 1 - 1
app/public/js/gcl_gather.js

@@ -398,7 +398,7 @@ const gclGatherModel = (function () {
         calculateGatherData();
         gatherDealBillsData();
         gclList.sort(function (a, b) {
-            return compareCode(a.b_code, b.b_code);
+            return compareCode(a.b_code, b.b_code) || ZhCalc.sub(a.unit_price, b.unit_price);
         });
 
         return gclList;

+ 3 - 1
app/public/js/ledger.js

@@ -1979,9 +1979,11 @@ $(document).ready(function() {
             const sortData = sheet.zh_data;
             const row = selection[0].row;
             const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
+            const select = SpreadJsObj.getSelectObject(sheet);
+            const porder = select ? select.porder : sortData.length + 1;
             const data = {
                 updateType: 'insert',
-                updateData: [ { lid: node.id, tid: tender.id, porder: Math.min(row + 1, sortData.length + 1) } ],
+                updateData: [ { lid: node.id, tid: tender.id, porder } ],
             };
             postData('/tender/' + getTenderId() + '/pos/update', data, function (result) {
                 const updateRst = pos.updateDatas(result.pos);

+ 5 - 5
app/public/js/material.js

@@ -133,9 +133,9 @@ $(document).ready(() => {
     );
     if (materialTax) {
         materialSpreadSettingCols = _.concat(materialSpreadSettingCols, [
-            {title: '本期材料调差|上涨幅度(%)', colSpan: '5|1', rowSpan: '1|1', field: 'm_up_risk', hAlign: 2, width: 90, type: 'Number', readOnly: 'readOnly.isEdit'},
-            {title: '|下跌幅度(%)', colSpan: '|1', rowSpan: '|1', field: 'm_down_risk', hAlign: 2, width: 90, type: 'Number', readOnly: 'readOnly.isEdit'},
-            {title: '|有效价差', colSpan: '|1', rowSpan: '|1', field: 'm_spread', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.m_spread'},
+            {title: '本期材料调差|上涨 幅度(%)', colSpan: '5|1', rowSpan: '1|1', field: 'm_up_risk', hAlign: 2, width: 50, type: 'Number', readOnly: 'readOnly.isEdit'},
+            {title: '|下跌 幅度(%)', colSpan: '|1', rowSpan: '|1', field: 'm_down_risk', hAlign: 2, width: 50, type: 'Number', readOnly: 'readOnly.isEdit'},
+            {title: '|有效价差', colSpan: '|1', rowSpan: '|1', field: 'm_spread', hAlign: 2, width: 60, type: 'Number', readOnly: true, getValue: 'getValue.m_spread'},
             {title: '|调差金额', colSpan: '|1', rowSpan: '|1', field: 'm_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true, getValue: 'getValue.m_tp'},
             {title: '|调差金额(材料税)', colSpan: '|1', rowSpan: '|1', field: 'm_tax_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true, getValue: 'getValue.m_tax_tp'},
             {title: '截止上期|调差金额', colSpan: '2|1', rowSpan: '1|1', field: 'pre_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true},
@@ -145,8 +145,8 @@ $(document).ready(() => {
         ])
     } else {
         materialSpreadSettingCols = _.concat(materialSpreadSettingCols, [
-            {title: '本期材料调差|上涨幅度(%)', colSpan: '4|1', rowSpan: '1|1', field: 'm_up_risk', hAlign: 2, width: 90, type: 'Number', readOnly: 'readOnly.isEdit'},
-            {title: '|下跌幅度(%)', colSpan: '|1', rowSpan: '|1', field: 'm_down_risk', hAlign: 2, width: 90, type: 'Number', readOnly: 'readOnly.isEdit'},
+            {title: '本期材料调差|上涨 幅度(%)', colSpan: '4|1', rowSpan: '1|1', field: 'm_up_risk', hAlign: 2, width: 50, type: 'Number', readOnly: 'readOnly.isEdit'},
+            {title: '|下跌 幅度(%)', colSpan: '|1', rowSpan: '|1', field: 'm_down_risk', hAlign: 2, width: 50, type: 'Number', readOnly: 'readOnly.isEdit'},
             {title: '|有效价差', colSpan: '|1', rowSpan: '|1', field: 'm_spread', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.m_spread'},
             {title: '|调差金额', colSpan: '|1', rowSpan: '|1', field: 'm_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true, getValue: 'getValue.m_tp'},
             {title: '截止上期|调差金额', colSpan: '1|1', rowSpan: '1|1', field: 'pre_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true},

+ 16 - 5
app/public/js/material_checklist.js

@@ -162,14 +162,25 @@ $(document).ready(() => {
             // 判断是否已不存在工料清单,台账修改过后删除之
             if (!gcl) {
                 removeData.push(mc.id);
-            }
-            // 更新had_bills值
-            if (mc.had_bills === 1) {
-                if (_.indexOf(hadBillsidList, gcl.leafXmjs ? gcl.leafXmjs[0].gcl_id : null) === -1) {
-                    updateData.push({ id: mc.id, mid: materialID, had_bills: 0 });
+            } else {
+                // 更新had_bills值
+                const updateObj = { id: mc.id };
+                if (mc.had_bills === 1) {
+                    if (_.indexOf(hadBillsidList, gcl.leafXmjs ? gcl.leafXmjs[0].gcl_id : null) === -1) {
+                        updateObj.mid = materialID;
+                        updateObj.had_bills = 0;
+                        // updateData.push({ id: mc.id, mid: materialID, had_bills: 0 });
+                    }
+                }
+                // 更新工程量及台账金额
+                if (mc.quantity !== (gcl.quantity ? gcl.quantity : null)) {
+                    updateObj.quantity = gcl.quantity ? gcl.quantity : null;
+                    updateObj.total_price = gcl.total_price ? gcl.total_price : null;
                 }
+                if(!_.isEqual(updateObj,{ id: mc.id })) updateData.push(updateObj);
             }
         }
+        console.log(pushData, removeData, updateData);
         setChecklistData(pushData, removeData, updateData, true);
     });
     function setChecklistData(pushData, removeData, updateData = [], sendmsg = false) {

+ 55 - 4
app/public/js/material_list.js

@@ -203,7 +203,7 @@ $(document).ready(() => {
         readOnly: true,
     };
     // 加载清单数据 - 暂时统一加载,如有需要,切换成动态加载并缓存
-    postData(window.location.pathname + '/load', {}, function (result) {
+    postData(window.location.pathname + '/load', {}, async function (result) {
         ledger = result.ledger;
         curLedgerData = result.curLedgerData;
         pos = result.pos;
@@ -214,15 +214,66 @@ $(document).ready(() => {
         // 解析清单汇总数据
         gclGatherModel.loadLedgerData(ledger, curLedgerData);
         gclGatherModel.loadPosData(pos, curPosData);
-        gclGatherData = gclGatherModel.gatherGclData().filter(item => {
-            return item.qc_qty || item.contract_qty
-        });
+        gclGatherData = gclGatherModel.gatherGclData();
         console.log(gclGatherData);
         if (openMaterialChecklist) {
+            const hadBillsidList = _.uniq(_.map(materialListData, 'gcl_id'));
+            console.log(hadBillsidList);
+            // 判断是否有修订影响到本次数据,并有几率修改清单设置页的值
+            const pushData = [];
+            const updateData = [];
+            for (const hb of hadBillsidList) {
+                const gcl = _.find(gclGatherData, function (item) {
+                    return item.leafXmjs && item.leafXmjs.length > 0 && _.findIndex(item.leafXmjs, { gcl_id : hb }) !== -1;
+                });
+                if (gcl) {
+                    const mc = _.find(materialChecklistData, { b_code: gcl.b_code, name: gcl.name, unit: gcl.unit, unit_price: gcl.unit_price });
+                    // const newOrder = _.indexOf(gclGatherData, gcl);
+                    // console.log(newOrder);
+                    if (!mc && _.findIndex(pushData, { b_code: gcl.b_code, name: gcl.name, unit: gcl.unit, unit_price: gcl.unit_price }) === -1) {
+                        pushData.push({ b_code: gcl.b_code, name: gcl.name, unit: gcl.unit, unit_price: gcl.unit_price, quantity: (gcl.quantity ? gcl.quantity : null), total_price: (gcl.total_price ? gcl.total_price : null), had_bills: 1 });
+                    }
+                }
+            }
+            const removeData = [];
+            for (const mc of materialChecklistData) {
+                const gcl = _.find(gclGatherData, { b_code: mc.b_code, name: mc.name, unit: mc.unit, unit_price: mc.unit_price });
+                // 判断是否已不存在工料清单,台账修改过后删除之
+                if (!gcl) {
+                    removeData.push(mc.id);
+                } else {
+                    // 更新had_bills值
+                    const updateObj = { id: mc.id };
+                    if (mc.had_bills === 1) {
+                        if (_.indexOf(hadBillsidList, gcl.leafXmjs ? gcl.leafXmjs[0].gcl_id : null) === -1) {
+                            updateObj.mid = materialID;
+                            updateObj.had_bills = 0;
+                            // updateData.push({ id: mc.id, mid: materialID, had_bills: 0 });
+                        }
+                    }
+                    // 更新工程量及台账金额
+                    if (mc.quantity !== (gcl.quantity ? gcl.quantity : null)) {
+                        updateObj.quantity = gcl.quantity ? gcl.quantity : null;
+                        updateObj.total_price = gcl.total_price ? gcl.total_price : null;
+                    }
+                    if(!_.isEqual(updateObj,{ id: mc.id })) updateData.push(updateObj);
+                }
+            }
+            console.log(pushData, removeData, updateData);
+            if (pushData.length > 0 || removeData.length > 0 || updateData.length > 0) {
+                // materialChecklistData = await postDataAsync('/tender/'+ tenderID +'/measure/material/'+ stage_order +'/checklist/save', { type: 'resetChecklist', pushData, removeData, updateData })
+            }
+            gclGatherData = gclGatherData.filter(item => {
+                return item.qc_qty || item.contract_qty
+            });
             // 取交集
             gclGatherData = _.filter(gclGatherData, function (item) {
                 return _.find(materialChecklistData, { b_code: item.b_code, name: item.name, unit: item.unit, unit_price: item.unit_price });
             });
+        } else {
+            gclGatherData = gclGatherData.filter(item => {
+                return item.qc_qty || item.contract_qty
+            });
         }
         calculateJiaCha(gclGatherData);
         SpreadJsObj.initSheet(leafXmjSpread.getActiveSheet(), leafXmjSpreadSetting);

+ 130 - 2
app/public/js/measure_material.js

@@ -235,7 +235,8 @@ $(function () {
             $('#s_order').val(order_array.join(','));
         }
     });
-
+    let interval;
+    let stop = false;
     // 提交表单判断
     $('#addMaterial').click(function () {
         $(this).attr('disabled', true);
@@ -245,8 +246,135 @@ $(function () {
             return false;
         }
         $(this).text('正在生成新一期数据中,请勿刷新本页!...');
-        $(this).parents('form').submit();
+        const _self = $(this);
+        const stage_id = [];
+        $('#add-qi input[name="stage_id[]"]:checked').each(function () {
+            stage_id.push(parseInt($(this).val()));
+        });
+        const newMaterialData = {
+            s_order: $('#s_order').val(),
+            period: $('#add-qi input[name="period"]').val(),
+            stage_id,
+        };
+        if (lastMaterialListNum === 0) {
+            console.log(newMaterialData);
+            postData(preUrl + '/measure/material/add', newMaterialData, function (result) {
+                window.location.href = preUrl + '/measure/material/' + result.order;
+            }, function () {
+                _self.attr('disabled', false).text('确认添加');
+            });
+            return;
+        }
+        $('#add-qi').modal('hide');
+        $('#okedit').modal('show');
+        interval = setInterval(progress, 50);
+        postData(preUrl + '/measure/material/gcl/load', { stage_id }, function (result) {
+            // console.log(result);
+            const ledger = result.ledger;
+            const curLedgerData = result.curLedgerData;
+            const pos = result.pos;
+            const curPosData = result.curPosData;
+            const gclList = result.gclList;
+            gclGatherModel.loadLedgerData(ledger, curLedgerData);
+            gclGatherModel.loadPosData(pos, curPosData);
+            const gclGatherData = gclGatherModel.gatherGclData();
+            // const gclGatherData = gclGatherModel.gatherGclData().filter(item => {
+            //     return item.qc_qty || item.contract_qty
+            // });
+            // 获取需要新增的工料关联清单
+            const insertGcl = [];
+            const removeGclList = [];
+            for (const g of gclList) {
+                const gcl = _.find(gclGatherData, function (item) {
+                    return item.leafXmjs && item.leafXmjs.length > 0 && _.findIndex(item.leafXmjs, { gcl_id : g.gcl_id }) !== -1;
+                });
+                if (gcl) {
+                    const ig = _.find(insertGcl, { gcl });
+                    if (ig) {
+                        if(_.findIndex(ig.bills, { mb_id: g.mb_id }) === -1) {
+                            ig.bills.push(g);
+                        }
+                    } else {
+                        insertGcl.push({
+                            bills: [g],
+                            gcl,
+                            leafXmjs: _.filter(gcl.leafXmjs, function(item) { return item.gather_qty !== undefined && item.gather_qty !== null }),
+                        })
+                    }
+                } else {
+                    removeGclList.push(g);
+                }
+            }
+            const insertList = [];
+            const insertGclList = [];
+            for (const one of insertGcl) {
+                if (one.leafXmjs && one.leafXmjs.length > 0) {
+                    for (const xmj of one.leafXmjs) {
+                        const newgcl = _.find(gclList, { gcl_id: xmj.gcl_id });
+                        for (const bill of one.bills) {
+                            insertList.push({
+                                gcl_id: xmj.gcl_id,
+                                mx_id: xmj.mx_id ? xmj.mx_id : null,
+                                xmj_id: xmj.id ? xmj.id : null,
+                                gather_qty: xmj.gather_qty,
+                                quantity: bill.quantity,
+                                expr: bill.expr,
+                                mb_id: bill.mb_id,
+                                order: bill.order,
+                            });
+                            if (!newgcl) {
+                                insertGclList.push({
+                                    gcl_id: xmj.gcl_id,
+                                    quantity: bill.quantity,
+                                    expr: bill.expr,
+                                    mb_id: bill.mb_id,
+                                    order: bill.order,
+                                });
+                            }
+                        }
+                    }
+                }
+            }
+            // 可能需要新增list_gcl表
+            newMaterialData.material_list = insertList;
+            newMaterialData.insertGclList = insertGclList;
+            newMaterialData.removeGclList = removeGclList;
+            console.log(newMaterialData);
+            postData(preUrl + '/measure/material/add', newMaterialData, function (result) {
+                window.location.href = preUrl + '/measure/material/' + result.order;
+            }, function () {
+                stop = true;
+                // clearInterval(interval);
+                // console.log(_self.parents('div[id="add-qi"]'));
+                // $('#add-qi').modal('show');
+                // $('#okedit').modal('hide');
+                // _self.parents('div[id="add-qi"]').modal('show');
+                _self.attr('disabled', false).text('确认添加');
+            });
+            // return;
+        });
+        // $(this).parents('form').submit();
     });
+
+    let value = 0;
+    function progress(){
+        if (stop) {
+            setTimeout(function () {
+                $('#add-qi').modal('show');
+                $('#okedit').modal('hide');
+                value = 0;
+                clearInterval(interval);
+            }, 500);
+        }
+        if (value < 100) {
+            value = parseInt(value) + 1;
+            $("#material-progress").css("width", value + "%").text(value + "%");
+        } else if (value === 100) {
+            value = parseInt(value) + 1;
+            value = 30;
+        }
+    };
+
     let editPeriod = null;
     $('.edit-material').on('click', function () {
         const order = parseInt($(this).data('order'));

+ 55 - 59
app/public/js/revise.js

@@ -1590,54 +1590,37 @@ $(document).ready(() => {
             }
 
             const data = {postType: 'pos'};
-            if (col.field === 'name') {
-                if (newText === '' && posData) {
-                    toastr.error('部位名称不可为空', 'error');
-                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                    return;
-                } else if (!posData) {
-                    if (newText && newText !== '') {
-                        data.posPostType = 'add';
-                        const sortData = info.sheet.zh_data;
-                        const order = (!sortData || sortData.length === 0) ? 1 : Math.max(sortData[sortData.length - 1].porder + 1, sortData.length + 1);
-                        data.postData = { name: newText, lid: node.id, porder: order};
-                    } else {
-                        return;
-                    }
-                } else {
-                    data.posPostType = 'update';
-                    data.postData = {id: posData.id, name: newText};
-                }
-            } else if (!posData) {
-                toastr.warning('新增计量单元请先输入名称');
-                SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                return;
+            if (!posData) {
+                const sortData = info.sheet.zh_data;
+                const order = (!sortData || sortData.length === 0) ? 1 : Math.max(sortData[sortData.length - 1].porder + 1, sortData.length + 1);
+                data.posPostType = 'add';
+                data.postData = { lid: node.id, porder: order };
             } else {
                 data.posPostType = 'update';
-                data.postData = {id: posData.id};
-                if (col.type === 'Number') {
-                    const exprInfo = getExprInfo(col.field);
-                    const num = _.toNumber(newText);
-                    if (_.isFinite(num)) {
-                        data.postData[col.field] = num;
+                data.postData = { id: posData.id };
+            }
+            if (col.type === 'Number') {
+                const exprInfo = getExprInfo(col.field);
+                const num = _.toNumber(newText);
+                if (_.isFinite(num)) {
+                    data.postData[col.field] = num;
+                    if (exprInfo) {
+                        data.postData[exprInfo.expr] = '';
+                    }
+                } else {
+                    try {
+                        data.postData[col.field] = math.evaluate(transExpr(newText));
                         if (exprInfo) {
-                            data.postData[exprInfo.expr] = '';
-                        }
-                    } else {
-                        try {
-                            data.postData[col.field] = math.evaluate(transExpr(newText));
-                            if (exprInfo) {
-                                data.postData[exprInfo.expr] = newText;
-                            }
-                        } catch(err) {
-                            toastr.error('输入的表达式非法');
-                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                            return;
+                            data.postData[exprInfo.expr] = newText;
                         }
+                    } catch(err) {
+                        toastr.error('输入的表达式非法');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
                     }
-                } else {
-                    data.postData[col.field] = newText;
                 }
+            } else {
+                data.postData[col.field] = newText;
             }
             postData(window.location.pathname + '/update', data, function (result) {
                 const updateRst = pos.updateDatas(result.pos);
@@ -1671,10 +1654,6 @@ $(document).ready(() => {
                     const data = {id: node.id};
                     for (let iCol = sel.col; iCol < sel.col + sel.colCount; iCol++) {
                         const colSetting = sheet.zh_setting.cols[iCol];
-                        if (colSetting.field === 'name') {
-                            toastr.error('部位名称不能为空');
-                            return;
-                        }
                         const style = sheet.getStyle(iRow, iCol);
                         if (!style.locked) {
                             const colSetting = sheet.zh_setting.cols[iCol];
@@ -1704,6 +1683,25 @@ $(document).ready(() => {
                 });
             }
         },
+        insertPos: function (sheet) {
+            const selection = sheet.getSelections();
+            const sortData = sheet.zh_data;
+            const row = selection[0].row;
+            const node = SpreadJsObj.getSelectObject(billsSheet);
+            const select = SpreadJsObj.getSelectObject(sheet);
+            const porder = select ? select.porder : sortData.length + 1;
+            const data = {
+                postType: 'pos',
+                posPostType: 'insert',
+                postData: [ { lid: node.id, porder } ],
+            };
+            postData(window.location.pathname + '/update', data, function (result) {
+                const updateRst = pos.updateDatas(result.pos);
+                posSpreadObj.loadCurPosData();
+                billsTreeSpreadObj.refreshOperationValid(billsSheet);
+            });
+
+        },
         /**
          * 删除 计量单元
          * @param sheet
@@ -1765,15 +1763,7 @@ $(document).ready(() => {
 
             const data = [];
             const sortData = info.sheet.zh_data || [];
-            if (sortData.length === 0 || info.cellRange.row + info.cellRange.rowCount > sortData.length) {
-                if (info.cellRange.col !== 0) {
-                    toastr.warning('新增计量单元请先输入名称');
-                    posSpreadObj.loadCurPosData();
-                    return;
-                }
-            }
             const hint = {
-                name: {type: 'warning', msg: '计量单元名称不可为空,已过滤'},
                 expr: {type: 'warning', msg: '粘贴了表达式非法,已过滤'},
             };
             const lastOrder = sortData.length > 0 ? sortData[sortData.length - 1].porder + 1 : 1;
@@ -1803,11 +1793,6 @@ $(document).ready(() => {
                                 bPaste = false;
                             }
                         }
-                    } else if (colSetting.field === 'name') {
-                        if (!posData[colSetting.field]) {
-                            toastMessageUniq(hint.name);
-                            bPaste = false;
-                        }
                     }
                 }
                 if (bPaste) {
@@ -1911,6 +1896,17 @@ $(document).ready(() => {
                 return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
             },
             items: {
+                'insert': {
+                    name: '插入',
+                    icon: 'fa-plus',
+                    disabled: function (key, opt) {
+                        const node = SpreadJsObj.getSelectObject(billsSheet);
+                        return node && node.children && node.children.length > 0;
+                    },
+                    callback: function (key, opt) {
+                        posSpreadObj.insertPos(posSheet);
+                    }
+                },
                 'delete': {
                     name: '删除',
                     icon: 'fa-remove',

+ 236 - 8
app/public/js/setting.js

@@ -25,12 +25,12 @@ $(document).ready(() => {
         const account = $(this).data('account');
         $('#edit-user input[name="account"]').val(account.account);
         $('#edit-user input[name="name"]').val(account.name);
-        $('#edit-user input[name="company"]').val(account.company);
+        $('#edit-user select[name="company"]').val(account.company);
         $('#edit-user input[name="role"]').val(account.role);
         $('#edit-user input[name="mobile"]').val(account.mobile);
         $('#edit-user input[name="telephone"]').val(account.telephone);
         $('#edit-user input[name="id"]').val(account.id);
-        $('#edit-user select[name="account_group"]').val(account.account_group);
+        $('#edit-user input[name="account_group"]').val(account.account_group);
         $('#edit-user input[class="account-check"]').val(account.account);
         $('#edit-user input[data-mobile="auth-mobile"]').val(account.auth_mobile);
         $('#edit-user input[name="mobile"]').attr('readOnly', account.bind === 1);
@@ -45,6 +45,13 @@ $(document).ready(() => {
         $('#edit-password input[name="reset_password"]').val('');
     });
 
+    // 选择单位自动配置账号组
+    $('select[name="company"]').change(function () {
+        const company = $(this).val();
+        const oneUnit = _.find(unitList, { name: company });
+        $(this).siblings('input[name="account_group"]').val(oneUnit ? oneUnit.type : 7);
+    });
+
     // 分配随机密码
     $("#rand-password").click(function() {
         const password = randPassword();
@@ -196,10 +203,214 @@ $(document).ready(() => {
     // 设置页显示数目
     $('.nav-tabs .nav-link').each(function () {
         const pageSize = getLocalCache('account-pageSize') ? getLocalCache('account-pageSize') : '';
-        if (getLocalCache('account-pageSize') && $(this).attr('href').indexOf('pageSize') === -1) {
+        if (getLocalCache('account-pageSize') && $(this).attr('href').indexOf('pageSize') === -1 && $(this).attr('href').indexOf('unit') === -1) {
             $(this).attr('href', $(this).attr('href') + '?pageSize=' + getLocalCache('account-pageSize'));
         }
     });
+    // 设置页显示数目
+    $('#user-list .list-group-item').each(function (k,v) {
+        const pageSize = getLocalCache('account-pageSize') ? getLocalCache('account-pageSize') : '';
+        if (pageSize) {
+            $(this).attr('href', $(this).attr('href') + '&pageSize=' + pageSize);
+        }
+    })
+
+    // 参建单位页切换单位右侧显示
+    $('#unit_list tr').click(function () {
+        const id = parseInt($(this).data('id'));
+        const one = _.find(unitList, { id });
+        if (one) {
+            $(this).siblings('tr').removeClass('table-warning');
+            $(this).addClass('table-warning');
+            $('#unit_name').val(one.name);
+            $('#unit_corporation').val(one.corporation);
+            $('#unit_credit_code').val(one.credit_code);
+            $('#unit_tel').val(one.tel);
+            $('#unit_website').val(one.website);
+            $('#unit_region').val(one.region);
+            $('#unit_address').val(one.address);
+            $('#unit_basic').val(one.basic);
+            $('#unit_type').val(one.type);
+            if(one.sign_path) {
+                $('#sign-show').html('<img src="' + fujianOssPath + one.sign_path + '" width="120">');
+                $('#delete-sign').show();
+                $('#upload-sign').hide();
+            } else {
+                $('#sign-show').html('');
+                $('#delete-sign').hide();
+                $('#upload-sign').show();
+            }
+            oneUnit = one;
+        }
+    });
+
+    // 参建单位编辑
+    // 回车提交
+    $('#one_unit input').on('keypress', function () {
+        if(window.event.keyCode === 13) {
+            $(this).blur();
+        }
+    });
+
+    $('#one_unit input').blur(function () {
+        console.log('hello');
+        const val_name = $(this).data('name');
+        let val = _.trim($(this).val()) !== '' ? _.trim($(this).val()) : null;
+        if (!oneUnit) {
+            toastr.error('所选单位有误,请重新选择');
+            return false;
+        }
+        switch(val_name) {
+            case 'name':
+                if(val && val.length > 100) {
+                    toastr.error('单位名称超过100个字,请缩减名称');
+                    $(this).val(oneUnit[val_name]);
+                    return false;
+                }
+                const tongming = _.find(unitList, { name: val });
+                if (tongming && tongming.id !== oneUnit.id) {
+                    toastr.error('单位名称不能重复');
+                    $(this).val(oneUnit[val_name]);
+                    return false;
+                }
+                break;
+            default:
+                if(val && val.length > 255) {
+                    toastr.error('超出字段范围,请缩减');
+                    $(this).val(oneUnit[val_name]);
+                    return false;
+                }
+                break;
+        }
+        if(oneUnit[val_name] !== val) {
+            const _self = $(this);
+            postData('/setting/user/unit/save', { type: 'update', id: oneUnit.id, val_name, val}, function (result) {
+                oneUnit[val_name] = val;
+                _self.val(oneUnit[val_name]);
+                if (val_name === 'name') {
+                    oneUnit.account_num = result.account_num;
+                    $('#unit_list tr[class="table-warning"]').find('a').text(oneUnit[val_name]);
+                    $('#unit_list tr[class="table-warning"]').children('td').eq(2).text(result.account_num);
+                }
+            }, function () {
+                _self.val(oneUnit[val_name]);
+            })
+        } else {
+            $(this).val(oneUnit[val_name]);
+        }
+    });
+
+    $('#one_unit textarea').blur(function () {
+        const val_name = $(this).data('name');
+        let val = _.trim($(this).val()) !== '' ? _.trim($(this).val()) : null;
+        if (!oneUnit) {
+            toastr.error('所选单位有误,请重新选择');
+            return false;
+        }
+        if(oneUnit[val_name] !== val) {
+            const _self = $(this);
+            postData('/setting/user/unit/save', { type: 'update', id: oneUnit.id, val_name, val}, function (result) {
+                oneUnit[val_name] = val;
+                _self.val(oneUnit[val_name]);
+                $('#unit_list tr[class="table-warning"]').children('td').eq(4).text(oneUnit[val_name]);
+            }, function () {
+                _self.val(oneUnit[val_name]);
+            })
+        } else {
+            $(this).val(oneUnit[val_name]);
+        }
+    });
+
+    $('#one_unit select').change(function () {
+        const val_name = $(this).attr('data-name');
+        let val = _.trim($(this).val()) !== '' ? _.trim($(this).val()) : null;
+        if (!oneUnit) {
+            toastr.error('所选单位有误,请重新选择');
+            return false;
+        }
+        if(oneUnit[val_name] !== val) {
+            const _self = $(this);
+            postData('/setting/user/unit/save', { type: 'update', id: oneUnit.id, name: oneUnit.name, val_name, val}, function (result) {
+                oneUnit[val_name] = val;
+                _self.val(oneUnit[val_name]);
+                $('#unit_list tr[class="table-warning"]').children('td').eq(3).text(accountGroup[parseInt(oneUnit[val_name])]);
+            }, function () {
+                _self.val(oneUnit[val_name]);
+            })
+        } else {
+            $(this).val(oneUnit[val_name]);
+        }
+    });
+
+    // 删除单位弹窗
+    $('#del-modal-btn').click(function () {
+        if (!oneUnit) {
+            toastr.error('所选单位有误,请重新选择');
+            return false;
+        }
+        if (oneUnit.account_num === 0) {
+            $('.del-btn').show();
+            $('.not-del-btn').hide();
+        } else {
+            $('.del-btn').hide();
+            $('.not-del-btn').show();
+        }
+    });
+
+    // 删除单位
+    $('#delete-unit').click(function () {
+        if (!oneUnit) {
+            toastr.error('所选单位有误,请重新选择');
+            return false;
+        }
+        postData('/setting/user/unit/save', { type: 'delete', name: oneUnit.name, id: oneUnit.id }, function (result) {
+            window.location.href = '/setting/user/unit';
+        })
+    });
+
+    // 上传签章
+    $('#sign-upload').change(function () {
+        if (!oneUnit) {
+            toastr.error('所选单位有误,请重新选择');
+            return false;
+        }
+        const file = this.files[0];
+        const ext = file.name.toLowerCase().split('.').splice(-1)[0];
+        const imgStr = /(jpg|jpeg|png|bmp|BMP|JPG|PNG|JPEG)$/;
+        if (!imgStr.test(ext)) {
+            toastr.error('请上传正确的图片格式文件');
+            return
+        }
+        if ($(this).val()) {
+            const formData = new FormData();
+            formData.append('file', this.files[0]);
+            formData.append('id', oneUnit.id);
+            postDataWithFile('/setting/user/unit/upload', formData, function (result) {
+                const html = '<img src="'+ fujianOssPath + result.sign_path +'" width="120">';
+                $('#sign-show').html(html);
+                $('#sign-upload').val('');
+                oneUnit.sign_path = result.sign_path;
+                $('#upload-sign').hide();
+                $('#delete-sign').show();
+                toastr.success('上传成功');
+            });
+        }
+    });
+
+    // 移除签章
+    $('#delete-sign').click(function () {
+        if (!oneUnit) {
+            toastr.error('所选单位有误,请重新选择');
+            return false;
+        }
+        postData('/setting/user/unit/save', { type: 'del-sign', id: oneUnit.id }, function (result) {
+            $('#sign-show').html('');
+            toastr.warning('已移除');
+            oneUnit.sign_path = null;
+            $('#upload-sign').show();
+            $('#delete-sign').hide();
+        })
+    });
 });
 
 function checkPasswordForm() {
@@ -226,7 +437,7 @@ function checkPasswordForm() {
 function checkUserForm(status) {
     try {
         if (status === 'add') {
-            if ($('#add-user select[name="account_group"]').val() == 0) {
+            if ($('#add-user input[name="account_group"]').val() == 0) {
                 throw '请选择账号组';
             }
             if ($('#add-user input[name="account"]').val() == '' || $('#add-user input[name="account"]').hasClass('is-invalid')) {
@@ -241,8 +452,8 @@ function checkUserForm(status) {
             if ($('#add-user input[name="name"]').val() == '') {
                 throw '姓名不能为空';
             }
-            if ($('#add-user input[name="company"]').val() == '') {
-                throw '单位名称不能为空';
+            if (_.findIndex(unitList, $('#add-user select[name="company"]').val()) === -1) {
+                throw '请选择单位名称';
             }
             if ($('#add-user input[name="role"]').val() == '') {
                 throw '职位名称不能为空';
@@ -262,8 +473,8 @@ function checkUserForm(status) {
             if ($('#edit-user input[name="name"]').val() == '') {
                 throw '姓名不能为空';
             }
-            if ($('#edit-user input[name="company"]').val() == '') {
-                throw '单位名称不能为空';
+            if (_.findIndex(unitList, $('#edit-user select[name="company"]').val()) === -1) {
+                throw '请选择单位名称';
             }
             if ($('#edit-user input[name="role"]').val() == '') {
                 throw '职位名称不能为空';
@@ -281,6 +492,23 @@ function checkUserForm(status) {
 }
 
 /**
+ * 表单检测
+ */
+function checkUnitForm() {
+    try {
+        if ($('#add-company input[name="name"]').val() == '') {
+            throw '单位名称不能为空';
+        }
+        if ($('#add-company select[name="type"]').val() == 0) {
+            throw '请选择类型';
+        }
+    } catch (err) {
+        toastr.error(err);
+        return false;
+    }
+}
+
+/**
  * 随机密码
  */
 function randPassword() {

+ 3 - 3
app/public/js/shares/cs_tools.js

@@ -959,14 +959,14 @@ const showSelectTab = function(select, spread, afterShow) {
                     { title: '清单编号', field: 'b_code', width: 80, formatter: '@' },
                     { title: '清单名称', field: 'name', width: 120, formatter: '@' },
                     { title: '单位', field: 'unit', width: 50, formatter: '@' },
-                    { title: '数量', field: 'qty', width: 60 },
+                    { title: setting.type === 'stage' ? '合同数量' : '数量', field: 'qty', width: 60 },
+                    { title: '变更数量', field: 'qc_qty', width: 60, visible: setting.type === 'stage' },
                     {
                         title: '类型', field: 'type', width: 100, getValue: function (x) {
                             switch (x.type) {
                                 case 'less': return '数量变少';
                                 case 'miss': return '找不到清单';
-                                case 'qc': return '变更';
-                                case 'miss-qc': return '变更(找不到清单)';
+                                case 'qc-conflict': return '变更冲突(已调用变更令)';
                                 default: return '';
                             }
                         }

+ 2 - 1
app/public/js/shares/gcl_gather_compare.js

@@ -457,7 +457,8 @@ const gclCompareModel = (function () {
      */
     function checkDiffer() {
         gclList.sort((a, b) => {
-            return compareCode(a.b_code, b.b_code);
+            const iCode = compareCode(a.b_code, b.b_code);
+            return iCode ? iCode : (a.unit_price - b.unit_price);
         });
         for (const gcl of gclList) {
             gcl.differ = false;

+ 1 - 1
app/public/js/shares/tender_select.js

@@ -181,7 +181,7 @@ const TenderSelect = function (setting) {
                 if (tsObj.setting.type === 'stage') updateData.cover = $('#ts-cover')[0].checked;
                 if (updateData.tenders.length > 0) {
                     postData(window.location.pathname + '/sumLoad', updateData, result => {
-                        tsObj.setting.afterLoad(result);
+                        tsObj.setting.afterLoad(result, tsObj.select);
                         $('#tender-select').modal('hide');
                     });
                 } else {

+ 2 - 2
app/public/js/spreadjs_rela/spreadjs_zh.js

@@ -661,6 +661,7 @@ const SpreadJsObj = {
                 sheet.extendCellType.checkbox = new spreadNS.CellTypes.CheckBox();
             }
             sheet.getRange(-1, col, -1, 1).cellType(sheet.extendCellType.checkbox);
+            sheet.getRange(-1, col, -1, 1).hAlign(col ? col.hAlign : spreadNS.HorizontalAlign.center);// 空白行不居中问题,checkbox要默认居中
         }
         if (colSetting.cellType === 'unit') {
             if (!sheet.extendCellType.unit) {
@@ -955,7 +956,6 @@ const SpreadJsObj = {
         return copyData.join('\r\n');
     },
     analysisPasteText: function (text, transpose = false) {
-        console.log(text);
         const result = [];
         if (text === '') return result;
 
@@ -973,7 +973,7 @@ const SpreadJsObj = {
     },
     transposePasteData: function (data) {
         const result = [];
-        const rowCount = data.length, colCount = data.reduce((r, x) => { return r ? Math.max(r, x.length) : x.length; });
+        const rowCount = data.length, colCount = data.reduce((r, x) => { return Math.max(r, x.length); }, 0);
         for (let c = 0; c < colCount; c++) {
             const newRow = [];
             for (let r = 0; r < rowCount; r++) {

+ 65 - 38
app/public/js/stage.js

@@ -594,7 +594,7 @@ $(document).ready(() => {
             if (this.changes) {
                 sheet.setSelection(0, 0, 1, 1);
                 this._filterChange(!$('#filterEmpty')[0].checked, $('#matchPos')[0].checked);
-                this._loadChangeDetail(this.changes[0]);
+                this._loadChangeDetail(this.displayChanges[0]);
             } else {
                 toastr.error('查询变更令有误,请刷新页面后重试');
             }
@@ -657,7 +657,7 @@ $(document).ready(() => {
             case 'dagl': data.dagl_url && window.open(data.dagl_url); break;
             case 'gxby': data.gxby_url && window.open(data.gxby_url); break;
             case 'qc_qty':
-                if (data.children && data.children.length > 0 || data.lock) return;
+                if (data.children && data.children.length > 0 || data.lock || data.is_import) return;
                 const nodePos = stagePos.getLedgerPos(data.id);
                 if (nodePos && nodePos.length > 0) return;
                 changesObj.loadChanges({bills: data});
@@ -830,6 +830,7 @@ $(document).ready(() => {
         relaSpread: slSpread,
         storeKey: 'stage-slm-' + stage.id,
         id: 'stage-slm',
+        type: 'stage',
         afterLocated:  function () {
             stagePosSpreadObj.loadCurPosData();
         },
@@ -1455,8 +1456,12 @@ $(document).ready(() => {
     const addTag = newTag({ledgerSheet: slSpread.getActiveSheet(), billsTag});
     const tenderSelect = TenderSelect({
         type: 'stage',
-        afterLoad: function (result) {
+        afterLoad: function (result, select) {
             const nodes = stageTree.loadPostStageData(result);
+            const posterity = stageTree.getPosterity(select);
+            for (const p of posterity) {
+                p.is_import = !!result.import_change.data.find(x => { return x.lid === p.id });
+            }
             stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
             if (detail) {
                 detail.loadStageLedgerUpdateData(result, nodes);
@@ -1469,6 +1474,8 @@ $(document).ready(() => {
             } else {
                 sumLoadMiss.clearMissData();
             }
+            // todo 优化,仅加载需要刷新的变更令
+            if (checkedChanges) checkedChanges.reloadChangeData();
         }
     });
     $.contextMenu({
@@ -2025,7 +2032,7 @@ $(document).ready(() => {
     });
 
     // 加载计量单元数据 - 暂时统一加载,如有需要,切换成动态加载并缓存
-    postData(window.location.pathname + '/load', { filter: 'ledger;pos;detail;change;tag;cooperation' }, function (result) {
+    postData(window.location.pathname + '/load', { filter: 'ledger;pos;detail;change;import_change;tag;cooperation' }, function (result) {
         // 加载树结构
         stageTree.loadDatas(result.ledgerData);
         checkShowLast(result.ledgerData.length);
@@ -2051,7 +2058,7 @@ $(document).ready(() => {
         SpreadJsObj.resetTopAndSelect(spSpread.getActiveSheet());
         // 加载中间计量
         stageIm.init(stage, imType, tenderInfo.decimal);
-        stageIm.loadData(result.ledgerData, result.posData, result.detailData, result.changeData, result.detailAtt);
+        stageIm.loadData(result.ledgerData, result.posData, result.detailData, result.changeData, result.import_change, result.detailAtt);
 
         errorList.loadHisErrorData();
         checkList.loadHisCheckData();
@@ -3500,17 +3507,6 @@ $(document).ready(() => {
                     $('#view-calc-remark').text(img_remark);
 
                     $('#edit-img').modal('hide');
-                    // updateImageData = updateData;
-                    // $('#edit-img').modal('hide');
-                    // postData(window.location.pathname + '/detail/merge-img', updateData, function (result) {
-                    //     stageIm.loadUpdateDetailData(result);
-                    //     data.calc_img = result.calc_img;
-                    //     data.calc_img_org = result.calc_img_org;
-                    //     const calcImgSrc = data && data.calc_img ? '/' + data.calc_img : '';
-                    //     $('#show-calc-img').attr('src', calcImgSrc);
-                    //     $('#calc-img').attr('src', calcImgSrc);
-                    //     $('#view-calc-img').attr('src', calcImgSrc);
-                    //     $('#edit-img').modal('hide');
                     // });
                 } else if (data.calc_img) {
                     self.updateImageData = {updateType: 'clear', lid: data.lid, pid: data.pid, uuid: data.uuid, calc_img_remark: img_remark};
@@ -3522,16 +3518,6 @@ $(document).ready(() => {
                     $('#view-calc-remark').val(img_remark);
                     $('#text-edit').val(img_remark);
                     $('#edit-img').modal('hide');
-                    // postData(window.location.pathname + '/detail/merge-img', {updateType: 'clear', lid: data.lid, pid: data.pid, uuid: data.uuid}, function (result) {
-                    //     stageIm.loadUpdateDetailData(result);
-                    //     data.calc_img = result.calc_img;
-                    //     data.calc_img_org = result.calc_img_org;
-                    //     const calcImgSrc = data && data.calc_img ? '/' + data.calc_img : '';
-                    //     $('#show-calc-img').attr('src', calcImgSrc);
-                    //     $('#calc-img').attr('src', calcImgSrc);
-                    //     $('#view-calc-img').attr('src', calcImgSrc);
-                    //     $('#edit-img').modal('hide');
-                    // });
                 } else {
                     self.updateImageData = updateData;
                     $('#show-calc-img').attr('src', '');
@@ -3660,7 +3646,7 @@ $(document).ready(() => {
             const self = this;
             this.changeSpreadSetting = {
                 cols: [
-                    {title: '变更令号', colSpan: '1', rowSpan: '1', field: 'p_code', hAlign: 0, width: 100, formatter: '@'},
+                    {title: '变更令号', colSpan: '1', rowSpan: '1', field: 'p_code', hAlign: 0, width: 100, formatter: '@', cellType: 'tip', getTip: function (x) { return x.t_name ? `所属标段:${x.t_name}` : ''; }},
                     {title: '变更名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 180, formatter: '@'},
                     {title: '金额', colSpan: '1', rowSpan: '1', field: 'total_price', hAlign: 2, width: 80, type: 'Number'},
                     {title: '批复文号', colSpan: '1', rowSpan: '1', field: 'w_code', hAlign: 0, width: 100, formatter: '@'},
@@ -3673,6 +3659,13 @@ $(document).ready(() => {
                 headerFont: '12px 微软雅黑',
                 font: '12px 微软雅黑',
                 readOnly: true,
+                getColor: function (sheet, data, row, col, defaultColor) {
+                    if (data && data.is_import) {
+                        return '#eee';
+                    } else {
+                        return defaultColor;
+                    }
+                }
             };
             this.changeSpread = SpreadJsObj.createNewSpread(setting.changeObj[0]);
             this.changeSheet = this.changeSpread.getActiveSheet();
@@ -3718,7 +3711,7 @@ $(document).ready(() => {
                 if (change.detail) {
                     self.loadChangeDetailData();
                 } else {
-                    postData(window.location.pathname + '/change/detail', {cid: change.cid}, function (result) {
+                    postData(window.location.pathname + '/change/detail', {cid: change.cid, is_import: change.is_import}, function (result) {
                         change.detail = result;
                         self.analyzeChange(change);
                         self.loadChangeDetailData();
@@ -3741,8 +3734,14 @@ $(document).ready(() => {
                         name: '定位至台账',
                         icon: 'fa-sign-in',
                         callback: function (key, opt) {
+                            const change = SpreadJsObj.getSelectObject(self.changeSheet);
                             const changeBills = SpreadJsObj.getSelectObject(self.changeBillsSheet);
-                            if (changeBills.gcl_id) {
+                            if (change.is_import) {
+                                if (changeBills.pos && changeBills.pos.length > 0) {
+                                    SpreadJsObj.locateTreeNode(slSpread.getActiveSheet(), changeBills.pos[0].ledger_id, true);
+                                    stagePosSpreadObj.loadCurPosData();
+                                }
+                            } else if (changeBills.gcl_id) {
                                 const node = stageTree.nodes.find(x => {return x.id === changeBills.gcl_id});
                                 SpreadJsObj.locateTreeNode(slSpread.getActiveSheet(), node.ledger_id, true);
                                 stagePosSpreadObj.loadCurPosData();
@@ -3793,23 +3792,23 @@ $(document).ready(() => {
         reloadChangeData() {
             const self = this;
             postData(window.location.pathname + '/change/data', null, function (result) {
-                self.changes = result.changes;
+                self.changes = [];
+                result.changes.forEach(x => { x.is_import = 0; self.changes.push(x); });
+                result.import_changes.forEach(x => { x.is_import = 1; delete x.total_price; self.changes.push(x); });
+                if (result.changes.length > 0) self.analyzeChange(result.changes[0]);
+                if (result.import_changes.length > 0) self.analyzeChange(result.import_changes[0]);
                 SpreadJsObj.loadSheetData(self.changeSheet, SpreadJsObj.DataType.Data, self.changes);
                 self.changeSheet.setSelection(0, 0, 1, 1);
-                if (self.changes.length > 0) self.analyzeChange(result.changes[0]);
                 self.curChangeId = self.changes.length > 0 ? self.changes[0].id : null;
                 self.loadChangeDetailData();
             });
         }
-        analyzeChange(change) {
-            change.bills = change.detail.bills;
+        _analyzeCommon(change) {
             for (const b of change.bills) {
-                const aub = change.detail.addUsedBills.find(function (x) {
-                    return x.id === b.id;
+                const eub = change.detail.endUsedBills.find(function (x) {
+                    return x.cbid === b.id;
                 });
-                if (aub) {
-                    b.used_qty = aub.used_qty;
-                }
+                if (eub) b.used_qty = eub.qty;
                 b.qty = _.toNumber(b.samount);
                 b.valid_qty = ZhCalc.sub(b.qty, b.used_qty);
                 b.tp = ZhCalc.round(ZhCalc.mul(b.qty, b.unit_price), tenderInfo.decimal.tp);
@@ -3821,6 +3820,34 @@ $(document).ready(() => {
                 }
             }
         }
+        _analyzeImport(change) {
+            for (const b of change.bills) {
+                const eub = change.detail.endUsedBills.find(function (x) {
+                    return x.rela_cbid === b.id;
+                });
+                if (eub) b.used_qty = eub.rela_qty;
+                b.qty = _.toNumber(b.samount);
+                delete b.unit_price;
+                // b.tp = ZhCalc.round(ZhCalc.mul(b.qty, b.unit_price), tenderInfo.decimal.tp);
+                b.pos = _.filter(change.detail.curUsedBills, { rela_cbid: b.id });
+                b.cur_qty = 0;
+                for (const p of b.pos) {
+                    b.qty = b.rela_qty;
+                    const node = stageTree.datas.find(x => { return x.id === p.lid; });
+                    p.ledger_id = node.ledger_id;
+                    p.f_qty = node.quantity;
+                    b.cur_qty = ZhCalc.add(b.cur_qty, p.rela_qty);
+                }
+            }
+        }
+        analyzeChange(change) {
+            change.bills = change.detail.bills;
+            if (change.is_import) {
+                this._analyzeImport(change);
+            } else {
+                this._analyzeCommon(change);
+            }
+        }
     }
     // 展开收起附件
     $('a', '.right-nav').bind('click', function () {

+ 56 - 15
app/public/js/stage_change.js

@@ -30,21 +30,16 @@ class ChangeAnalysis {
         }
         return null;
     }
-    analyze (change) {
-        change.filterBills = false;
-        change.attachments = change.detail.attachments;
-        change.bills = change.detail.bills;
+    _analyzeCommon(change) {
         for (const b of change.bills) {
-            const aub = change.detail.addUsedBills.filter(function (x) {
+            const eub = change.detail.endUsedBills.filter(function (x) {
                 return x.cbid === b.id;
             });
-            if (aub) {
-                b.used_qty = ZhCalc.sum(aub.map(x => { return x.qty}));
-            }
+            if (eub) b.used_qty = ZhCalc.sum(eub.map(x => { return x.qty }));
             b.qty = _.toNumber(b.samount);
             b.valid_qty = ZhCalc.sub(b.qty, b.used_qty);
             b.tp = ZhCalc.round(ZhCalc.mul(b.qty, b.unit_price), tenderInfo.decimal.tp);
-            b.pos = _.filter(change.detail.curUsedBills, {cbid: b.id});
+            b.pos = _.filter(change.detail.curUsedBills, { cbid: b.id });
             b.cur_qty = 0;
             for (const p of b.pos) {
                 // 查询最底层项目节
@@ -57,7 +52,45 @@ class ChangeAnalysis {
                 p.f_qty = p.p_qty ? p.p_qty : p.l_qty;
                 b.cur_qty = ZhCalc.add(b.cur_qty, p.qty);
             }
+        }
+    }
+    _analyzeImport(change) {
+        for (const b of change.bills) {
+            const eub = change.detail.endUsedBills.filter(function (x) {
+                return x.rela_cbid === b.id;
+            });
+            if (eub) b.used_qty = ZhCalc.sum(eub.map(x => { return x.rela_qty }));
+
+            // 移除单价、部位,不计算数量、金额、未变更
+            delete b.unit_price;
+            b.qty = _.toNumber(b.samount);
+            // b.valid_qty = ZhCalc.sub(b.qty, b.used_qty);
+            // b.tp = ZhCalc.round(ZhCalc.mul(b.qty, b.unit_price), tenderInfo.decimal.tp);
 
+            b.pos = _.filter(change.detail.curUsedBills, { rela_cbid: b.id });
+            b.cur_qty = 0;
+            for (const p of b.pos) {
+                p.qty = p.rela_qty;
+                // 查询最底层项目节
+                const node = this.ledgerTree.datas.find(x => { return x.id === p.lid; });
+                const leafXmj = this.getLeafXmj(node);
+                if (leafXmj) {
+                    p.leaf_xmj_code = leafXmj.code;
+                    p.leaf_xmj_name = leafXmj.name;
+                }
+                p.f_qty = node.quantity;
+                b.cur_qty = ZhCalc.add(b.cur_qty, p.qty);
+            }
+        }
+    }
+    analyze (change) {
+        change.filterBills = false;
+        change.attachments = change.detail.attachments;
+        change.bills = change.detail.bills;
+        if (change.is_import) {
+            this._analyzeImport(change);
+        } else {
+            this._analyzeCommon(change);
         }
     }
 }
@@ -70,7 +103,7 @@ $(document).ready(() => {
     // 初始化变更令spread
     const changeSpreadSetting = {
         cols: [
-            {title: '变更令号', colSpan: '1', rowSpan: '1', field: 'p_code', hAlign: 0, width: 150, formatter: '@'},
+            {title: '变更令号', colSpan: '1', rowSpan: '1', field: 'p_code', hAlign: 0, width: 150, formatter: '@', cellType: 'tip', getTip: function (x) { return x.t_name ? `所属标段:${x.t_name}` : ''; }},
             {title: '变更名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 350, formatter: '@'},
             {title: '金额', colSpan: '1', rowSpan: '1', field: 'total_price', hAlign: 2, width: 100, type: 'Number'},
             {title: '批复文号', colSpan: '1', rowSpan: '1', field: 'w_code', hAlign: 0, width: 150, formatter: '@'},
@@ -92,6 +125,13 @@ $(document).ready(() => {
             colWidth: true,
         },
         readOnly: true,
+        getColor: function (sheet, data, row, col, defaultColor) {
+            if (data && data.is_import) {
+                return '#eee';
+            } else {
+                return defaultColor;
+            }
+        }
     };
     const changeSpread = SpreadJsObj.createNewSpread($('#bgl-spread')[0]);
     SpreadJsObj.initSheet(changeSpread.getActiveSheet(), changeSpreadSetting);
@@ -316,14 +356,15 @@ $(document).ready(() => {
 
     postData(window.location.pathname + '/data', null, function (result) {
         tenderInfo = result.tenderInfo;
-        changes = result.changes;
-        usedChangesId = result.usedChangesId;
+        changes = [];
+        result.changes.forEach(x => { x.is_import = 0; changes.push(x); });
+        result.import_changes.forEach(x => { x.is_import = 1; delete x.total_price; changes.push(x); });
+        usedChangesId = [...result.usedChangesId, ...result.used_import_cid];
         ledger = result.ledger;
         // ------------begin 预处理数据
         analysis = new ChangeAnalysis(ledger);
-        if (changes.length > 0) {
-            analysis.analyze(changes[0]);
-        }
+        if (result.changes.length > 0) analysis.analyze(result.changes[0]);
+        if (result.import_changes.length > 0) analysis.analyze(result.import_changes[0]);
         // ------------end 预处理数据
         // ------------begin 加载数据至界面
         SpreadJsObj.loadSheetData(changeSpread.getActiveSheet(), SpreadJsObj.DataType.Data, changes);

+ 26 - 18
app/public/js/stage_im.js

@@ -13,7 +13,7 @@ const stageIm = (function () {
     const resetFields = ['peg', 'bw', 'xm', 'drawing_code', 'calc_memo', 'position', 'jldy'];
     const splitChar = '-';
     const mergeChar = ';';
-    let stage, imType, decimal, details, changes, detailsAtt, ImData, pre, orgImData;
+    let stage, imType, decimal, details, changes, importChanges, detailsAtt, ImData, pre, orgImData;
     let up_field = 'unit_price';
     const gsTreeSetting = {
         id: 'ledger_id',
@@ -67,7 +67,7 @@ const stageIm = (function () {
         }
     }
 
-    function loadData (ledger, pos, stageDetail, stageChange, stageDetailAtt) {
+    function loadData (ledger, pos, stageDetail, stageChange, stageImportChange, stageDetailAtt) {
         up_field = 'unit_price';
         gsTree.loadDatas(ledger);
         treeCalc.calculateAll(gsTree);
@@ -79,6 +79,7 @@ const stageIm = (function () {
         details = stageDetail;
 
         changes = stageChange;
+        importChanges = stageImportChange;
 
         detailsAtt = stageDetailAtt;
     }
@@ -94,6 +95,7 @@ const stageIm = (function () {
         details = stageDetail;
 
         changes = stageChange;
+        importChanges = [];
 
         detailsAtt = stageDetailAtt;
     }
@@ -515,6 +517,7 @@ const stageIm = (function () {
                         im.changes.push(c);
                     }
                 }
+                importChanges.forEach(x => { if (x.lid === p.id) im.changes.push(x); });
             } else {
                 for (const pp of posRange) {
                     if ((!pp.qc_qty || pp.qc_qty === 0)) {
@@ -706,6 +709,7 @@ const stageIm = (function () {
                             imDefault.changes.push(c);
                         }
                     }
+                    importChanges.forEach(x => { if (x.lid === p.id) imDefault.changes.push(x); });
 
                     imDefault.contract_jl = ZhCalc.add(imDefault.contract_jl, p.contract_tp);
                     imDefault.qc_jl = ZhCalc.add(imDefault.qc_jl, p.qc_tp);
@@ -784,11 +788,10 @@ const stageIm = (function () {
                     im.changes.push(c);
                 }
             }
+            importChanges.forEach(x => { if (x.lid === node.id) im.changes.push(x); });
         } else {
             for (const p of posRange) {
-                if ((!p.qc_qty || p.qc_qty === 0)) {
-                    continue;
-                }
+                if ((!p.qc_qty || p.qc_qty === 0)) continue;
                 for (const c of changes) {
                     if (c.lid === node.id && c.pid === p.id && c.qty && c.qty !== 0) {
                         im.changes.push(c);
@@ -910,6 +913,7 @@ const stageIm = (function () {
                             im.changes.push(c);
                         }
                     }
+                    importChanges.forEach(x => { if (x.lid === p.id) im.changes.push(x); });
                 }
             }
         }
@@ -1101,20 +1105,10 @@ const stageIm = (function () {
                 changes.push(c);
             }
         }
-        return refreshImData(refreshNodes);
-    }
-    function loadUpdatePosData(data, refreshNodes) {
-        if (data.pos) {
-            if (data.pos.pos && data.pos.pos.length > 0 && typeof data.pos.pos[0] === 'string') {
-                gsPos.removeDatas(data.pos.pos);
-            } else {
-                gsPos.updateDatas(data.pos.pos);
-            }
-            if (data.pos.curStageData) {
-                gsPos.loadCurStageData(data.pos.curStageData);
-            }
+        if (data.import_change) {
+            _.remove(importChanges, data.import_change.target);
+            data.import_change.data.forEach(x => { importChanges.push(x) });
         }
-        gsTree.loadPostStageData(data.ledger);
         return refreshImData(refreshNodes);
     }
     function loadUpdateChangeData(data, refreshNodes) {
@@ -1130,6 +1124,20 @@ const stageIm = (function () {
         }
         return refreshImData(refreshNodes);
     }
+    function loadUpdatePosData(data, refreshNodes) {
+        if (data.pos) {
+            if (data.pos.pos && data.pos.pos.length > 0 && typeof data.pos.pos[0] === 'string') {
+                gsPos.removeDatas(data.pos.pos);
+            } else {
+                gsPos.updateDatas(data.pos.pos);
+            }
+            if (data.pos.curStageData) {
+                gsPos.loadCurStageData(data.pos.curStageData);
+            }
+        }
+        gsTree.loadPostStageData(data.ledger);
+        return refreshImData(refreshNodes);
+    }
 
     function getParentCheckNode(node) {
         let parent = node;

+ 8 - 2
app/router.js

@@ -80,6 +80,10 @@ module.exports = app => {
     // 账号设置
     app.get('/setting/user', sessionAuth, 'settingController.user');
     app.get('/setting/user/permission/set', sessionAuth, 'settingController.userPermissionSet');
+    app.get('/setting/user/unit', sessionAuth, 'settingController.userUnit');
+    app.post('/setting/user/unit/add', sessionAuth, 'settingController.userUnitAdd');
+    app.post('/setting/user/unit/save', sessionAuth, 'settingController.userUnitSave');
+    app.post('/setting/user/unit/upload', sessionAuth, 'settingController.userUnitUpload');
     // 账号停用和启用
     app.post('/setting/user/switch', sessionAuth, 'settingController.userSwitch');
     app.post('/setting/user/add', sessionAuth, datetimeFill, 'settingController.addUser');
@@ -313,6 +317,7 @@ module.exports = app => {
     // 变更概况
     app.get('/tender/:id/measure/stage/:order/change', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.change');
     app.post('/tender/:id/measure/stage/:order/change/data', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.getChangeData');
+    app.post('/tender/:id/measure/stage/:order/change/iData', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.getImportChangeData');
     app.post('/tender/:id/measure/stage/:order/change/detail', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.changeDetail');
 
     // 审批
@@ -538,7 +543,8 @@ module.exports = app => {
     app.post('/tender/:id/measure/material/:order/file/find', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.getCurMatericalFiles');
     app.post('/tender/:id/measure/material/:order/file/delete', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.deleteFile');
     app.post('/tender/:id/measure/material/:order/file/download/compresse-file', sessionAuth, tenderCheck, uncheckTenderCheck, 'materialController.downloadZip');
-
+    // 调差新建期数据获取
+    app.post('/tender/:id/measure/material/gcl/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'materialController.loadGclData');
     // 个人账号相关
     app.get('/profile/info', sessionAuth, 'profileController.info');
     app.get('/profile/sms', sessionAuth, 'profileController.sms');
@@ -629,7 +635,7 @@ module.exports = app => {
     // 总分包
     app.post('/tender/:id/ledger/sumLoad', sessionAuth, tenderCheck, uncheckTenderCheck, 'tenderController.sumLoad');
     app.post('/tender/:id/measure/stage/:order/sumLoad', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'tenderController.sumLoad');
-    app.post('/tender/:id/revise/info/sumLoad', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'tenderController.sumLoad');
+    app.post('/tender/:id/revise/:rid/info/sumLoad', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'tenderController.sumLoad');
 
     // 扫码登录
     app.get('/wxAuth', 'loginController.wxAuth');

+ 1 - 0
app/service/change.js

@@ -124,6 +124,7 @@ module.exports = app => {
                         let content = planInfo.reason ? planInfo.reason.replace(/[\r\n]/g, '\r\n') : '';
                         content = content + (planInfo.content ? (planInfo.reason ? '\r\n' : '') + planInfo.content.replace(/[\r\n]/g, '\r\n') : '');
                         change.content = content ? content : null;
+                        change.expr = planInfo.expr;
                     }
                 }
                 const operate = await this.transaction.insert(this.tableName, change);

+ 117 - 0
app/service/construction_unit.js

@@ -0,0 +1,117 @@
+'use strict';
+
+/**
+ * Created by EllisRan on 2020/3/3.
+ */
+
+const BaseService = require('../base/base_service');
+
+module.exports = app => {
+
+    class ConstructionUnit extends BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'construction_unit';
+            this.dataId = 'id';
+        }
+
+        /**
+         * 数据验证规则
+         *
+         * @param {String} scene - 场景
+         * @return {Object} - 返回数据
+         */
+        rule(scene) {
+            let rule = {};
+            switch (scene) {
+                case 'add':
+                    rule = {
+                        name: { type: 'string', required: true },
+                        type: { type: 'string', required: true },
+                    };
+                    break;
+                default:
+                    break;
+            }
+            return rule;
+        }
+
+        /**
+         * 修改单位数据
+         *
+         * @param {Object} data - post过来的数据
+         * @return {Boolean} - 返回修改结果
+         */
+        async save(data) {
+            if (data._csrf_j !== undefined) {
+                delete data._csrf_j;
+            }
+            const id = data.id !== undefined ? parseInt(data.id) : 0;
+            const transaction = await this.db.beginTransaction();
+            try {
+                if (id > 0) {
+                    // 修改操作时
+                    delete data.create_time;
+                    data.id = id;
+                } else {
+                    // 重名检测
+                    const accountData = await this.db.select(this.tableName, {
+                        where: {
+                            name: data.name,
+                            pid: data.pid,
+                        },
+                    });
+                    if (accountData.length > 0) {
+                        throw '已存在对应的单位名';
+                    }
+                    data.create_time = new Date();
+                }
+                // 并修改用户的用户组及单位名称
+                const updateRow = {};
+                if (data.name) updateRow.company = data.name;
+                if (data.type) updateRow.account_group = data.type;
+                if (!this._.isEmpty(updateRow)) {
+                    const info = id !== 0 ? await this.getDataById(id) : null;
+                    await this.ctx.service.projectAccount.update(updateRow, {
+                        project_id: data.pid,
+                        company: info ? info.name : data.name,
+                    }, transaction);
+                    // egg 自带的update方法无法使用UPDATE zh_project_account SET company = ? WHERE project_id = ? AND company = ? ,未知原因
+                    // await transaction.update(this.ctx.service.projectAccount.tableName, updateRow,
+                    //     {
+                    //         where: {
+                    //             project_id: data.pid,
+                    //             company: info ? info.name : data.name,
+                    //         },
+                    //     }
+                    // );
+                }
+                const operate = id === 0 ? await transaction.insert(this.tableName, data) :
+                    await transaction.update(this.tableName, data);
+                transaction.commit();
+                const result = operate.affectedRows > 0;
+                return result;
+            } catch (e) {
+                transaction.rollback();
+            }
+        }
+
+        async del(id) {
+            // 先删除签章oss地址
+            const info = await this.getDataById(id);
+            if (info.sign_path) {
+                await this.ctx.app.fujianOss.delete(this.ctx.app.config.fujianOssFolder + info.sign_path);
+            }
+            // 删除单位
+            return await this.db.delete(this.tableName, { id });
+        }
+    }
+    return ConstructionUnit;
+};

+ 6 - 1
app/service/material.js

@@ -148,6 +148,7 @@ module.exports = app => {
                 s_order: data.s_order,
                 material_tax: this.ctx.session.sessionProject.page_show.openMaterialTax,
                 decimal: preMaterial && preMaterial.decimal ? preMaterial.decimal : JSON.stringify(materialConst.decimal),
+                is_new: 1,
             };
             const transaction = await this.db.beginTransaction();
             try {
@@ -174,7 +175,10 @@ module.exports = app => {
                     const preNotJoinList = await this.ctx.service.materialListNotjoin.getAllDataByCondition({ where: { tid: this.ctx.tender.id, mid: preMaterial.id } });
                     await this.ctx.service.materialListNotjoin.copyNewStageNotJoinList(transaction, preNotJoinList, newMaterial.id);
                     // 复制调差清单工料关联表
-                    await this.ctx.service.materialList.copyPreMaterialList(transaction, preMaterial, newMaterial);
+                    // await this.ctx.service.materialList.copyPreMaterialList(transaction, preMaterial, newMaterial);
+                    await this.ctx.service.materialList.copyPreMaterialList2(transaction, data.material_list, preNotJoinList, newMaterial);
+                    // 新增或删除list_gcl表
+                    await this.ctx.service.materialListGcl.insertOrDelGcl(transaction, data.insertGclList, data.removeGclList, newMaterial.id);
                     // 修改本期应耗数量值和有效价差,需要剔除不参与调差的清单数据,并返回总金额
                     const [m_tp, m_tax_tp] = await this.ctx.service.materialBills.updateNewMaterial(transaction, this.ctx.tender.id, newMaterial.id, this.ctx, newMaterial.stage_id, JSON.parse(newMaterial.decimal));
                     // 修改现行价格指数,并返回调差基数json
@@ -225,6 +229,7 @@ module.exports = app => {
                 await transaction.delete(this.ctx.service.materialAudit.tableName, { mid: id });
                 await transaction.delete(this.ctx.service.materialBills.tableName, { mid: id });
                 await transaction.delete(this.ctx.service.materialList.tableName, { mid: id });
+                await transaction.delete(this.ctx.service.materialListGcl.tableName, { mid: id });
                 await transaction.delete(this.ctx.service.materialListNotjoin.tableName, { mid: id });
                 await transaction.delete(this.ctx.service.materialBillsHistory.tableName, { mid: id });
                 await transaction.delete(this.ctx.service.materialFile.tableName, { mid: id });

+ 30 - 0
app/service/material_list.js

@@ -292,6 +292,36 @@ module.exports = app => {
             return copyMLArray.length !== 0 ? await transaction.insert(this.tableName, copyMLArray) : true;
         }
 
+        /**
+         * 复制上一期并生成新一期清单工料关联,计算新一期小计值
+         * @param transaction
+         * @param preMaterial
+         * @param newMid
+         * @return {Promise<void>}
+         */
+        async copyPreMaterialList2(transaction, materialListData, notJoinList, newMaterial) {
+            const copyMLArray = [];
+            for (const ml of materialListData) {
+                const is_join = this._.find(notJoinList, { gcl_id: ml.gcl_id, xmj_id: ml.xmj_id, mx_id: ml.mx_id });
+                const newMaterialList = {
+                    tid: newMaterial.tid,
+                    order: ml.order,
+                    mid: newMaterial.id,
+                    mb_id: ml.mb_id,
+                    gcl_id: ml.gcl_id,
+                    xmj_id: ml.xmj_id,
+                    mx_id: ml.mx_id,
+                    gather_qty: ml.gather_qty,
+                    quantity: ml.quantity,
+                    expr: ml.expr,
+                    is_join: is_join ? 1 : 0,
+                    in_time: new Date(),
+                };
+                copyMLArray.push(newMaterialList);
+            }
+            return copyMLArray.length !== 0 ? await transaction.insert(this.tableName, copyMLArray) : true;
+        }
+
 
         /**
          * 添加工料清单关联(多清单对应)

+ 68 - 0
app/service/material_list_gcl.js

@@ -0,0 +1,68 @@
+'use strict';
+/**
+ * 清单设置 数据模型
+ * @author LanJianRong
+ * @date 2020/6/30
+ * @version
+ */
+
+module.exports = app => {
+    class MaterialListGcl extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'material_list_gcl';
+        }
+
+        async setData(mid, data) {
+            if (!this.ctx.tender) {
+                throw '数据错误';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                const insertArray = [];
+                for (const d of data) {
+                    insertArray.push({
+                        tid: this.ctx.tender.id,
+                        mid,
+                        order: d.order,
+                        gcl_id: d.gcl_id,
+                        mb_id: d.mb_id,
+                        quantity: d.quantity,
+                        expr: d.expr,
+                    });
+                }
+                if (insertArray.length > 0) await transaction.insert(this.tableName, insertArray);
+                await transaction.update(this.ctx.service.material.tableName, { id: mid, is_new: 1 });
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                console.log(err);
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        async insertOrDelGcl(transaction, insertGclList, removeGclList, mid) {
+            if (insertGclList.length > 0) {
+                for (const gcl of insertGclList) {
+                    gcl.tid = this.ctx.tender.id;
+                    gcl.mid = mid;
+                }
+                await transaction.insert(this.tableName, insertGclList);
+            }
+            if (removeGclList.length > 0) {
+                for (const gcl of removeGclList) {
+                    await transaction.delete(this.tableName, { id: gcl.id });
+                }
+            }
+        }
+    }
+    return MaterialListGcl;
+};
+

+ 8 - 0
app/service/project_account.js

@@ -767,6 +767,14 @@ module.exports = app => {
                 operate: '=',
             });
 
+            // 单位名称筛选
+            if (data.company !== undefined && data.company !== '') {
+                this.sqlBuilder.setAndWhere('company', {
+                    value: this.db.escape(data.company),
+                    operate: '=',
+                });
+            }
+
             // 名字筛选
             if (data.keyword !== undefined && data.keyword !== '') {
                 this.sqlBuilder.setNewOrWhere([{

+ 2 - 0
app/service/report_memory.js

@@ -1380,7 +1380,9 @@ module.exports = app => {
             for (const c of this.changeInfo) {
                 c.cur_used = usedChangesId.indexOf(c.cid) >= 0;
                 c.used_ratio = c.used_pt;
+                c.is_import = 0;
             }
+            const importChange = await this.ctx.service.stageImportChange.get
             return this.changeInfo;
         }
 

+ 47 - 0
app/service/revise_pos.js

@@ -114,6 +114,53 @@ module.exports = app => {
             return {pos: data};
         }
 
+        async insertPos(tid, rid, data) {
+            if (!data.length) return;
+
+            let porder = data[0].porder, lid = data[0].lid;
+            for (const d of data) {
+                this._completeInsertPosData(tid, rid, d);
+                porder = Math.min(porder, d.porder);
+            }
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                this.initSqlBuilder();
+                this.sqlBuilder.setAndWhere('lid', {
+                    value: this.db.escape(lid),
+                    operate: '=',
+                });
+                this.sqlBuilder.setAndWhere('porder', {
+                    value: porder,
+                    operate: '>=',
+                });
+                this.sqlBuilder.setUpdateData('porder', {
+                    value: data.length,
+                    selfOperate: '+',
+                });
+                const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
+                const result = await transaction.query(sql, sqlParam);
+                await transaction.insert(this.tableName, data);
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+
+            this.initSqlBuilder();
+            this.sqlBuilder.setAndWhere('lid', {
+                value: this.db.escape(lid),
+                operate: '=',
+            });
+            this.sqlBuilder.setAndWhere('porder', {
+                value: porder,
+                operate: '>=',
+            });
+            const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
+            const update = await this.db.query(sql, sqlParam);
+            return { pos: update }
+        }
+
         async updatePos(tid, data) {
             if (data.sgfh_qty !== undefined || data.sjcl_qty !== undefined || data.qtcl_qty !== undefined) {
                 const op = await this.getDataById(data.id);

+ 14 - 2
app/service/stage.js

@@ -440,6 +440,17 @@ module.exports = app => {
             }
         }
 
+        async getChangeSubtotal(stage) {
+            const result = {};
+            const bg = await this.ctx.service.stageChange.getSubtotal(stage);
+            const importBg = await this.ctx.service.stageImportChange.getSubtotal(stage);
+            console.log(bg, importBg);
+            result.common = this.ctx.helper.add(bg.common, importBg.common);
+            result.great = this.ctx.helper.add(bg.great, importBg.great);
+            result.more = this.ctx.helper.add(bg.more, importBg.more);
+            return result;
+        }
+
         /**
          * 获取 当期的 计算基数
          * @return {Promise<any>}
@@ -447,9 +458,9 @@ module.exports = app => {
         async getStagePayCalcBase(stage, tenderInfo) {
             const calcBase = JSON.parse(JSON.stringify(payConst.calcBase));
             const param = tenderInfo.deal_param;
+            const sum = await this.ctx.service.stageBills.getSumTotalPrice(stage);
+            const bg = await this.getChangeSubtotal(stage);
             for (const cb of calcBase) {
-                const sum = await this.ctx.service.stageBills.getSumTotalPrice(stage);
-                const bg = await this.ctx.service.stageChange.getQualityTotalPrice(stage);
                 switch (cb.code) {
                     case 'htj':
                         cb.value = param.contractPrice;
@@ -533,6 +544,7 @@ module.exports = app => {
                 await transaction.delete(this.ctx.service.stageAudit.tableName, { sid: id });
                 await transaction.delete(this.ctx.service.stageBills.tableName, { sid: id });
                 await transaction.delete(this.ctx.service.stageChange.tableName, { sid: id });
+                await transaction.delete(this.ctx.service.stageImportChange.tableName, { sid: id });
                 await transaction.delete(this.ctx.service.stagePos.tableName, { sid: id });
                 await transaction.delete(this.ctx.service.stageDetail.tableName, { sid: id });
                 await transaction.delete(this.ctx.service.stagePosFinal.tableName, { sid: id });

+ 7 - 2
app/service/stage_bills.js

@@ -460,8 +460,6 @@ module.exports = app => {
                 for (const u of result.update) {
                     const sb = stageBills.find(x => { return x.lid === u.lid; });
                     if (!sb || sb.times !== this.ctx.stage.curTimes || sb.order !== this.ctx.stage.curOrder) {
-                        u.qc_qty = sb ? sb.qc_qty : null;
-                        u.qc_tp = sb ? sb.qc_tp : null;
                         u.postil = sb ? sb.postil : null;
                         u.tid = this.ctx.tender.id;
                         u.sid = this.ctx.stage.id;
@@ -474,10 +472,17 @@ module.exports = app => {
                         updateStageBills.push(u);
                     }
                 }
+                for (const cd of result.qc_detail) {
+                    cd.tid = this.ctx.tender.id;
+                    cd.sid = this.ctx.stage.id;
+                    cd.import_lid = lid;
+                }
 
                 const his = await this.ctx.service.sumLoadHistory.saveStageHistory(this.ctx.tender.id, this.ctx.stage.id, lid, tenders, result.errors, cover);
                 if (updateStageBills.length > 0) await conn.updateRows(this.tableName, updateStageBills);
                 if (insertStageBills.length > 0) await conn.insert(this.tableName, insertStageBills);
+                await conn.delete(this.ctx.service.stageImportChange.tableName, { import_lid: lid, sid: this.ctx.stage.id });
+                if (result.qc_detail.length > 0) await conn.insert(this.ctx.service.stageImportChange.tableName, result.qc_detail);
                 await conn.commit();
                 return { curStageData: result.update, sumLoadHis: his };
             } catch (err) {

+ 17 - 5
app/service/stage_change.js

@@ -308,7 +308,10 @@ module.exports = app => {
         }
 
         async getFinalUsedData(tid, cid) {
-            const stage = this.ctx.service.stage.getLastestStage(tid, true);
+            const stage = await this.ctx.service.stage.getLastestStage(tid, true);
+            if (!stage) { // 防止未创建期时调用
+                return [];
+            }
             if (stage.status === audit.stage.status.checked) {
                 const sql = 'SELECT scf.* ' +
                     '  FROM ' + this.ctx.service.stageChangeFinal.tableName + ' scf ' +
@@ -322,8 +325,8 @@ module.exports = app => {
                     '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s ON scf.sid = s.id' +
                     '  WHERE scf.tid = ? And scf.cid = ? And s.order < ?';
                 const pre = await this.db.query(preSql, [tid, cid, stage.order]);
-                const sql = 'SELECT * FROM ' + this.tableName + ' WHERE sid = ?';
-                const curAll = await this.db.query(sql, [stage.id]);
+                const sql = 'SELECT * FROM ' + this.tableName + ' WHERE sid = ? and cid = ?';
+                const curAll = await this.db.query(sql, [stage.id, cid]);
                 const cur = this.ctx.helper.filterLastestData(curAll, ['lid', 'pid', 'cid', 'cbid'], 'stimes', 'sorder');
                 return [...pre, ...cur];
             }
@@ -371,6 +374,15 @@ module.exports = app => {
             return this.ctx.helper.filterLastestData(data, ['lid', 'pid', 'cid', 'cbid'], 'stimes', 'sorder');
         }
 
+        async getSumLoadFinalData(sid) {
+            const sql = 'Select cf.tid, cf.sid, cf.lid, cf.cid, cf.cbid, cf.qty, cf.stimes, cf.sorder, c.code As c_code' +
+                '  FROM ' + this.tableName + ' cf' +
+                '  Left Join ' + this.ctx.service.change.tableName + ' c ON cf.cid = c.cid' +
+                '  Where cf.sid = ?';
+            const result = await this.db.query(sql, [sid]);
+            return this.ctx.helper.filterLastestData(result, ['lid', 'pid', 'cbid'], 'stimes', 'sorder');
+        }
+
         async _getTender(stage) {
             if (this.ctx.tender) return this.ctx.tender;
             const tender = { id: stage.tid };
@@ -379,12 +391,12 @@ module.exports = app => {
             return tender;
         }
 
-        async getQualityTotalPrice(stage) {
+        async getSubtotal(stage) {
             const helper = this.ctx.helper;
             const tender = await this._getTender(stage);
             const sql = 'SELECT sc.*, c.quality FROM ' + this.tableName + ' sc' +
                 '  LEFT JOIN ' + this.ctx.service.change.tableName + ' c ON sc.cid = c.cid' +
-                '  WHERE sid = ?';
+                '  WHERE sid = ? ' + (stage.readOnly ? `and (stimes < ${stage.curTimes} or (stimes = ${stage.curTimes} and sorder <= ${stage.curOrder}))` : '');
             let data = await this.db.query(sql, [stage.id]);
             data = helper.filterLastestData(data, ['lid', 'pid', 'cbid'], 'stimes', 'sorder');
             const bqData = [];

+ 2 - 3
app/service/stage_change_final.js

@@ -84,11 +84,10 @@ module.exports = app => {
         }
 
         async getSumLoadFinalData(sid) {
-            const sql = 'Select cf.lid, cf.cid, sum(cf.qty) as qty, c.code As c_code' +
+            const sql = 'Select cf.tid, cf.sid, cf.lid, cf.cid, cf.cbid, cf.qty, c.code As c_code' +
                 '  FROM ' + this.tableName + ' cf' +
                 '  Left Join ' + this.ctx.service.change.tableName + ' c ON cf.cid = c.cid' +
-                '  Where cf.sid = ?' +
-                '  Group By cf.lid, cf.cid';
+                '  Where cf.sid = ?';
             return await this.db.query(sql, [sid]);
         }
     }

+ 220 - 0
app/service/stage_import_change.js

@@ -0,0 +1,220 @@
+'use strict';
+
+/**
+ * 期 - 引入 变更数据
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const changeConst = require('../const/change');
+
+module.exports = app => {
+
+    class StageImportChange extends app.BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'stage_import_change';
+        }
+
+        async getImportLid(sid) {
+            const sql = 'Select lid, 1 as is_import From ' + this.tableName + ' Where sid = ? Group By lid';
+            return await this.db.query(sql, [sid]);
+        }
+
+        /**
+         * 获取最底层项目节(importLid)下导入过变更数据的台账id
+         * @param sid
+         * @param importLid
+         * @returns {Promise<void>}
+         */
+        async getLeafXmjImportLid(sid, importLid) {
+            const sql = 'Select lid, 1 as is_import From ' + this.tableName + ' Where sid = ? and import_lid = ? Group By lid';
+            return await this.db.query(sql, [sid, importLid]);
+        }
+
+        async getImportChange(sid) {
+            const sql =
+                `SELECT c.*, t.name as t_name FROM ${this.ctx.service.change.tableName} c` +
+                `  LEFT JOIN ${this.ctx.service.tender.tableName} t ON c.tid = t.id` +
+                `  WHERE c.cid IN ( SELECT rela_cid FROM ${this.tableName} WHERE sid = ? GROUP BY rela_cid)` +
+                '  Order By c.tid, c.sin_time';
+            return await this.db.query(sql, [sid]);
+        }
+
+        async getStageImportData(stage) {
+            const sql = 'SELECT scf.*, ' +
+                '    oc.p_code As c_code, oc.new_code As c_new_code, oc.quality, ocb.code as b_code, ocb.name, ocb.unit' +
+                '  FROM ' + this.tableName + ' scf ' +
+                '  LEFT JOIN ' + this.ctx.service.change.tableName + ' oc ON scf.rela_cid = oc.cid ' +
+                '  LEFT JOIN ' + this.ctx.service.changeAuditList.tableName + ' ocb ON scf.rela_cbid = ocb.id ' +
+                '  WHERE scf.sid = ?';
+            return await this.db.query(sql, [stage.id]);
+        }
+
+        async getEndStageImportData(stage) {
+            const sql = 'SELECT scf.*, ' +
+                '    oc.p_code As c_code, oc.new_code As c_new_code, oc.quality, ocb.code as b_code, ocb.name, ocb.unit' +
+                '  FROM ' + this.tableName + ' scf ' +
+                '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s ON scf.sid = s.id' +
+                '  LEFT JOIN ' + this.ctx.service.change.tableName + ' oc ON scf.rela_cid = oc.cid ' +
+                '  LEFT JOIN ' + this.ctx.service.changeAuditList.tableName + ' ocb ON scf.rela_cbid = ocb.id ' +
+                '  WHERE scf.tid = ? And s.order <= ?';
+            return await this.db.query(sql, [stage.tid, stage.order]);
+        }
+
+        async getLeafXmjStageImportData(stage, lid) {
+            const sql = 'SELECT scf.*, ' +
+                '    oc.p_code As c_code, oc.new_code As c_new_code, oc.quality, ocb.code as b_code, ocb.name, ocb.unit' +
+                '  FROM ' + this.tableName + ' scf ' +
+                '  LEFT JOIN ' + this.ctx.service.change.tableName + ' oc ON scf.rela_cid = oc.cid ' +
+                '  LEFT JOIN ' + this.ctx.service.changeAuditList.tableName + ' ocb ON scf.rela_cbid = ocb.id ' +
+                '  WHERE scf.sid = ? and import_lid = ?';
+            return await this.db.query(sql, [stage.id, lid]);
+        }
+
+        async getLeafXmjImportChange(sid, importLid) {
+            const sql =
+                `SELECT c.*, t.name as t_name FROM ${this.ctx.service.change.tableName} c` +
+                `  LEFT JOIN ${this.ctx.service.tender.tableName} t ON c.tid = t.id` +
+                `  WHERE c.cid IN ( SELECT rela_cid FROM ${this.tableName} WHERE sid = ? and import_lid = ? GROUP BY rela_cid)` +
+                '  Order By c.tid, c.sin_time';
+            return await this.db.query(sql, [sid, importLid]);
+        }
+
+        async getChangeWithUsedInfo(stage, importLid) {
+            const change = importLid
+                ? await this.getLeafXmjImportChange(stage.id, importLid)
+                : await this.getImportChange(stage.id);
+            if (change.length === 0) return [];
+
+            // 引入变更,不计算已执行等????
+            // const changeBills = await this.ctx.service.changeAuditList.getAllDataByCondition({
+            //     where: { cid: change.map(x => { return x.cid; }) }
+            // });
+            // const changeBillsIndex = {}, changeBillsPart = {};
+            // for (const cb of changeBills) {
+            //     changeBillsIndex[cb.id] = cb;
+            //     if (!changeBillsPart[cb.cid]) changeBillsPart[cb.cid] = [];
+            //     changeBillsPart[cb.cid].push(cb);
+            // }
+            // const stageChangeBills = await this.getEndStageImportChange(stage);
+            // for (const scb of stageChangeBills) {
+            //     if (!scb.rela_qty) continue;
+            //
+            //     const cb = changeBillsIndex[scb.rela_cbid];
+            //     if (cb) cb.used_qty = this.ctx.helper.add(cb.used_qty, scb.rela_qty);
+            // }
+            // for (const cid in changeBillsPart) {
+            //     const c = change.find(x => { return x.cid === cid });
+            //     if (!c) continue;
+            //
+            //     for (const cb of changeBillsPart[cid]) {
+            //         cb.tp = this.ctx.helper.mul(cb.spamount, cb.unit_price, c.tp_decimal || this.ctx.tender.info.decimal.tp);
+            //         cb.used_tp = this.ctx.helper.mul(cb.used_qty, cb.unit_price, this.ctx.tender.info.decimal.tp);
+            //
+            //         c.used_tp = this.ctx.helper.add(c.used_tp, cb.used_tp);
+            //         if (cb.spamount > 0) {
+            //             c.p_tp = this.ctx.helper.add(c.p_tp, cb.tp);
+            //             c.p_used_tp = this.ctx.helper.add(c.p_used_tp, cb.used_tp);
+            //         } else if (cb.spamount < 0){
+            //             c.n_tp = this.ctx.helper.add(c.n_tp, cb.tp);
+            //             c.n_used_tp = this.ctx.helper.add(c.n_used_tp, cb.used_tp);
+            //         }
+            //     }
+            //     c.used_pt = c.total_price ? this.ctx.helper.mul(this.ctx.helper.div(c.used_tp, c.total_price, 4), 100) : 0;
+            //     c.p_used_pt = c.p_tp ? this.ctx.helper.mul(this.ctx.helper.div(c.p_used_tp, c.p_tp, 4), 100) : 0;
+            //     c.n_used_pt = c.n_tp ? this.ctx.helper.mul(this.ctx.helper.div(c.n_used_tp, c.n_tp, 4), 100) : 0;
+            // }
+            return change;
+        }
+
+        /**
+         * 获取 变更令 - 变更清单 使用情况
+         * @param {Number} sid - 查询期id
+         * @param {uuid} cid - 变更令id
+         * @return {Promise<void>}
+         */
+        async getChangeUsedData(sid, cid) {
+            return await this.getAllDataByCondition({ where: { sid, rela_cid: cid } });
+        }
+
+        /**
+         * 获取 变更令 - 变更清单 使用情况
+         * @param {Number} sid - 查询期id
+         * @param {uuid} cid - 变更令id
+         * @return {Promise<void>}
+         */
+        async getChangeEndUsedData(tid, cid) {
+            const sql = 'SELECT scf.* ' +
+                '  FROM ' + this.tableName + ' scf ' +
+                '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s ON scf.sid = s.id' +
+                '  WHERE scf.tid = ? And scf.rela_cid = ? And s.order <= ?';
+            const result = await this.db.query(sql, [tid, cid, this.ctx.stage.order]);
+            return result;
+        }
+
+        async getChangeFinalUsedData(tid, cid) {
+            const stage = this.ctx.service.stage.getLastestStage(tid, true);
+            const sql = 'SELECT scf.* ' +
+                '  FROM ' + this.tableName + ' scf ' +
+                '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s ON scf.sid = s.id' +
+                '  WHERE scf.tid = ? And scf.rela_cid = ? And s.order <= ?';
+            const result = await this.db.query(sql, [tid, cid, stage.order]);
+            return result;
+        }
+
+        async getStageUsedChangeId(sid) {
+            const sql = 'SELECT rela_cid FROM ' + this.tableName + ' WHERE sid = ? Group By rela_cid';
+            const cur = await this.db.query(sql, [sid]);
+            return this._.map(cur, 'rela_cid');
+        }
+
+        async _getTender(stage) {
+            if (this.ctx.tender) return this.ctx.tender;
+            const tender = { id: stage.tid };
+            tender.data = await this.ctx.service.tender.getTender(stage.tid);
+            tender.info = await this.service.tenderInfo.getTenderInfo(tender.id);
+            return tender;
+        }
+
+        // 分类汇总变更金额
+        async getSubtotal(stage) {
+            const helper = this.ctx.helper;
+            const tender = await this._getTender(stage);
+            const sql = 'SELECT sc.*, c.quality, c.name FROM ' + this.tableName + ' sc' +
+                '  LEFT JOIN ' + this.ctx.service.change.tableName + ' c ON sc.rela_cid = c.cid' +
+                '  WHERE sid = ?';
+            const data = await this.db.query(sql, [stage.id]);
+            const bqData = [];
+            for (const d of data) {
+                if (!d.rela_qty) continue;
+                let bd = bqData.find(x => { return x.lid === d.lid && x.quality === d.quality; });
+                if (!bd) {
+                    const bills = await this.db.get(this.ctx.service.ledger.departTableName(tender.id), { id: d.lid });
+                    if (!bills) continue;
+                    bd = { lid: d.lid, quality: d.quality, unit_price: bills.unit_price, qty: d.rela_qty, cid: d.rela_cid, name: d.name };
+                    bqData.push(bd);
+                }
+                const tp = this.ctx.helper.mul(d.rela_qty, bd.unit_price, tender.info.decimal.tp);
+                bd.tp = this.ctx.helper.add(bd.tp, tp);
+            }
+            const result = {};
+            result.common = helper.sum(helper._.map(bqData.filter(x => {return x.quality === changeConst.quality.common.value; }), 'tp'));
+            result.more = helper.sum(helper._.map(bqData.filter(x => {return x.quality === changeConst.quality.more.value; }), 'tp'));
+            result.great = helper.sum(helper._.map(bqData.filter(x => {return x.quality === changeConst.quality.great.value; }), 'tp'));
+            return result;
+        }
+    }
+
+    return StageImportChange;
+
+};

+ 2 - 2
app/service/stage_rela.js

@@ -19,7 +19,7 @@ class srCache {
     }
 
     async _getCacheOrgTp() {
-        const bg = await this.ctx.service.stageChange.getQualityTotalPrice(this.stage);
+        const bg = await this.ctx.service.stageChange.getSubtotal(this.stage);
         const gcl100 = await this.ctx.service.stageBills.getSumTotalPriceGcl(this.stage, '^[^0-9]*1[0-9]{2}(-|$)');
         return {
             contract_tp: this.stage.contract_tp, qc_tp: this.stage.qc_tp,
@@ -290,7 +290,7 @@ module.exports = app => {
         }
 
         // async _getCacheOrgTp(stage) {
-        //     const bg = await this.ctx.service.stageChange.getQualityTotalPrice(stage);
+        //     const bg = await this.ctx.service.stageChange.getSubtotal(stage);
         //     const gcl100 = await this.ctx.service.stageBills.getSumTotalPriceGcl(stage, '^[^0-9]*1[0-9]{2}(-|$)');
         //     return {
         //         contract_tp: stage.contract_tp, qc_tp: stage.qc_tp,

+ 2 - 0
app/view/change/information.ejs

@@ -450,6 +450,7 @@
     };
     let changeInfo = Object.assign({}, back_changeInfo);
     let changeUsedData = JSON.parse(unescape('<%- escape(JSON.stringify(changeUsedData)) %>'));
+    console.log(changeUsedData);
 </script>
 <script src="/public/js/change_information_set.js?202001181"></script>
 <script src="/public/js/change_audit.js"></script>
@@ -466,6 +467,7 @@
     const aidList = _.map(auditList2, 'uid');
     aidList.splice(0, 1);
     let changeUsedData = JSON.parse(unescape('<%- escape(JSON.stringify(changeUsedData)) %>'));
+    // console.log(changeUsedData);
     const changeLedgerList = JSON.parse(unescape('<%- escape(JSON.stringify(changeLedgerList)) %>'));
     const changePosList = JSON.parse(unescape('<%- escape(JSON.stringify(changePosList)) %>'));
 </script>

+ 4 - 0
app/view/change/plan_information.ejs

@@ -107,6 +107,10 @@
                                 <th width="" class="text-center" style="vertical-align: middle">方案描述</th>
                                 <td colspan="3"><textarea class="form-control form-control-sm" data-name="memo" <% if (change.readOnly) { %>readonly<% } %> rows="3"><%- change.memo %></textarea></td>
                             </tr>
+                            <tr>
+                                <th width="" class="text-center" style="vertical-align: middle">工程量数量计算式</th>
+                                <td colspan="3"><textarea class="form-control form-control-sm" data-name="expr" <% if (change.readOnly) { %>readonly<% } %> rows="3"><%- change.expr %></textarea></td>
+                            </tr>
                         </table>
                         <h5 id="qingdan" class="d-inline-block">变更清单</h5>
                         <% if (change.status === auditConst.status.checked) { %>

+ 1 - 1
app/view/layout/layout.ejs

@@ -79,7 +79,7 @@
     }
     const userID = <%- ctx.session.sessionUser.accountId %>;
     let user = '<%= ctx.session.sessionUser.name %>';
-    <% if (ctx.app.config.is_debug) { %>
+    <% if (ctx.session.sessionUser.loginStatus) { %>
     const is_debug = true;
     <% } %>
 </script>

+ 6 - 0
app/view/material/index.ejs

@@ -92,12 +92,18 @@
     </div>
 </div>
 <script src="/public/js/sub_menu.js"></script>
+<script src="/public/js/decimal.min.js"></script>
+<script src="/public/js/zh_calc.js"></script>
+<script src="/public/js/path_tree.js"></script>
+<script src="/public/js/gcl_gather.js"></script>
 <script src="/public/js/datepicker/datepicker.min.js"></script>
 <script src="/public/js/datepicker/datepicker.zh.js"></script>
 
 <script>
     const tenderId = '<%- ctx.tender.id %>';
     const auditConst = JSON.parse('<%- auditConst2 %>');
+    const preUrl = JSON.parse(unescape('<%- escape(JSON.stringify(preUrl)) %>'));
+    const lastMaterialListNum = parseInt('<%- lastMaterialList ? lastMaterialList.length : 0 %>');
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',

+ 1 - 0
app/view/material/list.ejs

@@ -81,6 +81,7 @@
     const readOnly = <%- material.readOnly %>;
     const stage_order = <%- material.order %>;
     const materialID = <%- material.id %>;
+    const tenderID = <%- tender.id %>;
     const materialDecimal = JSON.parse(unescape('<%- escape(JSON.stringify(material.decimal)) %>'));
     const openMaterialChecklist = parseInt(<%- ctx.session.sessionProject.page_show.openMaterialChecklist %>);
     let materialListData, materialChecklistData, notJoinList, ledger, curLedgerData, pos, curPosData, gclGatherData;

+ 19 - 1
app/view/material/modal.ejs

@@ -33,12 +33,30 @@
             <div class="modal-footer">
                 <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>" />
                 <input type="hidden" name="s_order" value="" id="s_order" />
+                <input type="hidden" name="list" value="" id="insertList" />
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
-                <button type="submit" id="addMaterial" class="btn btn-primary btn-sm">确定添加</button>
+                <button type="button" id="addMaterial" class="btn btn-primary btn-sm">确定添加</button>
             </div>
         </form>
     </div>
 </div>
+<!--正在修订提示-->
+<div class="modal fade" id="okedit" data-backdrop="static">
+    <div class="modal-dialog " role="document" >
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">提示</h5>
+            </div>
+            <div class="modal-body">
+                <h5>正在生成新一期调差清单数据中,请等待...</h5>
+                <div class="progress">
+                    <div id="material-progress" class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<!--<script type="text/javascript">$('#okedit').modal('show');</script>-->
 <% } %>
 <% if (materials && materials.length >= 1) { %>
 <!--删除期-->

+ 1 - 1
app/view/revise/index.ejs

@@ -46,7 +46,7 @@
                     <% for (const lr of ledgerRevise) { %>
                     <tr>
                         <td><%- lr.corder %></td>
-                        <td><%- lr.in_time ? ctx.moment(lr.in_time).format('YYYY-MM-DD') : '' %></td>
+                        <td><%- lr.in_time ? ctx.moment(lr.in_time).format('YYYY-MM-DD HH:mm:ss') : '' %></td>
                         <td><%- lr.user_name %></td>
                         <% if (!lr.valid) {%>
                         <td class="text-danger">

+ 55 - 40
app/view/setting/user.ejs

@@ -4,9 +4,9 @@
         <div class="title-main">
             <h2>账号管理
                 <% if (projectData.max_user === 0 || projectData.max_user > user_total) { %>
-                <a href="#ver" data-toggle="modal" data-target="#add-user" class="btn btn-primary btn-sm pull-right">添加账号</a>
+                    <a href="#ver" data-toggle="modal" data-target="#add-user" class="btn btn-primary btn-sm pull-right">添加账号</a>
                 <% } else { %>
-                <a href="#add-unpass" data-toggle="modal" data-target="#add-unpass" class="btn btn-primary btn-sm pull-right">添加账号(受限)</a>
+                    <a href="#add-unpass" data-toggle="modal" data-target="#add-unpass" class="btn btn-primary btn-sm pull-right">添加账号(受限)</a>
                 <% } %>
             </h2>
         </div>
@@ -17,9 +17,11 @@
                 <nav class="nav nav-tabs m-3" role="tablist">
                     <a class="nav-item nav-link active" href="/setting/user">账号列表</a>
                     <a class="nav-item nav-link" href="/setting/user/permission/set">账号权限</a>
+                    <a class="nav-item nav-link" href="/setting/user/unit">参建单位</a>
                     <div class="ml-auto">
                         <form class="input-group input-group-sm" method="get">
                             <input type="text" class="form-control" value="<%- keyword %>" name="keyword" placeholder="账号/姓名/单位/手机 搜索" aria-label="账号/姓名/单位/手机 搜索" aria-describedby="button-addon2">
+                            <input type="hidden" class="form-control" value="<%- company %>" name="company">
                             <div class="input-group-append">
                                 <button class="btn btn-outline-primary" type="submit" id="button-addon2"><i class="fa fa-search"></i></button>
                             </div>
@@ -27,44 +29,56 @@
                     </div>
                 </nav>
                 <div class="tab-content m-3">
-                    <div id="user-list" class="tab-pane active">
-                        <table class="table table-hover table-bordered table-sm">
-                            <thead>
-                            <tr>
-                                <th>账号</th>
-                                <th>姓名</th>
-                                <th>单位</th>
-                                <th>角色/职位</th>
-                                <th>手机</th>
-                                <th>电话</th>
-                                <th class="text-center">操作</th></tr>
-                            </thead>
-                            <tbody>
-                            <% for (const account of accountData) { %>
-                                <tr <% if (account.enable !== 1) { %> class="table-danger"<% } %>>
-                                    <td><%= account.account %><% if (account.is_admin === 1) { %> <span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="管理员"><i class="fa fa-user-circle-o"></i></span><% } %></td>
-                                    <td><%= account.name %></td>
-                                    <td><%= account.company %></td>
-                                    <td><%= account.role %></td>
-                                    <td><%= account.mobile %><% if (account.bind === 1) { %>(已绑定第三方平台)<% } %></td>
-                                    <td><%= account.telephone %></td>
-                                    <td class="text-center"><a href="#edit-user" data-account="<%= JSON.stringify(account) %>" data-toggle="modal" data-target="#edit-user" class="btn btn-sm btn-outline-primary">编辑</a>
-                                        <% if (account.is_admin !== 1) { %>
-                                            <% if (account.enable !== 1) { %>
-                                                <!--<a href="" class="btn btn-sm btn-outline-success account-switch-btn" data-account="<%= account.id %>">启用</a>-->
-                                            <% } else { %>
-                                                <!--<a href="" class="btn btn-sm btn-outline-danger account-switch-btn" data-account="<%= account.id %>">停用</a>-->
-                                            <% } %>
-                                        <% } %>
-                                        <% if (account.bind === 1) { %>
-                                            <a href="#unlink-user" data-toggle="modal" data-target="#unlink-user" class="btn btn-sm btn-outline-dark unlink-user" data-account="<%= account.id %>" >解绑</a>
-                                        <% } %>
-                                    </td>
-                                </tr>
-                            <% } %>
-                            </tbody>
-                        </table>
-                        <% include ../layout/page.ejs %>
+                        <div id="user-list" class="tab-pane active">
+                        <div class="row">
+                            <div class="col-3">
+                                <div class="list-group">
+                                    <a href="/setting/user?company=" class="list-group-item list-group-item-action <% if (!company) { %>active<% } %>">全部</a>
+                                    <% for (const u of unitList) { %>
+                                    <a href="/setting/user?company=<%- u.name %>" class="list-group-item list-group-item-action <% if (company === u.name) { %>active<% } %>"><%- u.name %></a>
+                                    <% } %>
+                                </div>
+                            </div>
+                            <div class="col-9">
+                                <table class="table table-hover table-bordered table-sm">
+                                    <thead>
+                                    <tr>
+                                        <th>账号</th>
+                                        <th>姓名</th>
+                                        <th>单位</th>
+                                        <th>角色/职位</th>
+                                        <th>手机</th>
+                                        <th>电话</th>
+                                        <th class="text-center">操作</th></tr>
+                                    </thead>
+                                    <tbody>
+                                    <% for (const account of accountData) { %>
+                                        <tr <% if (account.enable !== 1) { %> class="table-danger"<% } %>>
+                                            <td><%= account.account %><% if (account.is_admin === 1) { %> <span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="管理员"><i class="fa fa-user-circle-o"></i></span><% } %></td>
+                                            <td><%= account.name %></td>
+                                            <td><%= account.company %></td>
+                                            <td><%= account.role %></td>
+                                            <td><%= account.mobile %><% if (account.bind === 1) { %>(已绑定第三方平台)<% } %></td>
+                                            <td><%= account.telephone %></td>
+                                            <td class="text-center"><a href="#edit-user" data-account="<%= JSON.stringify(account) %>" data-toggle="modal" data-target="#edit-user" class="btn btn-sm btn-outline-primary">编辑</a>
+                                                <% if (account.is_admin !== 1) { %>
+                                                    <% if (account.enable !== 1) { %>
+                                                        <!--<a href="" class="btn btn-sm btn-outline-success account-switch-btn" data-account="<%= account.id %>">启用</a>-->
+                                                    <% } else { %>
+                                                        <!--<a href="" class="btn btn-sm btn-outline-danger account-switch-btn" data-account="<%= account.id %>">停用</a>-->
+                                                    <% } %>
+                                                <% } %>
+                                                <% if (account.bind === 1) { %>
+                                                    <a href="#unlink-user" data-toggle="modal" data-target="#unlink-user" class="btn btn-sm btn-outline-dark unlink-user" data-account="<%= account.id %>" >解绑</a>
+                                                <% } %>
+                                            </td>
+                                        </tr>
+                                    <% } %>
+                                    </tbody>
+                                </table>
+                                <% include ../layout/page.ejs %>
+                            </div>
+                        </div>
                     </div>
                 </div>
             </div>
@@ -73,6 +87,7 @@
 </div>
 <script>
     const accountList = JSON.parse(unescape('<%- escape(JSON.stringify(accountData)) %>'));
+    const unitList = JSON.parse(unescape('<%- escape(JSON.stringify(unitList)) %>'));
 </script>
 <script src="/public/js/setting.js"></script>
 <script>autoFlashHeight();</script>

+ 34 - 20
app/view/setting/user_modal.ejs

@@ -7,15 +7,15 @@
             </div>
             <form method="post" action="/setting/user/add?_csrf_j=<%= ctx.csrf %>" onsubmit="return checkUserForm('add');">
             <div class="modal-body">
-                <div class="form-group">
-                    <label><b class="text-danger">*</b>账号组</label>
-                    <select class="form-control form-control-sm" name="account_group">
-                        <option value="0">请选择</option>
-                        <% for (const dw in accountGroup) { %>
-                        <option value="<%= dw %>"><%= accountGroup[dw] %></option>
-                        <% } %>
-                    </select>
-                </div>
+                <!--<div class="form-group">-->
+                    <!--<label><b class="text-danger">*</b>账号组</label>-->
+                    <!--<select class="form-control form-control-sm" name="account_group">-->
+                        <!--<option value="0">请选择</option>-->
+                        <!--<% for (const dw in accountGroup) { %>-->
+                        <!--<option value="<%= dw %>"><%= accountGroup[dw] %></option>-->
+                        <!--<% } %>-->
+                    <!--</select>-->
+                <!--</div>-->
                 <div class="form-group">
                     <label>登录账号<b class="text-danger">*</b></label>
                     <input class="form-control form-control-sm" name="account" placeholder="支持英文数字组合" type="text">
@@ -39,7 +39,14 @@
                 </div>
                 <div class="form-group">
                     <label>单位名称<b class="text-danger">*</b></label>
-                    <input class="form-control form-control-sm" name="company" value="" type="text">
+                    <!--<input class="form-control form-control-sm" name="company" value="" type="text">-->
+                    <input value="" name="account_group" type="hidden">
+                    <select class="form-control form-control-sm" name="company">
+                        <option>请选择</option>
+                        <% for (const u of unitList) { %>
+                            <option><%- u.name %></option>
+                        <% } %>
+                    </select>
                 </div>
                 <div class="form-group">
                     <label>角色/职位<b class="text-danger">*</b></label>
@@ -71,15 +78,15 @@
             </div>
             <form method="post" action="/setting/user/update?_csrf_j=<%= ctx.csrf %>" onsubmit="return checkUserForm('update');">
             <div class="modal-body">
-                <div class="form-group">
-                    <label>账号组<b class="text-danger">*</b></label>
-                    <select class="form-control form-control-sm" name="account_group">
-                        <option value="0">请选择</option>
-                        <% for (const dw in accountGroup) { %>
-                            <option value="<%= dw %>"><%= accountGroup[dw] %></option>
-                        <% } %>
-                    </select>
-                </div>
+                <!--<div class="form-group">-->
+                    <!--<label>账号组<b class="text-danger">*</b></label>-->
+                    <!--<select class="form-control form-control-sm" name="account_group">-->
+                        <!--<option value="0">请选择</option>-->
+                        <!--<% for (const dw in accountGroup) { %>-->
+                            <!--<option value="<%= dw %>"><%= accountGroup[dw] %></option>-->
+                        <!--<% } %>-->
+                    <!--</select>-->
+                <!--</div>-->
                 <div class="form-group">
                     <label>登录账号<b class="text-danger">*</b></label>
                     <input class="form-control form-control-sm" value="" name="account" readonly placeholder="支持英文数字组合" type="text">
@@ -109,7 +116,14 @@
                 </div>
                 <div class="form-group">
                     <label>单位名称<b class="text-danger">*</b></label>
-                    <input class="form-control form-control-sm" value="" name="company" type="text">
+                    <!--<input class="form-control form-control-sm" value="" name="company" type="text">-->
+                    <input value="" name="account_group" type="hidden">
+                    <select class="form-control form-control-sm" name="company">
+                        <option>请选择</option>
+                        <% for (const u of unitList) { %>
+                            <option><%- u.name %></option>
+                        <% } %>
+                    </select>
                 </div>
                 <div class="form-group">
                     <label>职位名称<b class="text-danger">*</b></label>

+ 54 - 40
app/view/setting/user_permission.ejs

@@ -4,9 +4,9 @@
         <div class="title-main">
             <h2>账号管理
                 <% if (projectData.max_user === 0 || projectData.max_user > user_total) { %>
-                <a href="#ver" data-toggle="modal" data-target="#add-user" class="btn btn-primary btn-sm pull-right">添加账号</a>
+                    <a href="#ver" data-toggle="modal" data-target="#add-user" class="btn btn-primary btn-sm pull-right">添加账号</a>
                 <% } else { %>
-                <a href="#add-unpass" data-toggle="modal" data-target="#add-unpass" class="btn btn-primary btn-sm pull-right">添加账号(受限)</a>
+                    <a href="#add-unpass" data-toggle="modal" data-target="#add-unpass" class="btn btn-primary btn-sm pull-right">添加账号(受限)</a>
                 <% } %>
             </h2>
         </div>
@@ -17,9 +17,11 @@
                 <nav class="nav nav-tabs m-3" role="tablist">
                     <a class="nav-item nav-link" href="/setting/user">账号列表</a>
                     <a class="nav-item nav-link active" href="/setting/user/permission/set">账号权限</a>
+                    <a class="nav-item nav-link" href="/setting/user/unit">参建单位</a>
                     <div class="ml-auto">
                         <form class="input-group input-group-sm" method="get">
                             <input type="text" class="form-control" value="<%- keyword %>" name="keyword" placeholder="账号/姓名/单位/手机 搜索" aria-label="账号/姓名/单位/手机 搜索" aria-describedby="button-addon2">
+                            <input type="hidden" class="form-control" value="<%- company %>" name="company">
                             <div class="input-group-append">
                                 <button class="btn btn-outline-primary" type="submit" id="button-addon2"><i class="fa fa-search"></i></button>
                             </div>
@@ -28,47 +30,59 @@
                 </nav>
                 <div class="tab-content m-3">
                     <div id="user-purview" class="tab-pane active">
-                        <table class="table table-hover table-bordered table-sm">
-                            <thead>
-                            <tr>
-                                <th>账号</th>
-                                <th>姓名</th>
-                                <th>单位</th>
-                                <th>角色/职位</th>
-                                <th>协作办公</th>
-                                <th>权限</th>
-                            </thead>
-                            <tbody>
-                            <% for (const account of accountData) { %>
-                                <tr>
-                                    <td><%= account.account %><% if (account.is_admin === 1) { %> <span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="管理员"><i class="fa fa-user-circle-o"></i></span><% } %></td>
-                                    <td><%= account.name %></td>
-                                    <td><%= account.company %></td>
-                                    <td><%= account.role %></td>
-                                    <td><% if (account.cooperation === 1) { %>启用<% } %></td>
-                                    <td>
-                                        <% if (account.permission !== '' && account.permission !== null) { %>
-                                            <% const accountPermission = JSON.parse(account.permission); %>
-                                            <% for(const ap in accountPermission) { %>
-                                                <% if (permission[ap].type === 'checkbox') { %>
-                                                    <% for (const tc in permission[ap].children) { %>
-                                                        <% if (accountPermission[ap].indexOf(permission[ap].children[tc].value.toString()) !== -1) { %>
-                                                            <%= permission[ap].children[tc].title %>
+                        <div class="row">
+                            <div class="col-3">
+                                <div class="list-group">
+                                    <a href="/setting/user?company=" class="list-group-item list-group-item-action <% if (!company) { %>active<% } %>">全部</a>
+                                    <% for (const u of unitList) { %>
+                                        <a href="/setting/user?company=<%- u.name %>" class="list-group-item list-group-item-action <% if (company === u.name) { %>active<% } %>"><%- u.name %></a>
+                                    <% } %>
+                                </div>
+                            </div>
+                            <div class="col-9">
+                                <table class="table table-hover table-bordered table-sm">
+                                    <thead>
+                                    <tr>
+                                        <th>账号</th>
+                                        <th>姓名</th>
+                                        <th>单位</th>
+                                        <th>角色/职位</th>
+                                        <th>协作办公</th>
+                                        <th>权限</th>
+                                    </thead>
+                                    <tbody>
+                                    <% for (const account of accountData) { %>
+                                        <tr>
+                                            <td><%= account.account %><% if (account.is_admin === 1) { %> <span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="管理员"><i class="fa fa-user-circle-o"></i></span><% } %></td>
+                                            <td><%= account.name %></td>
+                                            <td><%= account.company %></td>
+                                            <td><%= account.role %></td>
+                                            <td><% if (account.cooperation === 1) { %>启用<% } %></td>
+                                            <td>
+                                                <% if (account.permission !== '' && account.permission !== null) { %>
+                                                    <% const accountPermission = JSON.parse(account.permission); %>
+                                                    <% for(const ap in accountPermission) { %>
+                                                        <% if (permission[ap].type === 'checkbox') { %>
+                                                            <% for (const tc in permission[ap].children) { %>
+                                                                <% if (accountPermission[ap].indexOf(permission[ap].children[tc].value.toString()) !== -1) { %>
+                                                                    <%= permission[ap].children[tc].title %>
+                                                                <% } %>
+                                                            <% } %>
+                                                        <% } else if (permission[ap].type === 'radio') { %>
+                                                            <% if (parseInt(accountPermission[ap]) === permission[ap].children[0].value) { %>
+                                                                <%= permission[ap].title %>
+                                                            <% } %>
                                                         <% } %>
                                                     <% } %>
-                                                <% } else if (permission[ap].type === 'radio') { %>
-                                                    <% if (parseInt(accountPermission[ap]) === permission[ap].children[0].value) { %>
-                                                        <%= permission[ap].title %>
-                                                    <% } %>
                                                 <% } %>
-                                            <% } %>
-                                        <% } %>
-                                        <a href="#edit-user2" data-account="<%= JSON.stringify(account) %>" data-toggle="modal" data-target="#edit-user2" class="btn btn-sm btn-outline-primary pull-right">编辑</a></td>
-                                </tr>
-                            <% } %>
-                            </tbody>
-                        </table>
-                        <% include ../layout/page.ejs %>
+                                                <a href="#edit-user2" data-account="<%= JSON.stringify(account) %>" data-toggle="modal" data-target="#edit-user2" class="btn btn-sm btn-outline-primary pull-right">编辑</a></td>
+                                        </tr>
+                                    <% } %>
+                                    </tbody>
+                                </table>
+                                <% include ../layout/page.ejs %>
+                            </div>
+                        </div>
                     </div>
                 </div>
             </div>

+ 16 - 10
app/view/setting/user_permission_modal.ejs

@@ -7,15 +7,15 @@
             </div>
             <form method="post" action="/setting/user/add?_csrf_j=<%= ctx.csrf %>" onsubmit="return checkUserForm('add');">
             <div class="modal-body">
-                <div class="form-group">
-                    <label><b class="text-danger">*</b>账号组</label>
-                    <select class="form-control form-control-sm" name="account_group">
-                        <option value="0">请选择</option>
-                        <% for (const dw in accountGroup) { %>
-                        <option value="<%= dw %>"><%= accountGroup[dw] %></option>
-                        <% } %>
-                    </select>
-                </div>
+                <!--<div class="form-group">-->
+                    <!--<label><b class="text-danger">*</b>账号组</label>-->
+                    <!--<select class="form-control form-control-sm" name="account_group">-->
+                        <!--<option value="0">请选择</option>-->
+                        <!--<% for (const dw in accountGroup) { %>-->
+                        <!--<option value="<%= dw %>"><%= accountGroup[dw] %></option>-->
+                        <!--<% } %>-->
+                    <!--</select>-->
+                <!--</div>-->
                 <div class="form-group">
                     <label>登录账号<b class="text-danger">*</b></label>
                     <input class="form-control form-control-sm" name="account" placeholder="支持英文数字组合" type="text">
@@ -39,7 +39,13 @@
                 </div>
                 <div class="form-group">
                     <label>单位名称<b class="text-danger">*</b></label>
-                    <input class="form-control form-control-sm" name="company" value="" type="text">
+                    <input value="" name="account_group" type="hidden">
+                    <select class="form-control form-control-sm" name="company">
+                        <option>请选择</option>
+                        <% for (const u of unitList) { %>
+                            <option><%- u.name %></option>
+                        <% } %>
+                    </select>
                 </div>
                 <div class="form-group">
                     <label>角色/职位<b class="text-danger">*</b></label>

+ 139 - 0
app/view/setting/user_unit.ejs

@@ -0,0 +1,139 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main">
+            <h2>账号管理
+                <a href="#add-company" data-toggle="modal" data-target="#add-company" class="btn btn-primary btn-sm pull-right">添加单位</a>
+                <% if (projectData.max_user === 0 || projectData.max_user > user_total) { %>
+                <a href="#ver" data-toggle="modal" data-target="#add-user" class="btn btn-primary btn-sm pull-right mr-2">添加账号</a>
+                <% } else { %>
+                <a href="#add-unpass" data-toggle="modal" data-target="#add-unpass" class="btn btn-primary btn-sm pull-right mr-2">添加账号(受限)</a>
+                <% } %>
+            </h2>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-body">
+            <div class="sjs-height-0">
+                <nav class="nav nav-tabs m-3" role="tablist">
+                    <a class="nav-item nav-link" href="/setting/user">账号列表</a>
+                    <a class="nav-item nav-link" href="/setting/user/permission/set">账号权限</a>
+                    <a class="nav-item nav-link active" href="/setting/user/unit">参建单位</a>
+                </nav>
+                <div class="tab-content m-3">
+                    <div id="unit-list" class="tab-pane active">
+                        <div class="row">
+                            <div class="col-6">
+                                <table class="table table-hover table-bordered table-sm">
+                                    <thead>
+                                    <tr>
+                                        <th>序号</th>
+                                        <th>单位名称</th>
+                                        <th>账号数</th>
+                                        <th>类型</th>
+                                        <th class="text-center">备注</th></tr>
+                                    </thead>
+                                    <tbody id="unit_list" style="cursor: pointer">
+                                    <% if (unitList.length > 0) { %>
+                                    <% for (const [index, u] of unitList.entries()) { %>
+                                    <tr <% if (index === 0) { %>class="table-warning" <% } %> data-id="<%- u.id %>">
+                                        <td width="50px"><%- index+1 %></td>
+                                        <td ><a href="javascript:void(0)"><%- u.name %></a></td>
+                                        <td width="80px"><%- u.account_num %></td>
+                                        <td width="100px"><%- accountGroup[u.type] %></td>
+                                        <td width="150px"><%- u.basic %></td>
+                                    </tr>
+                                    <% } %>
+                                    <% } %>
+                                    </tbody>
+                                </table>
+                            </div>
+                            <% if (unitList.length > 0) { %>
+                            <div class="col-6">
+                                <div>
+                                    <div class="row px-3">
+                                        <div>
+                                            <legend>单位详情</legend>
+                                        </div>
+                                        <div class="ml-auto">
+                                            <a href="#del-company" data-toggle="modal" data-target="#del-company" id="del-modal-btn" class="btn btn-outline-danger btn-sm btn-block">删除单位</a>
+                                        </div>
+                                    </div>
+                                    <table class="table table-hover table-bordered table-sm" id="one_unit">
+                                        <tr>
+                                            <th width="120">单位名称</th>
+                                            <td width="200"><input class="form-control form-control-sm" type="text" data-name="name" id="unit_name" value="<%- unitList[0].name %>" placeholder=""></td>
+                                            <th width="120">法人代表</th>
+                                            <td width="200"><input class="form-control form-control-sm" type="text" data-name="corporation" id="unit_corporation" value="<%- unitList[0].corporation %>" placeholder=""></td>
+                                        </tr>
+                                        <tr>
+                                            <th>企业信用代码</th>
+                                            <td><input class="form-control form-control-sm" type="text" data-name="credit_code" id="unit_credit_code" value="<%- unitList[0].credit_code %>" placeholder=""></td>
+                                            <th>类型</th>
+                                            <td>
+                                                <select class="form-control form-control-sm" id="unit_type" data-name="type">
+                                                    <option value="0">请选择</option>
+                                                    <% for (const dw in accountGroup) { %>
+                                                        <option value="<%= dw %>" <% if (unitList[0].type === parseInt(dw)) { %>selected<% } %>><%- accountGroup[dw] %></option>
+                                                    <% } %>
+                                                </select>
+                                            </td>
+                                        </tr>
+                                        <tr>
+                                            <th>电话</th>
+                                            <td><input class="form-control form-control-sm" type="text" data-name="tel" id="unit_tel" value="<%- unitList[0].tel %>" placeholder=""></td>
+                                            <th>网站</th>
+                                            <td><input class="form-control form-control-sm" type="text" data-name="website" id="unit_website" value="<%- unitList[0].website %>" placeholder=""></td>
+                                        </tr>
+                                        <tr>
+                                            <th>所属地区</th>
+                                            <td><input class="form-control form-control-sm" type="text" data-name="region" id="unit_region" value="<%- unitList[0].region %>" placeholder=""></td>
+                                            <th>地址</th>
+                                            <td><input class="form-control form-control-sm" type="text" data-name="address" id="unit_address" value="<%- unitList[0].address %>" placeholder=""></td>
+                                        </tr>
+                                        <tr>
+                                            <th>备注</th>
+                                            <td colspan="3">
+                                                <textarea class="form-control form-control-sm" id="unit_basic" data-name="basic" value="<%- unitList[0].basic %>" rows="2"></textarea>
+                                            </td>
+                                        </tr>
+                                    </table>
+                                </div>
+                                <div <% if (unitList[0].sign_path) { %>style="display: none" <% } %> id="upload-sign" class="form-group">
+                                    <label for="sign-upload">上传签章</label>
+                                    <input type="file" class="form-control-file" id="sign-upload">
+                                    <small class="form-text text-danger">图片大小为500x500px,格式PNG透明背景。</small>
+                                </div>
+                                <button <% if (!unitList[0].sign_path) { %>style="display: none" <% } %> type="button" class="btn btn-sm btn-danger" id="delete-sign">移除签章</button>
+                                <div class="form-group">
+                                    <label>签章图预览</label>
+                                    <div>
+                                        <div class="position-relative">
+                                            <img src="/public/images/baobiao3.png">
+                                            <div class="position-absolute fixed-top" id="sign-show" style="left:290px;top:210px">
+                                                <% if (unitList[0].sign_path) { %>
+                                                    <img src="<%- fujianOssPath + unitList[0].sign_path %>" width="120">
+                                                <% } %>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <% } %>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+</script>
+<script src="/public/js/setting.js"></script>
+<script>
+    const unitList = JSON.parse(unescape('<%- escape(JSON.stringify(unitList)) %>'));
+    const accountGroup = JSON.parse(unescape('<%- escape(JSON.stringify(accountGroup)) %>'));
+    const fujianOssPath = JSON.parse(unescape('<%- escape(JSON.stringify(fujianOssPath)) %>'));
+    let oneUnit = unitList.length > 0 ? unitList[0] : null;
+</script>
+<script>autoFlashHeight();</script>

+ 137 - 0
app/view/setting/user_unit_modal.ejs

@@ -0,0 +1,137 @@
+<!--弹出添加账号-->
+<div class="modal fade" id="add-user" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">添加账号</h5>
+            </div>
+            <form method="post" action="/setting/user/add?_csrf_j=<%= ctx.csrf %>" onsubmit="return checkUserForm('add');">
+            <div class="modal-body">
+                <!--<div class="form-group">-->
+                    <!--<label><b class="text-danger">*</b>账号组</label>-->
+                    <!--<select class="form-control form-control-sm" name="account_group">-->
+                        <!--<option value="0">请选择</option>-->
+                        <!--<% for (const dw in accountGroup) { %>-->
+                        <!--<option value="<%= dw %>"><%= accountGroup[dw] %></option>-->
+                        <!--<% } %>-->
+                    <!--</select>-->
+                <!--</div>-->
+                <div class="form-group">
+                    <label>登录账号<b class="text-danger">*</b></label>
+                    <input class="form-control form-control-sm" name="account" placeholder="支持英文数字组合" type="text">
+                    <input value="" class="account-check" type="hidden">
+                    <div class="invalid-feedback">
+                        该账号已存在。
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label>登录密码<b class="text-danger">*</b></label>
+                    <div class="input-group">
+                        <input type="text" name="password" class="form-control form-control-sm" placeholder="密码支持英文数字及符号">
+                        <div class="input-group-append">
+                            <button id="rand-password" class="btn btn-outline-secondary btn-sm" type="button">随机密码</button>
+                        </div>
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label>姓名<b class="text-danger">*</b></label>
+                    <input class="form-control form-control-sm" name="name" value="" type="text">
+                </div>
+                <div class="form-group">
+                    <label>单位名称<b class="text-danger">*</b></label>
+                    <input value="" name="account_group" type="hidden">
+                    <select class="form-control form-control-sm" name="company">
+                        <option value="0">请选择</option>
+                        <% for (const u of unitList) { %>
+                            <option><%- u.name %></option>
+                        <% } %>
+                    </select>
+                </div>
+                <div class="form-group">
+                    <label>角色/职位<b class="text-danger">*</b></label>
+                    <input class="form-control form-control-sm" name="role" value="" type="text">
+                </div>
+                <div class="form-group">
+                    <label>手机</label>
+                    <input class="form-control form-control-sm" name="mobile" value="" type="number">
+                </div>
+                <div class="form-group">
+                    <label>电话</label>
+                    <input class="form-control form-control-sm" name="telephone" placeholder="格式000-0000000" type="text">
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button type="submit" class="btn btn-primary btn-sm">确定添加</button>
+            </div>
+            </form>
+        </div>
+    </div>
+</div>
+<!--弹出账号受限-->
+<div class="modal" tabindex="-1" role="dialog" id="add-unpass">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">无法添加账号</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <p>账号已超过最大账号数,无法添加新账号。</p>
+                <p>当前限制账号总数:<b><%= projectData.max_user %></b></p>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="modal fade" id="add-company" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <form method="post" class="modal-content" action="/setting/user/unit/add?_csrf_j=<%= ctx.csrf %>" onsubmit="return checkUnitForm();">
+            <div class="modal-header">
+                <h5 class="modal-title">添加单位</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label>单位名称<b class="text-danger">*</b></label>
+                    <input class="form-control form-control-sm" name="name" placeholder="请输入单位名称" value="" type="text">
+                </div>
+                <div class="form-group">
+                    <label>类型<b class="text-danger">*</b></label>
+                    <select class="form-control form-control-sm" name="type">
+                        <option value="0">请选择</option>
+                        <% for (const dw in accountGroup) { %>
+                            <option value="<%= dw %>"><%- accountGroup[dw] %></option>
+                        <% } %>
+                    </select>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="submit" class="btn btn-sm btn-sm btn-primary">确定添加</button>
+            </div>
+        </form>
+    </div>
+</div>
+<!--删除单位-->
+<div class="modal fade" id="del-company" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">删除单位</h5>
+            </div>
+            <div class="modal-body">
+                <h6 class="del-btn">确认删除当前单位?</h6>
+                <h6 class="not-del-btn">当前单位下存在账号,不允许删除。</h6>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary del-btn" data-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-sm btn-danger del-btn" id="delete-unit">确定删除</button>
+                <button type="button" class="btn btn-sm btn-secondary not-del-btn" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 7 - 1
config/config.default.js

@@ -65,7 +65,7 @@ module.exports = appInfo => {
     config.pageSize = 15;
 
     // 中间件
-    config.middleware = ['gzip', 'urlParse', 'sortFilter', 'autoLogger'];
+    config.middleware = ['gzip', 'urlParse', 'sortFilter', 'autoLogger', 'autoFinishLogger'];
 
     // session配置
     config.session = {
@@ -164,6 +164,12 @@ module.exports = appInfo => {
         },
         mixed: {
             file: path.join(appInfo.root, 'logs', appInfo.name, config.version, 'mixed.log'),
+        },
+        finish: {
+            file: path.join(appInfo.root, 'logs', appInfo.name, config.version, 'finish.log'),
+        },
+        warning: {
+            file: path.join(appInfo.root, 'logs', appInfo.name, config.version, 'warning.log'),
         }
     };
 

+ 0 - 2
db_script/depart-database-table.js

@@ -284,7 +284,6 @@ const generateAllCreateSql = function () {
 // generateAllCreateSql();
 
 // const tenders = querySql('Select * From zh_tender');
-// console.log(tenders.length);
 //
 const log = [];
 const depart = async function (info) {
@@ -293,7 +292,6 @@ const depart = async function (info) {
         const sql = 'Insert Into ' + info.table + '_' + i +
             '  Select * From ' + info.table +
             '  Where ' + info.relaId + ' % ' + info.count + ' = ' + i;
-        console.log(sql);
         log.push(sql);
         await querySql(sql);
     }

+ 2 - 95
sql/update.sql

@@ -1,105 +1,3 @@
-ALTER TABLE `zh_change_apply`
-ADD `total_price` DECIMAL(30,8) NULL DEFAULT NULL COMMENT '金额' AFTER `content`,
-ADD `decimal` VARCHAR(1000) NULL DEFAULT NULL COMMENT '小数位数设置JSON' AFTER `total_price`;
-
-CREATE TABLE `zh_change_plan`  (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `tid` int(11) NOT NULL COMMENT '标段id',
-  `selected` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '报表用,是否选择',
-  `uid` int(11) NOT NULL COMMENT '发起人',
-  `code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '变更方案编号',
-  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '变更工程名称',
-  `in_time` datetime NOT NULL COMMENT '发起时间',
-  `status` tinyint(2) NOT NULL COMMENT '方案状态',
-  `times` tinyint(2) NOT NULL COMMENT '审批次数',
-  `apply_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '变更申请编号',
-  `org_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '原设计图名称',
-  `peg` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '桩号',
-  `new_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '图号',
-  `c_new_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '变更图号',
-  `design_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '变更设计名称',
-  `class` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '工程变更类别',
-  `quality` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '工程变更性质',
-  `reason` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL COMMENT '变更原因',
-  `content` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL COMMENT '变更内容',
-  `memo` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL COMMENT '方案描述',
-  `total_price` decimal(30, 8) NULL DEFAULT NULL COMMENT '金额',
-  `decimal` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '小数位数设置JSON',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '变更方案表' ROW_FORMAT = Dynamic;
-
-CREATE TABLE `zh_change_plan_attachment`  (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `tid` int(11) NOT NULL COMMENT '标段id',
-  `cpid` int(11) NOT NULL COMMENT '方案id',
-  `uid` int(11) NOT NULL COMMENT '上传者id',
-  `filename` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件名称',
-  `fileext` varchar(5) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件后缀',
-  `filesize` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件大小',
-  `filepath` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件存储路径',
-  `upload_time` datetime NOT NULL COMMENT '上传时间',
-  `extra_upload` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否为审核通过后再次上传的文件,0为否',
-  PRIMARY KEY (`id`) USING BTREE,
-  INDEX `idx_cid`(`cpid`) USING BTREE
-) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '变更立项附件表' ROW_FORMAT = Dynamic;
-
-CREATE TABLE `zh_change_plan_audit`  (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
-  `tid` int(11) NOT NULL COMMENT '标段id',
-  `cpid` int(11) NOT NULL COMMENT '方案id',
-  `aid` int(11) NOT NULL COMMENT '审批人id',
-  `order` int(11) NOT NULL COMMENT '审批顺序',
-  `times` int(11) NOT NULL COMMENT '审批次数',
-  `status` tinyint(1) NOT NULL COMMENT '审批状态',
-  `begin_time` datetime NULL DEFAULT NULL COMMENT '开始审批时间',
-  `end_time` datetime NULL DEFAULT NULL COMMENT '结束审批时间',
-  `opinion` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '审批意见',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '变更立项审批表' ROW_FORMAT = Dynamic;
-
-CREATE TABLE `zh_change_plan_list`  (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `tid` int(11) NOT NULL COMMENT '标段id',
-  `cpid` int(11) NOT NULL COMMENT '方案id',
-  `code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '清单编号',
-  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '清单名称',
-  `unit` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '单位',
-  `unit_price` decimal(30, 8) NULL DEFAULT NULL COMMENT '单价',
-  `oamount` decimal(30, 8) NULL DEFAULT NULL COMMENT '原数量',
-  `camount` decimal(30, 8) NULL DEFAULT NULL COMMENT '变更数量',
-  `samount` decimal(30, 8) NULL DEFAULT NULL COMMENT '审批完成后变更数量',
-  `audit_amount` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '用户填的数目,json',
-  `spamount` decimal(30, 8) NULL DEFAULT NULL COMMENT '审批流程中读取数量',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '变更方案清单表' ROW_FORMAT = Dynamic;
-
-ALTER TABLE `zh_material` ADD `period` VARCHAR(255) NULL DEFAULT NULL COMMENT '调差周期' AFTER `s_order`;
-
-ALTER TABLE `zh_tender`
-ADD `c_apply_list_rule` VARCHAR(255) NULL DEFAULT NULL COMMENT '变更申请统一原设计数量读取规则' AFTER `pos_file`,
-ADD `c_plan_list_rule` VARCHAR(255) NULL DEFAULT NULL COMMENT '变更方案统一原设计数量读取规则' AFTER `c_apply_list_rule`;
-
-ALTER TABLE `zh_change` ADD `plan_code` VARCHAR(255) NULL DEFAULT NULL COMMENT '变更方案编号' AFTER `name`;
-
-CREATE TABLE `zh_change_apply_list`  (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `tid` int(11) NOT NULL COMMENT '标段id',
-  `caid` int(11) NOT NULL COMMENT '申请id',
-  `code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '清单编号',
-  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '清单名称',
-  `unit` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '单位',
-  `unit_price` decimal(30, 8) NULL DEFAULT NULL COMMENT '单价',
-  `oamount` decimal(30, 8) NULL DEFAULT NULL COMMENT '原数量',
-  `camount` decimal(30, 8) NULL DEFAULT NULL COMMENT '变更数量',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '变更申请清单表' ROW_FORMAT = Dynamic;
+ALTER TABLE `zh_material` ADD `is_new` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '是否是新建的调差,用于区分清单新建规则' AFTER `in_time`;
 
+ALTER TABLE `zh_change_plan` ADD `expr` TEXT NULL DEFAULT NULL COMMENT '工程量数量计算式' AFTER `memo`;

+ 105 - 0
sql/update20220402.sql

@@ -0,0 +1,105 @@
+ALTER TABLE `zh_change_apply`
+ADD `total_price` DECIMAL(30,8) NULL DEFAULT NULL COMMENT '金额' AFTER `content`,
+ADD `decimal` VARCHAR(1000) NULL DEFAULT NULL COMMENT '小数位数设置JSON' AFTER `total_price`;
+
+CREATE TABLE `zh_change_plan`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `selected` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '报表用,是否选择',
+  `uid` int(11) NOT NULL COMMENT '发起人',
+  `code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '变更方案编号',
+  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '变更工程名称',
+  `in_time` datetime NOT NULL COMMENT '发起时间',
+  `status` tinyint(2) NOT NULL COMMENT '方案状态',
+  `times` tinyint(2) NOT NULL COMMENT '审批次数',
+  `apply_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '变更申请编号',
+  `org_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '原设计图名称',
+  `peg` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '桩号',
+  `new_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '图号',
+  `c_new_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '变更图号',
+  `design_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '变更设计名称',
+  `class` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '工程变更类别',
+  `quality` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '工程变更性质',
+  `reason` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL COMMENT '变更原因',
+  `content` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL COMMENT '变更内容',
+  `memo` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL COMMENT '方案描述',
+  `total_price` decimal(30, 8) NULL DEFAULT NULL COMMENT '金额',
+  `decimal` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '小数位数设置JSON',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '变更方案表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for zh_change_plan_attachment
+-- ----------------------------
+CREATE TABLE `zh_change_plan_attachment`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `cpid` int(11) NOT NULL COMMENT '方案id',
+  `uid` int(11) NOT NULL COMMENT '上传者id',
+  `filename` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件名称',
+  `fileext` varchar(5) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件后缀',
+  `filesize` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件大小',
+  `filepath` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件存储路径',
+  `upload_time` datetime NOT NULL COMMENT '上传时间',
+  `extra_upload` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否为审核通过后再次上传的文件,0为否',
+  PRIMARY KEY (`id`) USING BTREE,
+  INDEX `idx_cid`(`cpid`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '变更立项附件表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for zh_change_plan_audit
+-- ----------------------------
+CREATE TABLE `zh_change_plan_audit`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `cpid` int(11) NOT NULL COMMENT '方案id',
+  `aid` int(11) NOT NULL COMMENT '审批人id',
+  `order` int(11) NOT NULL COMMENT '审批顺序',
+  `times` int(11) NOT NULL COMMENT '审批次数',
+  `status` tinyint(1) NOT NULL COMMENT '审批状态',
+  `begin_time` datetime NULL DEFAULT NULL COMMENT '开始审批时间',
+  `end_time` datetime NULL DEFAULT NULL COMMENT '结束审批时间',
+  `opinion` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '审批意见',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '变更立项审批表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for zh_change_plan_list
+-- ----------------------------
+CREATE TABLE `zh_change_plan_list`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `cpid` int(11) NOT NULL COMMENT '方案id',
+  `code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '清单编号',
+  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '清单名称',
+  `unit` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '单位',
+  `unit_price` decimal(30, 8) NULL DEFAULT NULL COMMENT '单价',
+  `oamount` decimal(30, 8) NULL DEFAULT NULL COMMENT '原数量',
+  `camount` decimal(30, 8) NULL DEFAULT NULL COMMENT '变更数量',
+  `samount` decimal(30, 8) NULL DEFAULT NULL COMMENT '审批完成后变更数量',
+  `audit_amount` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '用户填的数目,json',
+  `spamount` decimal(30, 8) NULL DEFAULT NULL COMMENT '审批流程中读取数量',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '变更方案清单表' ROW_FORMAT = Dynamic;
+
+ALTER TABLE `zh_material` ADD `period` VARCHAR(255) NULL DEFAULT NULL COMMENT '调差周期' AFTER `s_order`;
+
+ALTER TABLE `zh_tender`
+ADD `c_apply_list_rule` VARCHAR(255) NULL DEFAULT NULL COMMENT '变更申请统一原设计数量读取规则' AFTER `pos_file`,
+ADD `c_plan_list_rule` VARCHAR(255) NULL DEFAULT NULL COMMENT '变更方案统一原设计数量读取规则' AFTER `c_apply_list_rule`;
+
+ALTER TABLE `zh_change` ADD `plan_code` VARCHAR(255) NULL DEFAULT NULL COMMENT '变更方案编号' AFTER `name`;
+
+CREATE TABLE `zh_change_apply_list`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `caid` int(11) NOT NULL COMMENT '申请id',
+  `code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '清单编号',
+  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '清单名称',
+  `unit` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '单位',
+  `unit_price` decimal(30, 8) NULL DEFAULT NULL COMMENT '单价',
+  `oamount` decimal(30, 8) NULL DEFAULT NULL COMMENT '原数量',
+  `camount` decimal(30, 8) NULL DEFAULT NULL COMMENT '变更数量',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '变更申请清单表' ROW_FORMAT = Dynamic;
+

+ 0 - 1
test/app/lib/rpt_data_analysis.test.js

@@ -202,7 +202,6 @@ describe('test/app/service/report_memory.test.js', () => {
             mem_stage_im_zl: ['lid', 'code'],
         });
         reportDataAnalysis.analysisObj.loadCooperationData.fun(ctx, data, [], {table: 'mem_stage_im_zl', co_sign: [0, 1, 2, 3]}, null);
-        console.log(data.mem_stage_im_zl);
     });
     it('test join', function* () {
         const ctx = app.mockContext(mockData);