Sfoglia il codice sorgente

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

Tony Kang 1 anno fa
parent
commit
8ea8cbb2ef

+ 8 - 0
app/const/audit.js

@@ -399,6 +399,7 @@ const status = {
     back: 5, // 重新上报
     backnew: 6, // 退回
     revise: 9, // 修订变更
+    checkCancel: 11, // 撤回 // 该状态为上一审批人可发起,回到它到审批阶段,并同时新增一条新的审批中记录
 };
 const statusButton = [];
 statusButton[status.uncheck] = '上报';
@@ -408,6 +409,7 @@ statusButton[status.checked] = '';
 statusButton[status.back] = '重新上报';
 statusButton[status.backnew] = '审批';
 statusButton[status.revise] = '修订';
+statusButton[status.checkCancel] = '撤回';
 
 const statusButtonClass = [];
 statusButtonClass[status.uncheck] = 'btn-primary';
@@ -417,6 +419,7 @@ statusButtonClass[status.checked] = '';
 statusButtonClass[status.back] = 'btn-warning';
 statusButtonClass[status.backnew] = 'btn-success';
 statusButtonClass[status.revise] = 'btn-warning';
+statusButtonClass[status.checkCancel] = 'btn-warning';
 
 const statusString = [];
 statusString[status.uncheck] = '未上报';
@@ -426,6 +429,7 @@ statusString[status.checked] = '审批通过';
 statusString[status.back] = '审批退回';
 statusString[status.backnew] = '审批退回';
 statusString[status.revise] = '修订中';
+statusString[status.checkCancel] = '撤回';
 
 const statusClass = [];
 statusClass[status.uncheck] = '';
@@ -435,6 +439,7 @@ statusClass[status.checked] = 'text-success';
 statusClass[status.back] = 'text-warning';
 statusClass[status.backnew] = 'text-warning';
 statusClass[status.revise] = 'text-warning';
+statusClass[status.checkCancel] = 'text-warning';
 
 /* ------------------------------------------------------- */
 
@@ -449,6 +454,7 @@ const auditStatus = {
     checkAgain: 7, // 重新审批
     revise: 9, // 修订变更
     cancelRevise: 10, // 撤销修订
+    checkCancel: 11, // 撤回 // 该状态为上一审批人可发起,回到它到审批阶段,并同时新增一条新的审批中记录
 };
 
 const auditStatusString = [];
@@ -461,6 +467,7 @@ auditStatusString[auditStatus.backnew] = '审批退回';
 auditStatusString[auditStatus.checkAgain] = '重新审批';
 auditStatusString[auditStatus.revise] = '修订变更';
 auditStatusString[auditStatus.cancelRevise] = '撤销修订';
+auditStatusString[auditStatus.checkCancel] = '撤回';
 
 const auditStatusClass = [];
 auditStatusClass[auditStatus.uncheck] = '';
@@ -472,6 +479,7 @@ auditStatusClass[auditStatus.backnew] = 'text-warning';
 auditStatusClass[auditStatus.checkAgain] = 'text-warning';
 auditStatusClass[auditStatus.revise] = 'text-warning';
 auditStatusClass[auditStatus.cancelRevise] = 'text-success';
+auditStatusClass[auditStatus.checkCancel] = 'text-warning';
 
 /* ------------------------------------------------------- */
 

+ 5 - 0
app/const/spread.js

@@ -223,6 +223,11 @@ const stageTz = {
             {title: '|项目节数量2',  colSpan: '|1', rowSpan: '|1', field: 'deal_dgn_qty2', hAlign: 2, width: 60, type: 'Number'},
             {title: '变更|项目节数量1',  colSpan: '2|1', rowSpan: '1|1', field: 'c_dgn_qty1', hAlign: 2, width: 60, type: 'Number'},
             {title: '|项目节数量2',  colSpan: '|1', rowSpan: '|1', field: 'c_dgn_qty2', hAlign: 2, width: 60, type: 'Number'},
+            // {title: '预估变更|数量', colSpan: '3|1', rowSpan: '1|1', field: 'due_qc_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            // {title: '|不计价', colSpan: '|1', rowSpan: '|1', field: 'due_qc_minus_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            // {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'due_qc_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            // {title: '预估决算|数量', colSpan: '2|1', rowSpan: '1|1', field: 'due_final_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            // {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'due_final_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '经济指标',  colSpan: '1', rowSpan: '2', field: 'final_dgn_price', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '本期批注', colSpan: '1', rowSpan: '2', field: 'postil', hAlign: 0, width: 100, formatter: '@', cellType: 'autoTip'},
             {title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},

+ 1 - 0
app/const/standard.js

@@ -23,6 +23,7 @@ const nodeType = [
     {text: '索赔', value: 9},
     {text: '零星工程', value: 16},
     {text: '报废工程', value: 17},
+    {text: '代付代扣', value: 19},
     {text: '新增费用', value: 10},
     {text: '其他费用', value: 11},
     {text: '回收金额', value: 12},

+ 7 - 0
app/const/tender_info.js

@@ -28,6 +28,11 @@ const defaultInfo = {
             corporation: '',
             date: '',
         },
+        sub_build: {
+            company: '',
+            corporation: '',
+            date: '',
+        },
         contract1: {
             company: '',
             corporation: '',
@@ -73,6 +78,7 @@ const defaultInfo = {
         realStartDate: '',
         realEndDate: '',
         structureScale: '',
+        mainContent: '',
     },
     bid_info: {
         controlPrice: 0,
@@ -80,6 +86,7 @@ const defaultInfo = {
         bidStartDate: '',
         bidType: '公开招标',
         dealCalcType: '单价合同',
+        bidYuPrice: 0,
     },
     // 小数位数
     decimal: {

+ 17 - 0
app/controller/change_controller.js

@@ -1193,6 +1193,23 @@ module.exports = app => {
         }
 
         /**
+         * 撤回审批
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async checkAuditCancel(ctx) {
+            try {
+                if (!ctx.change.cancancel) throw '您无权进行该操作';
+
+                await ctx.service.changeAudit.checkCancel(ctx.change);
+                ctx.body = { err: 0, url: ctx.request.header.referer, msg: '' };
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err };
+            }
+        }
+
+        /**
          * 变更公司管理
          * @param {Object} ctx - egg全局变量
          * @return {void}

+ 10 - 0
app/controller/payment_controller.js

@@ -1327,6 +1327,16 @@ module.exports = app => {
                 ctx.body = this.ajaxErrorBody(err, '数据错误');
             }
         }
+        async safeDecimal(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await this.ctx.service.paymentSafeBills.setDecimal(data.decimal);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                this.log(err);
+                ctx.body = this.ajaxErrorBody(err, '设置小数位数错误');
+            }
+        }
     }
 
     return PaymentController;

+ 3 - 1
app/controller/stage_controller.js

@@ -226,7 +226,6 @@ module.exports = app => {
             if (this.ctx.session.sessionProject.dagl) this.ledgerExtraColumn.push('dagl_status', 'dagl_url', 'dagl_limit');
             this.ledgerMemoColumn = ['memo'];
 
-
             this.posColumn = ['id', 'tid', 'lid', 'name', 'position', 'porder', 'quantity', 'add_stage_order', 'drawing_code'];
             if (tender.info.display.stage.realComplete) this.posColumn.push('real_qty');
 
@@ -268,6 +267,8 @@ module.exports = app => {
                     await ctx.service.stageBills.deleteById(surplus);
                 }
             }
+            const changeData = await ctx.service.changeAuditList.getBillsSum(ctx.tender.id);
+
             // 查询截止上期数据
             const preStageData = ctx.stage.order > 1 ? await ctx.service.stageBillsFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1) : [];
             this.ctx.helper.assignRelaData(ledgerData, [
@@ -278,6 +279,7 @@ module.exports = app => {
                 { data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'postil'], prefix: '', relaId: 'lid' },
                 { data: preStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'used'], prefix: 'pre_', relaId: 'lid' },
                 { data: pcData, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'org_price'], prefix: '', relaId: 'lid' },
+                { data: changeData, fields: ['qc_qty', 'qc_tp', 'qc_minus_qty'], prefix: 'due_', relaId: 'gcl_id' },
             ]);
             return ledgerData;
         }

+ 26 - 6
app/lib/analysis_excel.js

@@ -13,7 +13,7 @@ const colDefineType = {
     pos: 2,
 };
 
-const mainReg = /^(GD*)?G?(\d\d)*\d$/i;
+const mainReg = /^(GD*)?G?(\d\d)*\d$/ig;
 const subReg = /^(GD*)?G?[A-Z]{2}(\d\d)+$/i;
 const gdXmjPartReg = /^(GD*)?G?/;
 const specCode106 = {
@@ -60,9 +60,25 @@ const aeUtils = {
             return x.code.indexOf('-') > 0;
         })
     },
+    check18MainCode(code) {
+        const splitPos = code.indexOf('-');
+        if (splitPos < 0) {
+            return !!code.match(mainReg);
+        } else {
+            const codeArr = code.split('-');
+            if (codeArr.length > 3) return false;
+
+            for (const codePart of codeArr) {
+                if (!codePart || !codePart.match(mainReg)) return false;
+            }
+            return true;
+        }
+    },
     isMatch18(tempData) {
+        const checkCode = this.check18MainCode;
         return _.every(tempData, x => {
-            return !x.code || !!x.code.match(mainReg);
+            return !x.code ||  checkCode(x.code);
+            // return !x.code || !!x.code.match(mainReg);
         });
     }
 };
@@ -380,6 +396,7 @@ class ImportStd18Tree extends ImportBaseTree {
         if (!parent.code) return false;
         const numberPart = parent.code.replace(gdXmjPartReg, '');
         if (!numberPart || !codeNumberPart || numberPart.length >= codeNumberPart.length) return false;
+        if (parent === '1060501' && code == '1060501-102') console.log(numberPart);
         return code.indexOf(numberPart) === 0 ||
             code.indexOf('G' + numberPart) === 0 ||
             code.indexOf('GD' + numberPart) === 0;
@@ -422,7 +439,8 @@ class ImportStd18Tree extends ImportBaseTree {
      * @returns {*}
      */
     findXmjParent(code) {
-        if (code.match(mainReg)) {
+        if (aeUtils.check18MainCode(code)) {
+        //if (code.match(mainReg)) {
             if (!this.cacheMainXmjNode) throw '主表项目节找不到父项';
             return this.findMainXmjParent(code);
         } else if (code.match(subReg)) {
@@ -438,7 +456,8 @@ class ImportStd18Tree extends ImportBaseTree {
     defineCacheData(node) {
         super.defineCacheData(node);
         if (node.code) {
-            if (node.code.match(mainReg)) {
+            if (aeUtils.check18MainCode(node.code)) {
+            //if (node.code.match(mainReg)) {
                 node.is_main = true;
                 this.cacheMainXmjNode = node;
                 this.cacheSubXmjNode = null;
@@ -465,7 +484,8 @@ class ImportStd18Tree extends ImportBaseTree {
      * @returns {*}
      */
     addXmjNode(node) {
-        if (!node.code || (!node.code.match(mainReg) && !node.code.match(subReg))) return null;
+        //if (!node.code || (!node.code.match(mainReg) && !node.code.match(subReg))) return null;
+        if (!node.code || (!aeUtils.check18MainCode(node.code) && !node.code.match(subReg))) return null;
 
         node.id = this.ctx.app.uuid.v4();
         node.children = [];
@@ -549,7 +569,7 @@ class AnalysisExcelTree {
     _loadXmjNode(row) {
         try {
             const node = {};
-            node.code = this.ctx.helper.replaceReturn(row[this.colsDef.code]);
+            node.code = _.trimEnd(this.ctx.helper.replaceReturn(row[this.colsDef.code]));
             node.name = this.ctx.helper.replaceReturn(row[this.colsDef.name]);
             node.unit = this.ctx.helper.replaceReturn(row[this.colsDef.unit]);
             node.dgn_qty1 = aeUtils.toNumber(row[this.colsDef.dgn_qty1]);

+ 28 - 16
app/lib/budget_final.js

@@ -211,6 +211,9 @@ class BudgetFinal {
 
     async _afterLoad() {
         const helper = this.ctx.helper;
+        const checkGaiExist = this.finalTree.datas.findIndex(x => {
+            if (x.gai_tp || x.gai_dgn_qty1 || x.gai_dgn_qty2) return true;
+        }) >= 0;
         this.finalTree.afterLoad(node => {
             node.dgn_price = helper.div(node.total_price, node.dgn_qty1, 2);
             node.dgn_qty = node.dgn_qty1
@@ -238,12 +241,21 @@ class BudgetFinal {
             node.final_dgn_qty = node.final_dgn_qty1
                 ? (node.final_dgn_qty2 ? node.final_dgn_qty1 + '/' + node.final_dgn_qty2 : node.final_dgn_qty1)
                 : (node.final_dgn_qty2 ? '/' + node.final_dgn_qty2 : '');
-            node.grow_dgn_qty1 = helper.mul(helper.div(helper.sub(node.final_dgn_qty1, node.gai_dgn_qty1), node.gai_dgn_qty1, 4), 100);
-            node.grow_dgn_qty2 = helper.mul(helper.div(helper.sub(node.final_dgn_qty2, node.gai_dgn_qty2), node.gai_dgn_qty2, 4), 100);
-            node.grow_dgn_qty = node.grow_dgn_qty1
-                ? (node.grow_dgn_qty2 ? node.grow_dgn_qty1 + '/' + node.grow_dgn_qty2 : node.grow_dgn_qty1)
-                : (node.grow_dgn_qty2 ? '/' + node.grow_dgn_qty2 : '');
-            node.grow_tp = helper.mul(helper.div(helper.sub(node.final_tp, node.gai_tp), node.gai_tp, 4), 100);
+            if (checkGaiExist) {
+                node.grow_dgn_qty1 = helper.mul(helper.div(helper.sub(node.final_dgn_qty1, node.gai_dgn_qty1), node.gai_dgn_qty1, 4), 100);
+                node.grow_dgn_qty2 = helper.mul(helper.div(helper.sub(node.final_dgn_qty2, node.gai_dgn_qty2), node.gai_dgn_qty2, 4), 100);
+                node.grow_dgn_qty = node.grow_dgn_qty1
+                    ? (node.grow_dgn_qty2 ? node.grow_dgn_qty1 + '/' + node.grow_dgn_qty2 : node.grow_dgn_qty1)
+                    : (node.grow_dgn_qty2 ? '/' + node.grow_dgn_qty2 : '');
+                node.grow_tp = helper.mul(helper.div(helper.sub(node.final_tp, node.gai_tp), node.gai_tp, 4), 100);
+            } else {
+                node.grow_dgn_qty1 = helper.mul(helper.div(helper.sub(node.final_dgn_qty1, node.yu_dgn_qty1), node.yu_dgn_qty1, 4), 100);
+                node.grow_dgn_qty2 = helper.mul(helper.div(helper.sub(node.final_dgn_qty2, node.yu_dgn_qty2), node.yu_dgn_qty2, 4), 100);
+                node.grow_dgn_qty = node.grow_dgn_qty1
+                    ? (node.grow_dgn_qty2 ? node.grow_dgn_qty1 + '/' + node.grow_dgn_qty2 : node.grow_dgn_qty1)
+                    : (node.grow_dgn_qty2 ? '/' + node.grow_dgn_qty2 : '');
+                node.grow_tp = helper.mul(helper.div(helper.sub(node.final_tp, node.yu_tp), node.yu_tp, 4), 100);
+            }
         });
         this.finalTree.resortChildrenByCustom(function (x, y) {
             const iCode = helper.compareCode(x.code, y.code);
@@ -259,20 +271,20 @@ class BudgetFinal {
         this.finalTree.datas.forEach(x => {
             data.push({
                 id: ctx.app.uuid.v4(), bid, final_id,
-                tree_id: x.id, tree_pid: x.pid, order: x.order, level: x.level, full_path: x.full_path, is_leaf: x.is_leaf,
-                code: x.code, name: x.name, unit: x.unit,
-                gu_dgn_qty1: x.gu_dgn_qty1 || 0, gu_dgn_qty2: x.gu_dgn_qty2 || 0, gu_dgn_qty: x.gu_dgn_qty, gu_dgn_price: x.gu_dgn_price || 0, gu_tp: x.gu_tp || 0,
-                gai_dgn_qty1: x.gai_dgn_qty1 || 0, gai_dgn_qty2: x.gai_dgn_qty2 || 0, gai_dgn_qty: x.gai_dgn_qty, gai_dgn_price: x.gai_dgn_price || 0, gai_tp: x.gai_tp || 0,
-                yu_dgn_qty1: x.yu_dgn_qty1 || 0, yu_dgn_qty2: x.yu_dgn_qty2 || 0, yu_dgn_qty: x.yu_dgn_qty, yu_dgn_price: x.yu_dgn_price || 0, yu_tp: x.yu_tp || 0,
-                zb_dgn_qty1: x.zb_dgn_qty1 || 0, zb_dgn_qty2: x.zb_dgn_qty2 || 0, zb_dgn_qty: x.zb_dgn_qty, zb_dgn_price: x.zb_dgn_price || 0, zb_tp: x.zb_tp || 0,
+                tree_id: x.id, tree_pid: x.pid, order: x.order, level: x.level, full_path: x.full_path, is_leaf: x.children && x.children.length > 0 ? 0 : 1,
+                code: x.code || '', name: x.name || '', unit: x.unit || '',
+                gu_dgn_qty1: x.gu_dgn_qty1 || 0, gu_dgn_qty2: x.gu_dgn_qty2 || 0, gu_dgn_qty: x.gu_dgn_qty || '', gu_dgn_price: x.gu_dgn_price || 0, gu_tp: x.gu_tp || 0,
+                gai_dgn_qty1: x.gai_dgn_qty1 || 0, gai_dgn_qty2: x.gai_dgn_qty2 || 0, gai_dgn_qty: x.gai_dgn_qty || '', gai_dgn_price: x.gai_dgn_price || 0, gai_tp: x.gai_tp || 0,
+                yu_dgn_qty1: x.yu_dgn_qty1 || 0, yu_dgn_qty2: x.yu_dgn_qty2 || 0, yu_dgn_qty: x.yu_dgn_qty || '', yu_dgn_price: x.yu_dgn_price || 0, yu_tp: x.yu_tp || 0,
+                zb_dgn_qty1: x.zb_dgn_qty1 || 0, zb_dgn_qty2: x.zb_dgn_qty2 || 0, zb_dgn_qty: x.zb_dgn_qty || '', zb_dgn_price: x.zb_dgn_price || 0, zb_tp: x.zb_tp || 0,
 
                 dgn_qty1: x.dgn_qty1 || 0, dgn_qty2: x.dgn_qty2 || 0, total_price: x.total_price || 0,
-                final_dgn_qty1: x.final_dgn_qty1 || 0, final_dgn_qty2: x.final_dgn_qty2 || 0, final_tp: x.final_tp || 0,
+                dgn_price: x.dgn_price || 0, dgn_qty: x.dgn_qty || '',
                 deal_dgn_qty1: x.deal_dgn_qty1 || 0, deal_dgn_qty2: x.deal_dgn_qty2 || 0, c_dgn_qty1: x.c_dgn_qty1 || 0, c_dgn_qty2: x.c_dgn_qty2 || 0,
+                final_dgn_qty1: x.final_dgn_qty1 || 0, final_dgn_qty2: x.final_dgn_qty2 || 0, final_tp: x.final_tp || 0,
                 final_contract_tp: x.final_contract_tp || 0, final_qc_tp: x.final_qc_tp || 0,
-                dgn_price: x.dgn_price || 0, dgn_qty: x.dgn_qty,
-                final_dgn_price: x.final_dgn_price || 0, final_dgn_qty: x.final_dgn_qty,
-                grow_dgn_qty1: x.grow_dgn_qty1 || 0, grow_dgn_qty2: x.grow_dgn_qty2 || 0, grow_dgn_qty: x.grow_dgn_qty, grow_tp: x.grow_tp || 0,
+                final_dgn_price: x.final_dgn_price || 0, final_dgn_qty: x.final_dgn_qty || '',
+                grow_dgn_qty1: x.grow_dgn_qty1 || 0, grow_dgn_qty2: x.grow_dgn_qty2 || 0, grow_dgn_qty: x.grow_dgn_qty || '', grow_tp: x.grow_tp || 0,
             })
         });
         return data;

+ 1 - 1
app/lib/revise_price.js

@@ -213,8 +213,8 @@ class revisePriceCalc {
             const p = this.findChangeBillsPrice(b.code, b.name, b.unit, b.unit_price, change.cid);
             let bills_tp;
             if (p) {
-                updateBills.push({ id: b.id, unit_price: p.new_price });
                 bills_tp = this.ctx.helper.mul(p.new_price, b.spamount, change.tp_decimal || decimal.tp);
+                updateBills.push({ id: b.id, unit_price: p.new_price, checked_price: bills_tp });
                 if (!p.rela_cid) p.his_rela_cid.push(change.cid);
             } else {
                 bills_tp = this.ctx.helper.mul(b.unit_price, b.spamount, change.tp_decimal || decimal.tp);

+ 8 - 0
app/middleware/change_check.js

@@ -58,6 +58,13 @@ module.exports = options => {
                     throw '您无权查看该数据';
                 }
                 change.readOnly = true;
+            } else if (change.status === status.back && change.uid !== accountId) {
+                const preAuditors = yield this.service.changeAudit.getListGroupByTimes(change.cid, change.times - 1);
+                const preAuditorIds = _.map(preAuditors, 'uid');
+                if (preAuditorIds.indexOf(accountId) === -1) {
+                    throw '您无权查看该数据';
+                }
+                change.readOnly = true;
             } else { // 其他不可见
                 throw '您无权查看该数据';
             }
@@ -66,6 +73,7 @@ module.exports = options => {
                 this.change.tp_decimal = this.tender.info.decimal.tp;
                 yield this.service.change.updateDecimalAndTp();
             }
+            yield this.service.change.doCheckChangeCanCancel(this.change);
             yield next;
         } catch (err) {
             console.log(err);

+ 1 - 0
app/middleware/payment_detail_check.js

@@ -31,6 +31,7 @@ module.exports = options => {
             if (!detail) {
                 throw '支付审批表单不存在';
             }
+            detail.decimal = detail.bills_decimal ? JSON.parse(detail.bills_decimal) : { qty: 3, tp: 2, up: 2};
             const trInfo = yield this.service.paymentTenderRpt.getDataById(detail.tr_id);
             if (!trInfo) {
                 throw '支付审批报表不存在';

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

@@ -284,7 +284,7 @@ $(document).ready(() => {
                                             <td class="text-left pl-3">${level2.name}</td>
                                             <td>${level2.gai_tp ? ZhCalc.div(level2.gai_tp, 10000) : 0}</td>
                                             <td>${level2.final_tp ? ZhCalc.div(level2.final_tp, 10000) : 0}</td>
-                                            <td class="${level2.gai_tp !== 0 ? (ZhCalc.sub(level2.final_tp, level2.gai_tp) > 0 ? 'text-danger' : ZhCalc.sub(level2.final_tp, level2.gai_tp) === 0 ? '' : 'text-success') : ''}">${level2.gai_tp ? ZhCalc.round(ZhCalc.div(ZhCalc.sub(level2.final_tp, level2.gai_tp), level2.gai_tp), 2) : 0}</td>
+                                            <td class="${level2.gai_tp !== 0 ? (ZhCalc.sub(level2.final_tp, level2.gai_tp) > 0 ? 'text-danger' : ZhCalc.sub(level2.final_tp, level2.gai_tp) === 0 ? '' : 'text-success') : ''}">${level2.gai_tp ? ZhCalc.round(ZhCalc.mul(ZhCalc.div(ZhCalc.sub(level2.final_tp, level2.gai_tp), level2.gai_tp), 100), 2) : 0}</td>
                                         </tr>`;
                 }
                 $('#jianan-table').html(jianAnHtml);

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

@@ -325,7 +325,7 @@ const gclGatherModel = (function () {
         for (const node of nodes) {
             if (node.b_code) {
                 if (node.children.length > 0) {
-                    const gcl = getGclNode(node);
+                    // const gcl = getGclNode(node);
                     recursiveGatherGclData(node.children, leafXmj);
                 } else {
                     loadGatherGclNode(node, leafXmj);

+ 19 - 0
app/public/js/payment_safe.js

@@ -575,6 +575,25 @@ $(document).ready(function() {
                 return !(valid && first && sameParent && !(first.tree_level === 1) && !nodeUsed);
             }
         };
+        $('#psb-decimal').on('show.bs.modal', () => {
+            const decimalObjs = [$('#psb-qty'), $('#psb-up'), $('#psb-tp')];
+            for (const obj of decimalObjs) {
+                obj.val(obj.attr('org'));
+            }
+        });
+        $('#psb-decimal-ok').click(() => {
+            const decimal = { qty: parseInt($('#psb-qty').val()), up: parseInt($('#psb-up').val()), tp: parseInt($('#psb-tp').val()) };
+            postData('decimal', { decimal }, function(result) {
+                if (result.calc) {
+                    const refreshNode = billsObj.tree.loadPostData(result);
+                    billsObj.refreshTree(refreshNode);
+                }
+                $('#psb-qty').attr('org', decimal.qty);
+                $('#psb-up').attr('org', decimal.up);
+                $('#psb-tp').attr('org', decimal.tp);
+                $('#psb-decimal').modal('hide');
+            });
+        });
     }
 
     class AttObj {

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

@@ -301,7 +301,7 @@ $(document).ready(() => {
             setObjEnable($('a[name=base-opr][type=up-move]'), valid && first && sameParent && first.level > 1 && preNode);
             setObjEnable($('a[name=base-opr][type=down-move]'), valid && first && sameParent && first.level > 1 && !tree.isLastSibling(last));
             const posRange = last ? pos.getLedgerPos(last.id) : [];
-            setObjEnable($('a[name=base-opr][type=up-level]'), valid && first && sameParent && tree.getParent(first) && !nodeUsed
+            setObjEnable($('a[name=base-opr][type=up-level]'), valid && first && sameParent && tree.getParent(first)
                 && first.level > 2 && ((!posRange || posRange.length === 0) || tree.isLastSibling(last)));
             const preNodePosRange = preNode ? pos.getLedgerPos(preNode.id) : [];
             setObjEnable($('a[name=base-opr][type=down-level]'), valid && first && sameParent

+ 53 - 1
app/public/js/revise_price.js

@@ -267,6 +267,31 @@ $(document).ready(() => {
                 setPriceHint(revisePrice.data.length > 0);
             });
         },
+        addRevisePrices(datas) {
+            if (datas.length === 0) {
+                toastr.warning('请选择需要调整单价的清单');
+                return;
+            }
+
+            const add = [];
+            for (const data of datas) {
+                const op = revisePrice.data.find(x => {
+                    return x.b_code === data.b_code && x.name === x.name && x.unit === x.unit && checkZero(ZhCalc.sub(x.org_price, data.unit_price)) && (!x.rela_lid || !x.rela_cid);
+                });
+                if (op) continue;
+                add.push({ b_code: data.b_code, name: data.name, unit: data.unit, unit_price: data.unit_price });
+            }
+            if (add.length === 0) {
+                toastr.warning('已存在该单价调整');
+                return;
+            }
+
+            postData(window.location.pathname + '/update', { add }, result => {
+                revisePrice.loadUpdateData(result);
+                SpreadJsObj.reLoadSheetData(priceSheet);
+                setPriceHint(revisePrice.data.length > 0);
+            });
+        },
         /**
          * 删除按钮响应事件
          * @param sheet
@@ -575,6 +600,7 @@ $(document).ready(() => {
 
     class LedgerGcl {
         constructor(setting) {
+            const self = this;
             this.setting = setting;
             this.spread = SpreadJsObj.createNewSpread($(this.setting.selector)[0]);
             this.sheet = this.spread.getActiveSheet();
@@ -587,8 +613,34 @@ $(document).ready(() => {
                     const gcl = SpreadJsObj.getSelectObject(info.sheet);
                     priceOprObj.addRevisePrice(gcl);
                 });
+
+                $.contextMenu({
+                    selector: setting.selector,
+                    build: function ($trigger, e) {
+                        const target = SpreadJsObj.safeRightClickSelection($trigger, e, self.spread);
+                        return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
+                    },
+                    items: {
+                        del: {
+                            name: '添加',
+                            icon: 'fa-sign-in',
+                            callback: function (key, opt) {
+                                const datas = [];
+                                const sel = self.sheet.getSelections()[0];
+                                const node = self.sheet.zh_data[sel.row];
+                                datas.push(node);
+                                if (sel.rowCount > 1) {
+                                    for (let r = 1; r < sel.rowCount; r++) {
+                                        const rNode = self.sheet.zh_data[sel.row + r];
+                                        if (rNode) datas.push(rNode);
+                                    }
+                                }
+                                priceOprObj.addRevisePrices(datas);
+                            },
+                        },
+                    },
+                });
             }
-            const self = this;
             this.spread.bind(spreadNS.Events.SelectionChanged, function (e, info) {
                 self.loadLeafXmj();
             });

+ 4 - 1
app/public/js/stage.js

@@ -266,7 +266,8 @@ $(document).ready(() => {
     // 台账树结构计算相关设置
     stageTreeSetting.updateFields = ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'postil', 'used', 'contract_expr'];
     stageTreeSetting.calcFields = ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp',
-        'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp', 'end_correct_tp', 'final_1_tp', 'end_final_1_tp'];
+        'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp', 'end_correct_tp',
+        'final_1_tp', 'end_final_1_tp', 'due_qc_tp', 'due_final_tp'];
     stageTreeSetting.calcFun = function (node) {
         if (!node.children || node.children.length === 0) {
             node.pre_gather_qty = ZhCalc.add(node.pre_contract_qty, node.pre_qc_qty);
@@ -279,6 +280,7 @@ $(document).ready(() => {
             node.final_1_tp = ZhCalc.mul(node.final_1_qty, node.unit_price, tenderInfo.decimal.tp);
             node.deal_final_1_qty = ZhCalc.add(node.end_qc_minus_qty, node.deal_qty);
             node.end_final_1_qty = ZhCalc.add(node.end_qc_qty, node.final_1_qty);
+            node.due_final_qty = ZhCalc.add(node.quantity, node.due_qc_qty);
         }
         node.pre_gather_tp = ZhCalc.add(node.pre_contract_tp, node.pre_qc_tp);
         node.gather_tp = ZhCalc.sum([node.contract_tp, node.qc_tp, node.pc_tp]);
@@ -287,6 +289,7 @@ $(document).ready(() => {
         node.end_gather_tp = ZhCalc.add(node.pre_gather_tp, node.gather_tp);
         node.end_final_tp = ZhCalc.add(node.end_qc_tp, node.total_price);
         node.end_final_1_tp = ZhCalc.add(node.end_qc_tp, node.final_1_tp);
+        node.due_final_tp = ZhCalc.add(node.quantity, node.due_qc_tp);
         if (!node.children || node.children.length === 0) {
             if (node.end_contract_qty) {
                 node.end_correct_tp = ZhCalc.add(node.end_qc_tp, ZhCalc.mul(node.end_contract_qty, node.unit_price, tenderInfo.decimal.tp));

+ 2 - 0
app/router.js

@@ -518,6 +518,7 @@ 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.post('/tender/:id/change/cancel/audit', sessionAuth, tenderCheck, uncheckTenderCheck, changeCheck, 'changeController.checkAuditCancel');
     // 变更新增部位页
     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');
@@ -798,6 +799,7 @@ module.exports = app => {
     app.get('/payment/:id/safe/:did/compare', sessionAuth, paymentTenderCheck, paymentDetailCheck, 'paymentController.safeCompare');
     app.post('/payment/:id/safe/:did/load', sessionAuth, paymentTenderCheck, paymentDetailCheck, 'paymentController.safeLoad');
     app.post('/payment/:id/safe/:did/update', sessionAuth, paymentTenderCheck, paymentDetailCheck, 'paymentController.safeUpdate');
+    app.post('/payment/:id/safe/:did/decimal', sessionAuth, paymentTenderCheck, paymentDetailCheck, 'paymentController.safeDecimal');
     app.post('/payment/:id/safe/:did/file/upload', sessionAuth, paymentTenderCheck, paymentDetailCheck, 'paymentController.uploadDetailFile');
     app.post('/payment/:id/safe/:did/file/delete', sessionAuth, paymentTenderCheck, paymentDetailCheck, 'paymentController.deleteDetailFile');
 

+ 91 - 13
app/service/change.js

@@ -267,7 +267,9 @@ module.exports = app => {
                     case 0: // 包含你的所有变更令
                         sql =
                             'SELECT a.* FROM ?? AS a WHERE a.tid = ? AND ' +
-                            '(a.uid = ? OR (a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid)) OR a.status = ? )' + stateSql;
+                            '(a.uid = ? OR (a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid))' +
+                            ' OR (a.status = ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times - 1 = b.times GROUP BY b.cid))' +
+                            'OR a.status = ? )' + stateSql;
                         sqlParam = [
                             this.tableName,
                             tenderId,
@@ -275,6 +277,9 @@ module.exports = app => {
                             audit.flow.status.uncheck,
                             this.ctx.service.changeAudit.tableName,
                             this.ctx.session.sessionUser.accountId,
+                            audit.flow.status.back,
+                            this.ctx.service.changeAudit.tableName,
+                            this.ctx.session.sessionUser.accountId,
                             audit.flow.status.checked,
                         ];
                         break;
@@ -284,11 +289,17 @@ module.exports = app => {
                         break;
                     case 5: // 待上报(所有的)PS:取未上报,退回,修订的变更令
                         sql =
-                            'SELECT a.* FROM ?? AS a WHERE ' +
-                            'a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? GROUP BY b.cid) AND ' +
-                            '(a.status = ? OR a.status = ? OR a.status = ?) AND a.tid = ?' + stateSql;
+                            'SELECT a.* FROM ?? AS a WHERE' +
+                            ' ((a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid))' +
+                            ' OR (a.status = ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times - 1 = b.times GROUP BY b.cid)))' +
+                            // 'a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? GROUP BY b.cid)' +
+                            ' AND (a.status = ? OR a.status = ? OR a.status = ?) AND a.tid = ?' + stateSql;
                         sqlParam = [
                             this.tableName,
+                            audit.flow.status.uncheck,
+                            this.ctx.service.changeAudit.tableName,
+                            this.ctx.session.sessionUser.accountId,
+                            audit.flow.status.back,
                             this.ctx.service.changeAudit.tableName,
                             this.ctx.session.sessionUser.accountId,
                             audit.flow.status.uncheck,
@@ -350,7 +361,9 @@ module.exports = app => {
                 case 0: // 包含你的所有变更令
                     const sql =
                         'SELECT count(*) AS count FROM ?? AS a WHERE a.tid = ? AND ' +
-                        '(a.uid = ? OR (a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid)) OR a.status = ? )' + stateSql;
+                        '(a.uid = ? OR (a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid))' +
+                        ' OR (a.status = ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times - 1 = b.times GROUP BY b.cid))' +
+                        ' OR a.status = ? )' + stateSql;
                     const sqlParam = [
                         this.tableName,
                         tenderId,
@@ -358,6 +371,9 @@ module.exports = app => {
                         audit.flow.status.uncheck,
                         this.ctx.service.changeAudit.tableName,
                         this.ctx.session.sessionUser.accountId,
+                        audit.flow.status.back,
+                        this.ctx.service.changeAudit.tableName,
+                        this.ctx.session.sessionUser.accountId,
                         audit.flow.status.checked,
                     ];
                     const result = await this.db.query(sql, sqlParam);
@@ -374,11 +390,17 @@ module.exports = app => {
                     return result6[0].count;
                 case 5: // 待上报(所有的)PS:取未上报,退回,修订的变更令
                     const sql2 =
-                        'SELECT count(*) AS count FROM ?? AS a WHERE ' +
-                        'a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid) ' +
-                        'AND (a.status = ? OR a.status = ? OR a.status = ?) AND a.tid = ?' + stateSql;
+                        'SELECT count(*) AS count FROM ?? AS a WHERE' +
+                        ' ((a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid))' +
+                        ' OR (a.status = ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times - 1 = b.times GROUP BY b.cid)))' +
+                        // 'a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid) ' +
+                        ' AND (a.status = ? OR a.status = ? OR a.status = ?) AND a.tid = ?' + stateSql;
                     const sqlParam2 = [
                         this.tableName,
+                        audit.flow.status.uncheck,
+                        this.ctx.service.changeAudit.tableName,
+                        this.ctx.session.sessionUser.accountId,
+                        audit.flow.status.back,
                         this.ctx.service.changeAudit.tableName,
                         this.ctx.session.sessionUser.accountId,
                         audit.flow.status.uncheck,
@@ -417,7 +439,9 @@ module.exports = app => {
                 case 0: // 包含你的所有变更令
                     const sql =
                         'SELECT SUM(cast (a.total_price as decimal(18,6))) AS total_price FROM ?? AS a WHERE a.tid = ? AND ' +
-                        '(a.uid = ? OR (a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid)) OR a.status = ? )';
+                        '(a.uid = ? OR (a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid))' +
+                        ' OR (a.status = ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times - 1 = b.times GROUP BY b.cid))' +
+                        ' OR a.status = ? )';
                     const sqlParam = [
                         this.tableName,
                         tenderId,
@@ -425,6 +449,9 @@ module.exports = app => {
                         audit.flow.status.uncheck,
                         this.ctx.service.changeAudit.tableName,
                         this.ctx.session.sessionUser.accountId,
+                        audit.flow.status.back,
+                        this.ctx.service.changeAudit.tableName,
+                        this.ctx.session.sessionUser.accountId,
                         audit.flow.status.checked,
                     ];
                     const result = await this.db.query(sql, sqlParam);
@@ -436,11 +463,17 @@ module.exports = app => {
                     return result6[0].total_price ? result6[0].total_price : 0;
                 case 5: // 待上报(所有的)PS:取未上报,退回,修订的变更令
                     const sql2 =
-                        'SELECT SUM(cast (a.total_price as decimal(18,6))) AS total_price FROM ?? AS a WHERE ' +
-                        'a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid) ' +
-                        'AND (a.status = ? OR a.status = ? OR a.status = ?) AND a.tid = ?';
+                        'SELECT SUM(cast (a.total_price as decimal(18,6))) AS total_price FROM ?? AS a WHERE' +
+                        ' ((a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid))' +
+                        ' OR (a.status = ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times - 1 = b.times GROUP BY b.cid)))' +
+                        // 'a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid) ' +
+                        ' AND (a.status = ? OR a.status = ? OR a.status = ?) AND a.tid = ?';
                     const sqlParam2 = [
                         this.tableName,
+                        audit.flow.status.uncheck,
+                        this.ctx.service.changeAudit.tableName,
+                        this.ctx.session.sessionUser.accountId,
+                        audit.flow.status.back,
                         this.ctx.service.changeAudit.tableName,
                         this.ctx.session.sessionUser.accountId,
                         audit.flow.status.uncheck,
@@ -856,6 +889,8 @@ module.exports = app => {
                         };
                         if (postData.audit_next_id === undefined) {
                             list_update.samount = amount !== null ? amount : '';
+                            list_update.checked_amount = amount !== null ? parseFloat(amount) : 0;
+                            list_update.checked_price = amount !== null ? this.ctx.helper.mul(changeListInfo.unit_price, list_update.checked_amount, tp_decimal) : 0;
                         }
                         await this.transaction.update(this.ctx.service.changeAuditList.tableName, list_update);
                     }
@@ -1043,6 +1078,8 @@ module.exports = app => {
                 const changeList = await this.ctx.service.changeAuditList.getAllDataByCondition({
                     where: { cid: changeInfo.cid },
                 });
+                // 生成内容保存表至zh_change_history中,用于撤回
+                await this.ctx.service.changeHistory.saveHistory(this.transaction, changeData, changeList);
                 let total_price = 0;
                 const tp_decimal = changeData.tp_decimal ? changeData.tp_decimal : this.ctx.tender.info.decimal.tp;
                 for (const cl of changeList) {
@@ -1189,7 +1226,7 @@ module.exports = app => {
                 const tp_decimal = changeData.tp_decimal ? changeData.tp_decimal : this.ctx.tender.info.decimal.tp;
                 for (const cl of changeList) {
                     const audit_amount = cl.audit_amount.split(',');
-                    const last_amount = audit_amount[audit_amount.length - 1];
+                    const last_amount = audit_amount[audit_amount.length - 1] ? audit_amount[audit_amount.length - 1] : 0;
                     audit_amount.splice(-1, 1);
                     const list_update = {
                         id: cl.id,
@@ -1234,6 +1271,7 @@ module.exports = app => {
                 };
                 await this.ctx.helper.sendWechat(lastauditInfo.uid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
             } catch (error) {
+                console.log(error);
                 await this.transaction.rollback();
                 result = false;
             }
@@ -1828,6 +1866,46 @@ module.exports = app => {
             }
             return 0;
         }
+
+        async doCheckChangeCanCancel(change) {
+            // 获取当前审批人的上一个审批人,判断是否是当前登录人,并赋予撤回功能,(当审批人存在有审批过时,上一人不允许再撤回)
+            const status = audit.flow.status;
+            const auditStatus = audit.flow.auditStatus;
+            const accountId = this.ctx.session.sessionUser.accountId;
+            const auditors = await this.ctx.service.changeAudit.getListOrderByTimes(change.cid, change.times);
+            change.cancancel = 0;
+            if (change.status !== status.checked && change.status !== status.uncheck) {
+                if (change.status !== status.back) {
+                    // 找出当前操作人上一个审批人,包括审批完成的和退回上一个审批人的,同时当前操作人为第一人时,就是则为原报
+                    const onAuditor = this._.find(auditors, function(item) {
+                        return item.uid === change.curAuditor.uid && item.status === auditStatus.checking;
+                    });
+
+                    const preAudit = onAuditor.usort > 1 ? this._.find(auditors, { usort: onAuditor.usort - 1 }) : false;
+                    const preAid = preAudit ? (preAudit.status !== auditStatus.checkAgain ? preAudit.uid : false) : change.uid;
+                    // console.log(onAuditor, preAudit, auditors);
+                    if (onAuditor.uid === preAid && preAudit.status === auditStatus.checkCancel) {
+                        return;// 不可以多次撤回
+                    } else if (preAid === accountId && (preAid !== change.uid || (preAid === change.uid && preAudit.usite !== 0))) {
+                        if (preAudit.status === auditStatus.checked) {
+                            change.cancancel = 2;// 审批人撤回审批通过
+                        } else if (preAudit.status === auditStatus.backnew) {
+                            change.cancancel = 3;// 审批人撤回审批退回上一人
+                        }
+                        change.preAudit = preAudit;
+                    } else if (preAid === accountId && preAid === change.uid && preAudit.usite === 0) {
+                        change.cancancel = 1;// 原报撤回
+                    }
+                } else {
+                    const lastAuditors = await this.service.changeAudit.getAuditors(change.cid, change.times - 1);
+                    const onAuditor = this._.findLast(lastAuditors, { status: auditStatus.back });
+                    if (onAuditor.uid === accountId) {
+                        change.cancancel = 4;// 审批人撤回退回原报
+                    }
+                }
+            }
+        }
+
     }
 
     return Change;

+ 345 - 1
app/service/change_audit.js

@@ -104,7 +104,7 @@ module.exports = app => {
             const statusConst = auditConst.status;
             const auditStatusConst = auditConst.auditStatus;
             const uid = this.ctx.session.sessionUser.accountId;
-            const changeAuditInfo = await this.getAllDataByCondition({ where: { cid: change.cid, times: change.times, uid }, orders: [['id', 'desc']], limit: 1, offset: 0 });
+            const changeAuditInfo = await this.getAllDataByCondition({ where: { cid: change.cid, times: change.times, uid }, orders: [['usort', 'desc']], limit: 1, offset: 0 });
             if (!change.status === statusConst.checked && (changeAuditInfo === null || changeAuditInfo[0] === undefined) && !ctx.tender.isTourist) {
                 // 无权限查看此变更令
                 return 0;
@@ -680,6 +680,350 @@ module.exports = app => {
             const result = await this.db.queryOne(sql, sqlParam);
             return result ? result.num : 0;
         }
+
+        /**
+         * 审批撤回
+         * @param {Number} stageId - 标段id
+         * @param {Number} times - 第几次审批
+         * @return {Promise<void>}
+         */
+        async checkCancel(change) {
+
+            // 分4种情况,根据ctx.cancancel值判断:
+            // 1.原报发起撤回,当前流程删除,并回到待上报
+            // 2.审批人撤回审批通过,增加流程,并回到它审批中
+            // 3.审批人撤回审批退回上一人,并删除退回人,增加流程,并回到它审批中,并更新计量期状态为审批中
+            // 4.审批人撤回退回原报操作,删除新增的审批流,增加流程,回滚到它审批中
+            switch (change.cancancel) {
+                case 1: await this._userCheckCancel(change); break;
+                case 2: await this._auditCheckCancel(change); break;
+                case 3: await this._auditCheckCancelNoPre(change); break;
+                case 4: await this._auditCheckCancelNo(change); break;
+                default: throw '不可撤回,请刷新页面重试';
+            }
+            // if (stage.cancancel === 5) {
+            //     await this._auditCheckCancelAnd(stage);
+            // } else {
+            //     switch (this.ctx.stage.cancancel) {
+            //         case 1: await this._userCheckCancel(stage); break;
+            //         case 2: await this._auditCheckCancel(stage); break;
+            //         case 3: await this._auditCheckCancelNoPre(stage); break;
+            //         case 4: await this._auditCheckCancelNo(stage); break;
+            //         default: throw '不可撤回,请刷新页面重试';
+            //     }
+            //     // 通知发送 - 第三方更新
+            //     if (this.ctx.session.sessionProject.custom && syncApiConst.notice_type.indexOf(this.ctx.session.sessionProject.customType) !== -1) {
+            //         const base_data = {
+            //             tid: stage.tid,
+            //             sid: stage.id,
+            //             op: 'update',
+            //         };
+            //         this.ctx.helper.syncNoticeSend(this.ctx.session.sessionProject.customType, JSON.stringify(base_data));
+            //         base_data.op = 'update';
+            //         base_data.sid = -1;
+            //         this.ctx.helper.syncNoticeSend(this.ctx.session.sessionProject.customType, JSON.stringify(base_data));
+            //     }
+            // }
+        }
+        /**
+         * 原报撤回,直接改动审批人状态
+         * 如果存在审批人数据,将其改为原报流程数据,但保留原提交人
+         *
+         * 一审 1 A checking  ->  A uncheck status改   pay/jl:删0(jl为增量数据,只删重复部分) 1->0 删1
+         * ...
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _userCheckCancel(change) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 整理当前流程审核人状态更新
+                const curAudit = await this.getDataByCondition({ cid: change.cid, times: change.times, status: auditConst.auditStatus.checking });
+                // // 审批人变成待审批状态
+                await transaction.update(this.tableName, {
+                    id: curAudit.id,
+                    status: auditConst.auditStatus.uncheck,
+                    sin_time: null,
+                    sdesc: null,
+                });
+                // 原报变成checking状态
+                const ybAudit = await this.getDataByCondition({ cid: change.cid, times: change.times, usite: 0, status: auditConst.auditStatus.checked });
+                await transaction.update(this.tableName, {
+                    id: ybAudit.id,
+                    status: auditConst.auditStatus.checking,
+                    sin_time: new Date(),
+                });
+                // 变更令变成待上报状态
+                const options = {
+                    where: {
+                        cid: change.cid,
+                    },
+                };
+                await transaction.update(this.ctx.service.change.tableName, {
+                    status: change.times === 1 ? auditConst.status.uncheck : auditConst.status.back,
+                }, options);
+                await transaction.commit();
+            } catch(err) {
+                await transaction.rollback();
+                throw err;
+            }
+
+        }
+        /**
+         * 审批人撤回审批通过,插入两条数据
+         *
+         * 一审 1 A checked             一审 1 A checked
+         * 二审 2 B checked   pre ->    二审 2 B checked
+         * 三审 3 C checking  cur       二审 3 B checkCancel  增                增extra_his     增tp_his
+         * 四审 4 D uncheck             二审 4 B checking     增                增pay_cur
+         *                             三审 5 C uncheck      order、status改
+         *                             四审 6 D uncheck      order改
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _auditCheckCancel(change) {
+            const time = new Date();
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 整理当前流程审核人状态更新
+                const curAudit = await this.getDataByCondition({ cid: change.cid, times: change.times, status: auditConst.status.checking });
+                const preAudit = change.preAudit;
+                if (!curAudit || curAudit.usite <= 1 || !preAudit) {
+                    throw '撤回用户数据错误';
+                }
+                // 顺移其后审核人流程顺序
+                const sql = 'UPDATE ' + this.tableName + ' SET `usort` = `usort` + 2 WHERE cid = ? AND times = ? AND `usort` > ?';
+                await transaction.query(sql, [change.cid, change.times, curAudit.usort]);
+                // 当前审批人2次添加至流程中
+                const newAuditors = [];
+                // 先入撤回记录
+                newAuditors.push({
+                    tid: change.tid,
+                    cid: change.cid,
+                    uid: preAudit.uid,
+                    name: preAudit.name,
+                    jobs: preAudit.jobs,
+                    company: preAudit.company,
+                    times: change.times,
+                    usite: preAudit.usite,
+                    usort: curAudit.usort,
+                    status: auditConst.auditStatus.checkCancel,
+                    sin_time: time,
+                    sdesc: '',
+                });
+                newAuditors.push({
+                    tid: change.tid,
+                    cid: change.cid,
+                    uid: preAudit.uid,
+                    name: preAudit.name,
+                    jobs: preAudit.jobs,
+                    company: preAudit.company,
+                    times: change.times,
+                    usite: preAudit.usite,
+                    usort: curAudit.usort + 1,
+                    status: auditConst.auditStatus.checking,
+                    sin_time: time,
+                });
+                await transaction.insert(this.tableName, newAuditors);
+                // 当前审批人变成待审批
+                await transaction.update(this.tableName, { id: curAudit.id, usort: curAudit.usort + 2, sin_time: null, status: auditConst.auditStatus.uncheck });
+                // 审批列表数据也要回退
+                const changeList = await this.ctx.service.changeAuditList.getAllDataByCondition({
+                    where: { cid: change.cid },
+                });
+                let total_price = 0;
+                const tp_decimal = change.tp_decimal ? change.tp_decimal : this.ctx.tender.info.decimal.tp;
+                const updateList = [];
+                for (const cl of changeList) {
+                    const audit_amount = cl.audit_amount.split(',');
+                    const last_amount = audit_amount[audit_amount.length - 1] ? audit_amount[audit_amount.length - 1] : 0;
+                    audit_amount.splice(-1, 1);
+                    const list_update = {
+                        id: cl.id,
+                        audit_amount: audit_amount.join(','),
+                        spamount: parseFloat(last_amount),
+                    };
+                    total_price = this.ctx.helper.add(total_price, this.ctx.helper.mul(cl.unit_price, parseFloat(last_amount), tp_decimal));
+                    updateList.push(list_update)
+                }
+                if (updateList.length > 0) await transaction.updateRows(this.ctx.service.changeAuditList.tableName, updateList);
+                // 变更令变成待上报状态
+                const options = {
+                    where: {
+                        cid: change.cid,
+                    },
+                };
+                await transaction.update(this.ctx.service.change.tableName, {
+                    status: auditConst.status.checking,
+                    total_price,
+                    cin_time: Date.parse(time) / 1000,
+                }, options);
+                await transaction.commit();
+            } catch(err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 审批人撤回审批退回上一人,插入两条数据
+         *
+         * 一审 1 A checked                   一审 1 A checked
+         * 二审 2 B checked                   二审 2 B checked
+         * 三审 3 C checkNoPre  pre   ->      三审 3 C checkNoPre
+         * 二审 4 B checking    cur           三审 4 C checkCancel   删4B 增4C    增extra_his    增tp_his
+         * 三审 5 C uncheck                   三审 5 C checking      status改     增pay_cur
+         * 四审 6 D uncheck                   四审 6 D uncheck
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _auditCheckCancelNoPre(change) {
+            const time = new Date();
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                const curAudit = await this.getDataByCondition({ cid: change.cid, times: change.times, status: auditConst.status.checking });
+                const preAudit = change.preAudit;
+                if (!curAudit || curAudit.order <= 1 || !preAudit) {
+                    throw '撤回用户数据错误';
+                }
+                // 整理当前流程审核人状态更新
+                // 删除当前审批人
+                await transaction.delete(this.tableName, { id: curAudit.id });
+                // 添加撤回人到审批流程中
+                const newAuditors = [];
+                newAuditors.push({
+                    tid: change.tid,
+                    cid: change.cid,
+                    uid: preAudit.uid,
+                    name: preAudit.name,
+                    jobs: preAudit.jobs,
+                    company: preAudit.company,
+                    times: change.times,
+                    usite: preAudit.usite,
+                    usort: curAudit.usort,
+                    status: auditConst.auditStatus.checkCancel,
+                    sin_time: time,
+                    sdesc: '',
+                });
+                await transaction.insert(this.tableName, newAuditors);
+                // 更新上一个人,最新审批状态为审批中
+                await transaction.update(this.tableName,  { sin_time: time, status: auditConst.status.checking }, {
+                    where: { cid: change.cid, times: change.times, usort: curAudit.usort + 1 }
+                });
+                // 回退spamount值数据
+                const changeList = await this.ctx.service.changeAuditList.getAllDataByCondition({
+                    where: { cid: change.cid },
+                });
+                let total_price = 0;
+                const tp_decimal = change.tp_decimal ? change.tp_decimal : this.ctx.tender.info.decimal.tp;
+                const updateList = [];
+                for (const cl of changeList) {
+                    const audit_amount = cl.audit_amount.split(',');
+                    const last_amount = audit_amount[audit_amount.length - 1] ? audit_amount[audit_amount.length - 1] : 0;
+                    const list_update = {
+                        id: cl.id,
+                        spamount: parseFloat(last_amount),
+                    };
+                    total_price = this.ctx.helper.add(total_price, this.ctx.helper.mul(cl.unit_price, parseFloat(last_amount), tp_decimal));
+                    updateList.push(list_update);
+                }
+                if (updateList.length > 0) await transaction.updateRows(this.ctx.service.changeAuditList.tableName, updateList);
+                // 变更令变成待上报状态
+                const options = {
+                    where: {
+                        cid: change.cid,
+                    },
+                };
+                await transaction.update(this.ctx.service.change.tableName, {
+                    status: auditConst.status.checking,
+                    total_price,
+                    cin_time: Date.parse(time) / 1000,
+                }, options);
+
+                await transaction.commit();
+            } catch(err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 审批人撤回审批退回原报
+         *
+         * 1# 一审 1 A checked              1# 一审 1 A checked
+         *    二审 2 B checkNo   pre   ->      二审 2 B checkNo
+         *    三审 3 C uncheck                 二审 3 B checkCancel    增       pay: 2#0 -> 1#3   jl: 2#0 -> 1#3   增tp_his   增extra_his
+         *                                    二审 4 B checking       增       pay: 2#0 -> 1#4
+         *                                    三审 5 C uncheck        order改
+         *
+         * 2# 一审 1 A uncheck              2#                        删       pay: 2#0删   jl: 2#0删
+         *    二审 2 B uncheck
+         *    三审 3 C uncheck
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _auditCheckCancelNo(change) {
+
+            const time = new Date();
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                const curAudit = await this.getDataByCondition({ cid: change.cid, times: change.times - 1, status: auditConst.auditStatus.back });
+                // 整理上一个流程审核人状态更新
+                // 顺移其后审核人流程顺序
+                const sql = 'UPDATE ' + this.tableName + ' SET `usort` = `usort` + 2 WHERE cid = ? AND times = ? AND `usort` > ?';
+                await transaction.query(sql, [change.cid, change.times -1, curAudit.usort]);
+                // 当前审批人2次添加至流程中
+                const newAuditors = [];
+                newAuditors.push({
+                    tid: change.tid,
+                    cid: change.cid,
+                    uid: curAudit.uid,
+                    name: curAudit.name,
+                    jobs: curAudit.jobs,
+                    company: curAudit.company,
+                    times: curAudit.times,
+                    usite: curAudit.usite,
+                    usort: curAudit.usort + 1,
+                    status: auditConst.auditStatus.checkCancel,
+                    sin_time: time,
+                    sdesc: '',
+                });
+                newAuditors.push({
+                    tid: change.tid,
+                    cid: change.cid,
+                    uid: curAudit.uid,
+                    name: curAudit.name,
+                    jobs: curAudit.jobs,
+                    company: curAudit.company,
+                    times: curAudit.times,
+                    usite: curAudit.usite,
+                    usort: curAudit.usort + 2,
+                    status: auditConst.auditStatus.checking,
+                    sin_time: time,
+                });
+                await transaction.insert(this.tableName, newAuditors);
+                // 删除当前次审批流
+                await transaction.delete(this.tableName, { cid: change.cid, times: change.times });
+                // 回退数据
+                await this.ctx.service.changeHistory.returnHistory(transaction, change.cid);
+                await transaction.delete(this.ctx.service.changeHistory.tableName, { cid: change.cid });
+                await transaction.commit();
+            } catch(err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
     }
 
     return ChangeAudit;

+ 32 - 0
app/service/change_audit_list.js

@@ -1049,6 +1049,38 @@ module.exports = app => {
                 },
             });
         }
+
+        async getBillsSum(tid) {
+            const sql = 'SELECT gcl_id, Sum(qc_qty) AS qc_qty, Sum(qc_tp) AS qc_tp, Sum(qc_minus_qty) AS qc_minus_qty' +
+                '  FROM(' +
+                '    SELECT cal.gcl_id, Sum(cal.checked_amount) AS qc_qty, Sum(cal.checked_price) AS qc_tp, 0 As qc_minus_qty' +
+                `      FROM ${this.tableName} cal LEFT JOIN ${this.ctx.service.change.tableName} c ON cal.cid = c.cid` +
+                '      WHERE c.tid = ? AND c.valid AND c.status = ? AND cal.is_valuation' +
+                '      GROUP BY cal.gcl_id' +
+                '    UNION ALL ' +
+                '    SELECT cal.gcl_id, 0 As qc_qty, 0 As qc_tp, Sum(cal.checked_amount) AS qc_minus_qty' +
+                `      FROM ${this.tableName} cal LEFT JOIN ${this.ctx.service.change.tableName} c ON cal.cid = c.cid` +
+                '      WHERE c.tid = ? AND c.valid AND c.status = ? AND not cal.is_valuation' +
+                '      GROUP BY cal.gcl_id) As TEMP' +
+                '  GROUP BY gcl_id';
+            return await this.db.query(sql, [tid, audit.flow.status.checked, tid, audit.flow.status.checked]);
+        }
+
+        async getPosSum(tid) {
+            const sql = 'SELECT mx_id, Sum(qc_qty) AS qc_qty, Sum(qc_tp) AS qc_tp, Sum(qc_minus_qty) AS qc_minus_qty' +
+                '  FROM(' +
+                '    SELECT cal.gcl_id, Sum(cal.checked_amount) AS qc_qty, Sum(cal.checked_price) AS qc_tp, 0 As qc_minus_qty' +
+                `      FROM ${this.tableName} cal LEFT JOIN ${this.ctx.service.change.tableName} c ON cal.cid = c.cid` +
+                '      WHERE c.tid = ? AND c.valid AND c.status = ? AND cal.is_valuation' +
+                '      GROUP BY cal.mx_id' +
+                '    UNION ALL ' +
+                '    SELECT cal.mx_id, 0 As qc_qty, 0 As qc_tp, Sum(cal.checked_amount) AS qc_minus_qty' +
+                `      FROM ${this.tableName} cal LEFT JOIN ${this.ctx.service.change.tableName} c ON cal.cid = c.cid` +
+                '      WHERE c.tid = ? AND c.valid AND c.status = ? AND not cal.is_valuation' +
+                '      GROUP BY cal.mx_id) As TEMP' +
+                '  GROUP BY mx_id';
+            return await this.db.query(sql, [tid, audit.flow.status.checked, tid, audit.flow.status.checked]);
+        }
     }
 
     return ChangeAuditList;

+ 3 - 2
app/service/payment_detail.js

@@ -11,7 +11,7 @@ module.exports = app => {
 
         async getValidDetails(tr_id) {
             const details = await this.db.select(this.tableName, {
-                column: ['id', 'in_time', 'tr_id', 'uid', 'status', 'order', 'times', 'code', 's_time'],
+                column: ['id', 'in_time', 'tr_id', 'uid', 'status', 'order', 'times', 'code', 's_time', 'bills_decimal'],
                 where: { tr_id },
                 orders: [['order', 'desc']],
             });
@@ -36,7 +36,7 @@ module.exports = app => {
 
         async getDetail(id) {
             const details = await this.db.select(this.tableName, {
-                column: ['id', 'in_time', 'tr_id', 'uid', 'status', 'order', 'times', 'code', 's_time'],
+                column: ['id', 'in_time', 'tr_id', 'uid', 'status', 'order', 'times', 'code', 's_time', 'bills_decimal'],
                 where: { id },
                 orders: [['order', 'desc']],
             });
@@ -91,6 +91,7 @@ module.exports = app => {
                     report_json: JSON.stringify(pageRst),
                     in_time: new Date(),
                 };
+                if (preDetail) newDetail.bills_decimal = preDetail.bills_decimal;
                 const result = await transaction.insert(this.tableName, newDetail);
                 if (result.affectedRows === 1) {
                     newDetail.id = result.insertId;

+ 0 - 1
app/service/payment_detail_audit.js

@@ -87,7 +87,6 @@ module.exports = app => {
             for (const i in result) {
                 result[i].max_sort = count.num;
             }
-            console.log(result);
             return result;
         }
 

+ 39 - 2
app/service/payment_safe_bills.js

@@ -21,6 +21,8 @@ SafeBillsFields.readQueryFields = [...SafeBillsFields.treeFields, ...SafeBillsFi
 SafeBillsFields.compareQueryFields = [...SafeBillsFields.treeFields, ...SafeBillsFields.textFields, 'unit_price', 'cur_his', 'cur_qty', 'cur_tp'];
 SafeBillsFields.preQueryFields = [...SafeBillsFields.treeFields, ...SafeBillsFields.textFields, 'tender_id', 'unit_price', 'end_qty', 'end_tp', 'add_user_id', 'add_time', 'update_user_id', 'update_time'];
 
+const auditConst = require('../const/audit').stage;
+
 module.exports = app => {
 
     class PaymentSafeBills extends app.BaseTreeService {
@@ -284,7 +286,7 @@ module.exports = app => {
             if (!targetData) throw '粘贴数据错误';
 
             const newParentPath = targetData.full_path.replace(targetData.ledger_id, '');
-            const tpDecimal = this.decimal;
+            const tpDecimal = this.ctx.detail && this.ctx.detail.decimal ? this.ctx.detail.decimal : this.decimal;
 
             const pasteBillsData = [], leafBillsId = [];
             let maxId = await this._getMaxLid(this.ctx.paymentTender.id);
@@ -463,7 +465,7 @@ module.exports = app => {
 
         async updateCalc(detail, data) {
             const helper = this.ctx.helper;
-            const decimal = this.decimal;
+            const decimal = this.ctx.detail && this.ctx.detail.decimal ? this.ctx.detail.decimal : this.decimal;
             // 简单验证数据
             if (!detail) throw '安全生产费不存在';
             if (!data) throw '提交数据错误';
@@ -511,6 +513,41 @@ module.exports = app => {
             }
             if (updateData.length > 0) await transaction.updateRows(this.tableName, updateData);
         }
+
+        async setDecimal(decimal) {
+            if (!this.ctx.detail) throw '读取数据错误';
+            if (this.ctx.detail.order !== this.ctx.detail.highOrder) throw '往期不可修改小数位数';
+            if (this.ctx.detail.status !== auditConst.status.uncheck && this.ctx.detail.status !== auditConst.status.checkNo) throw '仅原报可修改小数位数';
+            const orgDecimal = this.ctx.detail.decimal ? this.ctx.detail.decimal : this.decimal;
+
+            const calcQty = decimal.qty < orgDecimal.qty;
+            const calcUp = decimal.up < orgDecimal.up;
+            const calcTp = decimal.tp < orgDecimal.tp;
+            this.ctx.detail.decimal = { up: decimal.up, tp: decimal.tp, qty: decimal.qty };
+
+            const updateData = [];
+            if (calcQty || calcUp || calcTp) {
+                const calcData = await this.getAllDataByCondition({ where: {detail_id: this.ctx.detail.id, tree_is_leaf: 1 } });
+                for (const cd of calcData) {
+                    const nd = { id: cd.id, tree_id: cd.tree_id };
+                    nd.unit_price = calcUp ? this.ctx.helper.round(cd.unit_price, decimal.up) : cd.unit_price;
+                    nd.cur_qty = calcQty ? this.ctx.helper.round(cd.cur_qty, decimal.qty) : cd.cur_qty;
+                    nd.cur_tp = this.ctx.helper.mul(nd.unit_price, nd.cur_qty, decimal.tp);
+                    nd.end_qty = this.ctx.helper.add(nd.pre_qty, nd.cur_qty);
+                    nd.end_tp = this.ctx.helper.add(nd.pre_tp, nd.cur_tp);
+                    updateData.push(nd);
+                }
+            }
+            const conn = await this.db.beginTransaction();
+            try {
+                await conn.update(this.ctx.service.paymentDetail.tableName, { id: this.ctx.detail.id, decimal: JSON.stringify(this.ctx.detail.decimal)});
+                if (updateData.length > 0) await conn.update(this.tableName, updateData);
+                await conn.commit();
+            } catch(err) {
+                await conn.rollback();
+            }
+            return { calc: calcQty || calcUp || calcTp, update: updateData };
+        }
     }
 
     return PaymentSafeBills;

+ 17 - 1
app/service/report_memory.js

@@ -90,6 +90,8 @@ module.exports = app => {
                     p.final_1_qty = ctx.helper.add(p.end_qc_minus_qty, p.quantity);
                     p.end_final_1_qty = ctx.helper.add(p.end_qc_qty, p.final_1_qty);
                     p.final_1_ratio = ctx.helper.mul(ctx.helper.div(p.end_gather_qty, p.end_final_1_qty, 4), 100);
+
+                    p.due_final_qty = ctx.helper.add(p.quantity, p.due_qc_qty);
                 }
             });
             // 需要缓存的数据
@@ -114,6 +116,7 @@ module.exports = app => {
                     'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'final_1_tp',
                     'end_contract_pc_tp', 'end_qc_pc_tp', 'end_pc_tp',
                     'year_contract_tp', 'year_qc_tp', 'year_contract_pc_tp', 'year_qc_pc_tp', 'year_pc_tp', 'year_gather_tp',
+                    'due_qc_tp', 'due_final_tp',
                 ],
                 calc: function (node, helper, decimal) {
                     if (node.children && node.children.length === 0) {
@@ -123,8 +126,11 @@ module.exports = app => {
                         node.end_qc_qty = helper.add(node.pre_qc_qty, node.qc_qty);
                         node.end_gather_qty = helper.add(node.pre_gather_qty, node.gather_qty);
                         node.end_qc_minus_qty = helper.add(node.pre_qc_minus_qty, node.qc_minus_qty);
+
                         node.final_1_qty = helper.add(node.quantity, node.end_qc_minus_qty);
                         node.end_final_1_qty = helper.add(node.final_1_qty, node.end_qc_qty);
+
+                        node.due_final_qty = helper.add(node.quantity, node.due_qc_qty);
                     }
                     node.pre_gather_tp = helper.add(node.pre_contract_tp, node.pre_qc_tp);
                     node.gather_tp = helper.sum([node.contract_tp, node.qc_tp, node.pc_tp]);
@@ -138,6 +144,8 @@ module.exports = app => {
                     node.final_1_tp = helper.mul(node.unit_price, node.final_1_qty, decimal.tp);
                     node.end_final_1_tp = helper.add(node.final_1_tp, node.end_qc_tp);
                     node.final_1_ratio = helper.mul(helper.div(node.end_gather_tp, node.end_final_1_tp, 4), 100);
+
+                    node.due_final_tp = helper.add(node.total_price, node.due_qc_tp);
                 }
             });
         }
@@ -502,6 +510,9 @@ module.exports = app => {
                 const yearStage = this._checkFieldsExistReg(fields, 'year_') && this.ctx.stage.s_time
                     ? await this._loadStageBillsYear(this.ctx.tender, this.ctx.stage.s_time.split('-')[0])
                     : [];
+                const changeData = this._checkFieldsExistReg(fields, 'due_')
+                    ? await ctx.service.changeAuditList.getBillsSum(this.ctx.tender.id)
+                    : [];
                 this.ctx.helper.assignRelaData(billsData, [
                     { data: memoData, fields: ['memo'], prefix: '', relaId: 'id' },
                     { data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'postil'], prefix: '', relaId: 'lid' },
@@ -509,6 +520,7 @@ module.exports = app => {
                     { data: bpcStage, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'org_price'], prefix: '', relaId: 'lid' },
                     { data: endBpcStage, fields: ['end_contract_pc_tp', 'end_qc_pc_tp', 'end_pc_tp', 'org_price_his'], prefix: '', relaId: 'lid' },
                     { data: yearStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'gather_qty', 'gather_tp'], prefix: 'year_', relaId: 'lid' },
+                    { data: changeData, fields: ['qc_qty', 'qc_tp', 'qc_minus_qty'], prefix: 'due_', relaId: 'gcl_id' },
                 ]);
                 billsData.forEach(x => {
                     if (x.org_price_his && x.org_price_his.length > 0) {
@@ -582,9 +594,13 @@ module.exports = app => {
                 if (this._checkFieldsExist(fields, posFields.stageEnd)) {
                     prePosStage = this.ctx.stage.order > 1 ? await this.ctx.service.stagePosFinal.getFinalData(this.ctx.tender, this.ctx.stage.order - 1) : [];
                 }
+                const changeData = this._checkFieldsExistReg(fields, 'due_')
+                    ? await ctx.service.changeAuditList.getPosSum(this.ctx.tender.id)
+                    : [];
                 this.ctx.helper.assignRelaData(posData, [
                     {data: curPosStage, fields: ['contract_qty', 'qc_qty', 'qc_minus_qty', 'contract_expr', 'postil'], prefix: '', relaId: 'pid'},
-                    {data: prePosStage, fields: ['contract_qty', 'qc_qty', 'qc_minus_qty'], prefix: 'pre_', relaId: 'pid'}
+                    {data: prePosStage, fields: ['contract_qty', 'qc_qty', 'qc_minus_qty'], prefix: 'pre_', relaId: 'pid'},
+                    {data: changeData, fields: ['qc_qty', 'qc_minus_qty'], prefix: 'due_', relaId: 'mx_id'},
                 ]);
                 this.pos.loadDatas(posData);
                 this.pos.calculateAll();

+ 44 - 5
app/service/rpt_gather_memory.js

@@ -70,6 +70,11 @@ const gatherUtils = {
         gatherNode[prefix + "c_dgn_qty1"] = helper.add(gatherNode[prefix + "c_dgn_qty1"], sourceNode.c_dgn_qty1);
         gatherNode[prefix + "c_dgn_qty2"] = helper.add(gatherNode[prefix + "c_dgn_qty2"], sourceNode.c_dgn_qty2);
 
+        gatherNode[prefix + "due_qc_qty"] = helper.add(gatherNode[prefix + "due_qc_qty"], sourceNode.due_qc_qty);
+        gatherNode[prefix + "due_qc_tp"] = helper.add(gatherNode[prefix + "due_qc_tp"], sourceNode.due_qc_tp);
+        gatherNode[prefix + "due_final_qty"] = helper.add(gatherNode[prefix + "due_final_qty"], sourceNode.due_final_qty);
+        gatherNode[prefix + "due_final_tp"] = helper.add(gatherNode[prefix + "due_final_tp"], sourceNode.due_final_tp);
+
         gatherNode.s_qty = helper.add(gatherNode.s_qty, sourceNode.quantity);
         gatherNode.s_tp = helper.add(gatherNode.s_tp, sourceNode.total_price);
         gatherNode.s_deal_qty = helper.add(gatherNode.s_deal_qty, sourceNode.deal_qty);
@@ -104,6 +109,11 @@ const gatherUtils = {
         gatherNode.s_deal_dgn_qty2 = helper.add(gatherNode.s_deal_dgn_qty2, sourceNode.deal_dgn_qty2);
         gatherNode.s_c_dgn_qty1 = helper.add(gatherNode.s_c_dgn_qty1, sourceNode.c_dgn_qty1);
         gatherNode.s_c_dgn_qty2 = helper.add(gatherNode.s_c_dgn_qty2, sourceNode.c_dgn_qty2);
+
+        gatherNode.s_due_qc_qty = helper.add(gatherNode.s_due_qc_qty, sourceNode.due_qc_qty);
+        gatherNode.s_due_qc_tp = helper.add(gatherNode.s_due_qc_tp, sourceNode.due_qc_tp);
+        gatherNode.s_due_final_qty = helper.add(gatherNode.s_due_final_qty, sourceNode.due_final_qty);
+        gatherNode.s_due_final_tp = helper.add(gatherNode.s_due_final_tp, sourceNode.due_final_tp);
     },
     gatherZone: function (tender, gatherNode, sourceNode, prefix, helper) {
         gatherNode[prefix + 'id'] = tender.id;
@@ -188,6 +198,9 @@ const gatherUtils = {
         gatherNode[prefix + "end_qc_qty"] = helper.add(gatherNode[prefix + "end_qc_qty"], sourceNode.end_qc_qty);
         gatherNode[prefix + "end_gather_qty"] = helper.add(gatherNode[prefix + "end_gather_qty"], sourceNode.end_gather_qty);
 
+        gatherNode[prefix + "due_qc_qty"] = helper.add(gatherNode[prefix + "due_qc_qty"], sourceNode.due_qc_qty);
+        gatherNode[prefix + "due_final_qty"] = helper.add(gatherNode[prefix + "due_final_qty"], sourceNode.due_final_qty);
+
         gatherNode.s_qty = helper.add(gatherNode.s_qty, sourceNode.quantity);
 
         gatherNode.s_contract_qty = helper.add(gatherNode.s_contract_qty, sourceNode.contract_qty);
@@ -201,6 +214,9 @@ const gatherUtils = {
         gatherNode.s_end_contract_qty = helper.add(gatherNode.s_end_contract_qty, sourceNode.end_contract_qty);
         gatherNode.s_end_qc_qty = helper.add(gatherNode.s_end_qc_qty, sourceNode.end_qc_qty);
         gatherNode.s_end_gather_qty = helper.add(gatherNode.s_end_gather_qty, sourceNode.end_gather_qty);
+
+        gatherNode.s_due_qc_qty = helper.add(gatherNode.s_due_qc_qty, sourceNode.due_qc_qty);
+        gatherNode.s_due_final_qty = helper.add(gatherNode.s_due_final_qty, sourceNode.due_final_qty);
     },
     gatherZonePos: function (tender, gatherNode, sourceNode, prefix, helper) {
         gatherNode[prefix + 'id'] = tender.id;
@@ -252,6 +268,16 @@ module.exports = app => {
             this.resultDealBills = [];
         }
 
+        _checkFieldsExistReg(source, regStr) {
+            const reg = new RegExp(regStr, 'igm');
+            for (const s of source) {
+                if (reg.test(s)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
         async _getValidStages(tenderId, sort = 'desc') {
             const stages = await this.db.select(this.ctx.service.stage.tableName, {
                 where: { tid: tenderId },
@@ -530,7 +556,12 @@ module.exports = app => {
                 rootId: -1,
                 keys: ['id', 'tender_id', 'ledger_id'],
                 stageId: 'id',
-                calcFields: ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'gather_tp', 'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp'],
+                calcFields: ['deal_tp', 'total_price',
+                    'contract_tp', 'qc_tp', 'contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'gather_tp',
+                    'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp',
+                    'end_contract_tp', 'end_qc_tp', 'end_gather_tp',
+                    'due_qc_tp', 'due_final_tp',
+                ],
                 calc: function (node) {
                     if (node.children && node.children.length === 0) {
                         node.pre_gather_qty = helper.add(node.pre_contract_qty, node.pre_qc_qty);
@@ -538,20 +569,23 @@ module.exports = app => {
                         node.end_contract_qty = helper.add(node.pre_contract_qty, node.contract_qty);
                         node.end_qc_qty = helper.add(node.pre_qc_qty, node.qc_qty);
                         node.end_gather_qty = helper.add(node.pre_gather_qty, node.gather_qty);
+                        node.due_final_qty = helper.add(node.quantity, node.due_qc_qty);
                     }
                     node.pre_gather_tp = helper.add(node.pre_contract_tp, node.pre_qc_tp);
                     node.gather_tp = helper.sum([node.contract_tp, node.qc_tp, node.pc_tp]);
                     node.end_contract_tp = helper.sum([node.pre_contract_tp, node.contract_tp, node.contract_pc_tp]);
                     node.end_qc_tp = helper.sum([node.pre_qc_tp, node.qc_tp, node.qc_pc_tp]);
                     node.end_gather_tp = helper.add(node.pre_gather_tp, node.gather_tp);
+                    node.due_final_tp = helper.add(node.total_price, node.due_qc_tp);
                 }
             });
             const billsData = await this.ctx.service.ledger.getData(tender.id);
             const dgnData = await this.ctx.service.stageBillsDgn.getDgnData(tender.id);
-            for (const d of dgnData) {
-                const l = this.ctx.helper._.find(billsData, {id: d.id});
-                this.ctx.helper._.assignIn(l, d);
-            }
+            const changeBillsData = await ctx.service.changeAuditList.getBillsSum(this.ctx.tender.id);
+            this.ctx.helper.assignRelaData(billsData, [
+                { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id' },
+                { data: changeBillsData, fields: ['qc_qty', 'qc_tp', 'qc_minus_qty'], prefix: 'due_', relaId: 'gcl_id' },
+            ]);
             const pos = new Ledger.pos({
                 id: 'id', ledgerId: 'lid',
                 calc: function (node) {
@@ -560,9 +594,14 @@ module.exports = app => {
                     node.end_contract_qty = helper.add(node.pre_contract_qty, node.contract_qty);
                     node.end_qc_qty = helper.add(node.pre_qc_qty, node.qc_qty);
                     node.end_gather_qty = helper.add(node.pre_gather_qty, node.gather_qty);
+                    node.due_final_qty = ctx.helper.add(node.quantity, node.due_qc_qty);
                 },
             });
             const posData = await this.ctx.service.pos.getPosData({ tid: tender.id });
+            const changePosData = await ctx.service.changeAuditList.getPosSum(this.ctx.tender.id);
+            this.ctx.helper.assignRelaData(posData, [
+                { data: changePosData, fields: ['qc_qty', 'qc_minus_qty'], prefix: 'due_', relaId: 'mx_id' },
+            ]);
             if (stage) {
                 await this.ctx.service.stage.doCheckStage(stage);
                 const curStage = stage.readOnly

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

@@ -75,6 +75,9 @@
                     </div>
                 </div>
                 <div class="pull-right mr-3" id="sp-btn">
+                    <% if (ctx.change.cancancel) { %>
+                        <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-cancel" class="btn btn-danger btn-sm">撤回</a>
+                    <% } %>
                     <!--info状态区分-->
                     <% if (auditStatus === 1) { %>
                         <a href="#sub-ap" data-category="up_change" data-toggle="modal" data-target="#sub-ap" class="btn btn-primary btn-sm">上报审批</a>

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

@@ -297,7 +297,7 @@
                                                             <div class="timeline-item-icon bg-success text-light">
                                                                 <i class="fa fa-check"></i>
                                                             </div>
-                                                        <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise) {%>
+                                                        <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise || auditor.status === auditConst.auditStatus.checkCancel) {%>
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-level-up"></i>
                                                             </div>
@@ -452,7 +452,7 @@
                                                         <div class="timeline-item-icon bg-success text-light">
                                                             <i class="fa fa-check"></i>
                                                         </div>
-                                                    <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise) {%>
+                                                    <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise || auditor.status === auditConst.auditStatus.checkCancel) {%>
                                                         <div class="timeline-item-icon bg-warning text-light">
                                                             <i class="fa fa-level-up"></i>
                                                         </div>
@@ -582,7 +582,7 @@
                                                         <div class="timeline-item-icon bg-success text-light">
                                                             <i class="fa fa-check"></i>
                                                         </div>
-                                                    <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise) {%>
+                                                    <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise || auditor.status === auditConst.auditStatus.checkCancel) {%>
                                                         <div class="timeline-item-icon bg-warning text-light">
                                                             <i class="fa fa-level-up"></i>
                                                         </div>
@@ -671,7 +671,7 @@
                                                     <div class="timeline-item-icon bg-success text-light">
                                                         <i class="fa fa-check"></i>
                                                     </div>
-                                                <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise) {%>
+                                                <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise || auditor.status === auditConst.auditStatus.checkCancel) {%>
                                                     <div class="timeline-item-icon bg-warning text-light">
                                                         <i class="fa fa-level-up"></i>
                                                     </div>
@@ -819,7 +819,7 @@
                                                         <div class="timeline-item-icon bg-success text-light">
                                                             <i class="fa fa-check"></i>
                                                         </div>
-                                                    <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise) {%>
+                                                    <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise || auditor.status === auditConst.auditStatus.checkCancel) {%>
                                                         <div class="timeline-item-icon bg-warning text-light">
                                                             <i class="fa fa-level-up"></i>
                                                         </div>
@@ -908,7 +908,7 @@
                                                     <div class="timeline-item-icon bg-success text-light">
                                                         <i class="fa fa-check"></i>
                                                     </div>
-                                                <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise) {%>
+                                                <% } else if(auditor.status === auditConst.auditStatus.back || auditor.status === auditConst.auditStatus.backnew || auditor.status === auditConst.auditStatus.revise || auditor.status === auditConst.auditStatus.checkCancel) {%>
                                                     <div class="timeline-item-icon bg-warning text-light">
                                                         <i class="fa fa-level-up"></i>
                                                     </div>
@@ -1197,6 +1197,24 @@
         </div>
     </div>
 </div>
+<% if (ctx.change.cancancel) { %>
+    <div class="modal fade" id="sp-down-cancel" data-backdrop="static">
+        <div class="modal-dialog" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">撤回</h5>
+                </div>
+                <div class="modal-body">
+                    <h5>撤回后将回退到你的操作状态,确定撤回?</h5>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">取消</button>
+                    <button type="submit" class="btn btn-danger btn-sm" id="cancel-shenpi-btn">确定撤回</button>
+                </div>
+            </div>
+        </div>
+    </div>
+<% } %>
 <script type="text/javascript">
     const csrf = '<%= ctx.csrf %>';
     const authMobile = '<%= authMobile %>';
@@ -1320,4 +1338,25 @@
             }
         });
     })
+
+    <% if (ctx.change && ctx.change.cancancel) { %>
+    $("#cancel-shenpi-btn").click(function () {
+        const data = {
+            cid: '<%- change.cid %>',
+        };
+        $.ajax({
+            url: '/tender/<%- tender.id %>/change/cancel/audit?_csrf_j=' + csrf,
+            type: 'post',
+            data: data,
+            dataTye: 'json',
+            success: function (response) {
+                if (response.err === 0) {
+                    window.location.href = response.url;
+                } else {
+                    toast(response.msg, 'error');
+                }
+            }
+        });
+    });
+    <% } %>
 </script>

+ 3 - 0
app/view/payment_safe/index.ejs

@@ -27,6 +27,9 @@
                     <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>
                 </div>
+                <div class="d-inline-block">
+                    <a href="" class="btn btn-sm btn-light text-primary" data-toggle="modal" data-placement="bottom" title="设置" data-target="#psb-decimal"><i class="fa fa-cog" aria-hidden="true"></i></a>
+                </div>
                 <% } %>
             </div>
             <div class="ml-auto">

+ 46 - 0
app/view/payment_safe/modal.ejs

@@ -18,4 +18,50 @@
             </div>
         </div>
     </div>
+</div>
+<div class="modal fade show" id="psb-decimal" 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 mb-3">
+                    <h5>小数位数</h5>
+                    <div class="row">
+                        <div class="col-4">
+                            <div class="input-group input-group-sm">
+                                <div class="input-group-prepend">
+                                    <span class="input-group-text">单价</span>
+                                </div>
+                                <input type="number" id="psb-up" class="form-control" value="<%- ctx.detail.decimal.up %>" org="<%- ctx.detail.decimal.up %>" min="0" max="6" <% if (ctx.detail.readOnly) { %>disabled<% } %>>
+                            </div>
+                        </div>
+                        <div class="col-4">
+                            <div class="input-group input-group-sm">
+                                <div class="input-group-prepend">
+                                    <span class="input-group-text">数量</span>
+                                </div>
+                                <input type="number" id="psb-qty" class="form-control" value="<%- ctx.detail.decimal.qty %>" org="<%- ctx.detail.decimal.qty %>" min="0" max="6" <% if (ctx.detail.readOnly) { %>disabled<% } %>>
+                            </div>
+                        </div>
+                        <div class="col-4">
+                            <div class="input-group input-group-sm">
+                                <div class="input-group-prepend">
+                                    <span class="input-group-text">金额</span>
+                                </div>
+                                <input type="number" id="psb-tp" class="form-control" value="<%- ctx.detail.decimal.tp %>" org="<%- ctx.detail.decimal.tp %>" min="0" max="6" <% if (ctx.detail.readOnly) { %>disabled<% } %>>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <% if (!ctx.detail.readOnly) { %>
+                <button type="button" class="btn btn-sm btn-primary" id="psb-decimal-ok">确认</button>
+                <% } %>
+            </div>
+        </div>
+    </div>
 </div>

+ 21 - 0
app/view/sub_proj/data_index.ejs

@@ -76,6 +76,27 @@
                                 </div>
                             </div>
                         </div>
+                        <div class="py-2 font-weight-bold">土地使用及拆迁</div>
+                        <div class="row">
+                            <div class="col-3">
+                                <div class="form-group">
+                                    <label for="">批复用地(亩):</label>
+                                    <input type="text" class="form-control form-control-sm" name="reply_land" value="<%- info.reply_land %>" org="<%- info.reply_land %>"  placeholder="请输入" maxlength="20" oninput="limitMaxLength(this)" onblur="changeInfo(this)">
+                                </div>
+                            </div>
+                            <div class="col-3">
+                                <div class="form-group">
+                                    <label for="">永久占用土地(亩):</label>
+                                    <input type="text" class="form-control form-control-sm" name="occupy_land" value="<%- info.occupy_land %>" org="<%- info.occupy_land %>" placeholder="请输入" maxlength="20" oninput="limitMaxLength(this)" onblur="changeInfo(this)">
+                                </div>
+                            </div>
+                            <div class="col-3">
+                                <div class="form-group">
+                                    <label for="">实际拆迁房屋(m2):</label>
+                                    <input type="text" class="form-control form-control-sm" name="demolish_building" value="<%- info.demolish_building %>" org="<%- info.demolish_building %>" placeholder="请输入" maxlength="20" oninput="limitMaxLength(this)" onblur="changeInfo(this)">
+                                </div>
+                            </div>
+                        </div>
                     </form>
                 </div>
                 <div class="col-12 px-3">

+ 62 - 4
app/view/tender/detail_modal.ejs

@@ -122,6 +122,35 @@
                                 </div>
                             </div>
                             <div class="form-group mb-4">
+                                <label>代建单位:</label>
+                                <div class="row">
+                                    <div class="col-12 mb-2">
+                                        <div class="input-group input-group-sm">
+                                            <div class="input-group-prepend">
+                                                <span class="input-group-text">单位名称</span>
+                                            </div>
+                                            <input type="text" class="form-control" value="" id="sub-build-company" maxlength="100" oninput="limitReturn(this)">
+                                        </div>
+                                    </div>
+                                    <div class="col-6 pr-0">
+                                        <div class="input-group input-group-sm">
+                                            <div class="input-group-prepend">
+                                                <span class="input-group-text">法人代表</span>
+                                            </div>
+                                            <input type="text" class="form-control" value="" id="sub-build-corporation" maxlength="100" oninput="limitReturn(this)">
+                                        </div>
+                                    </div>
+                                    <div class="col-6">
+                                        <div class="input-group input-group-sm">
+                                            <div class="input-group-prepend">
+                                                <span class="input-group-text">签订日期</span>
+                                            </div>
+                                            <input type="date" class="form-control" value="" id="sub-build-date">
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="form-group mb-4">
                                 <label>承包单位1:</label>
                                 <div class="row">
                                     <div class="col-12 mb-2">
@@ -216,7 +245,7 @@
                                     </div>
                                 </div>
                             </div>
-                            <div class="form-group">
+                            <div class="form-group mb-4">
                                 <label>监理单位2:</label>
                                 <div class="row">
                                     <div class="col-12 mb-2">
@@ -245,7 +274,7 @@
                                     </div>
                                 </div>
                             </div>
-                            <div class="form-group">
+                            <div class="form-group mb-4">
                                 <label>检测单位:</label>
                                 <div class="row">
                                     <div class="col-12 mb-2">
@@ -274,7 +303,7 @@
                                     </div>
                                 </div>
                             </div>
-                            <div class="form-group">
+                            <div class="form-group mb-4">
                                 <label>跟踪审计单位:</label>
                                 <div class="row">
                                     <div class="col-12 mb-2">
@@ -394,7 +423,7 @@
                                             <input type="date" class="form-control" value="" id="real-end-date">
                                         </div>
                                     </div>
-                                    <div class="col-12">
+                                    <div class="col-12 mb-2">
                                         <div class="input-group input-group-sm">
                                             <div class="input-group-prepend">
                                                 <span class="input-group-text">构造物规模</span>
@@ -402,6 +431,14 @@
                                             <textarea class="form-control" id="structure-scale"></textarea>
                                         </div>
                                     </div>
+                                    <div class="col-12 mb-2">
+                                        <div class="input-group input-group-sm">
+                                            <div class="input-group-prepend">
+                                                <span class="input-group-text">主要工作内容</span>
+                                            </div>
+                                            <textarea class="form-control" id="main-content"></textarea>
+                                        </div>
+                                    </div>
                                 </div>
                             </div>
                         </div>
@@ -455,6 +492,14 @@
                                             </select>
                                         </div>
                                     </div>
+                                    <div class="col-12 mb-2">
+                                        <div class="input-group input-group-sm">
+                                            <div class="input-group-prepend">
+                                                <span class="input-group-text">招标清单预算</span>
+                                            </div>
+                                            <input type="number" class="form-control nospin" value="" id="bid-yu-price" onchange="checkNumberValid(this)">
+                                        </div>
+                                    </div>
                                 </div>
                             </div>
                         </div>
@@ -982,6 +1027,10 @@
         $('#build-company').val(property.construction_unit.build.company);
         $('#build-corporation').val(property.construction_unit.build.corporation);
         $('#build-date').val(property.construction_unit.build.date);
+        // 代建单位
+        $('#sub-build-company').val(property.construction_unit.sub_build.company);
+        $('#sub-build-corporation').val(property.construction_unit.sub_build.corporation);
+        $('#sub-build-date').val(property.construction_unit.sub_build.date);
         // 承包单位1
         $('#contract1-company').val(property.construction_unit.contract1.company);
         $('#contract1-corporation').val(property.construction_unit.contract1.corporation);
@@ -1020,10 +1069,12 @@
         $('#real-start-date').val(property.tech_param.realStartDate);
         $('#real-end-date').val(property.tech_param.realEndDate);
         $('#structure-scale').val(property.tech_param.structureScale);
+        $('#main-content').val(property.tech_param.mainContent);
 
         // 中标信息
         $('#control-price').val(property.bid_info.controlPrice);
         $('#bid-price').val(property.bid_info.bidPrice);
+        $('#bid-yu-price').val(property.bid_info.bidYuPrice);
         $('#bid-start-date').val(property.bid_info.bidStartDate);
         $('#bid-type').find("option:contains('" + property.bid_info.bidType + "')").attr("selected",true);
         $('#deal-calc-type').find("option:contains('" + property.bid_info.dealCalcType + "')").attr("selected",true);
@@ -1049,6 +1100,11 @@
                     corporation: $('#build-corporation').val(),
                     date: $('#build-date').val(),
                 },
+                sub_build: {
+                    company: $('#sub-build-company').val(),
+                    corporation: $('#sub-build-corporation').val(),
+                    date: $('#sub-build-date').val(),
+                },
                 contract1: {
                     company: $('#contract1-company').val(),
                     corporation: $('#contract1-corporation').val(),
@@ -1093,6 +1149,7 @@
                 realStartDate: $('#real-start-date').val(),
                 realEndDate: $('#real-end-date').val(),
                 structureScale: $('#structure-scale').val(),
+                mainContent: $('#main-content').val(),
             },
             bid_info: {
                 controlPrice: _.toNumber($('#control-price').val()),
@@ -1100,6 +1157,7 @@
                 bidStartDate: $('#bid-start-date').val(),
                 bidType: $('#bid-type').find("option:selected").text(),
                 dealCalcType: $('#deal-calc-type').find("option:selected").text(),
+                bidYuPrice: _.toNumber($('#bid-yu-price').val()),
             }
         };
         const tenderId = window.location.pathname.split('/')[2];

+ 60 - 0
db_script/change_bills.js

@@ -0,0 +1,60 @@
+// 计算变更令,正负变更数
+
+const defaultInfo = require('../app/const/tender_info');
+const BaseUtil = require('./baseUtils');
+const querySql = BaseUtil.querySql;
+const ZhCalc = BaseUtil.ZhCalc;
+
+const checkChange = async function(change, decimal) {
+    const changeBills = await querySql('Select * From zh_change_audit_list where cid = ?', [change.cid]);
+    for (const cb of changeBills) {
+        cb.checked_amount = cb.checked_amount ? cb.checked_amount : 0;
+        cb.checked_price = ZhCalc.mul(cb.checked_amount, cb.unit_price, change.tp_decimal || decimal.tp);
+        await querySql('Update zh_change_audit_list Set checked_price = ?, checked_amount = ? Where id = ?', [cb.checked_price, cb.checked_amount, cb.id]);
+    }
+    console.log(`Update Change ${change.cid}`);
+};
+
+const doComplete = async function() {
+    try {
+        const tender = await querySql('Select * From zh_tender');
+        for (const t of tender) {
+            console.log(`Update Tender ${t.id}:`);
+            const info = await querySql('Select * From zh_tender_info where tid = ?', [t.id]);
+            const decimal = info.length > 0 && info[0].decimal ? JSON.parse(info[0].decimal) : defaultInfo.defaultInfo.decimal;
+
+            const changes = await querySql('Select * From zh_change where tid = ?', [t.id]);
+            for (const c of changes) {
+                await checkChange(c, decimal);
+            }
+        }
+    } catch (err) {
+        console.log(err);
+    }
+    BaseUtil.closePool();
+};
+const doCompleteTest = async function(tid) {
+    try {
+        const tender = await querySql('Select * From zh_tender where id = ?', [tid]);
+        for (const t of tender) {
+            console.log(`Update Tender ${t.id}:`);
+            const info = await querySql('Select * From zh_tender_info where tid = ?', [t.id]);
+            const decimal = info.length > 0 && info[0].decimal ? JSON.parse(info[0].decimal) : defaultInfo.defaultInfo.decimal;
+
+            const changes = await querySql('Select * From zh_change where tid = ?', [t.id]);
+            for (const c of changes) {
+                await checkChange(c, decimal);
+            }
+        }
+    } catch (err) {
+        console.log(err);
+    }
+    BaseUtil.closePool();
+};
+
+const tenderId = process.argv[3];
+if (tenderId) {
+    doCompleteTest(tenderId);
+} else {
+    doComplete()
+}

+ 13 - 0
sql/update.sql

@@ -0,0 +1,13 @@
+ALTER TABLE `zh_change_audit_list`
+ADD COLUMN `checked_amount` decimal(30, 8) NULL DEFAULT 0 COMMENT '审批变更后数量(整型)' AFTER `samount`,
+ADD COLUMN `checked_price` decimal(30, 8) NULL DEFAULT 0 COMMENT '审批变更后金额(整型)' AFTER `checked_amount`;
+
+UPDATE `zh_change_audit_list` SET `checked_amount` = CONVERT(`samount`, DECIMAL) WHERE `samount` != '';
+
+ALTER TABLE `zh_sub_project_info`
+ADD COLUMN `reply_land`  varchar(20) NOT NULL DEFAULT '' COMMENT '批复用地' AFTER `quake_peak_value`,
+ADD COLUMN `occupy_land`  varchar(20) NOT NULL DEFAULT '' COMMENT '永久占用土地' AFTER `reply_land`,
+ADD COLUMN `demolish_building`  varchar(20) NOT NULL DEFAULT '' COMMENT '实际拆迁房屋' AFTER `occupy_land`;
+
+ALTER TABLE `zh_payment_detail`
+ADD COLUMN `bills_decimal`  varchar(100) NOT NULL DEFAULT '{"tp":2, "up": 2, "qty": 3}' COMMENT '小数位数' AFTER `in_time`;