Преглед изворни кода

造价对比,决算对比,标段可选择期相关

MaiXinRong пре 1 месец
родитељ
комит
d54d0977b2

+ 1 - 1
app/const/sp_page_show.js

@@ -88,7 +88,7 @@ const defaultSetting = {
     isOnlyChecked: 1,
     closeArchiveSignature: 0,
     openSettle: 0,
-    openContractExpr: 0,
+    openContractExpr: 1,
     openMultiStageCalc: 0,
     maxMultiStageCount: 5,
     openDataCollect: 1,

+ 2 - 1
app/controller/budget_controller.js

@@ -118,6 +118,7 @@ module.exports = app => {
                 if (ctx.budget.final_id) {
                     data.final = await ctx.service.budgetFinal.getAllDataByCondition({ where: { final_id: ctx.budget.final_id } });
                     data.finalInfo = await ctx.service.budgetFinalList.getFinal(ctx.budget.final_id);
+                    await ctx.service.budgetFinalList.clearExpiredFinal(ctx.budget);
                 } else {
                     data.gu = await ctx.service.budgetGu.getData(ctx.budget.id);
                     data.gai = await ctx.service.budgetGai.getData(ctx.budget.id);
@@ -144,7 +145,7 @@ module.exports = app => {
                     ctx.body = { err: 0, msg: `决算数据已在${ctx.moment(finalInfo.update_time).format('YYYY-DD-MM HH:mm:ss')}更新,请先查看后再决定是否生成`, data: { final, finalInfo } };
                 } else {
                     const finalInfo = await ctx.service.budgetFinalList.addFinal(ctx.budget, data.id);
-                    const final = await ctx.service.budget.doFinal(ctx.budget, finalInfo);
+                    const final = await ctx.service.budget.doFinal(ctx.budget, finalInfo, data.stage);
                     ctx.body = { err: 0, msg: '', data: { final, finalInfo } };
                 }
             } catch (err) {

+ 84 - 15
app/lib/budget_final.js

@@ -169,21 +169,86 @@ class BudgetFinal {
         });
     }
 
-    async _loadTender(id) {
+
+    async _getValidStages(tenderId, sort = 'desc', checked = false) {
+        const accountId = this.ctx.session.sessionUser.accountId;
+        const condition = { tid: tenderId };
+        if (checked) condition.status = auditConst.stage.status.checked;
+        const stages = await this.ctx.service.stage.getAllDataByCondition({ where: condition, orders: [['order', sort]] });
+        return stages.filter(s => { return s.status !== auditConst.stage.status.uncheck || s.user_id === accountId; });
+    }
+    async _filterOrder(id, order, checked) {
+        const stages = await this._getValidStages(id, 'desc', checked);
+        const stage = this.ctx.helper._.find(stages, { order });
+        if (stage) await this.ctx.service.stage.doCheckStage(stage);
+        return { type: 'stage', stage, filter: `第${order}期` };
+    }
+    async _filterMonth(id, month, checked) {
+        const stages = await this._getValidStages(id, 'desc', checked);
+        const stage = this.ctx.helper._.find(stages, { s_time: month });
+        if (stage) await this.ctx.service.stage.doCheckStage(stage);
+        return { type: 'stage', stage, filter: month };
+    }
+    async _filterFinal(id, checked) {
+        const stages = await this._getValidStages(id, 'desc', checked);
+        const stage = stages[0];
+        if (stage) await this.ctx.service.stage.doCheckStage(stage);
+        return { type: 'stage', stage, filter: stage ? `第${stage.order}期` : '' };
+    }
+    async _filterCheckedFinal(id, checked) {
+        const stages = await this._getValidStages(id, 'desc', checked);
+        const checkedStages = stages.filter(x => { return x.status === status.checked; });
+        const stage = checkedStages[0];
+        if (stage && stage.status !== auditConst.stage.status.checked) await this.ctx.service.stage.doCheckStage(stage);
+        return { type: 'stage', stage, filter: stage ? `第${stage.order}期` : '' };
+    }
+    async _filterUnderMonth(id, month, checked) {
+        const stages = await this._getValidStages(id, 'desc', checked);
+        let stage = this.ctx.helper._.find(stages, { s_time: month });
+        if (!stage) {
+            const checkTime = this.ctx.moment(month);
+            for (const s of stages) {
+                const rela_time = this.ctx.moment(s.s_time);
+                if (rela_time.isBefore(checkTime)) {
+                    stage = s;
+                    break;
+                }
+            }
+        }
+        if (stage) await this.ctx.service.stage.doCheckStage(stage);
+        return { type: 'stage', stage, filter: stage ? stage.s_time : '' };
+    }
+    async _filterStage(tender, stageInfo) {
+        switch(stageInfo.type) {
+            case 'stage':
+                return await this._filterOrder(tender, stageInfo.stage, stageInfo.checked);
+            case 'month':
+                return await this._filterMonth(tender, stageInfo.month, stageInfo.checked);
+            case 'final':
+                return await this._filterFinal(tender, stageInfo.checked);
+            case 'checked-final':
+                return await this._filterCheckedFinal(tender, stageInfo.checked);
+            case 'under-month':
+                return await this._filterUnderMonth(tender, stageInfo.month, stageInfo.checked);
+            default:
+                return await this._filterFinal(tender, stageInfo.checked);
+        }
+    }
+    async _loadTender(id, stageInfo) {
         const helper = this.ctx.helper;
         const bills = await this.ctx.service.ledger.getFinalData(id, ['id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf',
             'code', 'b_code', 'name', 'unit', 'unit_price', 'dgn_qty1', 'dgn_qty2', 'total_price']);
 
         const dgnData = await this.ctx.service.stageBillsDgn.getDgnData(id);
         // 使用最新一期对比
-        const stage = await this.ctx.service.stage.getLastestStage(id);
-        if (!stage) {
+        const stageFilter = await this._filterStage(id, stageInfo);
+        if (!stageFilter.stage) {
             helper.assignRelaData(bills, [
                 { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id' },
             ]);
             this.final.tender_info.push({ id, stageCount: 0 });
-        } else if (stage.status === auditConst.stage.status.checked) {
-            const finalBills = await this.ctx.service.stageBillsFinal.getFinalData({id}, stage.order);
+        } else if (stageFilter.stage.status === auditConst.stage.status.checked) {
+            const finalBills = await this.ctx.service.stageBillsFinal.getFinalData({ id }, stageFilter.stage.order);
             helper.assignRelaData(bills, [
                 { data: finalBills, fields: ['contract_tp', 'qc_tp'], prefix: 'end_', relaId: 'lid' },
                 { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id' },
@@ -191,14 +256,13 @@ class BudgetFinal {
             bills.forEach(b => {
                 b.end_gather_tp = helper.add(b.end_qc_tp, b.end_contract_tp);
             });
-            this.final.tender_info.push({ id, stageOrder: stage.order });
+            this.final.tender_info.push({ id, stageOrder: stageFilter.stage.order });
         } else {
-            await this.ctx.service.stage.doCheckStage(stage);
-            const curBills = stage.readOnly
-                ? await this.ctx.service.stageBills.getAuditorStageData2(id, stage.id, stage.curTimes, stage.curOrder)
-                : await this.ctx.service.stageBills.getLastestStageData2(id, stage.id);
-            const preBills = stage.preCheckedStage ? await this.ctx.service.stageBillsFinal.getFinalData({id}, stage.preCheckedStage.order) : [];
-            const bpcData = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: stage.id } });
+            const curBills = stageFilter.stage.readOnly
+                ? await this.ctx.service.stageBills.getAuditorStageData2(id, stageFilter.stage.id, stageFilter.stage.curTimes, stageFilter.stage.curOrder)
+                : await this.ctx.service.stageBills.getLastestStageData2(id, stageFilter.stage.id);
+            const preBills = stageFilter.stage.preCheckedStage ? await this.ctx.service.stageBillsFinal.getFinalData({ id }, stageFilter.stage.preCheckedStage.order) : [];
+            const bpcData = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: stageFilter.stage.id } });
             helper.assignRelaData(bills, [
                 { data: curBills, fields: ['contract_tp', 'qc_tp'], prefix: '', relaId: 'lid' },
                 { data: preBills, fields: ['contract_tp', 'qc_tp'], prefix: 'pre_', relaId: 'lid' },
@@ -210,7 +274,7 @@ class BudgetFinal {
                 b.end_qc_tp = helper.sum([b.qc_tp, b.pre_qc_tp, b.qc_pc_tp]);
                 b.end_gather_tp = helper.sum([b.qc_tp, b.contract_tp, b.pre_qc_tp, b.pre_contract_tp, b.pc_tp]);
             });
-            this.final.tender_info.push({ id, stageOrder: stage.order, stageStatus: stage.status, stageFlow: stage.curTimes + '-' + stage.curOrder });
+            this.final.tender_info.push({ id, stageOrder: stageFilter.stage.order, stageStatus: stageFilter.stage.status, stageFlow: stageFilter.stage.curTimes + '-' + stageFilter.stage.curOrder });
         }
         const tree = new BillsTree(this.ctx, this.tenderSetting);
         tree.loadDatas(bills);
@@ -239,6 +303,10 @@ class BudgetFinal {
         });
     }
 
+    async _loadDeal(deal) {
+        // todo
+    }
+
     async _afterLoad() {
         const helper = this.ctx.helper;
         const checkGaiExist = this.finalTree.datas.findIndex(x => {
@@ -326,19 +394,20 @@ class BudgetFinal {
         return data;
     }
 
-    async doFinal(budget, final) {
+    async doFinal(budget, final, stage) {
         this.budget = budget;
         this.final = final;
         this.finalTree.checkField = budget.final_type.split('_');
 
         for (const t of final.tender) {
-            await this._loadTender(t);
+            await this._loadTender(t, stage);
         }
         await this._loadCtrl(budget);
         await this._loadZb(budget);
         await this._loadYu(budget);
         await this._loadGai(budget);
         await this._loadGu(budget);
+        await this._loadDeal(final.deal);
         this._afterLoad();
         return this.getFinalData();
     }

+ 33 - 3
app/public/js/budget_compare.js

@@ -80,10 +80,10 @@ $(document).ready(() => {
             {title: '控制目标|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'ctrl_dgn_qty', hAlign: 2, width: 80, bc_type: 'number'},
             {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'ctrl_dgn_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'ctrl_tp', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
-            {title: '台账|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'dgn_qty', hAlign: 2, width: 80, bc_type: 'number', visible: false},
+            {title: '台账/合同|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'dgn_qty', hAlign: 2, width: 80, bc_type: 'number', visible: false},
             {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'dgn_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number', visible: false},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number', visible: false},
-            {title: '预估决算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'final_dgn_qty', hAlign: 2, width: 80, bc_type: 'number', visible: false},
+            {title: '计量/支付|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'final_dgn_qty', hAlign: 2, width: 80, bc_type: 'number', visible: false},
             {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'final_dgn_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number', visible: false},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'final_tp', hAlign: 2, width: 80, type: 'Number', bc_type: 'number', visible: false},
             {title: '数据对比', colSpan: '1', rowSpan: '2', field: 'stackedBar', hAlign: 0, width: 300, cellType: 'stackedBar', stackedBarCover: false, bc_type: 'grid', defaultVisible: true, visible: false, getTip: getStackedBarTip},
@@ -512,7 +512,22 @@ $(document).ready(() => {
             $('#select-final-ok').click(() => {
                 const rela = self.getSelects();
                 if (rela.length === 0) return;
-                const data = { final_id: compareObj.curFinalId(), id: rela, final_type: $('[name=final_type]:checked').val() };
+                const checked = $('#tsm-filter-checked')[0].checked;
+                const stage = { type: $('[name=tsm-source]:checked').val(), checked };
+                if (stage.type === 'month') {
+                    stage.month = $('#gather-month').val();
+                    if (stage.month === '') {
+                        toastr.warning('请选择 汇总年月');
+                        return;
+                    }
+                } else if (stage.type === 'under-month') {
+                    stage.month = $('#gather-under-month').val();
+                    if (stage.month === '') {
+                        toastr.warning('请选择 汇总年月');
+                        return;
+                    }
+                }
+                const data = { final_id: compareObj.curFinalId(), id: rela, final_type: $('[name=final_type]:checked').val(), stage };
                 postData(window.location.pathname + '/final', data, function(result, msg) {
                     compareObj.loadFinalData(result, msg);
                     $('#select-final').modal('hide');
@@ -630,4 +645,19 @@ $(document).ready(() => {
         if (gridIndex >= 0) excelSetting.cols.splice(gridIndex, 1);
         SpreadExcelObj.exportSimpleXlsxSheet(excelSetting, compareSheet.zh_tree.nodes, $('.sidebar-title').attr('data-original-title') + "-造价对比.xlsx");
     });
+
+    $('[name=tsm-source]').click(function() {
+        $('[name=gather-type]').hide();
+        const type = this.value;
+        const gatherBy = $(`#gather-by-${type}`);
+        if (gatherBy.length > 0) {
+            $('#tsm-stage-info-detail').show();
+            $(`#gather-by-${type}`).show();
+        } else {
+            $('#tsm-stage-info-detail').hide();
+        }
+    });
+    $('.datepicker-here').datepicker({
+        autoClose: true,
+    });
 });

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

@@ -3356,6 +3356,7 @@ $(document).ready(function() {
 
             const hint = {
                 expr: {type: 'warning', msg: '粘贴了表达式非法,已过滤'},
+                hasDetail: {type: 'warning', msg: '存在设计量明细,不可粘贴'}
             };
             if (info.sheet.zh_setting) {
                 const data = [];
@@ -3370,6 +3371,8 @@ $(document).ready(function() {
                 }
                 const lastOrder = sortData.length > 0 ? sortData[sortData.length - 1].porder + 1 : 1;
                 let filterExprFields = [];
+                const ledger = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
+                if (ledger.calc_template) filterExprFields.push('sgfh_expr', 'sgfh_qty');
                 for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
                     let bPaste = false;
                     const curRow = info.cellRange.row + iRow;

+ 2 - 2
app/public/js/stage.js

@@ -2763,9 +2763,9 @@ $(document).ready(() => {
             calcField: { qty: 'contract_qty', tp: 'contract_tp' }, calcType: TreeExprCalc.calcType.sortCache,
             exprFieldsIndex: {
                 tzsl: 'quantity', tzje: 'total_price', qysl: 'deal_qty', qyje: 'deal_tp',
-                bqhtje: 'contract_tp',
+                bqhtje: 'contract_tp', bqwcje: 'gather_tp',
             },
-            exprCacheKey: ['bqhtje'],
+            exprCacheKey: ['bqhtje', 'bqwcje'],
         });
         // 加载中间计量
         stageIm.init(tenderInfo, stage, imType, tenderInfo.decimal);

+ 2 - 2
app/service/budget.js

@@ -263,11 +263,11 @@ module.exports = app => {
             }
         }
 
-        async doFinal(budget, final) {
+        async doFinal(budget, final, stage) {
             const finalObj = new FinalObj(this.ctx);
             let finalData;
             try {
-                finalData = await finalObj.doFinal(budget, final);
+                finalData = await finalObj.doFinal(budget, final, stage);
             } catch (err) {
                 this.db.update(this.ctx.service.budgetFinalList.tableName, { id: final.id, status: 4});
                 this.ctx.log(err);

+ 17 - 0
app/service/budget_final_list.js

@@ -52,6 +52,23 @@ module.exports = app => {
             const result = await this.db.insert(this.tableName, final);
             return await this.getFinal(result.insertId);
         }
+
+        async clearExpiredFinal(budget) {
+            const datas = await this.getAllDataByCondition({ where: { bid: budget.id }, orders: [['id', 'DESC']]});
+            if (datas.length > 3) {
+                datas.splice(0, 3);
+                const conn = await this.db.beginTransaction();
+                try {
+                    const id = datas.map(x => { return x.id; });
+                    await conn.delete(this.tableName, { id });
+                    await conn.delete(this.ctx.service.budgetFinal.tableName, { final_id: id });
+                    await conn.commit();
+                } catch (err) {
+                    await conn.rollback();
+                    this.ctx.log(err);
+                }
+            }
+        }
     }
 
     return BudgetFinalList;

+ 36 - 0
app/view/budget/compare_modal.ejs

@@ -5,6 +5,42 @@
                 <h5 class="modal-title">选择决算标段</h5>
             </div>
             <div class="modal-body">
+                <div class="" id="sf-stage-info">
+                    <div class="form-row align-items-center mb-2">
+                        <div class="col-auto"><span class="form-check-label">汇总时间:</span></div>
+                        <div class="col-auto">
+                            <div class="form-check form-check-inline" stage-type="stage">
+                                <input class="form-check-input" type="radio" id="tsm-source-final" value="final" name="tsm-source" checked>
+                                <label class="form-check-label" for="tsm-source-final">最新期</label>
+                            </div>
+                            <div class="form-check form-check-inline" stage-type="under-month">
+                                <input class="form-check-input" type="radio" id="tsm-source-under-month" value="under-month" name="tsm-source">
+                                <label class="form-check-label" for="tsm-source-under-month">截止月</label>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="form-row align-items-center mb-2" id="tsm-stage-info-detail">
+                        <div class="col-auto"><span class="form-check-label">时间设置:</span>
+                        </div>
+                        <div class="d-flex">
+                            <div id="gather-by-under-month" name="gather-type" style="display: none">
+                                <div class="input-group input-group-sm">
+                                    <input id="gather-under-month" class="datepicker-here form-control mt-0" auto-close="true" autocomplete="off" placeholder="点击选择年月" data-view="months" data-min-view="months" data-date-format="yyyy-MM" data-language="zh" type="text" autocomplete="off">
+                                </div>
+                            </div>
+                            <div id="gather-by-final" name="gather-type">
+                                <div class="input-group input-group-sm">
+                                    <select class="form-control" style="width: 80px" disabled>
+                                    </select>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="form-check form-check-inline ml-2">
+                            <input class="form-check-input" type="checkbox" id="tsm-filter-checked">
+                            <label class="form-check-label" for="tsm-filter-checked">仅统计审批完成</label>
+                        </div>
+                    </div>
+                </div>
                 <h5>可选标段</h5>
                 <div id="sf-spread" style="height: 300px"></div>
             </div>

+ 1 - 0
app/view/ledger/explode_modal.ejs

@@ -545,6 +545,7 @@
                                         <tr><td class="text-center" colspan="2" name="sr" exprValue="tzje">台账金额</td></tr>
                                         <tr><td class="text-center" colspan="2" name="sr" exprValue="qyje">签约金额</td></tr>
                                         <tr><td class="text-center" colspan="2" name="sr" exprValue="bqhtje">本期合同金额</td></tr>
+                                        <tr><td class="text-center" colspan="2" name="sr" exprValue="bqwcje">本期完成金额</td></tr>
                                         <tr><th class="text-left" colspan="2">设置行<span id="target-row"></span></th></tr>
                                         <tr><td class="text-center" name="tr" exprValue="tzsl">台账数量</td><td class="text-center" name="tr" exprValue="tzje">台账金额</td></tr>
                                         <tr><td class="text-center" name="tr" exprValue="qysl">签约数量</td><td class="text-center" name="tr" exprValue="qyje">签约金额</td></tr>

+ 1 - 0
config/web.js

@@ -1496,6 +1496,7 @@ const JsFiles = {
                     '/public/js/decimal.min.js',
                     '/public/js/component/menu.js',
                     '/public/js/file-saver/FileSaver.js',
+                    '/public/js/datepicker/datepicker.min.js', '/public/js/datepicker/datepicker.zh.js',
                 ],
                 mergeFiles: [
                     '/public/js/sub_menu.js',