Browse Source

Merge branch 'dev' into uat

MaiXinRong 2 years ago
parent
commit
efb3696def

+ 3 - 1
app/controller/ledger_controller.js

@@ -130,6 +130,7 @@ module.exports = app => {
 
                 const whiteList = this.ctx.app.config.multipart.whitelist;
                 const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                const syncLedger = await this.ctx.service.specPull.syncLedger(this.ctx.session.sessionProject.id);
                 const renderData = {
                     tender: tender.data,
                     tenderInfo: tender.info,
@@ -151,6 +152,7 @@ module.exports = app => {
                     dealBillsPermission: this._canUpdateDealBills(tender.data, auditors.filter(x => {return x.audit_order > 0})),
                     shenpiConst,
                     categoryData,
+                    syncLedgerUrl: syncLedger ? `${ctx.app.config.url3f}/${syncLedger.pull_class}/sync-tz/${tender.id}` : '',
                 };
                 if ((tender.data.ledger_status === auditConst.status.uncheck || tender.data.ledger_status === auditConst.status.checkNo) && tender.data.user_id === ctx.session.sessionUser.accountId) {
                     // renderData.accountGroup = accountGroup;
@@ -231,7 +233,7 @@ module.exports = app => {
          * 复制粘贴整块
          *
          * @param ctx
-         * @return {Promise<void>}
+         * @return {Promise<void>
          */
         async _pasteBlock(ctx, data) {
             if ((isNaN(data.id) || data.id <= 0) ||

+ 5 - 5
app/controller/login_controller.js

@@ -191,11 +191,11 @@ module.exports = app => {
             } catch (error) {
                 this.log(error);
                 ctx.session.loginError = error;
-                if (ctx.request.body && ctx.request.body.project && parseInt(ctx.request.body.hide_code)) {
-                    ctx.redirect('/login/' + ctx.request.body.project);
-                } else {
-                    ctx.redirect('/login');
-                }
+                // if (ctx.request.body && ctx.request.body.project && parseInt(ctx.request.body.hide_code)) {
+                //     ctx.redirect('/login/' + ctx.request.body.project);
+                // } else {
+                ctx.redirect('/login');
+                // }
             }
         }
 

+ 36 - 8
app/lib/ledger.js

@@ -448,7 +448,7 @@ class gatherTree extends baseTree {
         return this._newId++;
     }
 
-    loadGatherNode(node, parent, loadFun) {
+    loadGatherNode(node, parent, loadFun, loadPosFun) {
         const siblings = parent ? parent.children : this.children;
         let cur = siblings.find(function (x) {
             return node.b_code
@@ -471,8 +471,12 @@ class gatherTree extends baseTree {
             this.datas.push(cur);
         }
         loadFun(cur, node);
-        for (const c of node.children) {
-            this.loadGatherNode(c, cur, loadFun);
+        if (node.children && node.children.length > 0) {
+            for (const c of node.children) {
+                this.loadGatherNode(c, cur, loadFun, loadPosFun);
+            }
+        } else if (loadPosFun) {
+            loadPosFun(cur, node);
         }
     }
 
@@ -490,11 +494,10 @@ class gatherTree extends baseTree {
         }
     }
 
-    loadGatherTree(sourceTree,  loadFun) {
+    loadGatherTree(sourceTree, loadFun, loadPosFun) {
         for (const c of sourceTree.children) {
-            this.loadGatherNode(c, null, loadFun);
+            this.loadGatherNode(c, null, loadFun, loadPosFun);
         }
-        // todo load Pos Data;
     }
     resortChildrenByCustom(fun) {
         for (const n of this.datas) {
@@ -564,8 +567,12 @@ class pos {
         }
     }
 
-    getPos(id) {
-        return this.items[itemsPre + id];
+    getLedgerPosKey() {
+        const result = [];
+        for (const prop in this.ledgerPos) {
+            result.push(prop);
+        }
+        return result;
     }
 
     getLedgerPos(mid) {
@@ -597,6 +604,26 @@ class pos {
     }
 }
 
+class gatherPos extends pos {
+    loadGatherPos(ledgerId, sourcePosRange, loadFun) {
+        let posRange = this.getLedgerPos(itemsPre + ledgerId);
+        if (!posRange) {
+            posRange = [];
+            this.ledgerPos[itemsPre + ledgerId] = posRange;
+        }
+        for (const spr of sourcePosRange) {
+            let gp = posRange.find(x => { return x.name === spr.name; });
+            if (!gp) {
+                gp = { name: spr.name };
+                gp[this.setting.ledgerId] = ledgerId;
+                this.datas.push(gp);
+                posRange.push(gp);
+            }
+            loadFun(gp, spr);
+        }
+    }
+}
+
 class checkData {
     constructor(ctx, measureType) {
         this.ctx = ctx;
@@ -1066,6 +1093,7 @@ module.exports = {
     filterTree,
     filterGatherTree,
     gatherTree,
+    gatherPos,
     checkData,
     reviseTree,
 };

+ 69 - 52
app/lib/rm/material.js

@@ -144,67 +144,84 @@ class ReportMemoryMaterial {
         }
     }
 
+    async _loadMaterialMonth(material, gl) {
+        const materialMonth = await this.ctx.service.materialMonth.getAllDataByCondition({
+            where: { mid: material.id },
+            orders: [['mb_id', 'asc'], ['yearmonth', 'asc']],
+        });
+
+        const month = this.ctx.helper._.uniq(materialMonth.map(x => { return x.yearmonth; }));
+        let g;
+        for (const mm of materialMonth) {
+            if (!g || g.id !== mm.mb_id) g = gl.find(x => { return mm.mb_id === x.id; });
+            if (!g.month_msg_tp) g.month_msg_tp = [];
+            if (!g.month) g.month = month.concat([]);
+            const index = month.indexOf(mm.yearmonth);
+            if (index >= 0) g.month_msg_tp[index] = mm.msg_tp;
+        }
+    }
+
     async getMaterialGl(tender_id, material_order, fields) {
         const materials = await this.ctx.service.material.getAllDataByCondition({
             where: {tid: tender_id},
             orders: [['order', 'desc']],
         });
-        if (materials.length > 0) {
-            let result, material;
-            if (materials[0].order === material_order) {
-                material = materials[0];
-                if (material.is_stage_self) {
-                    const sql = 'SELECT msb.id, msb.tid, msb.mid, msb.ms_id, ms.sid, ms.`order` as s_order, mb.order, mb.t_type, mb.code, mb.name, mb.unit, mb.spec, mb.m_type,' +
-                        '    msb.quantity, mb.expr,' +
-                        '    mb.basic_price, mb.basic_times, ' +
-                        '    msb.msg_tp, msb.msg_times, msb.msg_spread, mb.m_up_risk, mb.m_down_risk, msb.m_spread, msb.m_tp, mb.pre_tp, msb.m_tax_tp, mb.tax_pre_tp, mb.origin, ' +
-                        '    msb.remark, msb.is_summary, mb.m_tax, mb.in_time' +
-                        `  FROM ${this.ctx.service.materialStageBills.tableName} msb` +
-                        `  LEFT JOIN ${this.ctx.service.materialBills.tableName} mb ON msb.mb_id = mb.id` +
-                        '  LEFT JOIN ' + this.ctx.service.materialStage.tableName + ' ms ON msb.ms_id = ms.id ' +
-                        `  WHERE msb.mid = ?` +
-                        '  ORDER By msb.ms_id, mb.order';
-                    result = await this.ctx.app.mysql.query(sql, [material.id]);
-                } else {
-                    result = await this.ctx.service.materialBills.getAllDataByCondition({
-                        where: {tid: tender_id}
-                    });
-                }
+        if (materials.length === 0) return [];
+
+        let result, material;
+        if (materials[0].order === material_order) {
+            material = materials[0];
+            if (material.is_stage_self) {
+                const sql = 'SELECT msb.id, msb.tid, msb.mid, msb.ms_id, ms.sid, ms.`order` as s_order, mb.order, mb.t_type, mb.code, mb.name, mb.unit, mb.spec, mb.m_type,' +
+                    '    msb.quantity, mb.expr,' +
+                    '    mb.basic_price, mb.basic_times, ' +
+                    '    msb.msg_tp, msb.msg_times, msb.msg_spread, mb.m_up_risk, mb.m_down_risk, msb.m_spread, msb.m_tp, mb.pre_tp, msb.m_tax_tp, mb.tax_pre_tp, mb.origin, ' +
+                    '    msb.remark, msb.is_summary, mb.m_tax, mb.in_time, mb.origin' +
+                    `  FROM ${this.ctx.service.materialStageBills.tableName} msb` +
+                    `  LEFT JOIN ${this.ctx.service.materialBills.tableName} mb ON msb.mb_id = mb.id` +
+                    '  LEFT JOIN ' + this.ctx.service.materialStage.tableName + ' ms ON msb.ms_id = ms.id ' +
+                    `  WHERE msb.mid = ?` +
+                    '  ORDER By msb.ms_id, mb.order';
+                result = await this.ctx.app.mysql.query(sql, [material.id]);
             } else {
-                const material = this.ctx.helper._.find(materials, {order: material_order});
-                if (!material) return [];
-
-                if (material.is_stage_self) {
-                    const sql = 'SELECT msb.id, msb.tid, msb.mid, msb.ms_id, ms.sid, ms.`order` as s_order, mb.order, mb.t_type, mb.code, mb.name, mb.unit, mb.spec, mb.m_type,' +
-                        '    msb.quantity, mbh.expr,' +
-                        '    mb.basic_price, mb.basic_times, ' +
-                        '    msb.msg_tp, msb.msg_times, msb.msg_spread, mbh.m_up_risk, mbh.m_down_risk, msb.m_spread, msb.m_tp, mbh.pre_tp, msb.m_tax_tp, mbh.tax_pre_tp, mbh.origin, ' +
-                        '    msb.remark, msb.is_summary, mbh.m_tax, mb.in_time' +
-                        `  FROM ${this.ctx.service.materialStageBills.tableName} msb` +
-                        '  LEFT JOIN ' + this.ctx.service.materialBillsHistory.tableName + ' mbh ON msb.mb_id = mbh.mb_id AND msb.mid = mbh.mid' +
-                        '  LEFT JOIN ' + this.ctx.service.materialBills.tableName + ' mb ON msb.mb_id = mb.id ' +
-                        '  LEFT JOIN ' + this.ctx.service.materialStage.tableName + ' ms ON msb.ms_id = ms.id ' +
-                        '  WHERE msb.mid = ?'+
-                        '  ORDER By msb.ms_id, mb.order';
-                    result = await this.ctx.app.mysql.query(sql, [material.id]);
-                } else {
-                    const sql = 'SELECT mb.id, mb.tid, mb.mid, mb.order, mb.t_type, mb.code, mb.name, mb.unit, mb.spec, mb.m_type,' +
-                        '    mbh.quantity, mbh.expr,' +
-                        '    mb.basic_price, mb.basic_times, ' +
-                        '    mbh.msg_tp, mbh.msg_times, mbh.msg_spread, mbh.m_up_risk, mbh.m_down_risk, mbh.m_spread, mbh.m_tp, mbh.pre_tp, mbh.m_tax_tp, mbh.tax_pre_tp, mbh.origin, ' +
-                        '    mb.remark, mb.is_summary, mbh.m_tax, mb.in_time' +
-                        '  FROM ' + this.ctx.service.materialBillsHistory.tableName + ' mbh ' +
-                        '  LEFT JOIN ' + this.ctx.service.materialBills.tableName + ' mb ON mbh.mb_id = mb.id ' +
-                        '  WHERE mbh.tid = ? And mbh.mid = ?'+
-                        '  ORDER By mb.order';
-                    result = await this.ctx.app.mysql.query(sql, [tender_id, material.id]);
-                }
+                result = await this.ctx.service.materialBills.getAllDataByCondition({
+                    where: {tid: tender_id}
+                });
             }
-            this._completeMaterialGl(result);
-            return result;
         } else {
-            return [];
+            material = this.ctx.helper._.find(materials, {order: material_order});
+            if (!material) return [];
+
+            if (material.is_stage_self) {
+                const sql = 'SELECT msb.id, msb.tid, msb.mid, msb.ms_id, ms.sid, ms.`order` as s_order, mb.order, mb.t_type, mb.code, mb.name, mb.unit, mb.spec, mb.m_type,' +
+                    '    msb.quantity, mbh.expr,' +
+                    '    mb.basic_price, mb.basic_times, ' +
+                    '    msb.msg_tp, msb.msg_times, msb.msg_spread, mbh.m_up_risk, mbh.m_down_risk, msb.m_spread, msb.m_tp, mbh.pre_tp, msb.m_tax_tp, mbh.tax_pre_tp, mbh.origin, ' +
+                    '    msb.remark, msb.is_summary, mbh.m_tax, mb.in_time, mbh.origin' +
+                    `  FROM ${this.ctx.service.materialStageBills.tableName} msb` +
+                    '  LEFT JOIN ' + this.ctx.service.materialBillsHistory.tableName + ' mbh ON msb.mb_id = mbh.mb_id AND msb.mid = mbh.mid' +
+                    '  LEFT JOIN ' + this.ctx.service.materialBills.tableName + ' mb ON msb.mb_id = mb.id ' +
+                    '  LEFT JOIN ' + this.ctx.service.materialStage.tableName + ' ms ON msb.ms_id = ms.id ' +
+                    '  WHERE msb.mid = ?'+
+                    '  ORDER By msb.ms_id, mb.order';
+                result = await this.ctx.app.mysql.query(sql, [material.id]);
+            } else {
+                const sql = 'SELECT mb.id, mb.tid, mb.mid, mb.order, mb.t_type, mb.code, mb.name, mb.unit, mb.spec, mb.m_type,' +
+                    '    mbh.quantity, mbh.expr,' +
+                    '    mb.basic_price, mb.basic_times, ' +
+                    '    mbh.msg_tp, mbh.msg_times, mbh.msg_spread, mbh.m_up_risk, mbh.m_down_risk, mbh.m_spread, mbh.m_tp, mbh.pre_tp, mbh.m_tax_tp, mbh.tax_pre_tp, mbh.origin, ' +
+                    '    mb.remark, mb.is_summary, mbh.m_tax, mb.in_time, mbh.origin' +
+                    '  FROM ' + this.ctx.service.materialBillsHistory.tableName + ' mbh ' +
+                    '  LEFT JOIN ' + this.ctx.service.materialBills.tableName + ' mb ON mbh.mb_id = mb.id ' +
+                    '  WHERE mbh.tid = ? And mbh.mid = ?'+
+                    '  ORDER By mb.order';
+                result = await this.ctx.app.mysql.query(sql, [tender_id, material.id]);
+            }
         }
+        this._completeMaterialGl(result);
+
+        if (this._checkFieldsExist(fields, ['month_msg_tp', 'month'])) await this._loadMaterialMonth(material, result);
+        return result;
     }
 
     async getMaterialGlDetail(tender_id, material_order, fields) {

+ 1 - 0
app/lib/rpt_data_analysis.js

@@ -1214,6 +1214,7 @@ const gatherSelectConverse = {
         for (const t of options.table) {
             switch (t) {
                 case 'mem_gather_stage_bills':
+                case 'mem_gather_stage_pos':
                 case 'mem_gather_stage_pay':
                 case 'mem_gather_deal_bills':
                 case 'mem_union_data':

+ 10 - 0
app/public/js/ledger.js

@@ -3487,6 +3487,16 @@ $(document).ready(function() {
 
         SpreadExcelObj.exportSimpleXlsxSheet(setting, data, "台账分解.xlsx");
     });
+    $('#sync-ledger').click(function () {
+        postData(syncLedgerUrl, {}, function (result) {
+            ledgerTree.loadDatas(result.bills);
+            treeCalc.calculateAll(ledgerTree);
+            SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), 'tree', ledgerTree);
+            pos.loadDatas(result.pos);
+            posOperationObj.loadCurPosData();
+            checkShowLast(result.bills.length);
+        });
+    });
 
     const dataChecker = DataChecker({
         checkUrl: window.location.pathname + '/check',

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

@@ -255,7 +255,7 @@ $(document).ready(() => {
         },
     };
 
-    const needUpdateArray = ['quantity', 'msg_tp', 'msg_times', 'msg_spread', 'm_spread', 'm_tp', 'm_tax_tp', 'is_summary', 'remark'];
+    const needUpdateArray = ['quantity', 'expr', 'msg_tp', 'msg_times', 'msg_spread', 'm_spread', 'm_tp', 'm_tax_tp', 'is_summary', 'remark'];
 
     const materialSpreadObj = {
         getMaterialBillsData: function () {
@@ -1326,7 +1326,7 @@ $(document).ready(() => {
                 toastr.error(msg);
                 return false;
             }
-            postData(window.location.pathname + '/save', { type:'expr', id: $('#materialbillsId').val(), expr: expr }, function (result) {
+            postData(window.location.pathname + '/save', { type:'expr', id: $('#materialbillsId').val(), expr: expr, ms_id: $('#myTab').find('.active').data('msid') || null }, function (result) {
                 m_tp = result.m_tp;
                 if (materialTax) {
                     m_tax_tp = result.m_tax_tp;
@@ -1334,13 +1334,11 @@ $(document).ready(() => {
 
                 const sheet = materialSpread.getActiveSheet();
                 const select = SpreadJsObj.getSelectObject(sheet);
-                const index = materialBillsData.indexOf(select);
-                console.log(select, index, result.info);
+                const index = _.findIndex(materialBillsData, { id: select.id });
                 let newIndex = index;
                 if (isStageSelf) {
                     materialSpreadObj.updateMaterialData(result);
                     result.info = materialSpreadObj.updateOneMaterialBill(result.info);
-                    console.log(result.info);
                 }
                 if($('#bills0_list').is(':checked')) {
                     const newMaterialBillsData = _.filter(materialBillsData, function (item) {
@@ -1351,6 +1349,7 @@ $(document).ready(() => {
                     sheet.zh_data[newIndex] = result.info;
                 }
                 materialBillsData.splice(index, 1, result.info);
+                sheet.zh_data.splice(newIndex, 1, result.info);
                 SpreadJsObj.reLoadRowData(sheet, newIndex);
                 resetTpTable();
                 $('#bcyy').modal('hide');

+ 43 - 7
app/public/js/stage.js

@@ -334,6 +334,33 @@ $(document).ready(() => {
         $('#cooperationSelect').html(selectHtml.join(''));
     };
 
+    function checkUsed(tree, pos, node) {
+        if (node.children && node.children.length > 0) {
+            const posterity = tree.getPosterity(node);
+            for (const p of posterity) {
+                if (p.children && p.children.length > 0) continue;
+                if (!checkZero(p.contract_qty) || !checkZero(p.contract_tp) ||
+                    !checkZero(p.qc_qty) || !checkZero(p.qc_tp) || !checkZero(p.qc_minus_qty))
+                    return true;
+                const pPos = pos.getLedgerPos(p.id);
+                if (!pPos || pPos.length === 0) continue;
+                for (const pp of pPos) {
+                    if (!checkZero(pp.contract_qty) || !checkZero(pp.qc_qty) || !checkZero(pp.qc_minus_qty)) return true;
+                }
+            }
+            return false;
+        } else {
+            if (!checkZero(node.contract_qty) || !checkZero(node.contract_tp) ||
+                !checkZero(node.qc_qty) || !checkZero(node.qc_tp) || !checkZero(node.qc_minus_qty))
+                return true;
+            const pPos = pos.getLedgerPos(node.id);
+            if (!pPos || pPos.length === 0) return false;
+            for (const pp of pPos) {
+                if (!checkZero(pp.contract_qty) || !checkZero(pp.qc_qty) || !checkZero(pp.qc_minus_qty)) return true;
+            }
+        }
+    }
+
     const reloadCooperationHtml = function () {
         const html = [];
         const select = $('#cooperationSelect').val();
@@ -350,7 +377,9 @@ $(document).ready(() => {
                 html.push('<td>', p.check ? `已解锁:${p.pwd}` : `<a name="ledger-unlock" lid="${p.ledger_id}" href="javascript: void(0);">解锁</a>`, '</td>');
             }
             html.push('<td>', p.company, '</td>');
-            if (p.check && p.confirm) {
+            if (stage.status === auditConst.status.checking && !checkUsed(stageTree, stagePos, p.node)) {
+                html.push('<td>', '本期未计量', '</td>');
+            } else if (p.check && p.confirm) {
                 html.push('<td>', moment(p.confirm_time).format('YYYY-MM-DD HH:mm') + ' ' + (coopwd ? `<a name="ledger-unconfirm" href="javascript: void(0);" lid="${p.ledger_id}">重新审批</a>` : ''), '</td>');
             } else if (!p.check && p.confirm) {
                 html.push('<td>', moment(p.confirm_time).format('YYYY-MM-DD HH:mm'), '</td>');
@@ -2887,12 +2916,16 @@ $(document).ready(() => {
 
                 const uuid = $(this).attr('data-imid');
                 const file_id = $(this).attr('data-attid');
-
-                postData(window.location.pathname + '/im-file/del', { uuid, file_id }, function (result) {
-                    stageIm.loadUpdateDetailAtt(result);
-                    SpreadJsObj.reLoadRowData(self.sheet, sels[0].row);
-                    self.makeAttTable(select);
+                const att = select.attachment.find(x => {
+                    return x.file_id === file_id;
                 });
+                deleteAfterHint(function () {
+                    postData(window.location.pathname + '/im-file/del', { uuid, file_id }, function (result) {
+                        stageIm.loadUpdateDetailAtt(result);
+                        SpreadJsObj.reLoadRowData(self.sheet, sels[0].row);
+                        self.makeAttTable(select);
+                    });
+                }, `确认删除附件「${att.filename}」?`);
             });
             // 预览附件
             $('body').on('click', '.preview-att', function () {
@@ -4582,15 +4615,17 @@ $(document).ready(() => {
     });
     $('#audit-check0').submit(function (e) {
         // 再检查多人协同确认情况
-        const list = stageTree.pwd.find(x => {return !x.confirm });
+        const list = stageTree.pwd.find(x => {return !x.confirm && checkUsed(stageTree, stagePos, x.node) });
         if(list) {
             toastr.error('请检查多人协同确认情况再审批通过');
             $('#hide-all').hide();
             return false;
         }
         const checkType = parseInt($('[name=checkType]').val());
+        const noUsed = stageTree.pwd.filter(x => {return !checkUsed(stageTree, stagePos, x.node) });
         const data = {
             opinion: $(`${'#sp-done'}`).find('[name=opinion]').val().replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' '),
+            noUsed: noUsed.map(x => { return x.id; }),
             checkType,
         };
         $('#sp-done').modal('hide');
@@ -4663,6 +4698,7 @@ $(document).ready(() => {
         $('#unlock-ok').attr('lid', lid);
         $('#unlock').modal('show');
     });
+    $('#cooperation').on('show.bs.modal', function(){reloadCooperationHtml();});
     $('#unlock-ok').click(function () {
         const lid = this.getAttribute('lid');
         if (!lid) return;

+ 41 - 21
app/service/material_bills.js

@@ -525,30 +525,50 @@ module.exports = app => {
                 const returnData = {};
                 returnData.m_tp = this.ctx.material.m_tp;
                 if (this.ctx.material.is_stage_self) {
+                    if (!data.ms_id) throw '参数有误';
                     const mbInfo = await this.getDataById(data.id);
-                    let all_m_tp = 0;
-                    let all_tax_tp = 0;
-                    for (const sid of this.ctx.material.stage_id.split(',')) {
-                        const materialCalculator = new MaterialCalculator(this.ctx, sid, this.ctx.tender.info);
-                        const quantity = await materialCalculator.calculateExpr(data.expr);
-                        const msInfo = await this.ctx.service.materialStage.getDataByCondition({ mid: this.ctx.material.id, sid });
-                        const msbInfo = await this.ctx.service.materialStageBills.getDataByCondition({ mid: this.ctx.material.id, mb_id: data.id, ms_id: msInfo.id });
-                        const newQuantity = quantity !== 0 ? this.ctx.helper.round(quantity, this.ctx.material.decimal.qty) : null;
-                        const m_tp = newQuantity ? this.ctx.helper.round(this.ctx.helper.mul(newQuantity, msbInfo.m_spread), this.ctx.material.decimal.tp) : null;
-                        const updateData = {
-                            id: data.id,
-                            quantity: newQuantity,
-                            m_tp,
-                            m_tax_tp: this.ctx.helper.round(this.ctx.helper.mul(m_tp, (1 + this.ctx.helper.div(mbInfo.m_tax, 100))), this.ctx.material.decimal.tp),
-                        };
-                        const [one_bill_m_tp, one_bill_tax_tp] = await this.ctx.service.materialStageBills.update(transaction, updateData, msInfo.id);
-                        await this.ctx.service.materialStage.updateMtp(transaction, msInfo.id);
-                        all_m_tp = this.ctx.helper.round(one_bill_m_tp, this.ctx.material.decimal.tp);
-                        all_tax_tp = this.ctx.helper.round(one_bill_tax_tp, this.ctx.material.decimal.tp);
-                    }
-                    const updateBillsData = {
+                    const msInfo = await this.ctx.service.materialStage.getDataById(data.ms_id);
+                    const msbInfo = await this.ctx.service.materialStageBills.getDataByCondition({ mid: this.ctx.material.id, mb_id: data.id, ms_id: data.ms_id });
+                    // let all_m_tp = 0;
+                    // let all_tax_tp = 0;
+                    const materialCalculator = new MaterialCalculator(this.ctx, msInfo.sid, this.ctx.tender.info);
+                    const quantity = await materialCalculator.calculateExpr(data.expr);
+                    const newQuantity = quantity !== 0 ? this.ctx.helper.round(quantity, this.ctx.material.decimal.qty) : null;
+                    const m_tp = newQuantity ? this.ctx.helper.round(this.ctx.helper.mul(newQuantity, msbInfo.m_spread), this.ctx.material.decimal.tp) : null;
+                    const updateData = {
                         id: data.id,
+                        quantity: newQuantity,
                         expr: data.expr,
+                        m_tp,
+                        m_tax_tp: this.ctx.helper.round(this.ctx.helper.mul(m_tp, (1 + this.ctx.helper.div(mbInfo.m_tax, 100))), this.ctx.material.decimal.tp),
+                    };
+                    const [one_bill_m_tp, one_bill_tax_tp] = await this.ctx.service.materialStageBills.update(transaction, updateData, msInfo.id);
+                    await this.ctx.service.materialStage.updateMtp(transaction, msInfo.id);
+                    const all_m_tp = this.ctx.helper.round(one_bill_m_tp, this.ctx.material.decimal.tp);
+                    const all_tax_tp = this.ctx.helper.round(one_bill_tax_tp, this.ctx.material.decimal.tp);
+
+                    // for (const sid of this.ctx.material.stage_id.split(',')) {
+                    //     const materialCalculator = new MaterialCalculator(this.ctx, sid, this.ctx.tender.info);
+                    //     const quantity = await materialCalculator.calculateExpr(data.expr);
+                    //     const msInfo = await this.ctx.service.materialStage.getDataByCondition({ mid: this.ctx.material.id, sid });
+                    //     const msbInfo = await this.ctx.service.materialStageBills.getDataByCondition({ mid: this.ctx.material.id, mb_id: data.id, ms_id: msInfo.id });
+                    //     const newQuantity = quantity !== 0 ? this.ctx.helper.round(quantity, this.ctx.material.decimal.qty) : null;
+                    //     const m_tp = newQuantity ? this.ctx.helper.round(this.ctx.helper.mul(newQuantity, msbInfo.m_spread), this.ctx.material.decimal.tp) : null;
+                    //     const updateData = {
+                    //         id: data.id,
+                    //         quantity: newQuantity,
+                    //         m_tp,
+                    //         m_tax_tp: this.ctx.helper.round(this.ctx.helper.mul(m_tp, (1 + this.ctx.helper.div(mbInfo.m_tax, 100))), this.ctx.material.decimal.tp),
+                    //     };
+                    //     const [one_bill_m_tp, one_bill_tax_tp] = await this.ctx.service.materialStageBills.update(transaction, updateData, msInfo.id);
+                    //     await this.ctx.service.materialStage.updateMtp(transaction, msInfo.id);
+                    //     all_m_tp = this.ctx.helper.round(one_bill_m_tp, this.ctx.material.decimal.tp);
+                    //     all_tax_tp = this.ctx.helper.round(one_bill_tax_tp, this.ctx.material.decimal.tp);
+                    // }
+
+                    const updateBillsData = {
+                        id: data.id,
+                        // expr: data.expr,
                         m_tp: all_m_tp ? all_m_tp : null,
                         m_tax_tp: all_tax_tp ? all_tax_tp : null,
                     };

+ 3 - 1
app/service/material_stage_bills.js

@@ -11,7 +11,7 @@ const auditConst = require('../const/audit').material;
 const materialConst = require('../const/material');
 const MaterialCalculator = require('../lib/material_calc');
 
-const needUpdateArray = ['quantity', 'msg_tp', 'msg_times', 'msg_spread', 'm_spread', 'm_tp', 'm_tax_tp', 'is_summary', 'remark'];
+const needUpdateArray = ['quantity', 'expr', 'msg_tp', 'msg_times', 'msg_spread', 'm_spread', 'm_tp', 'm_tax_tp', 'is_summary', 'remark'];
 
 module.exports = app => {
     class MaterialStageBills extends app.BaseService {
@@ -59,6 +59,7 @@ module.exports = app => {
                     const oneBillsData = {
                         id: mb.id,
                         quantity: null,
+                        expr: null,
                         msg_tp: null,
                         msg_times: null,
                         msg_spread: newmsg_spread,
@@ -168,6 +169,7 @@ module.exports = app => {
                 const quantity = await materialCalculator.calculateExpr(mb.expr);
                 const newTp = quantity !== 0 && quantity !== null ? this.ctx.helper.round(this.ctx.helper.mul(this.ctx.helper.round(quantity, decimal.qty), newm_spread), decimal.tp) : null;
                 msb.quantity = quantity !== 0 && quantity !== null ? this.ctx.helper.round(quantity, decimal.qty) : null;
+                msb.expr = mb.expr;
                 msb.msg_spread = newmsg_spread;
                 msb.m_spread = newm_spread;
                 msb.m_tp = newTp;

+ 8 - 5
app/service/report.js

@@ -169,11 +169,6 @@ module.exports = app => {
                             runnableRst.push(service.reportMemory.getStageTempLand(params.tender_id, params.stage_id, memFieldKeys[filter]));
                             runnableKey.push(filter);
                             break;
-                        case 'mem_gather_stage_bills':
-                            runnableRst.push(service.rptGatherMemory.getGatherStageBills(memFieldKeys[filter],
-                                customDefine.gather_select, customSelect ? customSelect.gather_select : null));
-                            runnableKey.push(filter);
-                            break;
                         case 'mem_gather_tender_info':
                             runnableRst.push(service.rptGatherMemory.getGatherTenderInfo(memFieldKeys[filter],
                                 customDefine.gather_select, customSelect ? customSelect.gather_select : null));
@@ -374,6 +369,14 @@ module.exports = app => {
                         const jhHelper3 = new rptCustomData.jhHelper(this.ctx);
                         rst[filter] = await jhHelper3.gatherBills(memFieldKeys[filter], customDefine.gather_select, customSelect ? customSelect.gather_select : null);
                         break;
+                    case 'mem_gather_stage_bills':
+                        rst[filter] = await service.rptGatherMemory.getGatherStageBills(memFieldKeys[filter],
+                            customDefine.gather_select, customSelect ? customSelect.gather_select : null);
+                        break;
+                    case 'mem_gather_stage_pos':
+                        rst[filter] = await service.rptGatherMemory.getGatherStagePos(memFieldKeys[filter],
+                            customDefine.gather_select, customSelect ? customSelect.gather_select : null);
+                        break;
                     // case 'mem_material_bills':
                     //     rst[filter] = await service.rptGatherMemory.getMaterialBills(params.tender_id, params.material_order, memFieldKeys[filter]);
                     //     break;

+ 169 - 96
app/service/rpt_gather_memory.js

@@ -231,14 +231,8 @@ module.exports = app => {
          */
         constructor(ctx) {
             super(ctx);
-            this.resultTree = new Ledger.gatherTree(ctx, {
-                id: 'id',
-                pid: 'pid',
-                order: 'order',
-                level: 'level',
-                rootId: -1
-            });
-            this.resultPos = new Ledger.pos({ id: 'id', ledgerId: 'lid' });
+            this.resultTree = null;
+            this.resultPos = null;
             this.resultTenderInfo = [];
             this.resultDealPay = [];
             this.resultDealBills = [];
@@ -313,63 +307,9 @@ module.exports = app => {
         /**
          * 台账数据
          */
-        async _gatherStageData(completeData, tender, stage, hasPre) {
-            const helper = this.ctx.helper;
-            completeData.id = tender.id;
-            completeData.name = tender.name;
-            const billsTree = new Ledger.billsTree(this.ctx, {
-                id: 'ledger_id',
-                pid: 'ledger_pid',
-                order: 'order',
-                level: 'level',
-                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'],
-                calc: function (node) {
-                    if (node.children && node.children.length === 0) {
-                        node.pre_gather_qty = helper.add(node.pre_contract_qty, node.pre_qc_qty);
-                        node.gather_qty = helper.add(node.contract_qty, node.qc_qty);
-                        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.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);
-                }
-            });
-            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);
-            }
-
-            if (stage) {
-                await this.ctx.service.stage.doCheckStage(stage);
-                const curStage = stage.readOnly
-                    ? await this.ctx.service.stageBills.getAuditorStageData2(tender.id, stage.id, stage.curTimes, stage.curOrder)
-                    : await this.ctx.service.stageBills.getLastestStageData2(tender.id, stage.id);
-                const preStage = hasPre && stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(tender, stage.order - 1) : [];
-                const bpcStage = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: stage.id } });
-                this.ctx.helper.assignRelaData(billsData, [
-                    { data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid' },
-                    { data: preStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: 'pre_', relaId: 'lid' },
-                    { data: bpcStage, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp'], prefix: '', relaId: 'lid' },
-                ]);
-            }
-            billsTree.loadDatas(billsData);
-            billsTree.calculateAll();
-            this.resultTree.loadGatherTree(billsTree, function (gatherNode, sourceNode) {
-                gatherUtils.gatherStage(tender, gatherNode, sourceNode, completeData.prefix, helper);
-            });
-        }
 
         async _gatherStagesData(completeData, tender, stages) {
+            const resultPos = this.resultPos;
             const helper = this.ctx.helper;
             completeData.id = tender.id;
             completeData.name = tender.name;
@@ -413,71 +353,172 @@ module.exports = app => {
                 }
             });
             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 pos = new Ledger.pos({
+                id: 'id', ledgerId: 'lid',
+                calc: function (node) {
+                    node.gather_qty = helper.add(node.contract_qty, node.qc_qty);
+                },
+            });
+            const posData = await this.ctx.service.pos.getAllDataByCondition({ where: { tid: tender.id} });
             let billsIndexData = {};
             for (const bd of billsData) {
                 billsIndexData[indexPre + bd.id] = bd;
             }
+            let posIndexData = {};
+            for (const p of posData) {
+                posIndexData[indexPre + p.id] = p;
+            }
 
             for (const stage of stages) {
                 await this.ctx.service.stage.doCheckStage(stage);
                 const curStage = stage.readOnly
                     ? await this.ctx.service.stageBills.getAuditorStageData2(tender.id, stage.id, stage.curTimes, stage.curOrder)
                     : await this.ctx.service.stageBills.getLastestStageData2(tender.id, stage.id);
-                const bpcStage = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: this.ctx.stage.id } });
+                const bpcStage = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: stage.id } });
                 sumAssignRelaData(billsIndexData, [
                     {data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'},
                     { data: bpcStage, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp'], prefix: '', relaId: 'lid' },
                 ]);
+                const curStagePos = stage.readOnly
+                    ? await this.ctx.service.stagePos.getAuditorStageData2(tender.id, stage.id, stage.curTimes, stage.curOrder)
+                    : await this.ctx.service.stagePos.getLastestStageData2(tender.id, stage.id);
+                sumAssignRelaData(posIndexData, [
+                    {data: curStagePos, fields: ['contract_qty', 'qc_qty'], prefix: '', relaId: 'pid'},
+                ]);
             }
 
             billsTree.loadDatas(billsData);
             billsTree.calculateAll();
+            pos.loadDatas(posData);
+            pos.calculateAll();
             this.resultTree.loadGatherTree(billsTree, function (gatherNode, sourceNode) {
                 gatherUtils.gatherZone(tender, gatherNode, sourceNode, completeData.prefix, helper);
+            }, function (gatherNode, sourceNode) {
+                const posRange = pos.getLedgerPos(sourceNode.id);
+                if (!posRange || posRange.length === 0) return;
+                resultPos.loadGatherPos(gatherNode.id, posRange, (gatherPos, sourcePos) => {
+                    gatherUtils.gatherZonePos(tender, gatherPos, sourcePos, completeData.prefix, helper);
+                });
             });
         }
+        async _gatherZoneData(tender, completeData, zone) {
+            const stages = await this._getTimeZoneStages(tender, zone);
+            await this._gatherStagesData(completeData, tender, stages);
+        }
+
+        async _gatherStageData(completeData, tender, stage, hasPre) {
+            const helper = this.ctx.helper;
+            const resultPos = this.resultPos;
+            completeData.id = tender.id;
+            completeData.name = tender.name;
+
+            const billsTree = new Ledger.billsTree(this.ctx, {
+                id: 'ledger_id',
+                pid: 'ledger_pid',
+                order: 'order',
+                level: 'level',
+                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'],
+                calc: function (node) {
+                    if (node.children && node.children.length === 0) {
+                        node.pre_gather_qty = helper.add(node.pre_contract_qty, node.pre_qc_qty);
+                        node.gather_qty = helper.add(node.contract_qty, node.qc_qty);
+                        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.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);
+                }
+            });
+            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 pos = new Ledger.pos({
+                id: 'id', ledgerId: 'lid',
+                calc: function (node) {
+                    node.pre_gather_qty = helper.add(node.pre_contract_qty, node.pre_qc_qty);
+                    node.gather_qty = helper.add(node.contract_qty, node.qc_qty);
+                    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);
+                },
+            });
+            const posData = await this.ctx.service.pos.getPosData({ tid: tender.id });
+            if (stage) {
+                await this.ctx.service.stage.doCheckStage(stage);
+                const curStage = stage.readOnly
+                    ? await this.ctx.service.stageBills.getAuditorStageData2(tender.id, stage.id, stage.curTimes, stage.curOrder)
+                    : await this.ctx.service.stageBills.getLastestStageData2(tender.id, stage.id);
+                const preStage = hasPre && stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(tender, stage.order - 1) : [];
+                const bpcStage = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: stage.id } });
+                this.ctx.helper.assignRelaData(billsData, [
+                    { data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid' },
+                    { data: preStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: 'pre_', relaId: 'lid' },
+                    { data: bpcStage, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp'], prefix: '', relaId: 'lid' },
+                ]);
 
+                const curStagePos = stage.readOnly
+                    ? await this.ctx.service.stagePos.getAuditorStageData2(tender.id, stage.id, stage.curTimes, stage.curOrder)
+                    : await this.ctx.service.stagePos.getLastestStageData2(tender.id, stage.id);
+                const preStagePos = hasPre && stage.order > 1 ? await this.ctx.service.stagePosFinal.getFinalData(tender, stage.order - 1) : [];
+                this.ctx.helper.assignRelaData(posData, [
+                    { data: curStagePos, fields: ['contract_qty', 'qc_qty'], prefix: '', relaId: 'pid' },
+                    { data: preStagePos, fields: ['contract_qty', 'qc_qty'], prefix: 'pre_', relaId: 'pid' },
+                ]);
+            }
+            billsTree.loadDatas(billsData);
+            billsTree.calculateAll();
+            pos.loadDatas(posData);
+            pos.calculateAll();
+
+            this.resultTree.loadGatherTree(billsTree, function (gatherNode, sourceNode) {
+                gatherUtils.gatherStage(tender, gatherNode, sourceNode, completeData.prefix, helper);
+            }, function (gatherNode, sourceNode) {
+                const posRange = pos.getLedgerPos(sourceNode.id);
+                if (!posRange || posRange.length === 0) return;
+                resultPos.loadGatherPos(gatherNode.id, posRange, (gatherPos, sourcePos) => {
+                    gatherUtils.gatherStagePos(tender, gatherPos, sourcePos, completeData.prefix, helper);
+                });
+            });
+        }
         async _gatherMonthData(tender, completeData, month, hasPre) {
             const stages = await this._getValidStages(tender.id);
             const stage = this.ctx.helper._.find(stages, {s_time: month});
             await this._gatherStageData(completeData, tender, stage, hasPre);
         }
-
         async _gatherIndexData(tender, completeData, index, hasPre) {
             const stages = await this._getValidStages(tender.id);
             const stage = this.ctx.helper._.find(stages, {order: index});
             await this._gatherStageData(completeData, tender, stage, hasPre);
         }
-
-        async _gatherZoneData(tender, completeData, zone) {
-            const stages = await this._getTimeZoneStages(tender, zone);
-            await this._gatherStagesData(completeData, tender, stages);
-        }
-
         async _gatherIndexZoneData(tender, completeData, stageZone) {
             const stages = await this._getOrderZoneStages(tender, stageZone);
             await this._gatherStagesData(completeData, tender, stages);
         }
-
         async _gatherFinalData(tender, completeData, hasPre) {
             const stages = await this._getValidStages(tender.id);
             await this._gatherStageData(completeData, tender, stages[0], hasPre);
         }
-
         async _gatherCheckedFinalData(tender, completeData, hasPre) {
             const stages = await this._getCheckedStages(tender.id);
             await this._gatherStageData(completeData, tender, stages[0], hasPre);
         }
 
-        async _gatherLedgerData(tender, completeData) {
-            const helper = this.ctx.helper;
+        async _loadGatherLedger(tender) {
             const billsTree = new Ledger.billsTree(this.ctx, {
                 id: 'ledger_id',
                 pid: 'ledger_pid',
@@ -491,36 +532,59 @@ module.exports = app => {
             const billsData = await this.ctx.service.ledger.getData(tender.id);
             billsTree.loadDatas(billsData);
             billsTree.calculateAll();
+            return billsTree;
+        }
+        async _loadGatherPos(tender) {
+            const pos = new Ledger.pos({ id: 'id', ledgerId: 'lid' });
+            const posData = await this.ctx.service.pos.getAllDataByCondition({ where: { tid: tender.id } });
+            pos.loadDatas(posData);
+            return pos;
+        }
+        async _gatherLedgerData(tender, completeData) {
+            const resultPos = this.resultPos;
+            const helper = this.ctx.helper;
+            const billsTree = await this._loadGatherLedger(tender);
+            const pos = await this._loadGatherPos(tender);
             this.resultTree.loadGatherTree(billsTree, function (gatherNode, sourceNode) {
                 gatherUtils.gatherLedger(tender, gatherNode, sourceNode, completeData.prefix, helper);
+            }, function (gatherNode, sourceNode) {
+                const posRange = pos.getLedgerPos(sourceNode.id);
+                if (!posRange || posRange.length === 0) return;
+                resultPos.loadGatherPos(posRange, (gatherPos, sourcePos) => {
+                    gatherUtils.gatherPos(gatherPos, sourcePos, completeData.prefix, helper);
+                });
             });
         }
-
         async _gatherSpecialData(tender, sKey) {
+            const resultPos = this.resultPos;
             const helper = this.ctx.helper;
-            const billsTree = new Ledger.billsTree(this.ctx, {
-                id: 'ledger_id',
-                pid: 'ledger_pid',
-                order: 'order',
-                level: 'level',
-                rootId: -1,
-                keys: ['id', 'tender_id', 'ledger_id'],
-                stageId: 'id',
-                calcFields: ['deal_tp', 'total_price'],
-            });
-            const billsData = await this.ctx.service.ledger.getData(tender.id);
-            billsTree.loadDatas(billsData);
-            billsTree.calculateAll();
+            const billsTree = await this._loadGatherLedger(tender);
+            const pos = await this._loadGatherPos(tender);
             this.resultTree.loadGatherTree(billsTree, function (gatherNode, sourceNode) {
                 gatherUtils.gatherSpecial(gatherNode, sourceNode, 'ts_' + sKey + '_', helper);
+            }, function (gatherNode, sourceNode) {
+                const posRange = pos.getLedgerPos(sourceNode.id);
+                if (!posRange || posRange.length === 0) return;
+                resultPos.loadGatherPos(gatherNode.id, posRange, (gatherPos, sourcePos) => {
+                    gatherUtils.gatherSpecialPos(gatherPos, sourcePos, 'ts_' + sKey + '_', helper);
+                });
             });
         }
 
-        async getGatherStageBills(memFieldKeys, gsDefine, gsCustom) {
-            if (!gsDefine || !gsDefine.enable) return [];
-            if (!gsCustom || !gsCustom.tenders || gsCustom.tenders.length === 0) return [];
+        async _doGatherStageData(memFieldKeys, gsDefine, gsCustom) {
+            if (this.resultTree || this.resultPos) return;
+            if (!gsDefine || !gsDefine.enable) return;
+            if (!gsCustom || !gsCustom.tenders || gsCustom.tenders.length === 0) return;
+
+            this.resultTree = new Ledger.gatherTree(this.ctx, {
+                id: 'id',
+                pid: 'pid',
+                order: 'order',
+                level: 'level',
+                rootId: -1
+            });
+            this.resultPos = new Ledger.gatherPos({ id: 'id', ledgerId: 'lid' });
 
-            this.resultTree.clear();
             const gsSetting = JSON.parse(gsDefine.setting);
             let commonIndex = 0;
             const completeDatas = [];
@@ -563,7 +627,16 @@ module.exports = app => {
 
             this.resultTree.resortChildrenDefault();
             gatherUtils.completeGatherData(this.resultTree.nodes, completeDatas);
-            return this.resultTree.getDefaultDatas();
+            gatherUtils.completeGatherData(this.resultPos.datas, completeDatas);
+        }
+
+        async getGatherStageBills(memFieldKeys, gsDefine, gsCustom) {
+            await this._doGatherStageData(memFieldKeys, gsDefine, gsCustom);
+            return this.resultTree ? this.resultTree.getDefaultDatas() : [];
+        }
+        async getGatherStagePos(memFieldKeys, gsDefine, gsCustom) {
+            await this._doGatherStageData(memFieldKeys, gsDefine, gsCustom);
+            return this.resultPos ? this.resultPos.getDatas() : [];
         }
 
         /**

+ 31 - 0
app/service/spec_pull.js

@@ -0,0 +1,31 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date 2018/9/26
+ * @version
+ */
+
+module.exports = app => {
+    class SpecPull extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 's2b_spec_pull';
+        }
+
+        async syncLedger (pid) {
+            const pull = await this.getDataByCondition({ pid, valid: 1, pull_type: 'sync-ledger' });
+            return pull;
+        }
+    }
+
+    return SpecPull;
+};

+ 6 - 0
app/view/ledger/explode.ejs

@@ -42,6 +42,11 @@
                 <div class="d-inline-block ml-3">
                     <a id="exportLedger" class="btn btn-primary btn-sm" href="javascript: void(0)">导出台账Excel</a>
                 </div>
+                <% if (syncLedgerUrl) { %>
+                <div class="d-inline-block ml-1">
+                    <a id="sync-ledger" class="btn btn-primary btn-sm" href="javascript: void(0)">同步台账</a>
+                </div>
+                <% } %>
             </div>
             <div class="ml-auto">
                 <a class="btn btn-sm btn-primary mr-1" id="ledger-check2" href="javascript: void(0);">数据检查</a>
@@ -348,6 +353,7 @@
             $('.modal-title').text('重新上报')
         }
     });
+    const syncLedgerUrl = '<%- syncLedgerUrl %>';
 </script>
 <% if ((tender.ledger_status === auditConst.status.uncheck || tender.ledger_status === auditConst.status.checkNo) && ctx.session.sessionUser.accountId === tender.user_id) { %>
 <script>

+ 1 - 0
app/view/login/login.ejs

@@ -67,6 +67,7 @@
                 <div class="form-group <% if (projectData) { %>mb-5<% } else { %>mb-4<% } %>">
                     <div class="form-check d-flex justify-content-start pl-0">
                         <label class="form-check-label" for="exampleCheck1"><a href="#fg-password" data-toggle="modal" data-target="#fg-password">忘记密码?</a></label>
+                        <% if (projectData) { %><label class="form-check-label ml-auto" for="exampleCheck1"><a href="/">切换项目</a></label><% } %>
                     </div>
                 </div>
                 <button type="submit" class="btn btn-primary btn-block">登&nbsp;&nbsp;录</button>

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

@@ -627,4 +627,5 @@
 <% include ../shares/new_tag_modal.ejs %>
 <% include ../shares/tender_select_modal.ejs %>
 <% include ../shares/import_excel_modal.ejs%>
-<% include ../shares/stage_stash_modal.ejs%>
+<% include ../shares/stage_stash_modal.ejs%>
+<% include ../shares/delete_hint_modal.ejs%>

+ 2 - 0
config/config.default.js

@@ -241,5 +241,7 @@ module.exports = appInfo => {
     // 项目管理跳转路径
     config.managementPath = 'https://pm.smartcost.com.cn';
     config.managementProxyPath = 'https://pm.smartcost.com.cn';
+
+    config.url3f = '/3f';
     return config;
 };

+ 2 - 0
config/config.local.js

@@ -124,5 +124,7 @@ module.exports = appInfo => {
     // 项目管理跳转路径
     config.managementPath = 'http://localhost:3000';
     config.managementProxyPath = 'http://192.168.1.76:2020';
+
+    config.url3f = 'http://127.0.0.1:7005/3f';
     return config;
 };

+ 2 - 0
config/config.qa.js

@@ -86,5 +86,7 @@ module.exports = appInfo => {
     // 项目管理跳转路径
     config.managementPath = 'http://pmqa.smartcost.com.cn';
     config.managementProxyPath = 'http://192.168.1.76:2020';
+
+    config.url3f = 'http://jlqa.smartcost.com.cn:7005/3f';
     return config;
 };

+ 2 - 0
sql/update.sql

@@ -0,0 +1,2 @@
+ALTER TABLE `zh_stage_audit`
+ADD COLUMN `lock_lid`  text NULL COMMENT '锁定台账id(以,分隔)' AFTER `opinion`;