浏览代码

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

TonyKang 3 年之前
父节点
当前提交
8b699a885b
共有 53 个文件被更改,包括 6553 次插入334 次删除
  1. 2 2
      app/base/base_bills_service.js
  2. 1 1
      app/base/base_tree_service.js
  3. 281 2
      app/controller/change_controller.js
  4. 0 24
      app/controller/revise_controller.js
  5. 48 88
      app/controller/stage_controller.js
  6. 104 1
      app/controller/stage_rela_controller.js
  7. 1 1
      app/controller/tender_controller.js
  8. 1 1
      app/extend/helper.js
  9. 1 0
      app/lib/rpt_data_analysis.js
  10. 9 6
      app/lib/stage_im.js
  11. 13 4
      app/lib/sum_load.js
  12. 88 14
      app/public/js/change_information_set.js
  13. 2560 0
      app/public/js/change_revise.js
  14. 1 1
      app/public/js/gcl_gather.js
  15. 15 3
      app/public/js/ledger.js
  16. 18 0
      app/public/js/ledger_check.js
  17. 5 5
      app/public/js/material_list.js
  18. 11 5
      app/public/js/revise.js
  19. 10 4
      app/public/js/shares/cs_tools.js
  20. 5 1
      app/public/js/shares/tender_select.js
  21. 1 0
      app/public/js/spreadjs_rela/spreadjs_zh.js
  22. 102 11
      app/public/js/sr_detail.js
  23. 97 8
      app/public/js/stage.js
  24. 27 4
      app/public/js/stage_im.js
  25. 11 26
      app/public/js/stage_pay.js
  26. 7 1
      app/router.js
  27. 2 0
      app/service/change.js
  28. 482 0
      app/service/change_audit_list.js
  29. 1705 0
      app/service/change_ledger.js
  30. 397 0
      app/service/change_pos.js
  31. 51 0
      app/service/pay_att.js
  32. 1 0
      app/service/project_account.js
  33. 4 4
      app/service/stage_bills.js
  34. 19 13
      app/service/stage_detail_att.js
  35. 4 33
      app/service/stage_pay.js
  36. 3 2
      app/service/sum_load_history.js
  37. 1 1
      app/service/tender_info.js
  38. 3 0
      app/view/change/information.ejs
  39. 6 3
      app/view/change/information_modal.ejs
  40. 173 0
      app/view/change/revise.ejs
  41. 40 0
      app/view/change/revise_modal.ejs
  42. 12 0
      app/view/change/sub_menu.ejs
  43. 1 0
      app/view/change/sub_menu_list.ejs
  44. 16 0
      app/view/change/sub_mini_menu.ejs
  45. 6 0
      app/view/shares/tender_select_modal.ejs
  46. 0 4
      app/view/stage/index.ejs
  47. 1 1
      app/view/stage/modal.ejs
  48. 1 0
      app/view/stage/pay.ejs
  49. 30 0
      app/view/stage_rela/detail_modal.ejs
  50. 27 1
      config/web.js
  51. 67 0
      db_script/pay_att.js
  52. 15 59
      sql/update.sql
  53. 67 0
      sql/update20211103.sql

+ 2 - 2
app/base/base_bills_service.js

@@ -563,7 +563,7 @@ class BaseBillsSerivce extends TreeService {
             });
             if (pd[0].ledger_pid !== pasteData[0][0].ledger_pid) throw '复制数据错误:仅可操作同层节点';
         }
-
+        this.newBills = false;
         const selectData = await this.getDataByKid(tid, sid);
         if (!selectData) throw '粘贴数据错误';
         const newParentPath = selectData.full_path.replace(selectData.ledger_id, '');
@@ -698,4 +698,4 @@ class BaseBillsSerivce extends TreeService {
 
 }
 
-module.exports = BaseBillsSerivce;
+module.exports = BaseBillsSerivce;

+ 1 - 1
app/base/base_tree_service.js

@@ -1112,4 +1112,4 @@ class TreeService extends Service {
 
 }
 
-module.exports = TreeService;
+module.exports = TreeService;

+ 281 - 2
app/controller/change_controller.js

@@ -7,7 +7,11 @@
  * @date 2018/8/14
  * @version
  */
-
+const stdDataAddType = {
+    withParent: 1,
+    child: 2,
+    next: 3,
+};
 const moment = require('moment');
 const sendToWormhole = require('stream-wormhole');
 const fs = require('fs');
@@ -17,6 +21,9 @@ const codeRuleConst = require('../const/code_rule');
 const changeConst = require('../const/change');
 const accountGroup = require('../const/account_group').group;
 const shenpiConst = require('../const/shenpi');
+const tenderMenu = require('../../config/menu').tenderMenu;
+const measureType = require('../const/tender').measureType;
+const spreadConst = require('../const/spread');
 // const tenderMenu = require('../../config/menu').tenderMenu;
 
 module.exports = app => {
@@ -625,6 +632,8 @@ module.exports = app => {
                     // 获取已选清单
                     const changeList = await ctx.service.changeAuditList.getList(change.cid);
                     renderData.changeList = changeList;
+                    renderData.changeLedgerList = await ctx.service.changeLedger.getAllDataByCondition({ where: { tender_id: ctx.tender.id } });
+                    renderData.changePosList = await ctx.service.changePos.getAllDataByCondition({ where: { tid: ctx.tender.id } });
                 } else if (auditStatus === 3 || auditStatus === 4 || auditStatus === 5 || auditStatus === 7 || auditStatus === 8) {
                     // 展示页左侧审批流程列表和清单审批列表数据
                     const times = change.status === audit.flow.status.back ?
@@ -774,6 +783,11 @@ module.exports = app => {
                         // 取所有工料表
                         responseData.data = await ctx.service.changeAuditList.getList(ctx.change.cid);
                         break;
+                    case 'update_list':
+                        await ctx.service.changeAuditList.saveDatas(data.updateData);
+                        // 取所有工料表
+                        // responseData.data = await ctx.service.changeAuditList.getList(ctx.change.cid);
+                        break;
                     case 'info':
                         await ctx.service.change.saveInfo(data.updateData);
                         // 取所有工料表
@@ -921,9 +935,11 @@ module.exports = app => {
         async defaultBills(ctx) {
             try {
                 const ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
+                const changeLedgerData = await ctx.service.changeLedger.getData(ctx.tender.id);
                 const posData = await ctx.service.pos.getPosData({ tid: ctx.tender.id });
+                const changePosData = await ctx.service.changePos.getPosData({ tid: ctx.tender.id });
                 const dealBills = await ctx.service.dealBills.getAllDataByCondition({ where: { tender_id: ctx.tender.id } });
-                ctx.body = { err: 0, msg: '', data: { bills: ledgerData, pos: posData, dealBills } };
+                ctx.body = { err: 0, msg: '', data: { bills: ctx.helper._.concat(ledgerData, changeLedgerData), pos: ctx.helper._.concat(posData, changePosData), dealBills } };
             } catch (err) {
                 this.log(err);
                 ctx.body = { err: 1, msg: err.toString(), data: [] };
@@ -1477,6 +1493,269 @@ module.exports = app => {
             }
             ctx.body = responseData;
         }
+
+        /**
+         * 修订 详细页面(Get)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async reviseInfo(ctx) {
+            try {
+                const change = ctx.change;
+                let edit = true;
+                if (change.status !== audit.flow.status.uncheck && change.status !== audit.flow.status.backnew) {
+                    // throw '本条变更审批中或已完成,无法操作台账数据';
+                    edit = false;
+                }
+                // 判断是否在修订中,是则无法操作本页
+                const lastRevise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id);
+                if (lastRevise && lastRevise.status !== audit.revise.status.checked) {
+                    // throw '台账修订中,无法操作台账数据';
+                    edit = false;
+                }
+                const renderData = await this._getDefaultReviseInfoData(ctx, change);
+                // console.log(edit);
+                // 台账只读、使用数据
+                // renderData.readOnly = edit;
+                renderData.readOnly = false;
+                await this.layout('change/revise.ejs', renderData, 'change/revise_modal.ejs');
+            } catch (err) {
+                this.log(err);
+                ctx.redirect(ctx.request.header.referer);
+            }
+        }
+
+        async _getDefaultReviseInfoData(ctx, change) {
+            const [ledgerSpread, posSpread] = this._getSpreadSetting(change);
+            const sjsRela = await this.ctx.service.project.getSjsRela(ctx.session.sessionProject.id);
+            this.ctx.helper.refreshSpreadShow(sjsRela.ledgerCol, [ledgerSpread, posSpread]);
+            const [stdBills, stdChapters] = await this.ctx.service.valuation.getValuationStdList(
+                ctx.tender.data.valuation, ctx.tender.data.measure_type);
+            return {
+                change,
+                tender: ctx.tender.data,
+                ledgerSpread, posSpread, tenderMenu, measureType,
+                preUrl: '/tender/' + ctx.tender.id + '/change/' + ctx.change.cid + '/information',
+                jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.change.revise),
+                stdBills,
+                auditConst: audit.flow,
+                audit: audit.flow,
+                stdChapters,
+            };
+        }
+
+        /**
+         * 获取SpreadSetting
+         * @private
+         */
+        _getSpreadSetting(change) {
+            const _ = this.app._;
+            function removeFieldCols(setting, cols) {
+                _.remove(setting.cols, function(c) {
+                    return cols.indexOf(c.field) > -1;
+                });
+            }
+            const tender = this.ctx.tender;
+            const setting = tender.data.measure_type === measureType.tz.value
+                ? (tender.info.display.ledger.clQty ? spreadConst.withCl : spreadConst.withoutCl)
+                : (tender.info.display.ledger.clQty ? spreadConst.withClGcl : spreadConst.withoutClGcl);
+            const ledger = JSON.parse(JSON.stringify(setting.ledger));
+            const pos = setting.pos ? JSON.parse(JSON.stringify(setting.pos)) : spreadConst.blank;
+
+            if (change.status === audit.flow.status.checking || change.status === audit.flow.status.checked) {
+                ledger.readOnly = true;
+                pos.readOnly = true;
+            }
+            if (tender.data.measure_type === measureType.tz.value) {
+                removeFieldCols(ledger, spreadConst.filterCols.tzWithoutCols);
+            }
+            if (!tender.info.display.ledger.dgnQty) {
+                removeFieldCols(ledger, spreadConst.filterCols.dgnCols);
+            }
+            return [ledger, pos];
+        }
+
+        async updateRevise(ctx) {
+            try {
+                if (!ctx.tender.data) throw '标段数据错误';
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.postType || !data.postData) throw '数据错误';
+                const responseData = { err: 0, msg: '', data: {} };
+                console.log(data);
+
+                // const revise = await this.checkRevise(ctx);
+                //
+                switch (data.postType) {
+                    case 'add':
+                    case 'delete':
+                    case 'up-move':
+                    case 'down-move':
+                    case 'up-level':
+                    case 'down-level':
+                        responseData.data = await this._billsBase(ctx, data.postType, data.postData);
+                        break;
+                    case 'update':
+                        ctx.helper.checkDgnQtyPrecision(data.postData);
+                        responseData.data = await ctx.service.changeLedger.updateCalc(ctx.tender.id, data.postData);
+                        break;
+                    case 'paste-block':
+                        responseData.data = await this._pasteBlock(ctx, data.postData);
+                        break;
+                    case 'add-std':
+                        responseData.data = await this._addStd(ctx, data.postData);
+                        break;
+                    case 'add-deal':
+                        responseData.data = await this._addDeal(ctx, data.postData);
+                        break;
+                    // case 'batch-insert':
+                    //     responseData.data = await this._batchInsert(ctx, data.postData);
+                    //     break;
+                    case 'pos':
+                        responseData.data = await this._updatePos(ctx, data);
+                        break;
+                    default:
+                        throw '未知操作';
+                }
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = this.ajaxErrorBody(err, '数据错误');
+            }
+        }
+
+        async _billsBase(ctx, type, data) {
+            if (isNaN(data.id) || data.id <= 0) throw '数据错误';
+            if (type !== 'add') {
+                if (isNaN(data.count) || data.count <= 0) data.count = 1;
+            }
+            switch (type) {
+                case 'add':
+                    return await ctx.service.changeLedger.addNodeBatch(ctx.tender.id, data.id, { ccid: ctx.change.cid, check_calc: 1 }, data.count);
+                case 'delete':
+                    return await ctx.service.changeLedger.delete(ctx.tender.id, data.id, data.count);
+                case 'up-move':
+                    return await ctx.service.changeLedger.upMoveNode(ctx.tender.id, data.id, data.count);
+                case 'down-move':
+                    return await ctx.service.changeLedger.downMoveNode(ctx.tender.id, data.id, data.count);
+                case 'up-level':
+                    return await ctx.service.changeLedger.upLevelNode(ctx.tender.id, data.id, data.count);
+                case 'down-level':
+                    return await ctx.service.changeLedger.downLevelNode(ctx.tender.id, data.id, data.count);
+                default:
+                    throw '未知操作';
+            }
+        }
+        /**
+         * 复制粘贴整块
+         *
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async _pasteBlock(ctx, data) {
+            if ((isNaN(data.id) || data.id <= 0) ||
+                (!data.tid && data.tid <= 0) ||
+                (!data.block || data.block.length <= 0)) throw '参数错误';
+            return await ctx.service.changeLedger.pasteBlockData(ctx.tender.id, data.id, data.block);
+        }
+        /**
+         * 从标准项目表添加数据
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async _addStd(ctx, data) {
+            if ((isNaN(data.id) || data.id <= 0) || !data.stdType || !data.stdNode) throw '参数错误';
+            // todo 校验项目是否使用该库的权限
+
+            let stdLib,
+                addType;
+            switch (data.stdType) {
+                case 'xmj':
+                    stdLib = ctx.service.stdXmj;
+                    addType = stdDataAddType.withParent;
+                    break;
+                case 'gcl':
+                    stdLib = ctx.service.stdGcl;
+                    const selectNode = await ctx.service.changeLedger.getDataByNodeId(ctx.tender.id, data.id) || await ctx.service.ledger.getDataByNodeId(ctx.tender.id, data.id);
+                    if (selectNode.b_code) {
+                        addType = stdDataAddType.next;
+                    } else {
+                        addType = stdDataAddType.child;
+                    }
+                    break;
+                default:
+                    throw '未知标准库';
+            }
+            const stdData = await stdLib.getDataByDataId(data.stdLibId, data.stdNode);
+            switch (addType) {
+                case stdDataAddType.child:
+                    return await ctx.service.changeLedger.addStdNodeAsChild(ctx.tender.id, data.id, stdData, ctx.change.cid);
+                case stdDataAddType.next:
+                    return await ctx.service.changeLedger.addStdNode(ctx.tender.id, data.id, stdData, ctx.change.cid);
+                case stdDataAddType.withParent:
+                    return await ctx.service.changeLedger.addStdNodeWithParent(ctx.tender.id, stdData, stdLib, ctx.change.cid);
+                default:
+                    throw '未知添加方式';
+            }
+        }
+        /**
+         * 从签约清单添加节点
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async _addDeal(ctx, data) {
+            if (!data.type || !data.dealBills) throw '数据错误';
+            data.dealBills.unit_price = this.ctx.helper.round(data.dealBills.unit_price, ctx.tender.info.decimal.up);
+            if (data.type === 'child') {
+                return await ctx.service.changeLedger.addChild(ctx.tender.id, data.id, data.dealBills, ctx.change.cid);
+            } else if (data.type === 'next') {
+                data.dealBills.ccid = ctx.change.cid;
+                return await ctx.service.changeLedger.addNode(ctx.tender.id, data.id, data.dealBills);
+            }
+            throw '数据错误';
+
+        }
+        /**
+         * 批量插入数据
+         *
+         * data = {id, batchData, batchType}
+         * data.batchType = 'batchInsertChild'/'batchInsertNext'
+         * data.batchData = [{name, children}] -- 项目节列表
+         * data.batchData.children = [{code, name, unit, unit_price, quantity}] -- 工程量清单列表
+         *
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async _batchInsert(ctx, data) {
+            if ((isNaN(data.id) || data.id <= 0) || !data.batchType) throw '参数错误';
+
+            switch (data.batchType) {
+                case 'child':
+                    return await ctx.service.changeLedger.batchInsertChild(ctx.tender.id, data.id, data.batchData);
+                case 'next':
+                    return await ctx.service.changeLedger.batchInsertNext(ctx.tender.id, data.id, data.batchData);
+                default:
+                    throw '参数错误';
+            }
+        }
+        async _updatePos(ctx, data) {
+            await this.checkMeasureType(measureType.tz.value);
+            if (!data.posPostType) throw '数据错误';
+            switch (data.posPostType) {
+                case 'add':
+                    return await this.ctx.service.changePos.addPos(ctx.tender.id, ctx.change.cid, data.postData);
+                case 'update':
+                    if (data.postData instanceof Array) {
+                        return await this.ctx.service.changePos.updatePosArr(ctx.tender.id, data.postData);
+                    }
+                    return await this.ctx.service.changePos.updatePos(ctx.tender.id, data.postData);
+                case 'delete':
+                    return await this.ctx.service.changePos.deletePos(ctx.tender.id, data.postData);
+                case 'paste':
+                    return await this.ctx.service.changePos.pastePosData(ctx.tender.id, ctx.change.cid, data.postData);
+                default:
+                    throw '未知操作';
+            }
+        }
     }
 
     return ChangeController;

+ 0 - 24
app/controller/revise_controller.js

@@ -338,30 +338,6 @@ module.exports = app => {
             const renderData = await this._getDefaultReviseInfoData(ctx, revise);
             // 台账只读、使用数据
             renderData.readOnly = false;
-            // const lastStage = await ctx.service.stage.getLastestStage(ctx.tender.id, true);
-            // if (lastStage) {
-            //     if (lastStage.status === audit.stage.status.checked) {
-            //         const usedPreBills = await ctx.service.stageBillsFinal.getUsedBills(ctx.tender.id, lastStage.order);
-            //         for (const b of renderData.reviseBills) {
-            //             b.used = usedPreBills.indexOf(b.id) >= 0;
-            //         }
-            //         const usedPrePos = await ctx.service.stagePosFinal.getUsedPos(ctx.tender.id, lastStage.order);
-            //         for (const p of renderData.revisePos) {
-            //             p.used = usedPrePos.indexOf(p.id) >= 0;
-            //         }
-            //     } else {
-            //         const usedPreBills = lastStage.order > 1 ? await ctx.service.stageBillsFinal.getUsedBills(ctx.tender.id, lastStage.order - 1) : [];
-            //         const usedCurBills = await ctx.service.stageBills.getStageUsedBills(ctx.tender.id, lastStage.id);
-            //         for (const b of renderData.reviseBills) {
-            //             b.used = usedPreBills.indexOf(b.id) >= 0 || usedCurBills.indexOf(b.id) >= 0;
-            //         }
-            //         const usedPrePos = lastStage.order > 1 ? await ctx.service.stagePosFinal.getUsedPos(ctx.tender.id, lastStage.order - 1) : [];
-            //         const usedCurPos = await ctx.service.stagePos.getStageUsedPos(ctx.tender.id, lastStage.id);
-            //         for (const p of renderData.revisePos) {
-            //             p.used = usedPrePos.indexOf(p.id) >= 0 || usedCurPos.indexOf(p.id) >= 0;
-            //         }
-            //     }
-            // }
             // 修订历史
             renderData.history = false;
             renderData.historyRevise = [];

+ 48 - 88
app/controller/stage_controller.js

@@ -780,7 +780,7 @@ module.exports = app => {
                     }
                 }
                 const baseInfo = JSON.parse(parts.field.base);
-                const result = await ctx.service.stageDetailAtt.addFiles(baseInfo, uploadFiles);
+                const result = await ctx.service.stageDetailAtt.addFiles(this.ctx.stage, baseInfo, uploadFiles);
                 ctx.body = { err: 0, mgs: '', data: result };
             } catch (err) {
                 console.log(err);
@@ -855,19 +855,12 @@ module.exports = app => {
             try {
                 await this._getStageAuditViewData(ctx);
                 const renderData = await this._getDefaultRenderData(ctx);
-                const dealPay = await ctx.service.stagePay.getStagePays(ctx.stage);
+                renderData.dealPay = await ctx.service.stagePay.getStagePays(ctx.stage);
+                const payAtt = await ctx.service.payAtt.getStageData({ sid: ctx.stage.id });
                 // 附件不取下载地址
-                for (const index in dealPay) {
-                    if (dealPay[index].attachment !== null) {
-                        const attachments = JSON.parse(dealPay[index].attachment);
-                        for (const att_index in attachments) {
-                            delete attachments[att_index].filepath;
-                            attachments[att_index].username = (await ctx.service.projectAccount.getAccountInfoById(attachments[att_index].uid)).name;
-                        }
-                        dealPay[index].attachment = attachments;
-                    }
+                for (const dp of renderData.dealPay) {
+                    dp.attachment = payAtt.filter(x => { return x.pid === dp.pid });
                 }
-                renderData.dealPay = dealPay;
                 renderData.calcBase = await ctx.service.stage.getStagePayCalcBase(ctx.stage, ctx.tender.info);
                 renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.pay);
                 renderData.whiteList = this.ctx.app.config.multipart.whitelist;
@@ -879,17 +872,13 @@ module.exports = app => {
                 }
 
                 // 用户有无权限上传和删除附件
-                renderData.uploadPermission = (!(ctx.stage.readOnly || ctx.stage.revising) && ((ctx.stage.status === auditConst.status.uncheck || ctx.stage.status === auditConst.status.checkNo) && ctx.session.sessionUser.accountId === ctx.stage.user_id) ||
-                    (ctx.stage.status === auditConst.status.checkNoPre && ctx.session.sessionUser.accountId === ctx.stage.curAuditor.aid) ||
-                    (ctx.stage.status === auditConst.status.checking && ctx.stage.curAuditor && ctx.stage.curAuditor.aid === ctx.session.sessionUser.accountId)) ||
-                    (ctx.tender.isTourist && ctx.tender.touristPermission.file);
+                renderData.uploadPermission = !ctx.tender.isTourist || ctx.tender.touristPermission.file;
 
                 if (!ctx.stage.readOnly || ctx.tender.isTourist) {
                     // 计算 本期金额
                     const payCalculator = new PayCalculator(ctx, ctx.stage, ctx.tender.info);
                     await payCalculator.calculateAll(renderData.dealPay);
                     await this._updateStageCache(ctx, payCalculator);
-                    // renderData.calcBase = payCalculator.bases;
                 }
                 await this.layout('stage/pay.ejs', renderData, 'stage/pay_modal.ejs');
             } catch (err) {
@@ -1736,6 +1725,7 @@ module.exports = app => {
             }
 
         }
+
         /**
          * 合同支付上传附件
          * @param {Object} ctx - egg全局变量
@@ -1749,9 +1739,6 @@ module.exports = app => {
             };
             let stream;
             try {
-                // this._checkStageCanModify(ctx);
-                this._checkStageCanModifyRe(ctx);
-
                 const parts = ctx.multipart({ autoFields: true });
                 const files = [];
                 let index = 0;
@@ -1774,23 +1761,22 @@ module.exports = app => {
                     await sendToWormhole(stream);
                     // 插入到stage_pay对应的附件列表中
                     const attData = {
+                        tid: this.ctx.tender.id,
+                        sid: this.ctx.stage.id,
+                        uid: ctx.session.sessionUser.accountId,
+                        pid: parts.field.pay_id,
                         filename: fileInfo.name,
                         fileext: fileInfo.ext,
                         filesize: Array.isArray(parts.field.size) ? parts.field.size[index] : parts.field.size,
                         filepath: path.join(dirName, fileName),
-                        uid: ctx.session.sessionUser.accountId,
                         in_time: moment(create_time * 1000).format('YYYY-MM-DD'),
+                        renew: this.ctx.stage.status === auditConst.status.checked,
                     };
-                    const result = await ctx.service.stagePay.saveAtt(parts.field.pay_id, attData);
-                    if (!result) {
-                        throw '导入数据库保存失败';
-                    }
-                    delete attData.filepath;
                     attData.username = ctx.session.sessionUser.name;
                     files.length !== 0 ? files.unshift(attData) : files.push(attData);
                     ++index;
                 }
-                responseData.data = files;
+                responseData.data = await ctx.service.payAtt.addFiles(files);
             } catch (err) {
                 this.log(err);
                 // 失败需要消耗掉stream 以防卡死
@@ -1810,38 +1796,33 @@ module.exports = app => {
          * @return {void}
          */
         async payDownloadFile(ctx) {
-            const id = ctx.params.pid;
-            const index = ctx.params.index;
-            if (id && index) {
-                try {
-                    const payInfo = await ctx.service.stagePay.getDataById(id);
-                    if (payInfo !== undefined && payInfo.attachment !== null) {
-                        const fileInfo = JSON.parse(payInfo.attachment)[index];
-                        const fileName = path.join(this.app.baseDir, fileInfo.filepath);
-                        // 解决中文无法下载问题
-                        const userAgent = (ctx.request.header['user-agent'] || '').toLowerCase();
-                        let disposition = '';
-                        if (userAgent.indexOf('msie') >= 0 || userAgent.indexOf('chrome') >= 0) {
-                            disposition = 'attachment; filename=' + encodeURIComponent(fileInfo.filename + fileInfo.fileext);
-                        } else if (userAgent.indexOf('firefox') >= 0) {
-                            disposition = 'attachment; filename*="utf8\'\'' + encodeURIComponent(fileInfo.filename + fileInfo.fileext) + '"';
-                        } else {
-                            /* safari等其他非主流浏览器只能自求多福了 */
-                            disposition = 'attachment; filename=' + new Buffer(fileInfo.filename + fileInfo.fileext).toString('binary');
-                        }
-                        ctx.response.set({
-                            'Content-Type': 'application/octet-stream',
-                            'Content-Disposition': disposition,
-                            'Content-Length': fileInfo.filesize,
-                        });
-                        ctx.body = await fs.createReadStream(fileName);
-                    } else {
-                        throw '不存在该文件';
-                    }
-                } catch (err) {
-                    this.log(err);
-                    this.setMessage(err.toString(), this.messageType.ERROR);
+            const id = ctx.params.fid;
+            try {
+                if (!id) throw '数据错误';
+                const fileInfo = await ctx.service.payAtt.getDataById(id);
+                if (!fileInfo) throw '不存在该文件';
+
+                const fileName = path.join(this.app.baseDir, fileInfo.filepath);
+                // 解决中文无法下载问题
+                const userAgent = (ctx.request.header['user-agent'] || '').toLowerCase();
+                let disposition = '';
+                if (userAgent.indexOf('msie') >= 0 || userAgent.indexOf('chrome') >= 0) {
+                    disposition = 'attachment; filename=' + encodeURIComponent(fileInfo.filename + fileInfo.fileext);
+                } else if (userAgent.indexOf('firefox') >= 0) {
+                    disposition = 'attachment; filename*="utf8\'\'' + encodeURIComponent(fileInfo.filename + fileInfo.fileext) + '"';
+                } else {
+                    /* safari等其他非主流浏览器只能自求多福了 */
+                    disposition = 'attachment; filename=' + new Buffer(fileInfo.filename + fileInfo.fileext).toString('binary');
                 }
+                ctx.response.set({
+                    'Content-Type': 'application/octet-stream',
+                    'Content-Disposition': disposition,
+                    'Content-Length': fileInfo.filesize,
+                });
+                ctx.body = await fs.createReadStream(fileName);
+            } catch (err) {
+                this.log(err);
+                this.setMessage(err.toString(), this.messageType.ERROR);
             }
         }
 
@@ -1851,41 +1832,20 @@ module.exports = app => {
          * @return {void}
          */
         async payDeleteFile(ctx) {
-            const responseData = {
-                err: 0,
-                msg: '',
-                data: '',
-            };
             try {
-                // this._checkStageCanModify(ctx);
-                this._checkStageCanModifyRe(ctx);
-
                 const data = JSON.parse(ctx.request.body.data);
-                const payInfo = await ctx.service.stagePay.getDataById(data.id);
-                if (payInfo !== undefined) {
-                    const fileInfo = JSON.parse(payInfo.attachment)[data.index];
-                    // 先删除文件
-                    await fs.unlinkSync(path.join(this.app.baseDir, fileInfo.filepath));
-                    // 再删除数据库
-                    const attachment = JSON.parse(payInfo.attachment);
-                    attachment.splice(data.index, 1);
-                    const result = await ctx.service.stagePay.deleteAtt(data.id, attachment);
-                    responseData.data = '';
-                } else {
-                    throw '不存在该文件';
-                }
+                const fileInfo = await ctx.service.payAtt.getDataById(data.id);
+                if (!fileInfo) throw '不存在该文件';
 
-                // if (data.tid === undefined || data.uci === undefined || data.uc === undefined || data.ac === undefined) {
-                //     throw '参数有误';
-                // }
-                // const [addCompany, selectCompany] = await ctx.service.changeCompany.setCompanyList(data);
-                // responseData.data = { add: addCompany, select: selectCompany };
+                const result = await ctx.service.payAtt.delFiles(data.id);
+                if (fs.existsSync(path.join(ctx.app.baseDir, fileInfo.filepath))) {
+                    await fs.unlinkSync(path.join(ctx.app.baseDir, fileInfo.filepath));
+                }
+                ctx.body = { err: 0, msg: '', data: result };
             } catch (err) {
-                responseData.err = 1;
-                responseData.msg = err;
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '删除文件错误');
             }
-
-            ctx.body = responseData;
         }
 
 

+ 104 - 1
app/controller/stage_rela_controller.js

@@ -12,6 +12,8 @@ const auditConst = require('../const/audit').stage;
 const spreadSetting = require('../lib/spread_setting');
 const measureType = require('../const/tender').measureType;
 const tenderConst = require('../const/tender');
+const sendToWormhole = require('stream-wormhole');
+const path = require('path');
 
 module.exports = app => {
 
@@ -119,6 +121,7 @@ module.exports = app => {
         async _getStageRelaLedgerData(ctx, relaStage) {
             const ledgerData = await ctx.service.ledger.getData(relaStage.rela_tid);
             for (const l of ledgerData) {
+                l.org_unit_price = l.unit_price;
                 delete l.unit_price;
             }
             const dgnData = await ctx.service.stageBillsDgn.getDgnData(relaStage.rela_tid);
@@ -130,7 +133,7 @@ module.exports = app => {
             });
             this.ctx.helper.assignRelaData(ledgerData, [
                 { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id' },
-                { data: curStageData, fields: ['unit_price', 'contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid' },
+                { data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid' },
                 { data: endStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', ], prefix: 'end_', relaId: 'lid' },
             ]);
             return ledgerData;
@@ -154,6 +157,7 @@ module.exports = app => {
         async load(ctx) {
             try {
                 const relaStage = await this.ctx.service.stageRela.getDataById(ctx.params.trid);
+                const stage = await this.ctx.service.stage.getDataById(relaStage.rela_sid);
                 const data = JSON.parse(ctx.request.body.data);
                 const filter = data.filter.split(';');
                 const responseData = { err: 0, msg: '', data: {} };
@@ -167,6 +171,7 @@ module.exports = app => {
                             break;
                         case 'detail':
                             responseData.data.detailData = await ctx.service.stageDetail.getLastestStageData(relaStage.rela_tid, relaStage.rela_sid);
+                            responseData.data.detailAtt = await this.ctx.service.stageDetailAtt.getStageData(relaStage.rela_sid, stage.im_type);
                             break;
                         case 'change':
                             responseData.data.changeData = await ctx.service.stageChange.getLastestAllStageData(relaStage.rela_tid, relaStage.rela_sid);
@@ -185,6 +190,104 @@ module.exports = app => {
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
+
+
+        async uploadImFile(ctx) {
+            let stream;
+            try {
+                const relaStage = await this.ctx.service.stageRela.getDataById(ctx.params.trid);
+                const stage = await this.ctx.service.stage.getDataById(relaStage.rela_sid);
+                const parts = ctx.multipart({ autoFields: true });
+                let index = 0;
+                const create_time = Date.parse(new Date()) / 1000;
+                stream = await parts();
+                const uploadFiles = [];
+                while (stream !== undefined) {
+                    if (!stream.filename) throw '未发现上传文件!';
+
+                    const fileInfo = path.parse(stream.filename);
+                    const dirName = path.join('app', 'public', 'upload', relaStage.tid.toString(), 'im');
+                    const fileName = `${ctx.session.sessionUser.accountId}_${create_time}_${index}${fileInfo.ext}`;
+
+                    // 保存文件
+                    await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, dirName, fileName));
+                    await sendToWormhole(stream);
+
+                    // 插入到stage_pay对应的附件列表中
+                    uploadFiles.push({
+                        file_id: this.ctx.app.uuid.v4(),
+                        filename: fileInfo.name,
+                        fileext: fileInfo.ext,
+                        filesize: Array.isArray(parts.field.size) ? parts.field.size[index] : parts.field.size,
+                        filepath: path.join(dirName, fileName),
+                        uid: ctx.session.sessionUser.accountId,
+                        in_time: ctx.moment(create_time * 1000).format('YYYY-MM-DD'),
+                        renew: true,
+                    });
+                    ++index;
+                    if (Array.isArray(parts.field.size) && index < parts.field.size.length) {
+                        stream = await parts();
+                    } else {
+                        stream = undefined;
+                    }
+                }
+                const baseInfo = JSON.parse(parts.field.base);
+                const result = await ctx.service.stageDetailAtt.addFiles(stage, baseInfo, uploadFiles);
+                ctx.body = { err: 0, mgs: '', data: result };
+            } catch (err) {
+                console.log(err);
+                // 失败需要消耗掉stream 以防卡死
+                if (stream) await sendToWormhole(stream);
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '上传附件失败');
+            }
+        }
+
+        async deleteImFile(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.uuid || !data.file_id) throw '数据错误';
+
+                const result = await ctx.service.stageDetailAtt.delFiles(data.uuid, data.file_id);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '删除附件失败');
+            }
+        }
+
+        async downloadImFile(ctx) {
+            try {
+                const uuid = ctx.query.im_id;
+                const file_id = ctx.query.file_id;
+                if (!uuid || !file_id) throw '数据错误';
+
+                const fileInfo = await ctx.service.stageDetailAtt.getFiles(uuid, file_id);
+                if (!fileInfo) throw '文件不存在';
+
+                const fileName = path.join(this.app.baseDir, fileInfo.filepath);
+                // 解决中文无法下载问题
+                const userAgent = (ctx.request.header['user-agent'] || '').toLowerCase();
+                let disposition = '';
+                if (userAgent.indexOf('msie') >= 0 || userAgent.indexOf('chrome') >= 0) {
+                    disposition = 'attachment; filename=' + encodeURIComponent(fileInfo.filename + fileInfo.fileext);
+                } else if (userAgent.indexOf('firefox') >= 0) {
+                    disposition = 'attachment; filename*="utf8\'\'' + encodeURIComponent(fileInfo.filename + fileInfo.fileext) + '"';
+                } else {
+                    /* safari等其他非主流浏览器只能自求多福了 */
+                    disposition = 'attachment; filename=' + new Buffer(fileInfo.filename + fileInfo.fileext).toString('binary');
+                }
+                ctx.response.set({
+                    'Content-Type': 'application/octet-stream',
+                    'Content-Disposition': disposition,
+                    'Content-Length': fileInfo.filesize,
+                });
+                ctx.body = await fs.createReadStream(fileName);
+            } catch (err) {
+                ctx.log(err);
+                ctx.postError(err, '下载文件失败');
+            }
+        }
     }
 
 

+ 1 - 1
app/controller/tender_controller.js

@@ -1265,7 +1265,7 @@ module.exports = app => {
                         ctx.body = {err: 0, msg: '', data: reviseData};
                         break;
                     case 'stage':
-                        const stageData = await this.ctx.service.stageBills.sumLoad(data.lid, data.tenders);
+                        const stageData = await this.ctx.service.stageBills.sumLoad(data.lid, data.tenders, data.cover);
                         ctx.body = {err: 0, msg: '', data: stageData};
                         break;
                     default:

+ 1 - 1
app/extend/helper.js

@@ -1228,7 +1228,7 @@ module.exports = {
      * @return {Boolean} 匹配结果
      */
     canPreview(ext) {
-        const reg = /(.png)|(.gif)|(.txt)|(.jpg)|(.jpeg)|(.pdf)/;
+        const reg = /(.png)|(.gif)|(.txt)|(.jpg)|(.jpeg)|(.pdf)/i;
         return reg.test(ext);
     },
 

+ 1 - 0
app/lib/rpt_data_analysis.js

@@ -662,6 +662,7 @@ const gatherChapter = {
             return (x.visible !== undefined && x.visible !== null) ? x.visible : true;
 
         });
+        console.log(data[fieldsKey[0].table]);
     },
 };
 const join = {

+ 9 - 6
app/lib/stage_im.js

@@ -63,6 +63,7 @@ class StageIm {
         //
         this.imFields = ['uuid', 'doc_code', 'peg', 'bw', 'xm', 'drawing_code', 'calc_memo', 'calc_memo_org', 'calc_img_remark', 'calc_img', 'position', 'jldy'];
         this.splitChar = '-';
+
     }
 
     // 加载数据
@@ -328,7 +329,7 @@ class StageIm {
                         (!im.code || im.code === d.code) &&
                         (!im.name || im.name === d.name) &&
                         (!im.unit || im.unit === d.unit) &&
-                        self.ctx.helper.checkZero(self.ctx.helper.sub(im.unit_price, d.unit_price)) &&
+                        self.ctx.helper.checkZero(self.ctx.helper.sub(im[up_field], d[up_field])) &&
                         (!im.pid || im.pid === d.pid) &&
                         (!im.pos_name || im.pos_name === d.pos_name);
                 });
@@ -339,7 +340,7 @@ class StageIm {
                         (!im.code || im.code === d.code) &&
                         (!im.name || im.name === d.name) &&
                         (!im.unit || im.unit === d.unit) &&
-                        self.ctx.helper.checkZero(self.ctx.helper.sub(im.unit_price, d.unit_price)) &&
+                        self.ctx.helper.checkZero(self.ctx.helper.sub(im[up_field], d[up_field])) &&
                         (!im.pid || im.pid === d.pid) &&
                         (!im.pos_name || im.pos_name === d.pos_name);
                 });
@@ -838,13 +839,13 @@ class StageIm {
             let im = nodeImData.find(function(d) {
                 return d.lid === node.id &&
                     d.code === p.b_code && p.name === d.name && p.unit === d.unit &&
-                    self.ctx.helper.checkZero(self.ctx.helper.sub(p.unit_price, d.unit_price));
+                    self.ctx.helper.checkZero(self.ctx.helper.sub(p[up_field], d[up_field]));
             });
             if (!im) {
                 const peg = this._getPegNode(node);
                 im = {
                     id: this.ImData.length + 1,
-                    lid: node.id, pid: '', code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price,
+                    lid: node.id, pid: '', code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price, org_unit_price: p.org_unit_price,
                     quantity: 0, total_price: 0,
                     jl: 0, contract_jl: 0, qc_jl: 0,
                     pre_jl: 0, pre_contract_jl: 0, pre_qc_jl: 0,
@@ -919,7 +920,7 @@ class StageIm {
                     if (this.ctx.helper.checkZero(pp.contract_qty) && this.ctx.helper.checkZero(pp.qc_qty)) { continue; }
                     const im = {
                         id: this.ImData.length + 1,
-                        lid: node.id, code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price, pid: pp.id,
+                        lid: node.id, code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price, org_unit_price: p.org_unit_price, pid: pp.id,
                         quantity: pp.quantity,
                         jl: pp.gather_qty, contract_jl: pp.contract_qty, qc_jl: pp.qc_qty,
                         pre_jl: pp.pre_gather_qty, pre_contract_jl: pp.pre_contract_qty, pre_qc_jl: pp.pre_qc_qty,
@@ -958,7 +959,7 @@ class StageIm {
 
                 const im = {
                     id: this.ImData.length + 1,
-                    lid: node.id, code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price, pid: '',
+                    lid: node.id, code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price, org_unit_price: p.org_unit_price, pid: '',
                     quantity: p.quantity, total_price: p.total_price,
                     jl: p.gather_qty, contract_jl: p.contract_qty, qc_jl: p.qc_qty,
                     pre_jl: p.pre_gather_qty, pre_contract_jl: p.pre_contract_qty, pre_qc_jl: p.pre_qc_qty,
@@ -1015,6 +1016,7 @@ class StageIm {
 
     // 生成中间计量数据
     async buildImData() {
+        this.up_field = 'unit_price';
         const self = this;
         this.stage = this.ctx.stage;
         // 初始化
@@ -1053,6 +1055,7 @@ class StageIm {
     }
 
     buildRelaStageIm(stage, billsTree, pos, details, changes) {
+        this.up_field = 'org_unit_price';
         this.billsTree = billsTree;
         this.pos = pos;
         this.stage = stage;

+ 13 - 4
app/lib/sum_load.js

@@ -314,6 +314,10 @@ class updateReviseGclTree extends loadGclBaseTree {
 }
 
 class gatherStageGclTree extends loadGclBaseTree {
+    constructor (ctx, setting) {
+        super(ctx, setting);
+        this.cover = setting.cover;
+    }
     loadBase(datas, pos) {
         datas.sort((x, y) => { return x.level === y.level ? x.order - y.order : x.level - y.level; });
         const Index = {};
@@ -334,8 +338,8 @@ class gatherStageGclTree extends loadGclBaseTree {
                 name: d.name,
                 unit: d.unit,
                 unit_price: d.unit_price,
-                org_contract_qty: d.contract_qty || 0,
-                org_contract_tp: d.contract_tp || 0,
+                org_contract_qty: this.cover ? d.contract_qty || 0 : 0,
+                org_contract_tp: this.cover ? d.contract_tp || 0 : 0,
                 org_order: d.order,
                 contract_qty: 0,
                 contract_tp: 0,
@@ -392,6 +396,7 @@ class gatherStageGclTree extends loadGclBaseTree {
                 }
             }
         }
+        console.log(result.update.length);
         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) {
@@ -505,12 +510,16 @@ class sumLoad {
         }
     }
 
-    async stageGatherGcl(select, maxId, tenders, defaultData) {
+    async stageGatherGcl(select, maxId, tenders, defaultData, cover) {
         const ignoreParent = this.ctx.tender.info.fun_rela.sum_load.ignoreParent;
         this.loadTree = new gatherStageGclTree(this.ctx, {
-            parent: select, maxId, type: 'ledger', defaultData, ignoreParent,
+            parent: select, maxId, type: 'ledger', defaultData, ignoreParent, cover,
         });
         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);
+        this.ctx.helper.assignRelaData(posterity, [
+            { 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);
         this.loadTree.loadBase(posterity, pos);
 

+ 88 - 14
app/public/js/change_information_set.js

@@ -121,6 +121,9 @@ $(document).ready(() => {
                     indent: 16,
                     getColor: function (index, data) {
                         if (!data) return;
+                        if (_.findIndex(changeLedgerList, { id: data.gcl_id }) !== -1 || _.findIndex(changePosList, { id: data.mx_id }) !== -1) {
+                            return '#dc3545';
+                        }
                         if(data.lid != 0) return;
                         return '#007bff';
                     }
@@ -625,7 +628,7 @@ $(document).ready(() => {
         const isCheck = $(this).hasClass('table-success') ? true : false;
         const data_bwmx = $(this).attr('data-bwmx').split('$#$');
         const isDeal = $(this).data('gcl') !== undefined ? true : false;
-        let codeHtml = '<tr quantity="'+ $(this).children('td').eq(5).text() +'" gcl_id=""><td colspan="7" class="colspan_1">&nbsp;</td><td class="colspan_2"><input type="checkbox"></td></tr>';
+        let codeHtml = '<tr quantity="'+ $(this).children('td').eq(5).text() +'" gcl_id="" mx_id=""><td colspan="7" class="colspan_1">&nbsp;</td><td class="colspan_2"><input type="checkbox"></td></tr>';
         if (isDeal) {
             const lid = $(this).data('lid');
             let gcl = _.find(gclGatherData, function (item) {
@@ -638,27 +641,28 @@ $(document).ready(() => {
             for (const leaf of gcl.leafXmjs) {
                 const quantity = leaf.quantity !== undefined && leaf.quantity !== null ? leaf.quantity : 0;
                 const gcl_id = leaf.gcl_id ? leaf.gcl_id : '';
-                const bwmx = leaf.bwmx !== undefined ? leaf.bwmx : (gcl.leafXmjs.length > 1 && gcl.name ? gcl.name : '');
+                const mx_id = leaf.mx_id ? leaf.mx_id : '';
+                const bwmx = leaf.bwmx !== undefined ? leaf.bwmx : (gcl.leafXmjs.length > 1 && gcl.name ? gcl.name : undefined);
                 const isChecked = data_bwmx.indexOf(
                     leaf.code + '!_!' + (leaf.jldy ? leaf.jldy : '') + '!_!' +
                     (leaf.dwgc ? leaf.dwgc : '') + '!_!' + (leaf.fbgc ? leaf.fbgc : '') + '!_!' + (leaf.fxgc ? leaf.fxgc : '')
-                    + '!_!' + (leaf.gcl_id ? leaf.gcl_id : '0') + '!_!' +
-                    (bwmx !== '' ? bwmx : leaf.jldy ? leaf.jldy : '') + '*;*' + quantity) !== -1 && isCheck ?
+                    + '!_!' + (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 isDisabled = isUsed ? 'disabled ' : '';
-                codeHtml += '<tr quantity="' + quantity + '" gcl_id="' + gcl_id + '"><td>' + leaf.code + '</td>' +
+                codeHtml += '<tr quantity="' + quantity + '" gcl_id="' + gcl_id + '" mx_id="' + mx_id + '"><td>' + leaf.code + '</td>' +
                     '<td>' + (leaf.jldy ? leaf.jldy: '') + '</td>' +
                     '<td>' + (leaf.dwgc ? leaf.dwgc : '') + '</td>' +
                     '<td>' + (leaf.fbgc ? leaf.fbgc : '') + '</td>' +
                     '<td>' + (leaf.fxgc ? leaf.fxgc : '') + '</td>' +
-                    '<td>' + bwmx + '</td>' +
+                    '<td>' + (bwmx !== undefined ? bwmx : '') + '</td>' +
                     '<td class="text-right">' + (ZhCalc.round(quantity, findDecimal(gcl.unit)) ? ZhCalc.round(quantity, findDecimal(gcl.unit)) : 0) + '</td>' +
                     '<td class="text-center"><input type="checkbox" ' + isDisabled + isChecked +
                     '></td></tr>';
             }
         } else if (!isDeal && isCheck) {
-            codeHtml = '<tr quantity="'+ $(this).children('td').eq(5).text() +'" gcl_id=""><td colspan="7" class="colspan_1">&nbsp;</td><td class="colspan_2"><input type="checkbox" checked></td></tr>';
+            codeHtml = '<tr quantity="'+ $(this).children('td').eq(5).text() +'" gcl_id="" mx_id=""><td colspan="7" class="colspan_1">&nbsp;</td><td class="colspan_2"><input type="checkbox" checked></td></tr>';
         }
         $('#code-list').attr('data-index', $(this).children('td').eq(0).text());
         $('#code-input').val('');
@@ -682,12 +686,13 @@ $(document).ready(() => {
                 const tr = $(this).parents('tr');
                 const length = tr.children('td').length;
                 const gcl_id = tr.attr('gcl_id');
+                const mx_id = tr.attr('mx_id');
                 const bwmx = length === 8 ?
                     tr.children('td').eq(0).text() + '!_!' +
                     tr.children('td').eq(1).text() + '!_!' +
                     tr.children('td').eq(2).text() + '!_!' +
                     tr.children('td').eq(3).text() + '!_!' +
-                    tr.children('td').eq(4).text() + '!_!' + gcl_id + '!_!' +
+                    tr.children('td').eq(4).text() + '!_!' + gcl_id + '!_!' + mx_id + '!_!' +
                     (tr.children('td').eq(5).text() !== '' ? tr.children('td').eq(5).text() : tr.children('td').eq(1).text()) : '0';
                 const quantity = tr.attr('quantity');
                 const de_qu = bwmx + '*;*' + quantity;
@@ -704,12 +709,13 @@ $(document).ready(() => {
                     const tr = $(this).parents('tr');
                     const length = tr.children('td').length;
                     const gcl_id = tr.attr('gcl_id');
+                    const mx_id = tr.attr('mx_id');
                     const bwmx = length === 8 ?
                         tr.children('td').eq(0).text() + '!_!' +
                         tr.children('td').eq(1).text() + '!_!' +
                         tr.children('td').eq(2).text() + '!_!' +
                         tr.children('td').eq(3).text() + '!_!' +
-                        tr.children('td').eq(4).text() + '!_!' + gcl_id + '!_!' +
+                        tr.children('td').eq(4).text() + '!_!' + gcl_id + '!_!' + mx_id + '!_!' +
                         (tr.children('td').eq(5).text() !== '' ? tr.children('td').eq(5).text() : tr.children('td').eq(1).text()) : '0';
                     const quantity = tr.attr('quantity');
                     const de_qu = bwmx + '*;*' + quantity;
@@ -830,12 +836,13 @@ $(document).ready(() => {
                     const tr = $(this).parents('tr');
                     const length = tr.children('td').length;
                     const gcl_id = tr.attr('gcl_id');
+                    const mx_id = tr.attr('mx_id');
                     const bwmx = length === 8 ?
                         tr.children('td').eq(0).text() + '!_!' +
                         tr.children('td').eq(1).text() + '!_!' +
                         tr.children('td').eq(2).text() + '!_!' +
                         tr.children('td').eq(3).text() + '!_!' +
-                        tr.children('td').eq(4).text() + '!_!' + gcl_id + '!_!' +
+                        tr.children('td').eq(4).text() + '!_!' + gcl_id + '!_!' + mx_id + '!_!' +
                         (tr.children('td').eq(5).text() !== '' ? tr.children('td').eq(5).text() : tr.children('td').eq(1).text()) : '0';
                     const quantity = tr.attr('quantity');
                     const de_qu = bwmx + '*;*' + quantity;
@@ -922,11 +929,20 @@ $(document).ready(() => {
 });
 function checkSelectAll() {
     let check = $('#code-list tr').length > 0 ? true : false;
+    const codeNum = $('#code-list tr').length;
+    let i = 0;
     $('#code-list tr').each(function () {
         if ($(this).css('display') !== 'none' && !$(this).find('input').is(':checked')) {
             check = false;
+            return;
+        }
+        else if ($(this).css('display') === 'none') {
+            i++;
         }
     });
+    if (check && i === codeNum) {
+        check = false;
+    }
     $('#code-select-all').prop('checked', check);
 }
 
@@ -988,11 +1004,26 @@ function tableDataRemake(changeListData) {
     // 根据已添加的清单显示
     if (changeList.length > 0 && changeList[0]) {
         const removeList = [];
+        const updateList = [];
+        const updateGclIdList = [];
         for (const [index,clinfo] of changeList.entries()) {
             if (clinfo.lid != 0) {
                 let listinfo = changeListData.find(function (item) {
                     return (item.id !== undefined && item.id == clinfo.lid) || (item.id === undefined && item.leafXmjs !== undefined && item.leafXmjs.length !== 0 && item.leafXmjs[0].gcl_id == clinfo.lid);
                 });
+                let info = '';
+                if (listinfo === undefined || (clinfo.lid && clinfo.gcl_id && clinfo.lid !== clinfo.gcl_id)) {
+                    // 有可能这部分台账发生变化,此时要更新清单lid信息,防止数据丢失
+                    const newlistinfo = changeListData.find(function (item) {
+                        return (item.id !== undefined && item.id == clinfo.gcl_id) || (item.id === undefined && item.leafXmjs !== undefined && item.leafXmjs.length !== 0 && _.find(item.leafXmjs, {gcl_id: clinfo.gcl_id }));
+                    });
+                    if (listinfo === undefined && newlistinfo || (listinfo && newlistinfo && !isObjEqual(listinfo, newlistinfo))) {
+                        listinfo = newlistinfo;
+                        updateList.push({id: clinfo.id, lid: newlistinfo.leafXmjs[0].gcl_id});
+                        // 更新lid
+                        changeList[index].lid = newlistinfo.leafXmjs[0].gcl_id;
+                    }
+                }
                 if (listinfo === undefined) {
                     // 针对旧数据获取清单信息
                     listinfo = changeListData[clinfo.lid - 1];
@@ -1013,11 +1044,13 @@ function tableDataRemake(changeListData) {
                             return flag;
                         });
                         if (leafInfo) {
+                            info = leafInfo;
                             pushbwmx = leafInfo.code + '!_!' + (leafInfo.jldy !== undefined ? leafInfo.jldy : '') + '!_!' +
                                 (leafInfo.dwgc ? leafInfo.dwgc : '') + '!_!' +
                                 (leafInfo.fbgc ? leafInfo.fbgc : '') + '!_!' +
                                 (leafInfo.fxgc ? leafInfo.fxgc : '') + '!_!' +
                                 (leafInfo.gcl_id ? leafInfo.gcl_id : '') + '!_!' +
+                                (leafInfo.mx_id ? leafInfo.mx_id : '') + '!_!' +
                                 (leafInfo.bwmx !== undefined ? leafInfo.bwmx : (listinfo.leafXmjs.length > 1 && listinfo.name ? listinfo.name : (leafInfo.jldy !== undefined ? leafInfo.jldy : ''))) + '*;*' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
                         } else {
                             toastr.warning('台账清单列表已不存在'+ clinfo.code +',已更新变更清单列表');
@@ -1048,13 +1081,16 @@ function tableDataRemake(changeListData) {
                             return flag;
                         });
                         if (leafInfo) {
+                            info = leafInfo;
                             pushbwmx = leafInfo.code + '!_!' + (leafInfo.jldy !== undefined ? leafInfo.jldy : '') + '!_!' +
                                 (leafInfo.dwgc ? leafInfo.dwgc : '') + '!_!' +
                                 (leafInfo.fbgc ? leafInfo.fbgc : '') + '!_!' +
                                 (leafInfo.fxgc ? leafInfo.fxgc : '') + '!_!' +
                                 (leafInfo.gcl_id ? leafInfo.gcl_id : '') + '!_!' +
+                                (leafInfo.mx_id ? leafInfo.mx_id : '') + '!_!' +
                                 (leafInfo.bwmx !== undefined ? leafInfo.bwmx : (listinfo.leafXmjs.length > 1 && listinfo.name ? listinfo.name : (leafInfo.jldy !== undefined ? leafInfo.jldy : ''))) + '*;*' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
                         } else {
+                            console.log(clinfo, listinfo.leafXmjs);
                             toastr.warning('台账清单列表已不存在'+ clinfo.code +',已更新变更清单列表');
                             // changeList.splice(index, 1);
                             removeList.push(clinfo);
@@ -1072,14 +1108,50 @@ function tableDataRemake(changeListData) {
                         $('#table-list-select tr[data-lid="'+ clinfo.lid +'"]').attr('data-bwmx', pushbwmx);
                     }
                 }
+
+                if (info && _.findIndex(changeLedgerList, { id: clinfo.gcl_id }) !== -1) {
+                    // 可能因为升降级关系:细目,分部分项等会发生变化,更新清单
+                    const updateInfo = {};
+                    if (info.code !== clinfo.xmj_code) {
+                        updateInfo.xmj_code = info.code;
+                        changeList[index].xmj_code = info.code;
+                    }
+                    if (info.jldy !== clinfo.xmj_jldy) {
+                        updateInfo.xmj_jldy = info.jldy;
+                        changeList[index].xmj_jldy = info.jldy;
+                    }
+                    if (info.dwgc !== clinfo.xmj_dwgc) {
+                        updateInfo.xmj_dwgc = info.dwgc;
+                        changeList[index].xmj_dwgc = info.dwgc;
+                    }
+                    if (info.fbgc !== clinfo.xmj_fbgc) {
+                        updateInfo.xmj_fbgc = info.fbgc;
+                        changeList[index].xmj_fbgc = info.fbgc;
+                    }
+                    if (info.fxgc !== clinfo.xmj_fxgc) {
+                        updateInfo.xmj_fxgc = info.fxgc;
+                        changeList[index].xmj_fxgc = info.fxgc;
+                    }
+                    if (!_.isEmpty(updateInfo) && _.indexOf(updateGclIdList, clinfo.gcl_id) === -1) {
+                        updateGclIdList.push(clinfo.gcl_id);
+                        // updateInfo.gcl_id = info.id;
+                        updateList.push({ row: updateInfo, where: { tid: tenderId, gcl_id: clinfo.gcl_id } });
+                    }
+                }
             }
         }
-        if(removeList.length > 0) {
-            _.pullAll(changeList, removeList);
-            postData(window.location.pathname + '/save', { type:'remove_list', updateData: removeList }, function (result) {
+        if(updateList.length > 0) {
+            console.log(updateList);
+            postData(window.location.pathname + '/save', { type:'update_list', updateData: updateList }, function (result) {
             }, function () {
             });
         }
+        if(removeList.length > 0) {
+            _.pullAll(changeList, removeList);
+            // postData(window.location.pathname + '/save', { type:'remove_list', updateData: removeList }, function (result) {
+            // }, function () {
+            // });
+        }
     }
 }
 
@@ -1137,13 +1209,14 @@ function remakeChangeSpread() {
 
         for (const b of data_bwmx) {
             const oamount = b.split('*;*')[1] != '' ? b.split('*;*')[1] : 0;
-            let bwmx = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[6] : '';
+            let bwmx = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[7] : '';
             let xmj_code = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[0] : '';
             let xmj_jldy = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[1] : '';
             let xmj_dwgc = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[2] : '';
             let xmj_fbgc = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[3] : '';
             let xmj_fxgc = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[4] : '';
             let gcl_id = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[5] : '';
+            let mx_id = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[6] : '';
             let trlist = {
                 code,
                 name,
@@ -1160,6 +1233,7 @@ function remakeChangeSpread() {
                 xmj_fbgc,
                 xmj_fxgc,
                 gcl_id,
+                mx_id,
             };
             const radionInfo = changeList.find(function (info) {
                 return info.code === code && (info.lid == lid || parseInt(info.lid) === parseInt(lindex)) && gcl_id == info.gcl_id && (info.bwmx === bwmx || info.bwmx === xmj_jldy) && parseInt(info.oamount) === parseInt(oamount);

文件差异内容过多而无法显示
+ 2560 - 0
app/public/js/change_revise.js


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

@@ -534,4 +534,4 @@ const gclGatherModel = (function () {
         checkDiffer,
         gatherChapterData,
     };
-})();
+})();

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

@@ -42,6 +42,7 @@ const checkOption = {
         fields: ['sgfh_qty', 'qtcl_qty', 'sjcl_qty', 'quantity'],
     },
     zero: { enable: 1 },
+    zeroPos: { enable: 1 },
     tp: {
         enable: 1,
         fields: [
@@ -1844,7 +1845,7 @@ $(document).ready(function() {
                 const data = {};
                 if (col.field === 'name') {
                     if (newText === '' && posData) {
-                        toastr.error('部位名称不可为空');
+                        toastr.error('计量单元名称不可为空');
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                         return;
                     } else if (!posData) {
@@ -2012,6 +2013,10 @@ $(document).ready(function() {
                 return;
             }
 
+            const hint = {
+                name: {type: 'warning', msg: '计量单元名称不可为空,已过滤'},
+                expr: {type: 'warning', msg: '粘贴了表达式非法,已过滤'},
+            };
             if (info.sheet.zh_setting) {
                 const data = [];
                 const sortData = info.sheet.zh_data || [];
@@ -2047,11 +2052,18 @@ $(document).ready(function() {
                                         posData[exprInfo.expr] = trimInvalidChar(info.sheet.getText(curRow, curCol));
                                     }
                                     bPaste = true;
-                                } catch(err) {
+                                } catch (err) {
                                     delete posData[colSetting.field];
-                                    toastr.warning('粘贴了表达式非法,已过滤');
+                                    toastMessageUniq(hint.expr);
                                 }
                             }
+                        } else if (colSetting.field === 'name') {
+                            if (!posData[colSetting.field]) {
+                                delete posData[colSetting.field];
+                                toastMessageUniq(hint.name);
+                            } else {
+                                bPaste = true;
+                            }
                         } else {
                             bPaste = true;
                         }

+ 18 - 0
app/public/js/ledger_check.js

@@ -14,6 +14,7 @@ const ledgerCheckType = {
     empty_code: {value: 2, text: '项目节、清单编号同时为空', fun: 'checkCodeEmpty', },
     calc: {value: 3, text: '清单数量不等于计量单元之和', fun: 'checkCalc', },
     zero: {value: 4, text: '清单数量或单价为0', fun: 'checkZero', },
+    zeroPos: {value: 12, text: '计量单元数量为0', fun: 'checkZeroPos', },
     tp: {value: 5, text: '清单金额≠数量×单价', fun: 'checkTp', },
     over: {value: 6, text: '超计', fun: 'checkOver', },
     same_code: {value: 7, text: '重复项目节', fun: 'checkSameCode', },
@@ -91,6 +92,23 @@ const ledgerCheckUtil = {
         }
         return error;
     },
+    checkZeroPos: function (ledgerTree, ledgerPos, decimal, option) {
+        const error = [];
+        for (const node of ledgerTree.nodes) {
+            if (node.children && node.children.length > 0) continue;
+
+            const nodePos = ledgerPos.getLedgerPos(node.id);
+            if (!nodePos || nodePos.length === 0) continue;
+
+            for (const np of nodePos) {
+                if (checkZero(np.sgfh_qty) && checkZero(np.qtcl_qty) && checkZero(np.sjcl_qty) && checkZero(np.quantity)) {
+                    error.push(node);
+                    break;
+                }
+            }
+        }
+        return error;
+    },
     checkTp: function (ledgerTree, ledgerPos, decimal, option) {
         const error = [];
         for (const node of ledgerTree.nodes) {

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

@@ -158,14 +158,14 @@ $(document).ready(() => {
     function loadLeafXmjData(iGclRow) {
         const gcl = gclGatherData[iGclRow];
         if (gcl) {
-            gcl.leafXmjs = gcl.leafXmjs.filter(item => {
-                return item.qc_qty || item.contract_qty
-            });
             for (const [index, xmj] of gcl.leafXmjs.entries()) {
                 const jiacha = calcOneBQJC(xmj);
                 gcl.leafXmjs[index].jiacha = jiacha !== 0 ? ZhCalc.round(jiacha, 2) : null;
             }
-            SpreadJsObj.loadSheetData(leafXmjSpread.getActiveSheet(), SpreadJsObj.DataType.Data, gcl.leafXmjs);
+            const leafXmjs = gcl.leafXmjs.filter(item => {
+                return item.qc_qty || item.contract_qty
+            });
+            SpreadJsObj.loadSheetData(leafXmjSpread.getActiveSheet(), SpreadJsObj.DataType.Data, leafXmjs);
 
             // 对清单调差工料table的单位数量进行改变
             materialSpreadSetting.cols[materialSpreadSetting.cols.length - 2].title = '|' + gcl.unit + '数量 �';
@@ -298,7 +298,7 @@ $(document).ready(() => {
         } else {
             SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, []);
         }
-        SpreadJsObj.reLoadSheetData(leafXmjSpread.getActiveSheet());
+        // SpreadJsObj.reLoadSheetData(leafXmjSpread.getActiveSheet());
     }
 
     // SpreadJsObj.locateTreeNode(ledgerSpread.getActiveSheet(), )

+ 11 - 5
app/public/js/revise.js

@@ -36,6 +36,7 @@ const checkOption = {
         fields: ['sgfh_qty', 'qtcl_qty', 'sjcl_qty', 'quantity'],
     },
     zero: { enable: 1 },
+    zeroPos: { enable: 1 },
     tp: {
         enable: 1,
         fields: [
@@ -1682,7 +1683,10 @@ $(document).ready(() => {
                     return;
                 }
             }
-            let bHint = false;
+            const hint = {
+                name: {type: 'warning', msg: '计量单元名称不可为空,已过滤'},
+                expr: {type: 'warning', msg: '粘贴了表达式非法,已过滤'},
+            };
             const lastOrder = sortData.length > 0 ? sortData[sortData.length - 1].porder + 1 : 1;
             for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
                 let bPaste = true;
@@ -1706,13 +1710,15 @@ $(document).ready(() => {
                                     posData[exprInfo.expr] = trimInvalidChar(info.sheet.getText(curRow, curCol));
                                 }
                             } catch (err) {
-                                if (!bHint) {
-                                    toastr.warning('粘贴了非法表达式,已过滤');
-                                    bHint = true;
-                                }
+                                toastMessageUniq(hint.expr);
                                 bPaste = false;
                             }
                         }
+                    } else if (colSetting.field === 'name') {
+                        if (!posData[colSetting.field]) {
+                            toastMessageUniq(hint.name);
+                            bPaste = false;
+                        }
                     }
                 }
                 if (bPaste) {

+ 10 - 4
app/public/js/shares/cs_tools.js

@@ -482,7 +482,7 @@ const showSelectTab = function(select, spread, afterShow) {
     };
     $.billsSearch = function (setting) {
         if (!setting.selector || !setting.searchSpread || !setting.resultSpreadSetting) return;
-        if (!setting.searchRangeStr) setting.searchRangeStr = '项目节编号/清单编号/名称';
+        if (!setting.searchRangeStr) setting.searchRangeStr = '项目节编号/清单编号/名称/台账数量';
         const resultId = setting.id + '-search-result';
         const obj = $(setting.selector);
         let filter = [];
@@ -526,12 +526,15 @@ const showSelectTab = function(select, spread, afterShow) {
         };
         const searchBills = function () {
             const keyword = $('#searchKeyword', obj).val();
+            const keyNum = _.toNumber(keyword);
             searchResult = [];
             const sortData = SpreadJsObj.getSortData(searchSheet);
             for (const node of sortData) {
                 if ((node.code && node.code.indexOf(keyword) > -1) ||
-                    node.b_code && node.b_code.indexOf(keyword) > -1 ||
-                    node.name && node.name.indexOf(keyword) > -1) {
+                    (node.b_code && node.b_code.indexOf(keyword) > -1) ||
+                    (node.name && node.name.indexOf(keyword) > -1) ||
+                    (!_.isNaN(keyNum) && checkZero(ZhCalc.sub(keyNum, node.quantity)))
+                ) {
                     const data = JSON.parse(JSON.stringify(node));
                     data.visible = true;
                     searchResult.push(data);
@@ -545,6 +548,7 @@ const showSelectTab = function(select, spread, afterShow) {
         };
         const searchCustom = function (key) {
             const keyword = $('#searchKeyword', obj).val();
+            const keyNum = _.toNumber(keyword);
             const checkFun = getCheckFun(key);
             searchResult = [];
             const sortData = SpreadJsObj.getSortData(searchSheet);
@@ -554,7 +558,9 @@ const showSelectTab = function(select, spread, afterShow) {
                     if (!keyword ||
                         (node.code && node.code.indexOf(keyword) > -1) ||
                         (node.b_code && node.b_code.indexOf(keyword) > -1) ||
-                        (node.name && node.name.indexOf(keyword) > -1)) {
+                        (node.name && node.name.indexOf(keyword) > -1)
+                        (!_.isNaN(keyNum) && checkZero(ZhCalc.sub(keyNum, node.quantity)))
+                    ) {
                         const data = JSON.parse(JSON.stringify(node));
                         data.visible = true;
                         searchResult.push(data);

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

@@ -171,10 +171,14 @@ const TenderSelect = function (setting) {
             this.tenderSpread.bind(spreadNS.Events.ButtonClicked, tsObj.tsButtonClicked);
             if (this.setting.type === 'stage') {
                 this.resultSpread.bind(spreadNS.Events.EditEnded, tsObj.trEditEnded);
+                $('#tender-select-option').show();
+            } else {
+                $('#tender-select-option').hide();
             }
 
             $('#tender-select-ok').click(() => {
-                const updateData = {lid: tsObj.select.id, type: tsObj.setting.type, tenders: tsObj.trArray};
+                const updateData = { lid: tsObj.select.id, type: tsObj.setting.type, tenders: tsObj.trArray };
+                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);

+ 1 - 0
app/public/js/spreadjs_rela/spreadjs_zh.js

@@ -721,6 +721,7 @@ const SpreadJsObj = {
         const sortData = sheet.zh_dataType === 'tree' ? sheet.zh_tree.nodes : sheet.zh_data;
         this.beginMassOperation(sheet);
         try {
+            sheet.setRowCount(0, spreadNS.SheetArea.viewport);
             sheet.clear(0, 0, sheet.getRowCount(), sheet.getColumnCount(), spreadNS.SheetArea.viewport, spreadNS.StorageType.data);
             sheet.getRange(0, 0, sheet.getRowCount(), sheet.getColumnCount()).backColor(sheet.getDefaultStyle().backColor);
             // 设置总行数

+ 102 - 11
app/public/js/sr_detail.js

@@ -409,7 +409,7 @@ $(document).ready(() => {
                             spSpread.refresh();
                         }
                         const relaXmj = stageIm.getRelaXmj(leafUsedBills);
-                        const im = stageIm.getRelaImData(relaXmj, leafUsedBills, usedPos);
+                        const im = stageIm.getRelaImData4Rela(relaXmj, leafUsedBills, usedPos);
                         SpreadJsObj.locateData(detail.sheet, im);
                         detail.spread.refresh();
                         $('#zhongjian .sjs-bottom').height('400px');
@@ -519,15 +519,14 @@ $(document).ready(() => {
     // 加载计量单元数据 - 暂时统一加载,如有需要,切换成动态加载并缓存
     postData(window.location.pathname + '/load', { filter: 'ledger;pos;detail;change;tag;sumDeal' }, function (result) {
         for (const l of result.ledgerData) {
+            if (!l.is_leaf) continue;
             const sd = result.sumDeal.find(x => { return x.code === l.b_code && x.name === l.name && x.unit === l.unit; });
-            if (sd) {
-                l.unit_price = sd.unit_price;
-                l.deal_tp = ZhCalc.mul(l.unit_price, l.deal_qty, tenderInfo.decimal.tp);
-                l.sgfh_tp = ZhCalc.mul(l.unit_price, l.sgfh_qty, tenderInfo.decimal.tp);
-                l.sjcl_tp = ZhCalc.mul(l.unit_price, l.sjcl_qty, tenderInfo.decimal.tp);
-                l.qtcl_tp = ZhCalc.mul(l.unit_price, l.qtcl_qty, tenderInfo.decimal.tp);
-                l.total_price = ZhCalc.mul(l.unit_price, l.quantity, tenderInfo.decimal.tp);
-            }
+            l.unit_price = sd ? sd.unit_price : 0;
+            l.deal_tp = ZhCalc.mul(l.unit_price, l.deal_qty, tenderInfo.decimal.tp);
+            l.sgfh_tp = ZhCalc.mul(l.unit_price, l.sgfh_qty, tenderInfo.decimal.tp);
+            l.sjcl_tp = ZhCalc.mul(l.unit_price, l.sjcl_qty, tenderInfo.decimal.tp);
+            l.qtcl_tp = ZhCalc.mul(l.unit_price, l.qtcl_qty, tenderInfo.decimal.tp);
+            l.total_price = ZhCalc.mul(l.unit_price, l.quantity, tenderInfo.decimal.tp);
         }
         // 加载树结构
         stageTree.loadDatas(result.ledgerData);
@@ -545,7 +544,7 @@ $(document).ready(() => {
         SpreadJsObj.resetTopAndSelect(spSpread.getActiveSheet());
         // 加载中间计量
         stageIm.init(relaStage, imType, tenderInfo.decimal);
-        stageIm.loadData4Rela(result.ledgerData, result.posData, result.detailData, result.changeData);
+        stageIm.loadData4Rela(result.ledgerData, result.posData, result.detailData, result.changeData, result.detailAtt);
     }, null, true);
     spSpread.bind(spreadNS.Events.SelectionChanged, stagePosSpreadObj.selectionChanged);
 
@@ -754,6 +753,12 @@ $(document).ready(() => {
                         title: relaStage.im_type === imType.tz.value ? '本期计量金额' : '本期计量数量',
                         colSpan: '1', rowSpan: '1', field: 'jl', hAlign: 2, width: 85, formatter: '@'
                     },
+                    {
+                        title: '附件', colSpan: '1', rowSpan: '1', field: 'attachment', hAlign: 0, width: 60, readOnly: true, cellType: 'imageBtn',
+                        normalImg: '#rela-file-icon', hoverImg: '#rela-file-hover', getValue: function (data) {
+                            return data.attachment ? data.attachment.length : 0;
+                        }
+                    },
                 ],
                 headRows: 1,
                 emptyRows: 0,
@@ -764,6 +769,10 @@ $(document).ready(() => {
                 font: '12px 微软雅黑',
                 readOnly: true,
                 selectedBackColor: '#fffacd',
+                imageClick: function (data) {
+                    self.makeAttTable(data);
+                    $('#im-file').modal('show');
+                }
             };
             this.spread = SpreadJsObj.createNewSpread(obj[0]);
             this.sheet = this.spread.getActiveSheet();
@@ -782,8 +791,91 @@ $(document).ready(() => {
 
             this._initImTypeSetRela();
             this._initLocateRela();
+            this._initAttRela();
             this.reBuildImData();
         }
+        makeAttTable (data) {
+            let html = [];
+            if (data.attachment) {
+                for (const att of data.attachment) {
+                    const delHtml = (parseInt(att.uid) === userID && (att.renew || stage.status !== auditConst.status.checked))
+                        ? '<a class="delete-att text-danger ml-1" href="javascript:void(0);" data-imid="'+ data.att_uuid +'" data-attid="'+ att.file_id +'" title="删除"><i class="fa fa-remove "></i></a>'
+                        : '';
+                    const viewHtml = att.viewpath ? `<a class="ml-1" href="${att.viewpath}" target="_blank" title="预览"><i class="fa fa-eye"></i></a>` : '';
+                    const downloadHtml = '<a href="'+ window.location.pathname + '/im-file/download?im_id='+ data.att_uuid +'&file_id='+ att.file_id +'" title="下载"><i class="fa fa-download "></i></a>';
+                    html.push('<tr><td style="width: 200px">' + att.filename + att.fileext + '</td><td>' + att.username + '</td><td>' + att.in_time + '</td>',
+                        '<td>', downloadHtml, viewHtml, delHtml, '</td></tr>');
+                }
+            }
+            $('#im-attList').html(html.join(''));
+        }
+        _initAttRela() {
+            // 上传附件
+            const self = this;
+            $('#upload-im-file').change(function () {
+                const files = this.files;
+                const sels = self.sheet.getSelections();
+                const select = SpreadJsObj.getSelectObject(self.sheet);
+                if (!select) return;
+
+                const formData = new FormData();
+                const baseInfo = {};
+                if (select.att_uuid) {
+                    baseInfo.uuid = select.att_uuid;
+                } else {
+                    baseInfo.im_type = select.im_type;
+                    baseInfo.lid = select.lid;
+                    baseInfo.pid = select.pid;
+                    baseInfo.code = select.code;
+                    baseInfo.name = select.name;
+                    baseInfo.unit = select.unit;
+                    baseInfo.unit_price = select.unit_price;
+                    baseInfo.pos_name = select.pos_name;
+                }
+                formData.append('base', JSON.stringify(baseInfo));
+
+                for (const file of files) {
+                    if (file === undefined) {
+                        toast('未选择上传文件!', 'error');
+                        return false;
+                    }
+                    const filesize = file.size;
+                    if (filesize > 30 * 1024 * 1024) {
+                        toast('存在上传文件大小过大!', 'error');
+                        return false;
+                    }
+                    const fileext = '.' + file.name.toLowerCase().split('.').splice(-1)[0];
+                    if (whiteList.indexOf(fileext) === -1) {
+                        toast('只能上传指定格式的附件!', 'error');
+                        return false;
+                    }
+                    formData.append('size', filesize);
+                    formData.append('file[]', file);
+                }
+                postDataWithFile(window.location.pathname + '/im-file/upload', formData, function (data) {
+                    stageIm.loadUpdateDetailAtt(data);
+                    SpreadJsObj.reLoadRowData(self.sheet, sels[0].row);
+                    self.makeAttTable(select);
+                    $('#upload-im-file').val('');
+                });
+            });
+
+            // 删除附件
+            $('body').on('click', '.delete-att' ,function () {
+                const sels = self.sheet.getSelections();
+                const select = SpreadJsObj.getSelectObject(self.sheet);
+                if (!select) return;
+
+                const uuid = $(this).attr('data-imid');
+                const file_id = $(this).attr('data-attid');
+
+                postData(window.location.pathname + '/im-file/del', { uuid, file_id }, function (result) {
+                    stageIm.loadUpdateDetailAtt(result);
+                    SpreadJsObj.reLoadRowData(self.sheet, sels[0].row);
+                    self.makeAttTable(select);
+                });
+            });
+        }
         _initImTypeSetRela() {
             const self = this;
             const gatherConfirmPopover = {
@@ -953,7 +1045,6 @@ $(document).ready(() => {
                 searchLedger = $.billsSearch({
                     selector: '#search',
                     searchSpread: slSpread,
-                    searchRangeStr: '项目节编号/名称',
                     searchOver: true,
                     searchEmpty: true,
                     resultSpreadSetting: {

+ 97 - 8
app/public/js/stage.js

@@ -1624,7 +1624,7 @@ $(document).ready(() => {
             const sheet = slSpread.getActiveSheet();
             const node = SpreadJsObj.getSelectObject(sheet);
             if (node) {
-                spSpread.getActiveSheet().zh_setting.readOnly = node.lock;
+                spSpread.getActiveSheet().zh_setting.readOnly = node.lock || (node.children && node.children.length > 0);
                 const posData = stagePos.ledgerPos[itemsPre + node.id] || [];
                 SpreadJsObj.loadSheetData(spSpread.getActiveSheet(), 'data', posData);
                 getNodeList(node.id);
@@ -1633,6 +1633,7 @@ $(document).ready(() => {
                 //     $('#showAttachment').hide();
                 // }
             } else {
+                spSpread.getActiveSheet().zh_setting.readOnly = true;
                 SpreadJsObj.loadSheetData(spSpread.getActiveSheet(), 'data', []);
             }
             stagePosSpreadObj.loadExprToInput(spSpread.getActiveSheet());
@@ -1661,7 +1662,7 @@ $(document).ready(() => {
                     toastr.warning('数据错误, 请刷新页面后再试');
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
                     return;
-                } else if (newText !== '' && node.children && node.children > 0) {
+                } else if (newText !== '' && node.children && node.children.length > 0) {
                     toastr.error('父节点不可插入计量单元');
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
                     return;
@@ -1754,7 +1755,7 @@ $(document).ready(() => {
             const col = info.sheet.zh_setting.cols[sel[0].col];
             const node = SpreadJsObj.getSelectObject(info.sheet);
             const exprInfo = getExprInfo(col.field);
-            if (exprInfo) {
+            if (exprInfo && node) {
                 if (node[exprInfo.expr] && node[exprInfo.expr] !== '') {
                     info.sheet.getCell(info.row, info.col).text(node[exprInfo.expr]);
                 }
@@ -1798,6 +1799,7 @@ $(document).ready(() => {
                 const data = { updateType: '', updateData: [], };
                 const sortData = info.sheet.zh_data;
                 const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
+                if (node.children && node.children.length > 0) return;
                 if (sortData && (info.cellRange.row >= sortData.length)) {
                     data.updateType = 'add';
                     if (info.cellRange.col !== 0) {
@@ -2211,6 +2213,82 @@ $(document).ready(() => {
                     $('#calc-by-ratio').modal('show');
                 }
             },
+            'remainCur': {
+                name: '填剩余量(选中行)',
+                disabled: function (key, opt) {
+                    const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
+                    const curPos = SpreadJsObj.getSelectObject(spSpread.getActiveSheet());
+                    return readOnly || node.lock || !curPos;
+                },
+                callback: function (key, opt) {
+                    const sheet = spSpread.getActiveSheet();
+                    const sels = sheet.getSelections();
+                    if (!sels || !sels[0]) return;
+
+                    const data = { updateType: 'update', updateData: [] };
+                    for (let iRow = 0; iRow < sels[0].rowCount; iRow++) {
+                        const curRow = iRow + sels[0].row;
+                        const curPos = SpreadJsObj.getRowObject(sheet, curRow);
+                        if (!curPos) continue;
+
+                        const qty = ZhCalc.sub(curPos.quantity, curPos.pre_contract_qty);
+                        const differ = ZhCalc.sub(curPos.quantity, curPos.end_contract_qty);
+                        if (differ) data.updateData.push({ pid: curPos.id, lid: curPos.lid, contract_qty: qty });
+                    }
+                    if (data.updateData.length > 0) {
+                        postData(window.location.pathname + '/update', {pos: data}, function (result) {
+                            if (result.pos) {
+                                stagePos.updateDatas(result.pos.pos);
+                                stagePos.loadCurStageData(result.pos.curStageData);
+                            }
+                            const nodes = stageTree.loadPostStageData(result.ledger);
+                            stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
+                            stagePosSpreadObj.loadCurPosData();
+                            if (detail) {
+                                detail.loadStagePosUpdateData(result, nodes);
+                            } else {
+                                stageIm.loadUpdatePosData(result, nodes);
+                            }
+                        });
+                    }
+                },
+            },
+            'remainAll': {
+                name: '填剩余量(全部行)',
+                disabled: function (key, opt) {
+                    const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
+                    const curPos = SpreadJsObj.getSelectObject(spSpread.getActiveSheet());
+                    return readOnly || node.lock || !curPos;
+                },
+                callback: function (key, opt) {
+                    const sheet = spSpread.getActiveSheet();
+                    if (!sheet.zh_data || sheet.zh_data.length === 0) return;
+
+                    const data = { updateType: 'update', updateData: [] };
+                    for (const curPos of sheet.zh_data) {
+                        const qty = ZhCalc.sub(curPos.quantity, curPos.pre_contract_qty);
+                        const differ = ZhCalc.sub(curPos.quantity, curPos.end_contract_qty);
+                        if (differ) data.updateData.push({ pid: curPos.id, lid: curPos.lid, contract_qty: qty });
+                    }
+                    if (data.updateData.length > 0) {
+                        postData(window.location.pathname + '/update', {pos: data}, function (result) {
+                            if (result.pos) {
+                                stagePos.updateDatas(result.pos.pos);
+                                stagePos.loadCurStageData(result.pos.curStageData);
+                            }
+                            const nodes = stageTree.loadPostStageData(result.ledger);
+                            stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
+                            stagePosSpreadObj.loadCurPosData();
+                            if (detail) {
+                                detail.loadStagePosUpdateData(result, nodes);
+                            } else {
+                                stageIm.loadUpdatePosData(result, nodes);
+                            }
+                        });
+                    }
+
+                },
+            },
             'shoufangdan': {
                 name: '生成收方单',
                 visible: function(key, opt) {
@@ -2703,15 +2781,16 @@ $(document).ready(() => {
             if (data.attachment) {
                 for (const att of data.attachment) {
                     const delHtml = (parseInt(att.uid) === userID && (att.renew || stage.status !== auditConst.status.checked))
-                        ? '<a class="delete-att text-danger" href="javascript:void(0);" data-imid="'+ data.att_uuid +'" data-attid="'+ att.file_id +'" title="删除"><i class="fa fa-remove "></i></a>'
+                        ? '<a class="delete-att text-danger ml-1" href="javascript:void(0);" data-imid="'+ data.att_uuid +'" data-attid="'+ att.file_id +'" title="删除"><i class="fa fa-remove "></i></a>'
                         : '';
+                    const viewHtml = att.viewpath ? `<a class="ml-1" href="${att.viewpath}" target="_blank" title="预览"><i class="fa fa-eye"></i></a>` : '';
+                    const downloadHtml = '<a href="'+ window.location.pathname + '/im-file/download?im_id='+ data.att_uuid +'&file_id='+ att.file_id +'" title="下载"><i class="fa fa-download "></i></a>';
                     html.push('<tr><td style="width: 200px">' + att.filename + att.fileext + '</td><td>' + att.username + '</td><td>' + att.in_time + '</td>',
-                        '<td><a href="'+ window.location.pathname + '/im-file/download?im_id='+ data.att_uuid +'&file_id='+ att.file_id +'" title="下载"><i class="fa fa-download "></i></a> ',
-                        delHtml, '</td></tr>');
+                        '<td>', downloadHtml, viewHtml, delHtml, '</td></tr>');
                 }
             }
             $('#im-attList').html(html.join(''));
-        };
+        }
         _initAttRela() {
             // 上传附件
             const self = this;
@@ -2778,6 +2857,17 @@ $(document).ready(() => {
                     self.makeAttTable(select);
                 });
             });
+            // 预览附件
+            $('body').on('click', '.preview-att', function () {
+                const uuid = $(this).attr('data-imid');
+                const file_id = $(this).attr('data-attid');
+
+                postData(window.location.pathname + `/im-file/preview?uuid=${uuid}&file_id=${file_id}`, {}, function (result) {
+                    const { filepath } = result;
+                    $('#load-file').attr('href', filepath);
+                    $('#load-file')[0].click();
+                });
+            })
         }
         _initImTypeSetRela() {
             const self = this;
@@ -3748,7 +3838,6 @@ $(document).ready(() => {
                 searchLedger = $.billsSearch({
                     selector: '#search',
                     searchSpread: slSpread,
-                    searchRangeStr: '项目节编号/名称',
                     searchOver: true,
                     searchEmpty: true,
                     resultSpreadSetting: {

+ 27 - 4
app/public/js/stage_im.js

@@ -14,6 +14,7 @@ const stageIm = (function () {
     const splitChar = '-';
     const mergeChar = ';';
     let stage, imType, decimal, details, changes, detailsAtt, ImData, pre, orgImData;
+    let up_field = 'unit_price';
     const gsTreeSetting = {
         id: 'ledger_id',
         pid: 'ledger_pid',
@@ -67,6 +68,7 @@ const stageIm = (function () {
     }
 
     function loadData (ledger, pos, stageDetail, stageChange, stageDetailAtt) {
+        up_field = 'unit_price';
         gsTree.loadDatas(ledger);
         treeCalc.calculateAll(gsTree);
 
@@ -82,6 +84,7 @@ const stageIm = (function () {
     }
 
     function loadData4Rela(ledger, pos, stageDetail, stageChange, stageDetailAtt) {
+        up_field = 'org_unit_price';
         gsTree.loadDatas(ledger);
         treeCalc.calculateAll(gsTree);
 
@@ -807,12 +810,12 @@ const stageIm = (function () {
             if (!checkUsed(p)) { continue; }
             let im = nodeImData.find(function (d) {
                 return d.lid === node.id &&
-                    d.code === p.b_code && p.name === d.name && p.unit === d.unit && checkZero(ZhCalc.sub(p.unit_price, d.unit_price));
+                    d.code === p.b_code && p.name === d.name && p.unit === d.unit && checkZero(ZhCalc.sub(p[up_field], d.unit_price));
             });
             if (!im) {
                 const peg = getPegNode(node);
                 im = {
-                    lid: node.id, pid: '', code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price,
+                    lid: node.id, pid: '', code: p.b_code, name: p.name, unit: p.unit, unit_price: p[up_field],
                     jl: 0, contract_jl: 0, qc_jl: 0,
                     peg: peg ? getPegStr(peg.name) : '',
                     position: '',
@@ -857,7 +860,7 @@ const stageIm = (function () {
                 for (const pp of pPos) {
                     if (checkZero(pp.contract_qty) && checkZero(pp.qc_qty)) { continue }
                     const im = {
-                        lid: node.id, code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price, pid: pp.id,
+                        lid: node.id, code: p.b_code, name: p.name, unit: p.unit, unit_price: p[up_field], pid: pp.id,
                         jl: pp.gather_qty, contract_jl: pp.contract_qty, qc_jl: pp.qc_qty,
                         //im_code: getNewImCode(),
                         bw: bw, jldy: pp.name,
@@ -885,7 +888,7 @@ const stageIm = (function () {
                 if (checkZero(p.gather_qty) && checkZero(p.gather_tp)) { continue }
 
                 const im = {
-                    lid: node.id, code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price, pid: '',
+                    lid: node.id, code: p.b_code, name: p.name, unit: p.unit, unit_price: p[up_field], pid: '',
                     jl: p.gather_qty, contract_jl: p.contract_qty, qc_jl: p.qc_qty,
                     im_code: getNewImCode(),
                     bw: bw,
@@ -1170,6 +1173,25 @@ const stageIm = (function () {
             }
         }
     }
+    function getRelaImData4Rela(relaXmj, bills, pos) {
+        if (stage.im_type === imType.tz.value) {
+            return _.find(ImData, {lid: relaXmj.id});
+        } else if (stage.im_type === imType.zl.value) {
+            return _.find(ImData, {lid: relaXmj.id, code: bills.b_code, name: bills.name, unit: bills.unit, unit_price: bills.org_unit_price});
+        } else if (stage.im_type === imType.bw.value) {
+            if (pos) {
+                return _.find(ImData, {lid: relaXmj.id, code: bills.b_code, name: bills.name, unit: bills.unit, unit_price: bills.org_unit_price, pid: pos.id});
+            } else {
+                return _.find(ImData, {lid: relaXmj.id, code: bills.b_code, name: bills.name, unit: bills.unit, unit_price: bills.org_unit_price, pid: ''});
+            }
+        } else if (stage.im_type === imType.bb.value) {
+            if (pos) {
+                return _.find(ImData, {lid: relaXmj.id, pos_name: pos.name});
+            } else {
+                return _.find(ImData, {lid: relaXmj.id, pos_name: ''});
+            }
+        }
+    }
 
     return {
         init,
@@ -1191,6 +1213,7 @@ const stageIm = (function () {
         getFirstUsed: getFirstUsed,
         getRelaXmj: getRelaXmj,
         getRelaImData: getRelaImData,
+        getRelaImData4Rela: getRelaImData4Rela,
         resetFields,
     }
 })();

+ 11 - 26
app/public/js/stage_pay.js

@@ -33,12 +33,12 @@ function makeAttTable(payNode) {
     const id = payNode.id, attachment = payNode.attachment;
     let html = '';
     if (attachment !== null) {
-        for (const [index, att] of attachment.entries()) {
-            const delhtml = uploadPermission && (parseInt(att.uid) === userID || payNode.uid === userID || (payNode.uid === -1 && userID === stage.user_id))
-                ? '<a class="delete-att text-danger" href="javascript:void(0);" data-payid="'+ id +'" data-attindex="'+ index +'" title="删除"><i class="fa fa-remove "></i></a>'
+        for (const att of attachment) {
+            const delhtml = uploadPermission && parseInt(att.uid) === userID && (att.renew || stage.status !== auditConst.status.checked)
+                ? '<a class="delete-att text-danger" href="javascript:void(0);" data-fileid="'+ att.id +'" title="删除"><i class="fa fa-remove "></i></a>'
                 : '';
             html += '<tr><td style="width: 200px">' + att.filename + att.fileext + '</td><td>' + att.username + '</td><td>' + att.in_time + '</td>' +
-                '<td><a href="/tender/'+ tender.id + '/measure/stage/' + tender.ledger_times +'/pay/download/file/'+ id +'/'+ index +'" title="下载"><i class="fa fa-download "></i></a> ' +
+                '<td><a href="/tender/'+ tender.id + '/measure/stage/' + tender.ledger_times +'/pay/download/file/'+ att.id +'" title="下载"><i class="fa fa-download "></i></a> ' +
                 delhtml +
                 '</td></tr>';
         }
@@ -1194,8 +1194,7 @@ $(document).ready(() => {
         const files = this.files;
         const select = SpreadJsObj.getSelectObject(paySpread.getActiveSheet());
         const formData = new FormData();
-        // const sizes = [];
-        formData.append('pay_id', select.id);
+        formData.append('pay_id', select.pid);
         for (const file of files) {
             if (file === undefined) {
                 toast('未选择上传文件!', 'error');
@@ -1211,21 +1210,13 @@ $(document).ready(() => {
                 toast('只能上传指定格式的附件!', 'error');
                 return false;
             }
-            // sizes.push(filesize);
             formData.append('size', filesize);
             formData.append('file[]', file);
         }
-        // formData.append('size', sizes.join(','));
         postDataWithFile('/tender/' + tender.id + '/measure/stage/' + stage.order + '/pay/upload/file', formData, function (data) {
-            if (select.attachment === null) {
-                select.attachment = data;
-            } else {
-                select.attachment = data.concat(select.attachment);
-            }
+            select.attachment = data;
             makeAttTable(select);
-            const index = dealPay.indexOf(select);
-            dealPay.splice(index, 1, select);
-            SpreadJsObj.reLoadSheetData(paySpread.getActiveSheet());
+            SpreadJsObj.reLoadRowData(paySpread.getActiveSheet(), dealPay.indexOf(select));
         }, function () {
             toast('附件上传失败', 'error');
         });
@@ -1234,19 +1225,13 @@ $(document).ready(() => {
 
     // 删除附件
     $('body').on('click', '.delete-att' ,function () {
-        const id = $(this).attr('data-payid');
-        const index = $(this).attr('data-attindex');
-        const data = {
-            id,
-            index,
-        };
+        const id = $(this).attr('data-fileid');
+        const data = { id };
         const select = SpreadJsObj.getSelectObject(paySpread.getActiveSheet());
         postData('/tender/' + tender.id + '/measure/stage/' + stage.order + '/pay/delete/file', data, function (result) {
-            select.attachment.splice(index, 1);
+            select.attachment = result;
             makeAttTable(select);
-            const pay_index = dealPay.indexOf(select);
-            dealPay.splice(pay_index, 1, select);
-            SpreadJsObj.reLoadSheetData(paySpread.getActiveSheet());
+            SpreadJsObj.reLoadRowData(paySpread.getActiveSheet(), dealPay.indexOf(select));
         });
     })
 });

+ 7 - 1
app/router.js

@@ -288,7 +288,7 @@ module.exports = app => {
     app.post('/tender/:id/measure/stage/:order/pay/detail', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.chapterDetail');
     app.post('/tender/:id/measure/stage/:order/pay/save', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.savePayData');
     app.post('/tender/:id/measure/stage/:order/pay/upload/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.payUploadFile');
-    app.get('/tender/:id/measure/stage/:order/pay/download/file/:pid/:index', sessionAuth, 'stageController.payDownloadFile');
+    app.get('/tender/:id/measure/stage/:order/pay/download/file/:fid', sessionAuth, 'stageController.payDownloadFile');
     app.post('/tender/:id/measure/stage/:order/pay/delete/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.payDeleteFile');
 
     // 变更概况
@@ -346,6 +346,9 @@ module.exports = app => {
     app.post('/tender/:id/measure/stage/:order/rela/update', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageRelaController.update');
     app.get('/tender/:id/measure/stage/:order/rela/detail/:trid', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageRelaController.detail');
     app.post('/tender/:id/measure/stage/:order/rela/detail/:trid/load', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageRelaController.load');
+    app.post('/tender/:id/measure/stage/:order/rela/detail/:trid/im-file/del', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageRelaController.deleteImFile');
+    app.post('/tender/:id/measure/stage/:order/rela/detail/:trid/im-file/upload', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageRelaController.uploadImFile');
+    app.get('/tender/:id/measure/stage/:order/rela/detail/:trid/im-file/download', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageRelaController.downloadImFile');
 
     // 期审批管理
     app.get('/tender/:id/measure/stage/:order/manager', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.manager');
@@ -431,6 +434,9 @@ module.exports = app => {
     app.post('/tender/:id/change/:cid/information/copy', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.copyChange');
     app.post('/tender/:id/change/:cid/information/audit/add', sessionAuth, tenderCheck, uncheckTenderCheck, changeCheck, 'changeController.addAudit');
     app.post('/tender/:id/change/:cid/information/audit/delete', sessionAuth, tenderCheck, uncheckTenderCheck, changeCheck, 'changeController.deleteAudit');
+    // 变更新增部位页
+    app.get('/tender/:id/change/:cid/information/revise', sessionAuth, tenderCheck, uncheckTenderCheck, changeCheck, 'changeController.reviseInfo');
+    app.post('/tender/:id/change/:cid/information/revise/update', sessionAuth, tenderCheck, uncheckTenderCheck, changeCheck, 'changeController.updateRevise');
     // 材料调差
     app.get('/tender/:id/measure/material', sessionAuth, tenderCheck, uncheckTenderCheck, 'materialController.index');
     app.post('/tender/:id/measure/material/add', sessionAuth, tenderCheck, uncheckTenderCheck, 'materialController.add');

+ 2 - 0
app/service/change.js

@@ -748,6 +748,8 @@ module.exports = app => {
 
                     await this.ctx.service.tenderTag.saveTenderTag(changeData.tid, { bgl_time: new Date() }, this.transaction);
 
+                    await this.ctx.service.changeAuditList.updateToLedger(this.transaction, changeData.tid, changeData.cid);
+
                     // 添加短信通知-审批通过提醒功能
                     // const mobile_array = [];
                     const auditList = await this.ctx.service.changeAudit.getListGroupByTimes(changeData.cid, changeData.times);

+ 482 - 0
app/service/change_audit_list.js

@@ -374,6 +374,488 @@ module.exports = app => {
             const result = await this.db.query(sql, param);
             return result;
         }
+
+
+        /**
+         * 删除变更清单(form 变更新增部位页)
+         * Tony Kang
+         * @param {String} transaction - 队列
+         * @param {String} tid - 标段id
+         * @param {Array} ids - id列表
+         * @param {String} column - id所属字段
+         * @param {String} mx_id - mx_id为空列删除
+         * @return {void}
+         */
+        async deleteDataByRevise(transaction, tid, ids, column = 'gcl_id', mx_id = 'hello') {
+            if (ids.length > 0) {
+                const addSql = mx_id === '' ? ' AND (`mx_id` is NULL OR `mx_id` = "")' : '';
+                const sql = 'SELECT `cid` FROM ?? WHERE `tid` = ? AND ' + column + ' in (' + this.ctx.helper.getInArrStrSqlFilter(ids) + ')' + addSql + ' GROUP BY `cid`';
+                const params = [this.tableName, tid];
+                const changes = await transaction.query(sql, params);
+                if (changes.length > 0) {
+                    const delData = {
+                        tid,
+                    };
+                    delData[column] = ids;
+                    await transaction.delete(this.tableName, delData);
+                    for (const c of changes) {
+                        // 重算选了此清单的变更令已变更金额
+                        await this.reCalcTp(transaction, c.cid);
+                    }
+                }
+            }
+        }
+
+        /**
+         * 修改变更清单(form 变更新增部位页台账子节点清单编号编辑)
+         * Tony Kang
+         * @param {String} transaction - 队列
+         * @param {String} tid - 标段id
+         * @param {Array} datas - 更新列表
+         * @param {String} column - id所属字段
+         * @return {void}
+         */
+        async updateDataByReviseLedger(transaction, tid, datas, column = 'gcl_id') {
+            if (datas.length > 0) {
+                const ids = this._.map(datas, 'id');
+                const sql = 'SELECT ' + column + ' FROM ?? WHERE `tid` = ? AND ' + column + ' in (' + this.ctx.helper.getInArrStrSqlFilter(ids) + ') GROUP BY ' + column;
+                const params = [this.tableName, tid];
+                const changeAuditLists = await transaction.query(sql, params);
+                if (changeAuditLists.length > 0) {
+                    const updateArr = [];
+                    const cidList = [];
+                    for (const ca of changeAuditLists) {
+                        const d = this._.find(datas, { id: ca[column] });
+                        if (d.id) {
+                            const changePosNum = await transaction.count(this.ctx.service.changePos.tableName, { lid: d.id });
+                            const updateCol = {};
+                            if (column === 'gcl_id' && d.b_code !== undefined) updateCol.code = d.b_code;
+                            if (column === 'gcl_id' && d.sgfh_qty !== undefined && changePosNum === 0) updateCol.oamount = d.sgfh_qty ? d.sgfh_qty : 0;
+                            if (column === 'gcl_id' && d.unit_price !== undefined) updateCol.unit_price = d.unit_price ? d.unit_price : 0;
+                            if (column === 'gcl_id' && d.unit !== undefined) updateCol.unit = d.unit;
+                            if (column === 'gcl_id' && d.name !== undefined) updateCol.name = d.name;
+                            if (d.code !== undefined && d.b_code === null) {
+                                // 清单升级成了项目节,故删除变更已有的此清单,并找出需要重新计算的变更令
+                                const sql = 'SELECT `cid` FROM ?? WHERE `tid` = ? AND ' + column + ' = ? GROUP BY `cid`';
+                                const params = [this.tableName, tid, d.id];
+                                const changes = await transaction.query(sql, params);
+                                for (const c of changes) {
+                                    if (this._.indexOf(cidList, c.cid) === -1) {
+                                        cidList.push(c.cid);
+                                    }
+                                }
+                                const delData = {
+                                    tid,
+                                };
+                                delData[column] = d.id;
+                                await transaction.delete(this.tableName, delData);
+                            } else {
+                                const options = {
+                                    row: {},
+                                    where: {},
+                                };
+                                options.row = updateCol;
+                                options.where[column] = d.id;
+                                if (!this._.isEmpty(options.row)) updateArr.push(options);
+                                if (updateCol.unit !== undefined || updateCol.unit_price !== undefined) {
+                                    const sql = 'SELECT `cid` FROM ?? WHERE `tid` = ? AND ' + column + ' = ? GROUP BY `cid`';
+                                    const params = [this.tableName, tid, d.id];
+                                    const changes = await transaction.query(sql, params);
+                                    for (const c of changes) {
+                                        if (this._.indexOf(cidList, c.cid) === -1) {
+                                            cidList.push(c.cid);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    if (updateArr.length > 0) await transaction.updateRows(this.tableName, updateArr);
+                    if (cidList.length > 0) {
+                        for (const c of cidList) {
+                            await this.reCalcTp(transaction, c);
+                        }
+                    }
+                }
+                // 针对项目节更新可能对清单影响判断,修正变更清单项目节编号,细目,单位工程,分部分项工程数据
+                for (const data of datas) {
+                    const select = await transaction.get(this.ctx.service.changeLedger.tableName, { id: data.id });
+                    if (select && select.is_leaf === 0) {
+                        const lists = await this.ctx.service.changeLedger.getDataByFullPath(this.ctx.service.changeLedger.tableName, tid, select.full_path + '%', transaction);
+                        const childLists = this._.filter(lists, { level: select.level + 1 }); // 细目or项目节编号更新
+                        if (childLists.length > 0) {
+                            const d = { xmj_code: '', xmj_jldy: '' };
+                            if (select.code !== null) {
+                                d.xmj_code = select.code;
+                                d.xmj_jldy = select.name;
+                            } else {
+                                // 再找出上一个项目节节点并更新
+                                this.newBills = false;
+                                const parents = await this.ctx.service.changeLedger.getDataByKid(tid, select.ledger_pid);
+                                d.xmj_code = parents.code;
+                                d.xmj_jldy = parents.name;
+                            }
+                            for (const cl of childLists) {
+                                await transaction.update(this.tableName, { xmj_code: d.xmj_code, xmj_jldy: d.xmj_jldy }, { where: { tid, gcl_id: cl.id } });
+                            }
+                        }
+                        if (select.code !== null && data.name !== undefined) { // 名称修改则可能影响几个数据
+                            const secondChildLists = this._.filter(lists, { level: select.level + 2 }); // 分项工程更新
+                            const thirdChildLists = this._.filter(lists, { level: select.level + 3 }); // 分部工程更新
+                            const fourthChildLists = this._.filter(lists, { level: select.level + 4 }); // 单位工程更新
+                            if (secondChildLists.length > 0) {
+                                for (const sl of secondChildLists) {
+                                    await transaction.update(this.tableName, { xmj_fxgc: select.name }, { where: { tid, gcl_id: sl.id } });
+                                }
+                            }
+                            if (thirdChildLists.length > 0) {
+                                for (const tl of thirdChildLists) {
+                                    await transaction.update(this.tableName, { xmj_fbgc: select.name }, { where: { tid, gcl_id: tl.id } });
+                                }
+                            }
+                            if (fourthChildLists.length > 0 && select.level === 2) {
+                                for (const fl of fourthChildLists) {
+                                    await transaction.update(this.tableName, { xmj_dwgc: select.name }, { where: { tid, gcl_id: fl.id } });
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * 修改变更清单(form 变更新增部位页台账节点清单编号升降级)
+         * Tony Kang
+         * @param {String} transaction - 队列
+         * @param {String} tid - 标段id
+         * @param {Array} datas - 更新列表
+         * @param {String} column - id所属字段
+         * @return {void}
+         */
+        async updateDataByReviseLedgerUpDownLevel(transaction, tid, datas, column = 'gcl_id') {
+            if (datas.length > 0) {
+                console.log(datas);
+                // const ids = this._.map(datas, 'id');
+                // const sql = 'SELECT ' + column + ' FROM ?? WHERE `tid` = ? AND ' + column + ' in (' + this.ctx.helper.getInArrStrSqlFilter(ids) + ') GROUP BY ' + column;
+                // const params = [this.tableName, tid];
+                // const changeAuditLists = await transaction.query(sql, params);
+                // if (changeAuditLists.length > 0) {
+                //     const updateArr = [];
+                //     const cidList = [];
+                //     for (const ca of changeAuditLists) {
+                //         const d = this._.find(datas, { id: ca[column] });
+                //         console.log(d);
+                //         if (d.id) {
+                //             const changePosNum = await transaction.count(this.ctx.service.changePos.tableName, { lid: d.id });
+                //             const updateCol = {};
+                //             if (column === 'gcl_id' && d.b_code !== undefined) updateCol.code = d.b_code;
+                //             if (column === 'gcl_id' && d.sgfh_qty !== undefined && changePosNum === 0) updateCol.oamount = d.sgfh_qty ? d.sgfh_qty : 0;
+                //             if (column === 'gcl_id' && d.unit_price !== undefined) updateCol.unit_price = d.unit_price ? d.unit_price : 0;
+                //             if (column === 'gcl_id' && d.unit !== undefined) updateCol.unit = d.unit;
+                //             if (column === 'gcl_id' && d.name !== undefined) updateCol.name = d.name;
+                //             if (d.code !== undefined && d.b_code === null) {
+                //                 // 清单升级成了项目节,故删除变更已有的此清单,并找出需要重新计算的变更令
+                //                 const sql = 'SELECT `cid` FROM ?? WHERE `tid` = ? AND ' + column + ' = ? GROUP BY `cid`';
+                //                 const params = [this.tableName, tid, d.id];
+                //                 const changes = await transaction.query(sql, params);
+                //                 for (const c of changes) {
+                //                     if (this._.indexOf(cidList, c.cid) === -1) {
+                //                         cidList.push(c.cid);
+                //                     }
+                //                 }
+                //                 const delData = {
+                //                     tid,
+                //                 };
+                //                 delData[column] = d.id;
+                //                 console.log(delData);
+                //                 await transaction.delete(this.tableName, delData);
+                //             } else {
+                //                 const options = {
+                //                     row: {},
+                //                     where: {},
+                //                 };
+                //                 options.row = updateCol;
+                //                 options.where[column] = d.id;
+                //                 if (!this._.isEmpty(options.row)) updateArr.push(options);
+                //                 if (updateCol.unit !== undefined || updateCol.unit_price !== undefined) {
+                //                     const sql = 'SELECT `cid` FROM ?? WHERE `tid` = ? AND ' + column + ' = ? GROUP BY `cid`';
+                //                     const params = [this.tableName, tid, d.id];
+                //                     const changes = await transaction.query(sql, params);
+                //                     for (const c of changes) {
+                //                         if (this._.indexOf(cidList, c.cid) === -1) {
+                //                             cidList.push(c.cid);
+                //                         }
+                //                     }
+                //                 }
+                //             }
+                //         }
+                //     }
+                //     console.log(updateArr, cidList);
+                //     if (updateArr.length > 0) await transaction.updateRows(this.tableName, updateArr);
+                //     if (cidList.length > 0) {
+                //         for (const c of cidList) {
+                //             await this.reCalcTp(transaction, c);
+                //         }
+                //     }
+                // }
+                // 针对项目节更新可能对清单影响判断,修正变更清单项目节编号,细目,单位工程,分部分项工程数据
+                // for (const data of datas) {
+                //     const select = await transaction.get(this.ctx.service.changeLedger.tableName, { id: data.id });
+                //     console.log(select);
+                //     if (select && select.is_leaf === 0) {
+                //         const lists = await this.ctx.service.changeLedger.getDataByFullPath(this.ctx.service.changeLedger.tableName, tid, select.full_path + '%', transaction);
+                //         const childLists = this._.filter(lists, { level: select.level + 1 }); // 细目or项目节编号更新
+                //         if (childLists.length > 0) {
+                //             const d = { xmj_code: '', xmj_jldy: '' };
+                //             if (select.code !== null) {
+                //                 d.xmj_code = select.code;
+                //                 d.xmj_jldy = select.name;
+                //             } else {
+                //                 // 再找出上一个项目节节点并更新
+                //                 this.newBills = false;
+                //                 const parents = await this.ctx.service.changeLedger.getDataByKid(tid, select.ledger_pid);
+                //                 console.log('hello :', parents);
+                //                 d.xmj_code = parents.code;
+                //                 d.xmj_jldy = parents.name;
+                //             }
+                //             for (const cl of childLists) {
+                //                 await transaction.update(this.tableName, { xmj_code: d.xmj_code, xmj_jldy: d.xmj_jldy }, { where: { tid, gcl_id: cl.id } });
+                //             }
+                //         }
+                //         if (select.code !== null && data.name !== undefined) { // 名称修改则可能影响几个数据
+                //             const secondChildLists = this._.filter(lists, { level: select.level + 2 }); // 分项工程更新
+                //             const thirdChildLists = this._.filter(lists, { level: select.level + 3 }); // 分部工程更新
+                //             const fourthChildLists = this._.filter(lists, { level: select.level + 4 }); // 单位工程更新
+                //             if (secondChildLists.length > 0) {
+                //                 for (const sl of secondChildLists) {
+                //                     await transaction.update(this.tableName, { xmj_fxgc: select.name }, { where: { tid, gcl_id: sl.id } });
+                //                 }
+                //             }
+                //             if (thirdChildLists.length > 0) {
+                //                 for (const tl of thirdChildLists) {
+                //                     await transaction.update(this.tableName, { xmj_fbgc: select.name }, { where: { tid, gcl_id: tl.id } });
+                //                 }
+                //             }
+                //             if (fourthChildLists.length > 0) {
+                //                 for (const fl of fourthChildLists) {
+                //                     await transaction.update(this.tableName, { xmj_dwgc: select.name }, { where: { tid, gcl_id: fl.id } });
+                //                 }
+                //             }
+                //         }
+                //     }
+                // }
+            }
+        }
+
+        /**
+         * 修改变更清单(form 变更新增部位页计量单元编辑)
+         * Tony Kang
+         * @param {String} transaction - 队列
+         * @param {String} tid - 标段id
+         * @param {Array} datas - 更新列表
+         * @param {String} column - id所属字段
+         * @return {void}
+         */
+        async updateDataByRevisePos(transaction, tid, datas, column = 'mx_id') {
+            if (datas.length > 0) {
+                const ids = this._.map(datas, 'id');
+                const sql = 'SELECT ' + column + ' FROM ?? WHERE `tid` = ? AND ' + column + ' in (' + this.ctx.helper.getInArrStrSqlFilter(ids) + ') GROUP BY ' + column;
+                const params = [this.tableName, tid];
+                const changeAuditLists = await transaction.query(sql, params);
+                if (changeAuditLists.length > 0) {
+                    const updateArr = [];
+                    for (const ca of changeAuditLists) {
+                        const d = this._.find(datas, { id: ca[column] });
+                        if (d.id) {
+                            const updateCol = {};
+                            if (column === 'mx_id' && d.name !== undefined) updateCol.bwmx = d.name;
+                            if (column === 'mx_id' && d.sgfh_qty !== undefined) updateCol.oamount = d.sgfh_qty ? d.sgfh_qty : 0;
+                            if (column === 'mx_id' && d.sgfh_qty === undefined && d.sgfh_expr === '') updateCol.oamount = 0;
+                            const options = {
+                                row: {},
+                                where: {},
+                            };
+                            options.row = updateCol;
+                            options.where[column] = d.id;
+                            // if (!this._.isEmpty(updateCol)) await transaction.update(this.tableName, updateCol, options);
+                            if (!this._.isEmpty(options.row)) updateArr.push(options);
+                        }
+                    }
+                    console.log(updateArr);
+                    if (updateArr.length > 0) await transaction.updateRows(this.tableName, updateArr);
+                }
+            }
+        }
+
+        /**
+         * 重算变更令总金额(变更新增部位设置时使用)
+         * @param {String} transaction - 队列
+         * @param {String} cid - 变更令id
+         */
+        async reCalcTp(transaction, cid) {
+            const change = await transaction.get(this.ctx.service.change.tableName, { cid });
+            let count = '';
+            if (change.status === audit.flow.status.uncheck || change.status === audit.flow.status.back || change.status === audit.flow.status.revise) {
+                count = '`camount`';
+            } else if (change.status === audit.flow.status.checking || change.status === audit.flow.status.backnew) {
+                count = '`spamount`';
+            }
+            if (count) {
+                const sql = 'SELECT `unit_price`, ' + count + ' as `count` FROM ?? WHERE `cid` = ?';
+                const params = [this.tableName, change.cid];
+                const caLists = await transaction.query(sql, params);
+                let tp = 0;
+                const tpUnit = change.tp_decimal ? change.tp_decimal : this.ctx.tender.info.decimal.tp;
+                for (const ca of caLists) {
+                    const catp = this.ctx.helper.round(this.ctx.helper.mul(ca.unit_price, ca.count), tpUnit);
+                    tp = this.ctx.helper.add(tp, catp);
+                }
+                console.log(tp);
+                if (tp !== change.total_price) {
+                    const options = {
+                        where: {
+                            cid: change.cid,
+                        },
+                    };
+                    const change_update = {
+                        total_price: tp,
+                    };
+                    await transaction.update(this.ctx.service.change.tableName, change_update, options);
+                }
+            }
+        }
+
+        async updateToLedger(transaction, tid, cid) {
+            // 找出本条变更属于新增部位的数据
+            const sql = 'SELECT a.* FROM ?? a LEFT JOIN ?? b ON a.id = b.gcl_id WHERE b.tid = ? AND b.cid = ? GROUP BY a.id';
+            const sqlParam = [this.ctx.service.changeLedger.tableName, this.tableName, tid, cid];
+            const result = await transaction.query(sql, sqlParam);
+            const sql2 = 'SELECT a.* FROM ?? a LEFT JOIN ?? b ON a.id = b.mx_id WHERE b.tid = ? AND b.cid = ?';
+            const sqlParam2 = [this.ctx.service.changePos.tableName, this.tableName, tid, cid];
+            const result2 = await transaction.query(sql2, sqlParam2);
+            if (result.length > 0 || result2.length > 0) {
+                const changeLedgerGclIdList = this._.map(result, 'id');
+                const changeLedgerIdList = this._.uniq(this._.map(result, 'ledger_pid'));// 父节点集合
+                const needUpdateLedgerList = [];// 找出需要更新的原台账清单的id
+                const needUpdateChangeLedgerList = [];// 找出需要更新的新台账清单的id
+                const tpDecimal = this.ctx.tender.info.decimal.tp;
+                // 要更新的ledger节点,数量及总数
+                for (const data of result2) {
+                    if (this._.indexOf(changeLedgerGclIdList, data.lid) === -1) {
+                        const info = this._.find(needUpdateLedgerList, { id: data.lid });
+                        if (info) {
+                            info.quantity = this.ctx.helper.add(info.quantity, data.quantity);
+                        } else {
+                            needUpdateLedgerList.push({ id: data.lid, quantity: data.quantity });
+                        }
+                    } else {
+                        const info = this._.find(needUpdateChangeLedgerList, { id: data.lid });
+                        if (info) {
+                            info.quantity = this.ctx.helper.add(info.quantity, data.quantity);
+                        } else {
+                            needUpdateChangeLedgerList.push({ id: data.lid, quantity: data.quantity });
+                        }
+                    }
+                }
+                // 更新到result上
+                if (needUpdateChangeLedgerList.length > 0) {
+                    for (const nucl of needUpdateChangeLedgerList) {
+                        const now = this._.find(result, { id: nucl.id });
+                        now.quantity = nucl.quantity;
+                        now.sgfh_qty = nucl.quantity;
+                        now.sgfh_tp = this.ctx.helper.mul(now.sgfh_qty, now.unit_price, tpDecimal);
+                        now.total_price = this.ctx.helper.mul(now.quantity, now.unit_price, tpDecimal);
+                    }
+                }
+                // 更新到ledger上
+                if (needUpdateLedgerList.length > 0) {
+                    for (const nul of needUpdateLedgerList) {
+                        const ledgerInfo = await this.ctx.service.ledger.getDataById(nul.id);
+                        ledgerInfo.quantity = this.ctx.helper.add(ledgerInfo.quantity, nul.quantity);
+                        ledgerInfo.sgfh_qty = this.ctx.helper.add(ledgerInfo.sgfh_qty, nul.quantity);
+                        ledgerInfo.sgfh_tp = this.ctx.helper.mul(ledgerInfo.sgfh_qty, ledgerInfo.unit_price, tpDecimal);
+                        ledgerInfo.total_price = this.ctx.helper.mul(ledgerInfo.quantity, ledgerInfo.unit_price, tpDecimal);
+                        await transaction.update(this.ctx.service.ledger.tableName, ledgerInfo);
+                    }
+                }
+                // 找出所有新增的父节点并插入到result中
+                for (const r of changeLedgerIdList) {
+                    await this._findParents(transaction, tid, r, result);
+                }
+                // 插入到计量单元表,并删除变更的计量单元数据, 插入清单表,并删除变更的清单表
+                await this._insertByChangeRevise(transaction, tid, result, result2);
+                // 更新标段总金额
+                const sumSql = 'SELECT Sum(total_price) As total_price, Sum(deal_tp) As deal_tp' +
+                    '  FROM ' + this.ctx.service.ledger.tableName + this.ctx.helper.whereSql({ tender_id: tid });
+                const sum = await transaction.queryOne(sumSql);
+                await transaction.update(this.ctx.service.tender.tableName, {
+                    id: tid,
+                    total_price: sum.total_price,
+                    deal_tp: sum.deal_tp,
+                });
+            }
+        }
+
+        async _findParents(transaction, tid, id, result) {
+            const info = await transaction.get(this.ctx.service.changeLedger.tableName, { tender_id: tid, ledger_id: id });
+            if (info && this._.findIndex(result, { ledger_id: info.id }) === -1) {
+                result.push(info);
+                await this._findParents(transaction, tid, info.ledger_pid, result);
+            } else {
+                return;
+            }
+        }
+
+        async _insertByChangeRevise(transaction, tid, ledgerList, posList) {
+            if (ledgerList.length > 0) {
+                const insertLedgerArr = [];
+                for (const l of ledgerList) {
+                    const insertL = [
+                        l.id, l.code, l.b_code, l.name, l.unit, l.source, l.remark, l.ledger_id,
+                        l.ledger_pid, l.level, l.order, l.full_path, l.is_leaf, l.quantity, l.total_price,
+                        l.unit_price, l.drawing_code, l.memo, l.dgn_qty1, l.dgn_qty2, l.deal_qty, l.deal_tp,
+                        l.sgfh_qty, l.sgfh_tp, l.sjcl_qty, l.sjcl_tp, l.qtcl_qty, l.qtcl_tp, l.node_type, l.crid,
+                        l.tender_id, l.is_tp, l.sgfh_expr, l.sjcl_expr, l.qtcl_expr, l.check_calc, l.gxby_status,
+                        l.dagl_status, l.dagl_url, l.gxby_limit, l.dagl_limit, l.ex_memo1, l.ex_memo2, l.ex_memo3,
+                    ];
+                    insertLedgerArr.push('(' + this.ctx.helper.getInArrStrSqlFilter(insertL) + ')');
+                    await transaction.delete(this.ctx.service.changeLedger.tableName, { id: l.id });
+                }
+                const bSql = 'Insert Into ' +
+                    this.ctx.service.ledger.tableName +
+                    '  (id, code, b_code, name, unit, source, remark, ledger_id, ledger_pid, level, `order`, full_path, is_leaf,' +
+                    '     quantity, total_price, unit_price, drawing_code, memo, dgn_qty1, dgn_qty2, deal_qty, deal_tp,' +
+                    '     sgfh_qty, sgfh_tp, sjcl_qty, sjcl_tp, qtcl_qty, qtcl_tp, node_type, crid, tender_id, is_tp,' +
+                    '     sgfh_expr, sjcl_expr, qtcl_expr, check_calc,' +
+                    '     gxby_status, dagl_status, dagl_url, gxby_limit,  dagl_limit,' +
+                    '     ex_memo1, ex_memo2, ex_memo3) VALUES ' + insertLedgerArr.join(',') + ';';
+                await transaction.query(bSql, []);
+            }
+            if (posList.length > 0) {
+                const insertPosArr = [];
+                for (const p of posList) {
+                    const insertp = [
+                        p.id, p.tid, p.lid, p.name, p.drawing_code, p.quantity, p.add_stage, p.add_times,
+                        p.add_user, p.sgfh_qty, p.sjcl_qty, p.qtcl_qty, p.crid, p.porder, p.position,
+                        p.sgfh_expr, p.sjcl_expr, p.qtcl_expr, p.real_qty,
+                        p.gxby_status, p.dagl_status, p.dagl_url, p.gxby_limit, p.dagl_limit,
+                        p.ex_memo1, p.ex_memo2, p.ex_memo3,
+                    ];
+                    insertPosArr.push('(' + this.ctx.helper.getInArrStrSqlFilter(insertp) + ')');
+                    await transaction.delete(this.ctx.service.changePos.tableName, { id: p.id });
+                }
+                const pSql =
+                    'Insert Into ' +
+                    this.ctx.service.pos.tableName +
+                    '  (id, tid, lid, name, drawing_code, quantity, add_stage, add_times, add_user,' +
+                    '     sgfh_qty, sjcl_qty, qtcl_qty, crid, porder, position, ' +
+                    '     sgfh_expr, sjcl_expr, qtcl_expr, real_qty,' +
+                    '     gxby_status, dagl_status, dagl_url, gxby_limit,  dagl_limit,' +
+                    '     ex_memo1, ex_memo2, ex_memo3)  VALUES ' + insertPosArr.join(',') + ';';
+                await transaction.query(pSql, []);
+            }
+        }
     }
 
     return ChangeAuditList;

文件差异内容过多而无法显示
+ 1705 - 0
app/service/change_ledger.js


+ 397 - 0
app/service/change_pos.js

@@ -0,0 +1,397 @@
+'use strict';
+
+/**
+ * 变更插入新增 - 部位明细
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = app => {
+
+    class ChangePos extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            // this.depart = 20;
+            this.tableName = 'change_pos';
+        }
+
+        async getPosData(condition) {
+            if (!condition.tid) throw '查询计量单元缺少必要信息';
+            return await this.db.select(this.tableName, {
+                where: condition,
+                columns: ['id', 'tid', 'lid', 'name', 'quantity', 'position', 'drawing_code', 'sgfh_qty', 'sjcl_qty',
+                    'qtcl_qty', 'in_time', 'porder', 'add_stage', 'sgfh_expr', 'sjcl_expr', 'qtcl_expr', 'real_qty',
+                    'dagl_status', 'dagl_url', 'gxby_status', 'gxby_limit', 'dagl_limit', 'dagl_status',
+                    'ex_memo1', 'ex_memo2', 'ex_memo3', 'ccid'],
+                order: [['porder', 'ASC']],
+            });
+        }
+
+        async _completeInsertPosData(tid, cid, data) {
+            data.id = this.uuid.v4();
+            data.tid = tid;
+            // todo 新增期
+            data.add_stage = 0;
+            data.add_times = 0;
+            data.in_time = new Date();
+            data.add_user = this.ctx.session.sessionUser.accountId;
+            data.ccid = cid;
+        }
+
+        async addPos(tid, cid, data) {
+            if (data instanceof Array) {
+                for (const d of data) {
+                    this._completeInsertPosData(tid, cid, d);
+                }
+            } else {
+                this._completeInsertPosData(tid, cid, data);
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                await this.ctx.service.changeAuditList.deleteDataByRevise(transaction, tid, [data.lid], 'gcl_id', '');
+                await transaction.insert(this.tableName, data);
+                await transaction.commit();
+                return { pos: data };
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        async updatePos(tid, data) {
+            if (data.sgfh_qty !== undefined || data.sjcl_qty !== undefined || data.qtcl_qty !== undefined) {
+                const op = await this.getDataById(data.id);
+                let bills = await this.ctx.service.changeLedger.getDataById(op.lid);
+                let newBills = false;
+                let billsPos = await this.getAllDataByCondition({ where: { tid: tid, lid: op.lid } });
+                if (bills) {
+                    newBills = true;
+                } else {
+                    bills = await this.ctx.service.ledger.getDataById(op.lid);
+                    const posData = await this.ctx.service.pos.getAllDataByCondition({ where: { tid: tid, lid: op.lid } });
+                    billsPos = this._.concat(posData, billsPos);
+                }
+
+                const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
+                if (data.sgfh_qty !== undefined) {
+                    data.sgfh_qty = this.round(data.sgfh_qty, precision.value);
+                } else if (op) {
+                    data.sgfh_qty = op.sgfh_qty;
+                }
+                if (data.sjcl_qty !== undefined) {
+                    data.sjcl_qty = this.round(data.sjcl_qty, precision.value);
+                } else if (op) {
+                    data.sjcl_qty = op.sjcl_qty;
+                }
+                if (data.qtcl_qty !== undefined) {
+                    data.qtcl_qty = this.round(data.qtcl_qty, precision.value);
+                } else if (op) {
+                    data.qtcl_qty = op.qtcl_qty;
+                }
+                data.quantity = this.ctx.helper.sum([data.sgfh_qty, data.qtcl_qty, data.sjcl_qty]);
+
+                const updateBills = { id: bills.id };
+                for (const bp of billsPos) {
+                    const calcData = bp.id === data.id ? data : bp;
+                    updateBills.sgfh_qty = this.ctx.helper.add(updateBills.sgfh_qty, calcData.sgfh_qty);
+                    updateBills.sjcl_qty = this.ctx.helper.add(updateBills.sjcl_qty, calcData.sjcl_qty);
+                    updateBills.qtcl_qty = this.ctx.helper.add(updateBills.qtcl_qty, calcData.qtcl_qty);
+                    updateBills.quantity = this.ctx.helper.add(updateBills.quantity, calcData.quantity);
+                }
+                const info = this.ctx.tender.info;
+                updateBills.sgfh_tp = this.ctx.helper.mul(updateBills.sgfh_qty, bills.unit_price, info.decimal.tp);
+                updateBills.sjcl_tp = this.ctx.helper.mul(updateBills.sjcl_qty, bills.unit_price, info.decimal.tp);
+                updateBills.qtcl_tp = this.ctx.helper.mul(updateBills.qtcl_qty, bills.unit_price, info.decimal.tp);
+                updateBills.total_price = this.ctx.helper.mul(updateBills.quantity, bills.unit_price, info.decimal.tp);
+                const transaction = await this.db.beginTransaction();
+                try {
+                    await transaction.update(this.tableName, data);
+                    if (newBills) await transaction.update(this.ctx.service.changeLedger.tableName, updateBills);
+                    await this.ctx.service.changeAuditList.updateDataByRevisePos(transaction, tid, [data]);
+                    await transaction.commit();
+                    updateBills.ledger_id = bills.ledger_id;
+                    return {
+                        ledger: { update: [updateBills] },
+                        pos: data,
+                    };
+                } catch (err) {
+                    await transaction.rollback();
+                    throw err;
+                }
+            } else {
+                const transaction = await this.db.beginTransaction();
+                try {
+                    await transaction.update(this.tableName, data, { tid, id: data.id });
+                    await this.ctx.service.changeAuditList.updateDataByRevisePos(transaction, tid, [data]);
+                    await transaction.commit();
+                    return { pos: data };
+                } catch (err) {
+                    await transaction.rollback();
+                    throw err;
+                }
+            }
+        }
+
+        async updatePosArr(tid, data) {
+            console.log(data);
+            if (data.length === 0) return;
+            const op = await this.getDataById(data[0].id) || await this.ctx.service.pos.getDataById(data[0].id);
+            let bills = await this.ctx.service.changeLedger.getDataById(op.lid);
+            let newBills = false;
+            let billsPos = await this.getAllDataByCondition({ where: { tid: tid, lid: op.lid } });
+            if (bills) {
+                newBills = true;
+            } else {
+                bills = await this.ctx.service.ledger.getDataById(op.lid);
+                const posData = await this.ctx.service.pos.getAllDataByCondition({ where: { tid: tid, lid: op.lid } });
+                billsPos = this._.concat(posData, billsPos);
+            }
+            const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
+            let needUpdateBills;
+            for (const d of data) {
+                if (d.sgfh_qty !== undefined || d.sjcl_qty !== undefined || d.qtcl_qty !== undefined) {
+                    if (d.sgfh_qty !== undefined) {
+                        d.sgfh_qty = this.round(d.sgfh_qty, precision.value);
+                    } else if (op) {
+                        d.sgfh_qty = op.sgfh_qty;
+                    }
+                    if (d.sjcl_qty !== undefined) {
+                        d.sjcl_qty = this.round(d.sjcl_qty, precision.value);
+                    } else if (op) {
+                        d.sjcl_qty = op.sjcl_qty;
+                    }
+                    if (d.qtcl_qty !== undefined) {
+                        d.qtcl_qty = this.round(d.qtcl_qty, precision.value);
+                    } else if (op) {
+                        d.qtcl_qty = op.qtcl_qty;
+                    }
+                    d.quantity = this.ctx.helper.sum([d.sgfh_qty, d.qtcl_qty, d.sjcl_qty]);
+                    needUpdateBills = true;
+                }
+            }
+            const updateBills = { id: bills.id };
+            if (needUpdateBills) {
+                for (const bp of billsPos) {
+                    const newPos = data.find(function(x) { return x.id === bp.id; });
+                    updateBills.sgfh_qty = newPos && newPos.sgfh_qty !== undefined
+                        ? this.ctx.helper.add(updateBills.sgfh_qty, newPos.sgfh_qty)
+                        : this.ctx.helper.add(updateBills.sgfh_qty, bp.sgfh_qty);
+                    updateBills.sjcl_qty = newPos && newPos.sjcl_qty !== undefined
+                        ? this.ctx.helper.add(updateBills.sjcl_qty, newPos.sjcl_qty)
+                        : this.ctx.helper.add(updateBills.sjcl_qty, bp.sjcl_qty);
+                    updateBills.qtcl_qty = newPos && newPos.qtcl_qty !== undefined
+                        ? this.ctx.helper.add(updateBills.qtcl_qty, newPos.qtcl_qty)
+                        : this.ctx.helper.add(updateBills.qtcl_qty, bp.qtcl_qty);
+                    updateBills.quantity = newPos && newPos.quantity !== undefined
+                        ? this.ctx.helper.add(updateBills.quantity, newPos.quantity)
+                        : this.ctx.helper.add(updateBills.quantity, bp.quantity);
+                }
+                const info = this.ctx.tender.info;
+                updateBills.sgfh_tp = this.ctx.helper.mul(updateBills.sgfh_qty, bills.unit_price, info.decimal.tp);
+                updateBills.sjcl_tp = this.ctx.helper.mul(updateBills.sjcl_qty, bills.unit_price, info.decimal.tp);
+                updateBills.qtcl_tp = this.ctx.helper.mul(updateBills.qtcl_qty, bills.unit_price, info.decimal.tp);
+                updateBills.total_price = this.ctx.helper.mul(updateBills.quantity, bills.unit_price, info.decimal.tp);
+            }
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                for (const d of data) {
+                    // 区分d属于pos还是changePos,分别更新对应的表
+                    const np = await this.getDataById(d.id);
+                    np ? await transaction.update(this.tableName, d) : await transaction.update(this.ctx.service.pos.tableName, d);
+                }
+                if (needUpdateBills && newBills) await transaction.update(this.ctx.service.changeLedger.tableName, updateBills);
+                await this.ctx.service.changeAuditList.updateDataByRevisePos(transaction, tid, data);
+                await transaction.commit();
+                updateBills.ledger_id = bills.ledger_id;
+                return {
+                    ledger: { update: needUpdateBills ? [updateBills] : [] },
+                    pos: data,
+                };
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        async deletePos(tid, data) {
+            if (!data || data.length === 0) {
+                throw '提交数据错误';
+            }
+
+            const pos = await this.getPosData({tid: tid, id: data});
+            let bills = await this.ctx.service.changeLedger.getDataById(pos[0].lid);
+            let newBills = false;
+            let billsPos = await this.getAllDataByCondition({ where: { tid: tid, lid: pos[0].lid } });
+            if (bills) {
+                newBills = true;
+            } else {
+                bills = await this.ctx.service.ledger.getDataById(pos[0].lid);
+                const posData = await this.ctx.service.pos.getAllDataByCondition({ where: { tid: tid, lid: pos[0].lid } });
+                billsPos = this._.concat(posData, billsPos);
+            }
+            // const bills = await this.ctx.service.reviseBills.getDataById(pos[0].lid);
+            // const billsPos = await this.getAllDataByCondition({ where: {tid: tid, lid: bills.id} });
+            const updateBills = {id: bills.id, sgfh_qty: null, sjcl_qty: null, qtcl_qty: null, quantity: null};
+            for (const bp of billsPos) {
+                if (data.indexOf(bp.id) >= 0) continue;
+                updateBills.sgfh_qty = this.ctx.helper.add(updateBills.sgfh_qty, bp.sgfh_qty);
+                updateBills.sjcl_qty = this.ctx.helper.add(updateBills.sjcl_qty, bp.sjcl_qty);
+                updateBills.qtcl_qty = this.ctx.helper.add(updateBills.qtcl_qty, bp.qtcl_qty);
+                updateBills.quantity = this.ctx.helper.add(updateBills.quantity, bp.quantity);
+            }
+            const info = this.ctx.tender.info;
+            updateBills.sgfh_tp = this.ctx.helper.mul(updateBills.sgfh_qty, bills.unit_price, info.decimal.tp);
+            updateBills.sjcl_tp = this.ctx.helper.mul(updateBills.sjcl_qty, bills.unit_price, info.decimal.tp);
+            updateBills.qtcl_tp = this.ctx.helper.mul(updateBills.qtcl_qty, bills.unit_price, info.decimal.tp);
+            updateBills.total_price = this.ctx.helper.mul(updateBills.quantity, bills.unit_price, info.decimal.tp);
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.delete(this.tableName, {tid: tid, id: data});
+                if (newBills) await transaction.update(this.ctx.service.changeLedger.tableName, updateBills);
+                await this.ctx.service.changeAuditList.deleteDataByRevise(transaction, tid, data, 'mx_id');
+                await transaction.commit();
+                updateBills.ledger_id = bills.ledger_id;
+                return {
+                    ledger: { update: [updateBills] },
+                    pos: data,
+                };
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+        /**
+         * 复制粘贴 部位明细数据
+         * @param {Array} data - 复制粘贴的数据
+         * @param {Number} tid - 标段id
+         * @returns {Promise<{ledger: {}, pos: null}>}
+         */
+        async pastePosData(tid, cid, data) {
+            if (!(data instanceof Array)) throw '提交数据错误';
+
+            const transaction = await this.db.beginTransaction();
+            const result = { ledger: {}, pos: null };
+            const orgPos = await this.getPosData({ tid: tid, id: this._.map(data, 'id') });
+
+            // const bills = await this.ctx.service.reviseBills.getDataById(data[0].lid);
+            // const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
+            // const updateBills = {id: bills.id};
+            // const billsPos = await this.getAllDataByCondition({where: {tid: tid, lid: bills.id} });
+            let bills = await this.ctx.service.changeLedger.getDataById(data[0].lid);
+            let newBills = false;
+            let billsPos = await this.getAllDataByCondition({ where: { tid: tid, lid: data[0].lid } });
+            if (bills) {
+                newBills = true;
+            } else {
+                bills = await this.ctx.service.ledger.getDataById(data[0].lid);
+                const posData = await this.ctx.service.pos.getAllDataByCondition({ where: { tid: tid, lid: data[0].lid } });
+                billsPos = this._.concat(posData, billsPos);
+            }
+            const updateBills = { id: bills.id };
+            const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
+            let needUpdateBills;
+            for (const bp of billsPos) {
+                const d = data.find(function(x) {
+                    return bp.id ? x.id === bp.id : false;
+                });
+                if (d) continue;
+                updateBills.sgfh_qty = this.ctx.helper.add(updateBills.sgfh_qty, bp.sgfh_qty);
+                updateBills.sjcl_qty = this.ctx.helper.add(updateBills.sjcl_qty, bp.sjcl_qty);
+                updateBills.qtcl_qty = this.ctx.helper.add(updateBills.qtcl_qty, bp.qtcl_qty);
+                updateBills.quantity = this.ctx.helper.add(updateBills.quantity, bp.quantity);
+            }
+
+            try {
+                for (const d of data) {
+                    const op = d.id ? this._.find(orgPos, { id: d.id }) : null;
+                    if (d.sgfh_qty !== undefined || d.sjcl_qty !== undefined || d.qtcl_qty !== undefined) {
+                        if (d.sgfh_qty !== undefined) {
+                            d.sgfh_qty = this.round(d.sgfh_qty, precision.value);
+                        } else if (op) {
+                            d.sgfh_qty = op.sgfh_qty;
+                        }
+                        updateBills.sgfh_qty = this.ctx.helper.add(updateBills.sgfh_qty, d.sgfh_qty);
+                        if (d.sjcl_qty !== undefined) {
+                            d.sjcl_qty = this.round(d.sjcl_qty, precision.value);
+                        } else if (op) {
+                            d.sjcl_qty = op.sjcl_qty;
+                        }
+                        updateBills.sjcl_qty = this.ctx.helper.add(updateBills.sjcl_qty, d.sjcl_qty);
+                        if (d.qtcl_qty) {
+                            d.qtcl_qty = this.round(d.qtcl_qty, precision.value);
+                        } else if (op) {
+                            d.qtcl_qty = op.qtcl_qty;
+                        }
+                        updateBills.qtcl_qty = this.ctx.helper.add(updateBills.qtcl_qty, d.qtcl_qty);
+                        d.quantity = this.ctx.helper.sum([d.sgfh_qty, d.qtcl_qty, d.sjcl_qty]);
+                        updateBills.quantity = this.ctx.helper.add(updateBills.quantity, d.quantity);
+                        needUpdateBills = true;
+                    }
+                    if (d.id) {
+                        await transaction.update(this.tableName, d);
+                    } else {
+                        await this._insertPosData(transaction, d, tid, cid);
+                    }
+                }
+                const info = this.ctx.tender.info;
+                if (needUpdateBills) {
+                    updateBills.sgfh_tp = this.ctx.helper.mul(updateBills.sgfh_qty, bills.unit_price, info.decimal.tp);
+                    updateBills.sjcl_tp = this.ctx.helper.mul(updateBills.sjcl_qty, bills.unit_price, info.decimal.tp);
+                    updateBills.qtcl_tp = this.ctx.helper.mul(updateBills.qtcl_qty, bills.unit_price, info.decimal.tp);
+                    updateBills.total_price = this.ctx.helper.mul(updateBills.quantity, bills.unit_price, info.decimal.tp);
+                    if (newBills) await transaction.update(this.ctx.service.changeLedger.tableName, updateBills);
+                    updateBills.ledger_id = bills.ledger_id;
+                }
+                await this.ctx.service.changeAuditList.updateDataByRevisePos(transaction, tid, data);
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+            result.pos = data;
+            result.ledger.update = needUpdateBills ? [updateBills] : [];
+            return result;
+        }
+
+        async _insertPosData(transaction, data, tid, cid) {
+            data.id = this.uuid.v4();
+            data.tid = tid;
+            // todo 新增期
+            data.add_stage = 0;
+            data.add_times = 0;
+            data.in_time = new Date();
+            data.add_user = this.ctx.session.sessionUser.accountId;
+            data.ccid = cid;
+            if (data.quantity) {
+                const bills = await this.ctx.service.changeLedger.getDataById(data.lid) || await this.ctx.service.ledger.getDataById(data.lid);
+                const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
+                data.quantity = this.round(data.quantity, precision.value);
+            }
+            const addRst = await transaction.insert(this.tableName, data);
+        }
+
+        /**
+         * 删除清单下部位明细数据(删除清单时调用)
+         *
+         * @param transaction - 事务
+         * @param tid - 标段id
+         * @param lid - 清单id
+         * @returns {Promise<void>}
+         */
+        async deletePosData(transaction, tid, lid) {
+            await transaction.delete(this.tableName, {tid: tid, lid: lid});
+        }
+    }
+
+    return ChangePos;
+};

+ 51 - 0
app/service/pay_att.js

@@ -0,0 +1,51 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = app => {
+    class PayAtt extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'pay_attachment';
+        }
+
+        async getStageData(condition) {
+            const files = await this.getAllDataByCondition({
+                where: condition,
+                orders: [['id', 'desc']]
+            });
+            for (const f of files) {
+                delete f.filepath;
+            }
+            return files;
+        }
+
+        async addFiles(files) {
+            await this.db.insert(this.tableName, files);
+            return await this.getStageData({ sid: files[0].sid, pid: files[0].pid });
+        }
+
+        async delFiles(fileId) {
+            const fileInfo = await this.getDataById(fileId);
+            if (!fileInfo) throw '不存在该文件';
+            if (fileInfo.uid !== this.ctx.session.sessionUser.accountId) throw '您无权删除该文件';
+
+            await this.db.delete(this.tableName, { id: fileId });
+            return await this.getStageData({ sid: fileInfo.sid, pid: fileInfo.pid });
+        }
+    }
+
+    return PayAtt;
+};

+ 1 - 0
app/service/project_account.js

@@ -355,6 +355,7 @@ module.exports = app => {
         }
 
         async getAccountInfoById(id) {
+            if (!id) throw new Error('id未定义');
             this.initSqlBuilder();
             this.sqlBuilder.columns = ['id', 'name', 'company', 'role'];
             this.sqlBuilder.setAndWhere('id', {

+ 4 - 4
app/service/stage_bills.js

@@ -127,7 +127,7 @@ module.exports = app => {
         }
 
         async getStageUsedBills(tid, sid) {
-            const sql = 'SELECT Bills.lid, ((not IsNull(Bills.contract_qty) and Bills.contract_qty <> 0) or (not IsNull(Bills.qc_qty) and Bills.qc_qty <> 0)) As used' +
+            const sql = 'SELECT Bills.lid, ((not IsNull(Bills.contract_qty) and Bills.contract_qty <> 0) or (not IsNull(Bills.contract_tp) and Bills.contract_tp <> 0) or (not IsNull(Bills.qc_qty) and Bills.qc_qty <> 0) or (not IsNull(Bills.qc_tp) and Bills.qc_tp <> 0)) As used' +
                 '  FROM ' + this.tableName + ' As Bills ' +
                 '  INNER JOIN ( ' +
                 '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `progress`, `lid`, `sid` From ' + this.tableName +
@@ -445,13 +445,13 @@ module.exports = app => {
             return { contract_tp, qc_tp };
         }
 
-        async sumLoad(lid, tenders) {
+        async sumLoad(lid, tenders, cover) {
             const conn = await this.db.beginTransaction();
             try {
                 const maxId = await this.ctx.service.ledger._getMaxLid(this.ctx.tender.id);
                 const select = await this.ctx.service.ledger.getDataById(lid);
                 const sumLoad = new SumLoad(this.ctx);
-                const loadTree = await sumLoad.stageGatherGcl(select, maxId, tenders);
+                const loadTree = await sumLoad.stageGatherGcl(select, maxId, tenders, null, cover);
                 const result = loadTree.getUpdateData();
                 if (result.errors.length > 100) throw '您导入的数据存在大量数据错误,请您仔细检查';
 
@@ -475,7 +475,7 @@ module.exports = app => {
                     }
                 }
 
-                const his = await this.ctx.service.sumLoadHistory.saveStageHistory(this.ctx.tender.id, this.ctx.stage.id, lid, tenders, result.errors);
+                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.commit();

+ 19 - 13
app/service/stage_detail_att.js

@@ -9,6 +9,8 @@
  */
 
 const ImTypeConst = require('../const/tender').imType;
+const path = require('path');
+const fs = require('fs');
 
 module.exports = app => {
     class StageDetailAtt extends app.BaseService {
@@ -33,19 +35,19 @@ module.exports = app => {
                     case ImTypeConst.zl.value:
                     case ImTypeConst.bw.value:
                         return this.getDataByCondition({
-                            im_type: field.im_type, sid: this.ctx.stage.id,
+                            im_type: field.im_type, sid: field.sid,
                             lid: field.lid, code: field.code, name: field.name, unit: field.unit, unit_price: field.unit_price,
                             pid: field.pid, pos_name: field.pos_name
                         });
                     case ImTypeConst.tz.value:
                         return this.getDataByCondition({
-                            im_type: field.im_type, sid: this.ctx.stage.id,
+                            im_type: field.im_type, sid: field.sid,
                             lid: field.lid, code: field.code, name: field.name, unit: field.unit,
                             pid: field.pid, pos_name: field.pos_name
                         });
                     case ImTypeConst.bb.value:
                         return this.getDataByCondition({
-                            im_type: field.im_type, sid: this.ctx.stage.id,
+                            im_type: field.im_type, sid: field.sid,
                             lid: field.lid, name: field.name, unit: field.unit,
                             pid: field.pid, pos_name: field.pos_name
                         });
@@ -54,12 +56,9 @@ module.exports = app => {
         }
 
         _analysisDetailAtt(data) {
-            if (data instanceof Array) {
-                for (const a of data) {
-                    a.attachment = a.attachment ? JSON.parse(a.attachment) : [];
-                }
-            } else {
-                data.attachment = data.attachment ? JSON.parse(data.attachment) : [];
+            const datas = data instanceof Array ? data : [data];
+            for (const a of datas) {
+                a.attachment = a.attachment ? JSON.parse(a.attachment) : [];
             }
         }
 
@@ -73,6 +72,7 @@ module.exports = app => {
             const datas = data instanceof Array ? data : [data];
             for (const d of datas) {
                 for (const a of d.attachment) {
+                    if (this.ctx.helper.canPreview(a.fileext)) a.viewpath = a.filepath.replace(/^app|\/app/, '');
                     delete a.filepath;
                     a.username = (await this.getUserTemp(a.uid)).name;
                 }
@@ -88,8 +88,8 @@ module.exports = app => {
             return result;
         }
 
-        async addFiles(field, files) {
-            const detailAtt = await this.getDetailAtt(field);
+        async addFiles(stage, field, files) {
+            const detailAtt = await this.getDetailAtt({ sid: stage.id, ...field });
             if (detailAtt) {
                 detailAtt && this._analysisDetailAtt(detailAtt);
                 for (const f of files) {
@@ -100,7 +100,7 @@ module.exports = app => {
                 return detailAtt;
             } else {
                 const insertData = {
-                    tid: this.ctx.tender.id, sid: this.ctx.stage.id, sorder: this.ctx.stage.order,
+                    tid: stage.tid, sid: stage.id, sorder: stage.order,
                     uuid: this.uuid.v4(), im_type: field.im_type,
                     lid: field.lid, code: field.code, name: field.name, unit: field.unit, unit_price: field.unit_price,
                     pid: field.pid, pos_name: field.pos_name,
@@ -120,10 +120,16 @@ module.exports = app => {
 
             const index = detailAtt.attachment.findIndex(x => { return x.file_id === fileId });
             if (index < 0) throw '不存在改文件';
-            if (detailAtt.attachment[index].uid !== this.ctx.session.sessionUser.accountId) throw '您无权删除该文件';
+            const att = detailAtt.attachment[index];
+            if (att.uid !== this.ctx.session.sessionUser.accountId) throw '您无权删除该文件';
 
             detailAtt.attachment.splice(index, 1);
             await this.db.update(this.tableName, { id: detailAtt.id, attachment: JSON.stringify(detailAtt.attachment) });
+
+            if (fs.existsSync(path.join(this.ctx.app.baseDir, att.filepath))) {
+                await fs.unlinkSync(path.join(this.ctx.app.baseDir, att.filepath));
+            }
+
             await this._complete4Output(detailAtt);
             return detailAtt;
         }

+ 4 - 33
app/service/stage_pay.js

@@ -287,9 +287,9 @@ module.exports = app => {
             if (!stage || !transaction || !times || order === undefined) {
                 throw '数据错误';
             }
-            const sql = 'INSERT INTO ?? (`tid`, `sid`, `pid`, `stimes`, `sorder`, `name`, `tp`, `expr`, `pause`, `attachment`,' +
+            const sql = 'INSERT INTO ?? (`tid`, `sid`, `pid`, `stimes`, `sorder`, `name`, `tp`, `expr`, `pause`,' +
                         '    `pre_tp`, `end_tp`, `pre_used`, `pre_finish`, `start_stage_order`) ' +
-                        '  SELECT SP.`tid`, SP.`sid`, SP.`pid`, ?, ?, SP.name, SP.`tp`, SP.`expr`, SP.`pause`, SP.`attachment`,' +
+                        '  SELECT SP.`tid`, SP.`sid`, SP.`pid`, ?, ?, SP.name, SP.`tp`, SP.`expr`, SP.`pause`,' +
                         '     SP.`pre_tp`, SP.`end_tp`, SP.`pre_used`, SP.`pre_finish`, SP.`start_stage_order` ' +
                         '  FROM ?? As SP, ?? As P ' +
                         '  WHERE SP.`sid` = ? AND SP.`stimes` = ? AND SP.`sorder` = ? And SP.`pid` = P.`id` And P.`valid`';
@@ -316,9 +316,9 @@ module.exports = app => {
          * @returns {Promise<*>}
          */
         async copyStagePays4DeleteTimes(stage, times, order, copyTimes, copyOrder, transaction) {
-            const sql = 'INSERT INTO ?? (`tid`, `sid`, `pid`, `stimes`, `sorder`, `name`, `tp`, `expr`, `pause`, `attachment`,' +
+            const sql = 'INSERT INTO ?? (`tid`, `sid`, `pid`, `stimes`, `sorder`, `name`, `tp`, `expr`, `pause`,' +
                 '    `pre_tp`, `end_tp`, `pre_used`, `pre_finish`, `start_stage_order`) ' +
-                '  SELECT SP.`tid`, SP.`sid`, SP.`pid`, ?, ?, SP.name, SP.`tp`, SP.`expr`, SP.`pause`, SP.`attachment`,' +
+                '  SELECT SP.`tid`, SP.`sid`, SP.`pid`, ?, ?, SP.name, SP.`tp`, SP.`expr`, SP.`pause`,' +
                 '     SP.`pre_tp`, SP.`end_tp`, SP.`pre_used`, SP.`pre_finish`, SP.`start_stage_order` ' +
                 '  FROM ?? As SP, ?? As P ' +
                 '  WHERE SP.`sid` = ? AND SP.`stimes` = ? AND SP.`sorder` = ? And SP.`pid` = P.`id` And P.`valid`';
@@ -326,35 +326,6 @@ module.exports = app => {
                 stage.id, copyTimes, copyOrder];
             return await transaction.query(sql, sqlParam);
         }
-
-        /**
-         * 保存附件
-         * @param data
-         * @returns {Promise<void>}
-         */
-        async saveAtt(id, att) {
-            // 获取attachment
-            const info = await this.getDataById(id);
-            let attachment = null;
-            if (info.attachment !== null && JSON.parse(info.attachment).length !== 0) {
-                attachment = JSON.parse(info.attachment);
-                attachment.unshift(att);
-            } else {
-                attachment = new Array(att);
-            }
-            const result = await this.db.update(this.tableName, { id, attachment: JSON.stringify(attachment) });
-            return result.affectedRows === 1;
-        }
-
-        /**
-         * 删除附件
-         * @param data
-         * @returns {Promise<void>}
-         */
-        async deleteAtt(id, attachment) {
-            const result = await this.db.update(this.tableName, { id, attachment: JSON.stringify(attachment) });
-            return result.affectedRows === 1;
-        }
     }
 
     return StagePay;

+ 3 - 2
app/service/sum_load_history.js

@@ -72,11 +72,12 @@ module.exports = app => {
             return data;
         }
 
-        async saveStageHistory(tid, sid, lid, tenders, errors) {
+        async saveStageHistory(tid, sid, lid, tenders, errors, cover) {
             const data = {
-                tid, lid, type: 'revise', sid,
+                tid, lid, type: 'stage', sid,
                 load_time: new Date(), uid: this.ctx.session.sessionUser.accountId,
                 tenders: JSON.stringify(tenders), errors: errors ? JSON.stringify(errors) : '',
+                cover: cover,
             };
             await this.db.insert(this.tableName, data);
             data.tenders = tenders;

+ 1 - 1
app/service/tender_info.js

@@ -398,7 +398,7 @@ module.exports = app => {
             const columns = [];
             typeArr.forEach(item => {
                 if (item === 'tender') {
-                    columns.push('deal_info', 'construction_unit', 'tech_param');
+                    columns.push('deal_info', 'construction_unit', 'tech_param', 'bid_info');
                 } else if (item === 'chapter') {
                     columns.push('chapter');
                 } else if (item === 'pay_account') {

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

@@ -384,6 +384,7 @@
 </div>
 <script>
     const tenderName = '<%- tender.name %>';
+    const tenderId = '<%- tender.id %>';
     const totalPriceUnit = '<%- tpUnit %>';
     const unitPriceUnit = '<%- upUnit %>';
     const accountId = parseInt('<%- ctx.session.sessionUser.accountId %>');
@@ -418,6 +419,8 @@
     changeUnits.push('');
     const accountGroup = JSON.parse(unescape('<%- escape(JSON.stringify(accountGroup)) %>'));
     const accountList = JSON.parse(unescape('<%- escape(JSON.stringify(accountList)) %>'));
+    const changeLedgerList = JSON.parse(unescape('<%- escape(JSON.stringify(changeLedgerList)) %>'));
+    const changePosList = JSON.parse(unescape('<%- escape(JSON.stringify(changePosList)) %>'));
     const shenpi_status = <%- ctx.tender.info.shenpi.change %>;
     const shenpiConst = JSON.parse('<%- JSON.stringify(shenpiConst) %>');
     const changesUid = <%- change.uid %>;

+ 6 - 3
app/view/change/information_modal.ejs

@@ -128,12 +128,15 @@
                         </div>
                     </div>
                     <div class="col-12">
-                        <div class="row mb-2 mt-2 mx-0 p-0">
-                            <div class="col-6 pl-0 p-0 search-group mt-2">
+                        <div class="row mb-2 mt-3 mx-0 p-0">
+                            <div class="col-6 p-0 search-group">
                                 <input class="form-control form-control-sm" id="code-input" placeholder="输入 项目节编号、名称、计量单元 检索">
                                 <a href="javascript:void(0);" style="display: none" data-btn="code" class="text-danger remove-btn" title="移除关键词"><i class="fa fa-times-circle "></i></a>
                             </div>
-                            <div class="ml-auto mt-2">
+                            <div class="col-3 pl-3 mt-1">
+                                <a href="/tender/<%- change.tid %>/change/<%- change.cid %>/information/revise" class="btn btn-primary btn-sm">新增部位</a>
+                            </div>
+                            <div class="ml-auto mt-1">
                                 <div class="custom-control custom-checkbox mt-1">
                                     <input type="checkbox" id="code-select-all" class="custom-control-input">
                                     <label class="custom-control-label" for="code-select-all">全选</label>

+ 173 - 0
app/view/change/revise.ejs

@@ -0,0 +1,173 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main  d-flex">
+            <% include ./sub_mini_menu.ejs %>
+            <!--工具-->
+            <div>
+                <div class="d-inline-block">
+                    <div class="dropdown">
+                        <button class="btn btn-sm btn-light dropdown-toggle text-primary" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                            <i class="fa fa-list-ol"></i> 显示层级
+                        </button>
+                        <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+                            <a class="dropdown-item" name="showLevel" tag="1" href="javascript: void(0);">第一层</a>
+                            <a class="dropdown-item" name="showLevel" tag="2" href="javascript: void(0);">第二层</a>
+                            <a class="dropdown-item" name="showLevel" tag="3" href="javascript: void(0);">第三层</a>
+                            <a class="dropdown-item" name="showLevel" tag="4" href="javascript: void(0);">第四层</a>
+                            <a class="dropdown-item" name="showLevel" tag="5" href="javascript: void(0);">第五层</a>
+                            <a class="dropdown-item" name="showLevel" tag="last" href="javascript: void(0);">最底层</a>
+                            <a class="dropdown-item" name="showLevel" tag="leafXmj" href="javascript: void(0);">只显示项目节</a>
+                        </div>
+                    </div>
+                </div>
+                <div class="d-inline-block">
+                    <a href="javascript: void(0);" name="base-opr" type="add" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="新增"><i class="fa fa-plus" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="base-opr" type="delete" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="删除"><i class="fa fa-remove" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="base-opr" type="up-level" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="升级"><i class="fa fa-arrow-left" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="base-opr" type="down-level" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="降级"><i class="fa fa-arrow-right" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="base-opr" type="down-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="base-opr" type="up-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="cpc" type="copy" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="复制"><i class="fa fa-files-o" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="cpc" type="cut" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="剪切"><i class="fa fa-scissors" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="cpc" type="paste" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="粘贴"><i class="fa fa-clipboard" aria-hidden="true"></i></a>
+                </div>
+                <div class="d-inline-block">
+                    <div class="input-group input-group-sm ml-2">
+                        <div class="input-group-prepend">
+                            <span class="input-group-text" id="basic-addon1">表达式</span>
+                        </div>
+                        <input type="text" class="form-control form-control-sm m-0" id="bills-expr" readonly="">
+                    </div>
+                </div>
+            </div>
+            <div class="ml-auto">
+                <a class="btn btn-sm btn-primary mr-1" id="ledger-check2" href="javascript: void(0);">数据检查</a>
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap row pr-46">
+        <div class="c-header p-0 col-12"></div>
+        <!--核心内容(两栏)-->
+        <div class="row w-100 sub-content">
+            <!--左栏-->
+            <div class="c-body" id="left-view" style="width: 100%">
+                <!--0号台账模式-->
+                <div class="sjs-height-1" style="overflow: hidden" id="bills-spread">
+                </div>
+                <% if (ctx.tender.data.measure_type === measureType.tz.value) { %>
+                <div class="bcontent-wrap">
+                    <div id="revise-resize" class="resize-y" id="top-spr" r-Type="height" div1=".sjs-height-1" div2=".bcontent-wrap" title="调整大小"><!--调整上下高度条--></div>
+                    <div class="bc-bar mb-1">
+                        <ul class="nav nav-tabs">
+                            <li class="nav-item">
+                                <a class="nav-link active" href="javascript:void(0)">计量单元</a>
+                            </li>
+                            <li class="ml-2 nav-item">
+                                <div class="d-inline-flex">
+                                    <a href="javascript: void(0);" name="pos-opr" type="down-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
+                                    <a href="javascript: void(0);" name="pos-opr" type="up-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
+                                </div>
+                            </li>
+                            <li class="nav-item" id="pos-search">
+                            </li>
+                            <li class="nav-item">
+                                <div class="d-inline-block">
+                                    <div class="input-group input-group-sm ml-2">
+                                        <div class="input-group-prepend">
+                                            <span class="input-group-text" id="basic-addon1">表达式</span>
+                                        </div>
+                                        <input type="text" class="form-control form-control-sm m-0" id="pos-expr" readonly="">
+                                    </div>
+                                </div>
+                            </li>
+                        </ul>
+                    </div>
+                    <div class="sp-wrap" id="pos-spread">
+                    </div>
+                </div>
+                <% } %>
+            </div>
+            <!--右栏-->
+            <div class="c-body" id="right-view" style="display: none; width: 33%;">
+                <div class="resize-x" id="revise-right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
+                <div class="tab-content">
+                    <div id="search" class="tab-pane">
+                    </div>
+                    <div id="std-xmj" class="tab-pane">
+                        <div class="sjs-bar">
+                            <div class="pb-1">
+                                <select class="form-control form-control-sm">
+                                    <% for (const c of stdChapters) { %>
+                                    <option value="<%- c.id %>" <% if (stdChapters.indexOf(c) === 0) { %>selected<% } %>><%- c.name %></option>
+                                    <% } %>
+                                </select>
+                            </div>
+                        </div>
+                        <div id="std-xmj-spread" class="sjs-sh">
+                        </div>
+                    </div>
+                    <div id="std-gcl" class="tab-pane">
+                        <div class="sjs-bar">
+                            <div class="pb-1">
+                                <select class="form-control form-control-sm">
+                                    <% for (const b of stdBills) { %>
+                                    <option value="<%- b.id %>" <% if (stdBills.indexOf(b) === 0) { %>selected<% } %>><%- b.name %></option>
+                                    <% } %>
+                                </select>
+                            </div>
+                        </div>
+                        <div id="std-gcl-spread" class="sjs-sh">
+                        </div>
+                    </div>
+                    <div id="deal-bills" class="tab-pane">
+                        <div class="sjs-bar">
+                            签约清单
+                            <a href="/tender/<%- ctx.tender.id %>/deal/download/签约清单.xlsx" class="btn btn-sm btn-primary" style="display: none">下载签约清单</a>
+                        </div>
+                        <div id="deal-bills-spread" class="sjs-sh">
+                        </div>
+                    </div>
+                    <div id="error-list" class="tab-pane">
+                    </div>
+                    <div id="check-list" class="tab-pane">
+                    </div>
+                    <div id="sum-load-miss" class="tab-pane tab-select-show">
+                    </div>
+                </div>
+            </div>
+        </div>
+        <!--右侧菜单-->
+        <div class="side-menu">
+            <!--右侧菜单-->
+            <ul class="nav flex-column right-nav" id="side-menu">
+                <li class="nav-item">
+                    <a class="nav-link" content="#search" href="javascript: void(0);">查找定位</a>
+                </li>
+                <li class="nav-item">
+                    <a class="nav-link" content="#std-xmj" href="javascript: void(0);">项目节</a>
+                </li>
+                <li class="nav-item">
+                    <a class="nav-link" content="#std-gcl" href="javascript: void(0);">标准清单</a>
+                </li>
+                <li class="nav-item">
+                    <a class="nav-link" content="#deal-bills" href="javascript: void(0);">签约清单</a>
+                </li>
+                <li class="nav-item">
+                    <a class="nav-link" content="#error-list" id="error-list-tab" href="javascript: void(0);" style="display: none;">错误列表</a>
+                </li>
+                <li class="nav-item">
+                    <a class="nav-link" content="#check-list" id="check-list-tab" href="javascript: void(0);" style="display: none;">数据检查</a>
+                </li>
+            </ul>
+        </div>
+    </div>
+</div>
+<script>
+    const readOnly = <%- readOnly %>;
+    const isTz = <%- ctx.tender.data.measure_type === measureType.tz.value %>;
+    const billsSpreadSetting = JSON.parse('<%- JSON.stringify(ledgerSpread) %>');
+    const posSpreadSetting = JSON.parse('<%- JSON.stringify(posSpread) %>');
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
+    const decimal = JSON.parse('<%- JSON.stringify(ctx.tender.info.decimal) %>');
+</script>

+ 40 - 0
app/view/change/revise_modal.ejs

@@ -0,0 +1,40 @@
+<!--批量添加清单部位-->
+<div class="modal fade" id="batch" data-backdrop="static">
+    <div class="modal-dialog modal-xl" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">批量插入清单-计量单元</h5>
+            </div>
+            <div class="modal-body">
+                <div class="custom-control custom-checkbox mb-2">
+                    <label class="form-check-label">
+                        <input class="form-check-input" type="checkbox" name="batch-filter">
+                        过滤无计量单元的清单
+                    </label>
+                </div>
+                <div class="row">
+                    <div class="col-6">
+                        <h6>清单信息</h6>
+                        <div class="batch-l-t">
+                        </div>
+                        <h6>计量单元数量复核表</h6>
+                        <div class="batch-l-b">
+                        </div>
+                    </div>
+                    <div class="col-6">
+                        <h6>签约清单</h6>
+                        <div class="batch-r">
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-primary btn-sm" id="batch-ok">确定</button>
+            </div>
+        </div>
+    </div>
+</div>
+<% include ../shares/delete_hint_modal.ejs %>
+<% include ../shares/check_data_modal.ejs %>
+<% include ../shares/check_modal2.ejs %>

+ 12 - 0
app/view/change/sub_menu.ejs

@@ -0,0 +1,12 @@
+<div class="panel-sidebar" id="sub-menu">
+    <div class="sidebar-title" data-toggle="tooltip" data-placement="right" data-original-title="变更台账新增部位">变更台账新增部位</div>
+    <div class="scrollbar-auto">
+        <% include ./sub_menu_list.ejs %>
+        <div class="side-fold"><a href="javascript: void(0)" data-toggle="tooltip" data-placement="top" data-original-title="折叠侧栏" id="to-mini-menu"><i class="fa fa-upload fa-rotate-270"></i></a></div>
+    </div>
+    <script>
+        new Vue({
+            el: '.scrollbar-auto',
+        });
+    </script>
+</div>

+ 1 - 0
app/view/change/sub_menu_list.ejs

@@ -0,0 +1 @@
+<nav-menu title="返回" url="<%- preUrl %>" tclass="text-primary" ml="1" icon="fa-chevron-left"></nav-menu>

+ 16 - 0
app/view/change/sub_mini_menu.ejs

@@ -0,0 +1,16 @@
+<!--折起的菜单-->
+<div class="min-side" id="sub-mini-menu" style="display: none;">
+    <div id="sub-mini-hint" class="side-switch" data-container="body" data-toggle="popover" data-placement="bottom" data-content="这里打开收起的菜单栏"></div>
+    <div class="side-switch">
+        <i class="fa fa-bars"></i>
+    </div>
+    <div class="side-menu" id="mini-menu-list" style="display: none">
+        <% include ./sub_menu_list.ejs %>
+        <div class="side-fold"><a href="javascript: void(0);" data-toggle="tooltip" data-placement="top" data-original-title="展开侧栏" id="to-menu"><i class="fa fa-upload fa-rotate-90"></i></a></div>
+    </div>
+</div>
+<script>
+    new Vue({
+        el: '.side-menu',
+    });
+</script>

+ 6 - 0
app/view/shares/tender_select_modal.ejs

@@ -18,6 +18,12 @@
                         <h5>已选标段 </h5>
                         <div class="modal-height-300" id="ts-result-spread">
                         </div>
+                        <div id="tender-select-option" style="display: none;">
+                            <div class="form-check form-check-inline">
+                                <input class="form-check-input" type="checkbox" id="ts-cover">
+                                <label class="form-check-label" for="ts-cover">覆盖数据</label>
+                            </div>
+                        </div>
                     </div>
                     <div id="tender-select-hint">
                         <div class="text-danger text-center ml-3 d-inline-block" id="ts-hint-text">我是提示呀</div>

+ 0 - 4
app/view/stage/index.ejs

@@ -485,10 +485,6 @@
         <img src="/public/images/file_clip.png" id="rela-file-icon">
         <img src="/public/images/file_clip_hover.png" id="rela-file-hover">
     </div>
-    <div style="display: none">
-        <img src="/public/images/file_clip.png" id="rela-file-icon" />
-        <img src="/public/images/file_clip_hover.png" id="rela-file-hover" />
-    </div>
 </div>
 <script src="/public/js/moment/moment.min.js"></script>
 <script>

+ 1 - 1
app/view/stage/modal.ejs

@@ -608,7 +608,7 @@
                 <div class="modal-height-500" style="overflow:auto;">
                     <table class="table table-sm table-bordered" style="word-break:break-all; table-layout: fixed">
                         <thead>
-                        <tr><th width="240">文件名</th><th>上传人</th><th>上传时间</th><th width="40">操作</th></tr>
+                        <tr><th width="240">文件名</th><th>上传人</th><th>上传时间</th><th width="52">操作</th></tr>
                         </thead>
                         <tbody id="im-attList">
                         </tbody>

+ 1 - 0
app/view/stage/pay.ejs

@@ -68,4 +68,5 @@
     const preContractTp = <%- (pre.contract_tp || 0) %>;
     const preQcTp = <%- (pre.qc_tp || 0) %>;
     const preGatherTp = <%- (pre.gather_tp || 0) %>;
+    const auditConst = JSON.parse('<%- JSON.stringify(auditConst) %>');
 </script>

+ 30 - 0
app/view/stage_rela/detail_modal.ejs

@@ -18,4 +18,34 @@
             </div>
         </div>
     </div>
+</div>
+<!--中间计量 附件-->
+<div class="modal fade" id="im-file" 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">
+                <div class="form-group">
+                    <% if (!ctx.tender.isTourist || (ctx.tender.isTourist && ctx.tender.touristPermission.file) || stage.filePermission) { %>
+                    <label for="formGroupExampleInput">大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
+                    <input type="file" class="" id="upload-im-file" multiple>
+                    <% } %>
+                </div>
+                <div class="modal-height-500" style="overflow:auto;">
+                    <table class="table table-sm table-bordered" style="word-break:break-all; table-layout: fixed">
+                        <thead>
+                        <tr><th width="240">文件名</th><th>上传人</th><th>上传时间</th><th width="52">操作</th></tr>
+                        </thead>
+                        <tbody id="im-attList">
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
 </div>

+ 27 - 1
config/web.js

@@ -830,21 +830,47 @@ const JsFiles = {
                 files: [
                     '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
                     '/public/js/decimal.min.js',
+                    '/public/js/moment/moment.min.js',
                 ],
                 mergeFiles: [
                     '/public/js/sub_menu.js',
                     '/public/js/div_resizer.js',
                     '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
                     '/public/js/zh_calc.js',
                     '/public/js/path_tree.js',
                     '/public/js/gcl_gather.js',
                     '/public/js/jquery/jquery.form.min.js',
-                    '/public/js/moment/moment.min.js',
                     // '/public/js/change_calculation.js',
                     '/public/js/change_information.js',
                 ],
                 mergeFile: 'information',
             },
+            revise: {
+                files: [
+                    '/public/js/js-xlsx/xlsx.full.min.js',
+                    '/public/js/js-xlsx/xlsx.utils.js',
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/math.min.js',
+                    '/public/js/component/menu.js',
+                    '/public/js/moment/moment.min.js',
+                ],
+                mergeFiles: [
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/std_lib.js',
+                    '/public/js/shares/tenders2tree.js',
+                    '/public/js/ledger_check.js',
+                    '/public/js/change_revise.js',
+                ],
+                mergeFile: 'change_revise',
+            },
         },
         datacollect: {
             index: {

+ 67 - 0
db_script/pay_att.js

@@ -0,0 +1,67 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+const audit = require('../app/const/audit');
+
+const mysql = require('mysql');
+const config = process.argv[2];
+console.log(config);
+const mysqlOptions = require(`../config/config.${config}`)({ baseDir: __dirname + '/app', root: __dirname, name: 'calc' }).mysql;
+
+console.log(mysqlOptions);
+const pool = mysql.createPool(mysqlOptions.client);
+
+const querySql = async function (sql, sqlParam) {
+    return new Promise(function (resolve, reject) {
+        pool.getConnection(function (err, conn) {
+            if (err) {
+                reject(err);
+            } else {
+                conn.query(sql, sqlParam, function (err, rows, fields) {
+                    //释放连接
+                    conn.release();
+                    //传递Promise回调对象
+                    resolve({"err": err, "rows": rows, "fields": fields});
+                });
+            }
+        });
+    });
+};
+
+const doComplete = async function () {
+    try {
+        const tenders = await querySql('Select * From zh_tender where ledger_status = ?', [audit.ledger.status.checked]);
+        for (const t of tenders.rows) {
+            const stages = await querySql('Select * From zh_stage where tid = ?', [t.id]);
+            for (const s of stages.rows) {
+                console.log('处理标段: ' + t.name + ' 期: ' + s.order);
+                const auditors = await querySql('Select * From zh_stage_audit where sid = ? and times = ? order by `order`', [s.id, s.times ? s.times : 1]);
+                const curAuditor = auditors.rows.find(x => { return x.status === audit.stage.status.checking});
+                const order = curAuditor ? curAuditor.order : (auditors.rows.length > 0 ? auditors.rows.length : 0);
+                const latestStagePay = await querySql('Select * From zh_stage_pay where sid = ? and stimes = ? and sorder = ?', [s.id, s.times ? s.times : 1, order]);
+                const insertData = [];
+                for (const lsp of latestStagePay.rows) {
+                    if (!lsp.attachment) continue;
+
+                    const attachment = JSON.parse(lsp.attachment);
+                    for (const a of attachment) {
+                        insertData.unshift([t.id, s.id, lsp.pid, a.uid, a.filename, a.fileext, a.filesize, a.filepath, a.in_time, a.username || '']);
+                    }
+                }
+                const result = await querySql('Insert Into zh_pay_attachment (tid, sid, pid, uid, filename, fileext, filesize, filepath, in_time, username ) Values ?', [insertData]);
+            }
+        }
+        await querySql("Update zh_pay_attachment att Left Join zh_project_account acc On att.uid = acc.id Set att.username = acc.name Where att.username = ''");
+        pool.end();
+    } catch(err) {
+        console.log(err);
+    }
+};
+
+doComplete();

+ 15 - 59
sql/update.sql

@@ -1,68 +1,22 @@
+ALTER TABLE `zh_sum_load_history`
+ADD COLUMN `cover`  tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '覆盖数据' AFTER `load_time`;
 
-ALTER TABLE `zh_stage`
-ADD COLUMN `im_start_num`  int(11) NOT NULL DEFAULT 1 AFTER `im_gather_node`;
-
-CREATE TABLE `zh_stage_safe_prod` (
-  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+CREATE TABLE `zh_pay_attachment` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
   `tid` int(11) unsigned NOT NULL COMMENT '标段id',
   `sid` int(11) unsigned NOT NULL COMMENT '期id',
-  `sorder` tinyint(4) unsigned NOT NULL COMMENT '期序号',
-  `uuid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '安全生产标识id',
-  `add_sid` int(11) unsigned NOT NULL COMMENT '新增期id',
-  `add_uid` int(11) NOT NULL COMMENT '新增人id',
-  `add_time` datetime NOT NULL COMMENT '新增时间',
-  `order` int(11) unsigned NOT NULL COMMENT '排序',
-  `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '名称',
-  `unit` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '单位',
-  `unit_price` decimal(24,8) DEFAULT NULL COMMENT '单价',
-  `quantity` decimal(24,8) DEFAULT NULL COMMENT '计划-数量',
-  `total_price` decimal(24,8) DEFAULT NULL COMMENT '计划-金额',
-  `qty` decimal(24,8) DEFAULT NULL COMMENT '本期-数量',
-  `tp` decimal(24,8) DEFAULT NULL COMMENT '本期-金额',
-  `shistory` text CHARACTER SET utf8 COMMENT '本期历史数据',
-  `pre_used` tinyint(4) DEFAULT '0' COMMENT '往期是否使用',
-  `memo` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '备注',
+  `pid` int(11) NOT NULL COMMENT '合同支付id',
+  `uid` int(11) unsigned NOT NULL COMMENT '用户id',
+  `filename` varchar(255) CHARACTER SET utf16 NOT NULL COMMENT '文件名',
+  `fileext` varchar(10) COLLATE utf8_unicode_ci NOT NULL COMMENT '文件后缀',
+  `filesize` varchar(30) COLLATE utf8_unicode_ci NOT NULL COMMENT '文件大小',
+  `filepath` varchar(500) COLLATE utf8_unicode_ci NOT NULL COMMENT '文件存储路径',
+  `in_time` varchar(20) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
+  `renew` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '是否审批通过后上传',
+  `username` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '用户名',
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
 
-CREATE TABLE `zh_stage_temp_land` (
-  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
-  `tid` int(11) unsigned NOT NULL COMMENT '标段id',
-  `sid` int(11) unsigned NOT NULL COMMENT '期id',
-  `sorder` tinyint(4) unsigned NOT NULL COMMENT '期序号',
-  `uuid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '安全生产标识id',
-  `add_sid` int(11) unsigned NOT NULL COMMENT '新增期id',
-  `add_uid` int(11) NOT NULL COMMENT '新增人id',
-  `add_time` datetime NOT NULL COMMENT '新增时间',
-  `order` int(11) unsigned NOT NULL COMMENT '排序',
-  `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '名称',
-  `unit` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '单位',
-  `unit_price` decimal(24,8) DEFAULT NULL COMMENT '单价',
-  `qty` decimal(24,8) DEFAULT NULL COMMENT '本期-数量',
-  `tp` decimal(24,8) DEFAULT NULL COMMENT '本期-金额',
-  `shistory` text CHARACTER SET utf8 COMMENT '本期历史数据',
-  `pre_used` tinyint(4) DEFAULT '0' COMMENT '往期是否使用',
-  `memo` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '备注',
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
-
-CREATE TABLE `zh_stage_detail_attachment` (
-  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
-  `tid` int(11) unsigned NOT NULL COMMENT '标段id',
-  `sid` int(11) unsigned NOT NULL COMMENT '标段id',
-  `sorder` tinyint(4) unsigned NOT NULL COMMENT '期序号',
-  `uuid` varchar(50) CHARACTER SET ascii NOT NULL COMMENT '中间计量附件标识uuid',
-  `lid` varchar(50) CHARACTER SET ascii NOT NULL COMMENT '台账id(标识)',
-  `pid` varchar(50) CHARACTER SET ascii NOT NULL COMMENT '计量单元id(标识)',
-  `code` varchar(50) CHARACTER SET utf8 DEFAULT NULL COMMENT '编号(标识)',
-  `name` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '名称(标识)',
-  `unit` varchar(20) CHARACTER SET utf8 DEFAULT NULL COMMENT '单位(标识)',
-  `unit_price` decimal(24,8) DEFAULT NULL COMMENT '单价(标识)',
-  `pos_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '计量单元名称(标识)',
-  `im_type` tinyint(1) NOT NULL COMMENT '中间计量类型(标识)',
-  `attachment` text CHARACTER SET utf8 COMMENT '附件',
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+ALTER TABLE `zh_change_audit_list` ADD `mx_id` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '计量单元id' AFTER `gcl_id`;
 
+ALTER TABLE `zh_change_audit_list` CHANGE `gcl_id` `gcl_id` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '台账对应id(非准确)';

+ 67 - 0
sql/update20211103.sql

@@ -0,0 +1,67 @@
+
+ALTER TABLE `zh_stage`
+ADD COLUMN `im_start_num`  int(11) NOT NULL DEFAULT 1 AFTER `im_gather_node`;
+
+-- 安全生产
+CREATE TABLE `zh_stage_safe_prod` (
+    `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+    `tid` int(11) unsigned NOT NULL COMMENT '标段id',
+    `sid` int(11) unsigned NOT NULL COMMENT '期id',
+    `sorder` tinyint(4) unsigned NOT NULL COMMENT '期序号',
+    `uuid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '安全生产标识id',
+    `add_sid` int(11) unsigned NOT NULL COMMENT '新增期id',
+    `add_uid` int(11) NOT NULL COMMENT '新增人id',
+    `add_time` datetime NOT NULL COMMENT '新增时间',
+    `order` int(11) unsigned NOT NULL COMMENT '排序',
+    `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '名称',
+    `unit` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '单位',
+    `unit_price` decimal(24,8) DEFAULT NULL COMMENT '单价',
+    `quantity` decimal(24,8) DEFAULT NULL COMMENT '计划-数量',
+    `total_price` decimal(24,8) DEFAULT NULL COMMENT '计划-金额',
+    `qty` decimal(24,8) DEFAULT NULL COMMENT '本期-数量',
+    `tp` decimal(24,8) DEFAULT NULL COMMENT '本期-金额',
+    `shistory` text CHARACTER SET utf8 COMMENT '本期历史数据',
+    `pre_used` tinyint(4) DEFAULT '0' COMMENT '往期是否使用',
+    `memo` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '备注',
+    PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+-- 临时占地
+CREATE TABLE `zh_stage_temp_land` (
+    `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+    `tid` int(11) unsigned NOT NULL COMMENT '标段id',
+    `sid` int(11) unsigned NOT NULL COMMENT '期id',
+    `sorder` tinyint(4) unsigned NOT NULL COMMENT '期序号',
+    `uuid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '安全生产标识id',
+    `add_sid` int(11) unsigned NOT NULL COMMENT '新增期id',
+    `add_uid` int(11) NOT NULL COMMENT '新增人id',
+    `add_time` datetime NOT NULL COMMENT '新增时间',
+    `order` int(11) unsigned NOT NULL COMMENT '排序',
+    `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '名称',
+    `unit` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '单位',
+    `unit_price` decimal(24,8) DEFAULT NULL COMMENT '单价',
+    `qty` decimal(24,8) DEFAULT NULL COMMENT '本期-数量',
+    `tp` decimal(24,8) DEFAULT NULL COMMENT '本期-金额',
+    `shistory` text CHARACTER SET utf8 COMMENT '本期历史数据',
+    `pre_used` tinyint(4) DEFAULT '0' COMMENT '往期是否使用',
+    `memo` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '备注',
+    PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+CREATE TABLE `zh_stage_detail_attachment` (
+    `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+    `tid` int(11) unsigned NOT NULL COMMENT '标段id',
+    `sid` int(11) unsigned NOT NULL COMMENT '标段id',
+    `sorder` tinyint(4) unsigned NOT NULL COMMENT '期序号',
+    `uuid` varchar(50) CHARACTER SET ascii NOT NULL COMMENT '中间计量附件标识uuid',
+    `lid` varchar(50) CHARACTER SET ascii NOT NULL COMMENT '台账id(标识)',
+    `pid` varchar(50) CHARACTER SET ascii NOT NULL COMMENT '计量单元id(标识)',
+    `code` varchar(50) CHARACTER SET utf8 DEFAULT NULL COMMENT '编号(标识)',
+    `name` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '名称(标识)',
+    `unit` varchar(20) CHARACTER SET utf8 DEFAULT NULL COMMENT '单位(标识)',
+    `unit_price` decimal(24,8) DEFAULT NULL COMMENT '单价(标识)',
+    `pos_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '计量单元名称(标识)',
+    `im_type` tinyint(1) NOT NULL COMMENT '中间计量类型(标识)',
+    `attachment` text CHARACTER SET utf8 COMMENT '附件',
+    PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;