Jelajahi Sumber

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

Tony Kang 2 minggu lalu
induk
melakukan
b68f72d02d
47 mengubah file dengan 2317 tambahan dan 407 penghapusan
  1. 1 1
      app/const/sp_page_show.js
  2. 9 2
      app/controller/budget_controller.js
  3. 49 24
      app/controller/contract_controller.js
  4. 13 2
      app/controller/cost_controller.js
  5. 81 50
      app/controller/datacollect_controller.js
  6. 1 0
      app/controller/sub_proj_setting_controller.js
  7. 117 15
      app/lib/budget_final.js
  8. 3 0
      app/public/css/main.css
  9. TEMPAT SAMPAH
      app/public/images/juecedaping07.png
  10. 91 5
      app/public/js/budget_compare.js
  11. 96 37
      app/public/js/contract_detail.js
  12. 23 0
      app/public/js/cost_stage_book.js
  13. 22 0
      app/public/js/cost_stage_ledger.js
  14. 4 1
      app/public/js/ledger.js
  15. 2 2
      app/public/js/stage.js
  16. 1 0
      app/router.js
  17. 2 2
      app/service/budget.js
  18. 17 0
      app/service/budget_final_list.js
  19. 4 3
      app/service/contract.js
  20. 9 5
      app/service/contract_pay.js
  21. 270 170
      app/service/contract_sp_audit.js
  22. 10 7
      app/service/contract_supplement.js
  23. 12 4
      app/service/contract_tree.js
  24. 21 4
      app/service/cost_stage.js
  25. 0 33
      app/service/cost_stage_analysis.js
  26. 71 2
      app/service/cost_stage_book.js
  27. 39 10
      app/service/cost_stage_ledger.js
  28. 9 8
      app/service/pos.js
  29. 3 1
      app/service/shenpi_audit.js
  30. 5 0
      app/service/stage.js
  31. 1 0
      app/view/budget/compare.ejs
  32. 54 2
      app/view/budget/compare_modal.ejs
  33. 16 12
      app/view/contract/detail.ejs
  34. 3 1
      app/view/contract/detail_modal.ejs
  35. 5 0
      app/view/cost/book.ejs
  36. 2 1
      app/view/cost/book_modal.ejs
  37. 66 0
      app/view/cost/cost_decimal.ejs
  38. 3 0
      app/view/cost/ledger.ejs
  39. 1 0
      app/view/cost/ledger_modal.ejs
  40. 3 1
      app/view/datacollect/index.ejs
  41. 5 1
      app/view/datacollect/index4GY18Y.ejs
  42. 1131 0
      app/view/datacollect/index_cost.ejs
  43. 7 0
      app/view/layout/menu.ejs
  44. 1 0
      app/view/ledger/explode_modal.ejs
  45. 25 0
      app/view/sp_setting/datacollect.ejs
  46. 1 0
      config/web.js
  47. 8 1
      sql/update.sql

+ 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,

+ 9 - 2
app/controller/budget_controller.js

@@ -57,7 +57,6 @@ module.exports = app => {
                 renderData.categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
                 await this.layout('budget/list.ejs', renderData, 'budget/list_modal.ejs');
             } catch (err) {
-                console.log(err);
                 ctx.log(err);
                 ctx.session.postError = err.toString();
                 ctx.redirect(this.menu.menu.dashboard.url);
@@ -92,6 +91,12 @@ module.exports = app => {
             }
         }
 
+        async _getContractTree(condition) {
+            const data = await this.ctx.service.contractTree.getAllDataByCondition({ where: condition });
+            const contract_data = await this.ctx.service.contract.getAllDataByCondition({ where: condition });
+            return [...data, ...contract_data];
+        }
+
         async compare(ctx) {
             try {
                 const renderData = {
@@ -106,6 +111,7 @@ module.exports = app => {
                     return { id: y.id, name: y.name, lastStageOrder: y.lastStage.order, lastStageStatus: auditConst.stage.statusString[y.lastStage.status], category: y.category };
                 });
                 renderData.categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
+                renderData.contract = await this._getContractTree({ spid: this.ctx.subProject.id, contract_type: 1 });
                 await this.layout('budget/compare.ejs', renderData, 'budget/compare_modal.ejs');
             } catch (err) {
                 ctx.log(err);
@@ -118,6 +124,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 +151,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, data.contracts);
                     ctx.body = { err: 0, msg: '', data: { final, finalInfo } };
                 }
             } catch (err) {

+ 49 - 24
app/controller/contract_controller.js

@@ -406,26 +406,36 @@ module.exports = app => {
         async checkAudit(ctx) {
             try {
                 const data = JSON.parse(ctx.request.body.data);
-                if (!data.postType || !data.id) throw '数据错误';
+                if (!data.postType || !data.id || !data.type) throw '数据错误';
                 const responseData = {
                     err: 0, msg: '', data: {},
                 };
-                const contract = isNaN(data.id) ? await ctx.service.contract.getDataById(data.id) : await ctx.service.contractPay.getDataById(data.id);
+                const contract = await this._getContractByType(ctx, data);
                 if (!contract) {
                     throw '未找到合同相关数据';
                 }
-                const tip = isNaN(data.id) ? '' : contractConst.typeName[contract.contract_type];
+                let tip = '';
                 // 判断功能是否已关闭
                 const shenpi_status = ctx.contract_tender ? ctx.subProject.page_show.openContractTenderShenpi : ctx.subProject.page_show.openContractSubProjectShenpi;
                 const pay_shenpi_status = ctx.contract_tender ? ctx.subProject.page_show.openContractPayTenderShenpi : ctx.subProject.page_show.openContractPaySubProjectShenpi;
-                if (isNaN(data.id)) {
-                    if (!shenpi_status) {
-                        throw '合同审批功能已关闭, 无法进行审批操作, 请刷新页面';
-                    }
-                } else {
-                    if (!pay_shenpi_status) {
-                        throw '合同' + tip + '审批功能已关闭, 无法进行审批操作, 请刷新页面';
-                    }
+                switch (data.type) {
+                    case 'contract':
+                        if (!shenpi_status) {
+                            throw '合同审批功能已关闭, 无法进行审批操作, 请刷新页面';
+                        }
+                        break;
+                    case 'pay':
+                        if (!pay_shenpi_status) {
+                            throw '合同' + tip + '审批功能已关闭, 无法进行审批操作, 请刷新页面';
+                        }
+                        tip = contractConst.typeName[contract.contract_type];
+                        break;
+                    case 'supplement':
+                        if (!shenpi_status) {
+                            throw '合同审批功能已关闭, 无法进行审批操作, 请刷新页面';
+                        }
+                        break;
+                    default: throw '数据错误';
                 }
                 let shenpi = '';
                 if (ctx.contract_tender) {
@@ -434,16 +444,14 @@ module.exports = app => {
                 } else {
                     shenpi = ctx.subProject.shenpi;
                 }
-                await ctx.service.contractSpAudit.loadUser(contract);
-                await ctx.service.contractSpAudit.loadAuditViewData(contract);
-                const cid = contract.cid ? contract.cid : contract.id;
-                const cpid = contract.cid ? contract.id : null;
+                await ctx.service.contractSpAudit.loadUser(contract, data.type);
+                await ctx.service.contractSpAudit.loadAuditViewData(contract, data.type);
                 switch (data.postType) {
                     case 'start':
                         if (contract.status !== auditConst.contract.status.uncheck && contract.status !== auditConst.contract.status.checkNo) {
                             throw '该合同' + tip + '当前无法上报';
                         }
-                        await ctx.service.contractSpAudit.start(ctx.contractOptions, shenpi, cid, cpid, contract.times);
+                        await ctx.service.contractSpAudit.start(ctx.contractOptions, shenpi, contract, data.type, contract.times);
                         break;
                     case 'check':
                         if (contract.status !== auditConst.contract.status.checking && contract.status !== auditConst.contract.status.checkNoPre) {
@@ -459,7 +467,7 @@ module.exports = app => {
                         if (!checkData.checkType || isNaN(checkData.checkType)) {
                             throw '提交数据错误';
                         }
-                        await ctx.service.contractSpAudit.check(contract, checkData);
+                        await ctx.service.contractSpAudit.check(contract, data.type, checkData);
                         break;
                     case 'add-audit':
                         if (shenpi.contract === shenpiConst.sp_status.gdspl) {
@@ -477,7 +485,7 @@ module.exports = app => {
                             throw '当前不允许添加审核人';
                         }
 
-                        const auditorList = await ctx.service.contractSpAudit.getAuditors(cid, cpid, contract.times, 'asc', true);
+                        const auditorList = await ctx.service.contractSpAudit.getAuditors(contract, data.type, contract.times, 'asc', true);
                         // 检查审核人是否已存在
                         const exist = this.app._.find(auditorList, { aid: add_id });
                         if (exist) {
@@ -486,7 +494,7 @@ module.exports = app => {
                         const shenpiCondition = ctx.helper._.assign({ sp_type: shenpiConst.sp_type.contract, sp_status: shenpiConst.sp_status.gdzs }, ctx.contractOptions);
                         const shenpiInfo = await ctx.service.shenpiAudit.getDataByCondition(shenpiCondition);
                         const is_gdzs = shenpiInfo && shenpi.contract === shenpiConst.sp_status.gdzs ? 1 : 0;
-                        const add_result = await ctx.service.contractSpAudit.addAuditor(ctx.contractOptions, cid, cpid, add_id, contract.times, is_gdzs);
+                        const add_result = await ctx.service.contractSpAudit.addAuditor(ctx.contractOptions, contract, data.type, add_id, contract.times, is_gdzs);
                         if (!add_result) {
                             throw '添加审核人失败';
                         }
@@ -507,7 +515,7 @@ module.exports = app => {
                             throw '当前不允许移除审核人';
                         }
 
-                        const del_result = await ctx.service.contractSpAudit.deleteAuditor(cid, cpid, del_id, contract.times);
+                        const del_result = await ctx.service.contractSpAudit.deleteAuditor(contract, data.type, del_id, contract.times);
                         if (!del_result) {
                             throw '移除审核人失败';
                         }
@@ -519,7 +527,7 @@ module.exports = app => {
                         if (contract.status !== auditConst.contract.status.uncheck && contract.status !== auditConst.contract.status.checkNo) {
                             throw '当前状态不允许切换审批组';
                         }
-                        const chagne_result = await ctx.service.contractSpAudit.changeSpGroup(contract, data.sp_group);
+                        const chagne_result = await ctx.service.contractSpAudit.changeSpGroup(contract, data.type, data.sp_group);
                         if (!chagne_result) {
                             throw '切换审批组失败';
                         }
@@ -527,9 +535,9 @@ module.exports = app => {
                     default:
                         throw '参数错误';
                 }
-                const newContract = isNaN(data.id) ? await ctx.service.contract.getDataById(data.id) : await ctx.service.contractPay.getDataById(data.id);
-                await ctx.service.contractSpAudit.loadUser(newContract);
-                await ctx.service.contractSpAudit.loadAuditViewData(newContract);
+                const newContract = await this._getContractByType(ctx, data);
+                await ctx.service.contractSpAudit.loadUser(newContract, data.type);
+                await ctx.service.contractSpAudit.loadAuditViewData(newContract, data.type);
                 responseData.data = newContract;
                 ctx.body = responseData;
             } catch (err) {
@@ -539,6 +547,23 @@ module.exports = app => {
             }
         }
 
+        async _getContractByType(ctx, data) {
+            let contract = null;
+            switch (data.type) {
+                case 'contract':
+                    contract = await ctx.service.contract.getDataById(data.id);
+                    break;
+                case 'pay':
+                    contract = await ctx.service.contractPay.getDataById(data.id);
+                    break;
+                case 'supplement':
+                    contract = await ctx.service.contractSupplement.getDataById(data.id);
+                    break;
+                default: throw '数据错误';
+            }
+            return contract;
+        }
+
         async updateBills(ctx) {
             try {
                 const data = JSON.parse(ctx.request.body.data);

+ 13 - 2
app/controller/cost_controller.js

@@ -385,7 +385,7 @@ module.exports = app => {
                             ledgerC.forEach(l => { delete l.postil; delete l.memo; });
                             const bookC = await ctx.service.costStageBook.getCompareData(ctx.costStage);
                             ctx.helper.assignRelaData(ledgerC, [
-                                { data: bookC, fields: ['pre_in_tp', 'pre_in_excl_tax_tp', 'calc_his', 'postil', 'memo'], prefix: '', relaId: 'ledger_id' },
+                                { data: bookC, fields: ['pre_in_tp', 'pre_in_excl_tax_tp', 'in_tp', 'in_excl_tax_tp', 'calc_his', 'postil', 'memo'], prefix: '', relaId: 'ledger_id' },
                             ]);
                             responseData.data[f] = ledgerC;
                             break;
@@ -406,7 +406,7 @@ module.exports = app => {
                             detailC.forEach(l => { delete l.postil; delete l.memo; });
                             const bookDetailC = await ctx.service.costStageBook.getCompareData(ctx.costStage);
                             ctx.helper.assignRelaData(detailC, [
-                                { data: bookDetailC, fields: ['calc_his', 'postil', 'memo'], prefix: '', relaId: 'detail_id' },
+                                { data: bookDetailC, fields: ['in_tp', 'in_excl_tax_tp', 'calc_his', 'postil', 'memo'], prefix: '', relaId: 'detail_id' },
                             ]);
                             responseData.data[f] = detailC;
                             break;
@@ -617,6 +617,17 @@ module.exports = app => {
                 ctx.ajaxErrorBody('未知期数据类型', '保存数据错误');
             }
         }
+        async setDecimal(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const stageTypeInfo = ctx.service.costStage.stageType[ctx.costStage.stage_type];
+                const result = await this.ctx.service[stageTypeInfo.dataService].setDecimal(data.decimal);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                this.log(err);
+                ctx.body = this.ajaxErrorBody(err, '设置小数位数错误');
+            }
+        }
 
         async uploadStageFile(ctx) {
             let stream;

+ 81 - 50
app/controller/datacollect_controller.js

@@ -58,17 +58,28 @@ module.exports = app => {
                     return ctx.app._.indexOf(noTids, item.id) === -1;
                 });
                 const tenderidList = ctx.helper._.map(tenderList, 'id');
-                const noticeList = await ctx.service.noticePush.getNoticeByDataCollect(ctx.session.sessionProject.id, tenderidList);
                 // 获取分类
                 const categoryData = await this.ctx.service.category.getListByCategoryLevel(ctx.subProject.id);
                 // 默认坐标,否则则取办事处坐标
                 const projectData = await ctx.service.project.getDataById(ctx.session.sessionProject.id);
                 ctx.subProject.data_collect_pages = ctx.subProject.data_collect_pages ? ctx.subProject.data_collect_pages.split(',') : [];
+                // 判断是否只有'7',如果只有7,则不用获取其它数据
+                let only_cost = false;
+                if (ctx.subProject.data_collect_pages.length === 1 && ctx.subProject.data_collect_pages[0] === '7') {
+                    only_cost = true;
+                }
+                let noticeList = [];
+                if (!only_cost) {
+                    noticeList = await ctx.service.noticePush.getNoticeByDataCollect(ctx.session.sessionProject.id, tenderidList);
+                }
                 if (ctx.params.index) {
                     ctx.subProject.data_collect = parseInt(ctx.params.index);
                 }
+                const is_cost = ctx.subProject.data_collect_pages.includes('7') && ctx.subProject.data_collect === 7;
                 const is_dz2 = ['P0505', 'P0506', 'P1201', 'P1202', 'GY18Y', 'GYJJ1', 'P1103', 'KLG25', 'I2U5B', 'SEN48'].indexOf(ctx.session.sessionProject.code) !== -1
                     && ctx.subProject.data_collect_pages.includes('6') && ctx.subProject.data_collect === 6;
+                const is_dz3 = ctx.subProject.data_collect_pages.includes('8') && ctx.subProject.data_collect === 8;
+                const shj_url = 'http://112.74.42.187:3300/#/project/viewer?projectId=b03i54j0j8ai';
                 const renderData = {
                     projectData,
                     noticeList,
@@ -82,8 +93,11 @@ module.exports = app => {
                     acAdvance: auditConst.advance,
                     pushType: auditConst.pushType,
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.datacollect.index),
+                    shj_url,
                 };
-                if (is_dz2) {
+                if (is_cost) {
+                    await this.layout('datacollect/index_cost.ejs', renderData);
+                } else if (is_dz2) {
                     // 判断并更新common_json
                     if (!ctx.subProject.common_json) {
                         ctx.subProject.common_json = await ctx.service.subProject.updateCommonJsonDaping06(ctx.subProject, projectData);
@@ -97,6 +111,8 @@ module.exports = app => {
                     }
                     renderData.dpCategory = dpCategory;
                     await this.layout('datacollect/index4GY18Y.ejs', renderData);
+                } else if (is_dz3) {
+                    ctx.redirect(shj_url);
                 } else {
                     await this.layout('datacollect/index.ejs', renderData);
                 }
@@ -119,62 +135,77 @@ module.exports = app => {
                 tenderList = ctx.app._.filter(tenderList, function(item) {
                     return ctx.app._.indexOf(noTids, item.id) === -1;
                 });
+                // 判断是否只有'7',如果只有7,则不用获取其它数据
+                let only_cost = false;
+                if (ctx.subProject.data_collect_pages.length === 1 && ctx.subProject.data_collect_pages[0] === '7') {
+                    only_cost = true;
+                }
+                const have_cost = ctx.subProject.data_collect_pages.includes('7');
                 const thisMonth = new Date();
                 const [startMonth, endMonth] = ctx.helper.getStartEndMonth(thisMonth);
                 // 统计方法总时长
                 // let sumStageByDataCollectTime = 0;
                 for (const t of tenderList) {
-                    // const tenderInfo = await ctx.service.tenderInfo.getTenderInfo(t.id);
-                    // t.contract_price = tenderInfo.deal_param.contractPrice;
-                    // let bCalcTp = t.ledger_status === auditConst.ledger.status.checkNo || t.ledger_status === auditConst.ledger.status.uncheck;
-                    // t.advance_tp = await ctx.service.advance.getSumAdvance(t.id);
-                    //
-                    // if (t.ledger_status === auditConst.ledger.status.checked) {
-                    //     t.lastStage = await ctx.service.stage.getLastestStage(t.id);
-                    //     if (t.lastStage) await ctx.service.stage.checkStageGatherDataByDataCollect(t.lastStage, true);
-                    //     t.completeStage = await ctx.service.stage.getLastestCompleteStage(t.id);
-                    //     if ((!bCalcTp) && t.measure_type === measureType.gcl.value) {
-                    //         bCalcTp = t.lastStage && t.lastStage.status !== auditConst.stage.status.checked && !t.lastStage.readOnly;
-                    //     }
-                    // }
-                    // if (bCalcTp) {
-                    //     const sum = await this.ctx.service.ledger.addUp({ tender_id: t.id/* , is_leaf: true*/ });
-                    //     t.total_price = sum.total_price;
-                    //     t.deal_tp = sum.deal_tp;
-                    // }
                     // 用标段管理的方法获取t数据
-                    await this.ctx.service.tenderCache.loadTenderCache(t, '');
-                    t.total_price = t.ledger_tp && t.ledger_tp.total_price ? t.ledger_tp.total_price : 0;
-                    [t.change_tp, t.change_p_tp, t.change_n_tp, t.change_valuation_tp, t.change_unvaluation_tp] = await ctx.service.change.getChangeTp(t.id);
-                    // t.material_tp = await ctx.service.material.getSumMaterial(t.id);
-                    // 获取本标段 本月计量期审批通过数目,变更令审批通过数目,台账修订通过数目,材料调差通过数目
-                    t.month_stage_num = await ctx.service.stageAudit.getNumByMonth(t.id, startMonth, endMonth);
-                    t.month_change_num = await ctx.service.changeAudit.getNumByMonth(t.id, startMonth, endMonth);
-                    t.month_revise_num = await ctx.service.reviseAudit.getNumByMonth(t.id, startMonth, endMonth);
-                    t.month_material_num = await ctx.service.materialAudit.getNumByMonth(t.id, startMonth, endMonth);
-                    // 获取标段计量月统计及截止月累计计量
-                    // 这个慢,统计下面这个方法时长
-                    const stageList = await ctx.service.stage.getStageByDataCollect(t.id, t.stage_tp);
-                    // const stageList = [];
-                    const month_stage = [];
-                    for (const s of stageList) {
-                        const monthOneStage = ctx.app._.find(month_stage, { yearmonth: s.s_time });
-                        if (monthOneStage) {
-                            monthOneStage.tp = ctx.helper.add(monthOneStage.tp, s.tp);
-                            monthOneStage.end_yf_tp = ctx.helper.add(monthOneStage.end_yf_tp, s.yf_tp);
-                            monthOneStage.end_sf_tp = ctx.helper.add(monthOneStage.end_sf_tp, s.sf_tp);
-                        } else {
-                            const data = {
-                                yearmonth: s.s_time,
-                                tp: s.tp,
-                                end_tp: s.end_tp,
-                                end_yf_tp: s.yf_tp,
-                                end_sf_tp: s.sf_tp,
-                            };
-                            month_stage.push(data);
+                    if (!only_cost) {
+                        await this.ctx.service.tenderCache.loadTenderCache(t, '');
+                        t.total_price = t.ledger_tp && t.ledger_tp.total_price ? t.ledger_tp.total_price : 0;
+                        [t.change_tp, t.change_p_tp, t.change_n_tp, t.change_valuation_tp, t.change_unvaluation_tp] = await ctx.service.change.getChangeTp(t.id);
+                        // t.material_tp = await ctx.service.material.getSumMaterial(t.id);
+                        // 获取本标段 本月计量期审批通过数目,变更令审批通过数目,台账修订通过数目,材料调差通过数目
+                        t.month_stage_num = await ctx.service.stageAudit.getNumByMonth(t.id, startMonth, endMonth);
+                        t.month_change_num = await ctx.service.changeAudit.getNumByMonth(t.id, startMonth, endMonth);
+                        t.month_revise_num = await ctx.service.reviseAudit.getNumByMonth(t.id, startMonth, endMonth);
+                        t.month_material_num = await ctx.service.materialAudit.getNumByMonth(t.id, startMonth, endMonth);
+                        // 获取标段计量月统计及截止月累计计量
+                        // 这个慢,统计下面这个方法时长
+                        const stageList = await ctx.service.stage.getStageByDataCollect(t.id, t.stage_tp);
+                        // const stageList = [];
+                        const month_stage = [];
+                        for (const s of stageList) {
+                            const monthOneStage = ctx.app._.find(month_stage, { yearmonth: s.s_time });
+                            if (monthOneStage) {
+                                monthOneStage.tp = ctx.helper.add(monthOneStage.tp, s.tp);
+                                monthOneStage.end_yf_tp = ctx.helper.add(monthOneStage.end_yf_tp, s.yf_tp);
+                                monthOneStage.end_sf_tp = ctx.helper.add(monthOneStage.end_sf_tp, s.sf_tp);
+                            } else {
+                                const data = {
+                                    yearmonth: s.s_time,
+                                    tp: s.tp,
+                                    end_tp: s.end_tp,
+                                    end_yf_tp: s.yf_tp,
+                                    end_sf_tp: s.sf_tp,
+                                };
+                                month_stage.push(data);
+                            }
                         }
+                        t.month_stage = month_stage;
+                    }
+                    if (have_cost) {
+                        // 取最新已审批完成的成本管理-成本分析期数据
+                        t.cost_analysis = await ctx.service.costStage.getLastCheckedStage(t.id, 'analysis');
+                        t.stage_num = await ctx.service.stage.getNumByChecked(t.id);
+                        t.cost_ledger_num = await ctx.service.costStage.getNumByChecked(t.id, 'ledger');
+                        t.cost_analysis_num = await ctx.service.costStage.getNumByChecked(t.id, 'analysis');
+                        t.cost_book_num = await ctx.service.costStage.getNumByChecked(t.id, 'book');
+                    }
+                }
+                if (have_cost) {
+                    const data = {};
+                    const budget = await ctx.service.budget.getCurBudget(ctx.subProject.budget_id);
+                    budget.name = ctx.subProject.name || '';
+                    if (!budget) throw '未设置概预算标准';
+                    if (budget.final_id) {
+                        data.final = await ctx.service.budgetFinal.getAllDataByCondition({ where: { final_id: budget.final_id } });
+                        data.finalInfo = await ctx.service.budgetFinalList.getFinal(budget.final_id);
+                    } else {
+                        data.gu = await ctx.service.budgetGu.getData(budget.id);
+                        data.gai = await ctx.service.budgetGai.getData(budget.id);
+                        data.yu = await ctx.service.budgetYu.getData(budget.id);
+                        data.zb = await ctx.service.budgetZb.getData(budget.id);
+                        data.ctrl = await ctx.service.budgetCtrl.getData(budget.id);
                     }
-                    t.month_stage = month_stage;
+                    responseData.data.budget = data;
                 }
                 // const tenderMapList = await ctx.service.tenderMap.getAllDataByCondition({ where: { tid: tender.id } });
                 responseData.data.tenderList = tenderList;

+ 1 - 0
app/controller/sub_proj_setting_controller.js

@@ -453,6 +453,7 @@ module.exports = app => {
                     dcTenders,
                     is_dz1: ['P0505', 'MI22U'].indexOf(ctx.session.sessionProject.code) !== -1 ? 5 : false,
                     is_dz2,
+                    is_dz3: 8,
                     selfCategoryLevel: await ctx.service.projectAccount.getSelfCategoryLevel(this.ctx.session.sessionUser.accountId),
                 };
                 if (is_dz2) {

+ 117 - 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,43 @@ class BudgetFinal {
         });
     }
 
+    async _loadDeal(deal, stage) {
+        const helper = this.ctx.helper;
+        const treeData = await this.ctx.service.contractTree.getAllDataByCondition({ where: { id: deal, spid: this.ctx.subProject.id, contract_type: 1 } });
+        const contractData = await this.ctx.service.contract.getAllDataByCondition({ where: { id: deal, spid: this.ctx.subProject.id, contract_type: 1 } });
+
+        const paySqlParam = [];
+        const timeFilter = stage.type === 'month' ? `AND pay_time <= ? ` : '';
+        if (stage.type === 'month') {
+            paySqlParam.push(this.ctx.moment(stage.month, 'YYYY-MM').endOf('month').format('YYYY-MM-DD HH:mm:ss'));
+        }
+        const checkedFilter = stage.checked && this.ctx.subProject.page_show.openContractPaySubProjectShenpi ? ' AND status = 3 ' : '';
+        const paySql = `SELECT cid, SUM(yf_price) AS yf_tp FROM ${this.ctx.service.contractPay.tableName}` +
+            `    WHERE cid IN(${contractData.map(x => { return `'${x.id}'`}).join(', ')}) ` + timeFilter + checkedFilter +
+            '    GROUP BY cid';
+        const contractPayData = await this.ctx.service.contractPay.db.query(paySql, paySqlParam);
+        for (const c of contractData) {
+            const cp = contractPayData.find(x => { return x.cid === c.id; });
+            c.yf_tp = cp ? cp.yf_tp || 0 : 0;
+        }
+
+        const tree = new BillsTree(this.ctx, {
+            id: 'contract_id',
+            pid: 'contract_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            calcFields: ['total_price', 'yf_tp'],
+            calc: function (node, helper, decimal) {},
+        });
+        tree.loadDatas([...treeData, ...contractData]);
+        this.finalTree.loadTree(tree, function (cur, source) {
+            cur.total_price = helper.add(cur.total_price, source.total_price);
+            cur.final_contract_tp = helper.add(cur.final_contract_tp, source.yf_tp);
+            cur.final_tp = helper.add(cur.final_tp, source.yf_tp);
+        });
+    }
+
     async _afterLoad() {
         const helper = this.ctx.helper;
         const checkGaiExist = this.finalTree.datas.findIndex(x => {
@@ -326,19 +427,20 @@ class BudgetFinal {
         return data;
     }
 
-    async doFinal(budget, final) {
+    async doFinal(budget, final, stage, contract) {
         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(contract, stage);
         this._afterLoad();
         return this.getFinalData();
     }

+ 3 - 0
app/public/css/main.css

@@ -1665,6 +1665,9 @@ overflow-y: auto;
 .height-20{
   height: 19%;
 }
+.height-25 {
+    height: 24%;
+}
 .review_box{
   height: 100%;
   overflow: hidden;

TEMPAT SAMPAH
app/public/images/juecedaping07.png


+ 91 - 5
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},
@@ -467,7 +467,7 @@ $(document).ready(() => {
             if (compareObj.finalInfo) {
                 this.selectTree.datas.forEach(x => {
                     x.selected = compareObj.finalInfo.tender.indexOf(x.tid + '') >= 0;
-                })
+                });
             }
             const sfSpreadSetting = {
                 cols: [
@@ -486,11 +486,31 @@ $(document).ready(() => {
                 selectedBackColor: '#fffacd',
                 readOnly: true,
             };
-            this.spread = SpreadJsObj.createNewSpread($('#sf-spread')[0]);
+            this.spread = SpreadJsObj.createNewSpread($('#sf-tender-spread')[0]);
             this.sheet = this.spread.getActiveSheet();
             SpreadJsObj.initSheet(this.sheet, sfSpreadSetting);
             SpreadJsObj.loadSheetData(this.sheet, SpreadJsObj.DataType.Tree, this.selectTree);
 
+            this.contractTree = createNewPathTree('base', { id: 'contract_id', pid: 'contract_pid', order: 'order', level: 'level', fullPath: 'full_path', rootId: -1 });
+            this.contractTree.loadDatas(contract);
+            this.contractSpread = SpreadJsObj.createNewSpread($('#sf-contract-spread')[0]);
+            this.contractSheet = this.contractSpread.getActiveSheet();
+            SpreadJsObj.initSheet(this.contractSheet, {
+                cols: [
+                    {title: '选择', colSpan: '1', rowSpan: '1', field: 'selected', hAlign: 1, width: 40, formatter: '@', cellType: 'checkbox'},
+                    {title: '编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 120, formatter: '@', cellType: 'tree'},
+                    {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 200, formatter: '@'},
+                ],
+                emptyRows: 0,
+                headRows: 1,
+                headRowHeight: [32],
+                defaultRowHeight: 21,
+                headerFont: '12px 微软雅黑',
+                font: '12px 微软雅黑',
+                readOnly: true,
+            });
+            SpreadJsObj.loadSheetData(this.contractSheet, SpreadJsObj.DataType.Tree, this.contractTree);
+
             this.spread.bind(spreadNS.Events.ButtonClicked, function (e, info) {
                 if (!info.sheet.zh_setting) return;
 
@@ -502,6 +522,24 @@ $(document).ready(() => {
                 SpreadJsObj.reloadColData(info.sheet, 0);
             });
 
+            this.contractSpread.bind(spreadNS.Events.ButtonClicked, function (e, info) {
+                if (!info.sheet.zh_setting) return;
+
+                const col = info.sheet.zh_setting.cols[info.col];
+                if (col.field !== 'selected') return;
+
+                const node = SpreadJsObj.getSelectObject(info.sheet);
+                self.selectContract(node, !node[col.field]);
+                SpreadJsObj.reloadColData(info.sheet, 0);
+            });
+
+            $('[name=sf-tab]').click(function() {
+                const content = this.getAttribute('href');
+                if (content === '#sf-contract') {
+                    self.contractSpread.refresh();
+                }
+                if (content === '#sf-tender') self.spread.refresh();
+            });
 
             $('#sf-select-all').click(function() {
                 for (const n of self.selectTree.nodes) {
@@ -512,7 +550,23 @@ $(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 contracts = self.getSelectContracts();
+                const data = { final_id: compareObj.curFinalId(), id: rela, final_type: $('[name=final_type]:checked').val(), stage, contracts };
                 postData(window.location.pathname + '/final', data, function(result, msg) {
                     compareObj.loadFinalData(result, msg);
                     $('#select-final').modal('hide');
@@ -534,6 +588,19 @@ $(document).ready(() => {
                 p.selected = select;
             }
         }
+        selectContract(node, select) {
+            const posterity = this.contractTree.getPosterity(node);
+            posterity.unshift(node);
+            for (const p of posterity) {
+                if (p) p.selected = select;
+            }
+            if (select) {
+                const parents = this.contractTree.getAllParents(node);
+                for(const p of parents) {
+                    if (p) p.selected = select;
+                }
+            }
+        }
         getSelects() {
             const select = [];
             for (const n of this.selectTree.nodes) {
@@ -541,6 +608,10 @@ $(document).ready(() => {
             }
             return select;
         }
+        getSelectContracts() {
+            const select = this.contractTree.nodes.filter(x => { return !!x.selected; }).map(x => { return x.id; });
+            return select;
+        }
     }
 
     $('#select-final').on('shown.bs.modal', () => {
@@ -630,4 +701,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,
+    });
 });

+ 96 - 37
app/public/js/contract_detail.js

@@ -153,7 +153,7 @@ $(document).ready(function() {
     };
     const contractTree = createNewPathTree('revise', treeSetting);
 
-    const contractDetail = ['c_code', 'name', 'total_price', 'pay_price', 'debit_price','yf_price', 'sf_price', 'party_a', 'party_a_user', 'party_b', 'party_b_user', 'sign_date', 'address', 'entity', 'bank', 'bank_account', 'remark', 'settle_code', 'tax', 'create_time'];
+    const contractDetail = ['c_code', 'name', 'price', 'supplement_price', 'total_price', 'pay_price', 'debit_price','yf_price', 'sf_price', 'party_a', 'party_a_user', 'party_b', 'party_b_user', 'sign_date', 'address', 'entity', 'bank', 'bank_account', 'remark', 'settle_code', 'tax', 'create_time'];
 
     // 清单 相关方法&绑定spreadjs事件
     const contractTreeSpreadObj = {
@@ -1882,14 +1882,15 @@ $(document).ready(function() {
                     data[c] = $('#htdetail_' + c + ' input').val();
                 }
             }
+            data.total_price = ZhCalc.add(node.supplement_price, parseFloat(data.price));
             if (data.c_code === '' || data.name === '') {
                 toastr.warning('合同编号和合同名称不能为空');
                 return;
             }
-            if (data.total_price === '') {
+            if (data.price === '') {
                 toastr.warning('合同金额不能为空');
                 return;
-            } else if (!/^\d+(\.\d+)?$/.test(data.total_price)) {
+            } else if (!/^\d+(\.\d+)?$/.test(data.price)) {
                 toastr.warning('合同金额只能输入数字');
                 return;
             }
@@ -2188,6 +2189,17 @@ $(document).ready(function() {
             toastr.warning('已结算的合同不能删除补充合同');
             return;
         }
+        const supplementInfo = _.find(contractSupplements, { id: csid });
+        const oldPrice = supplementInfo ? supplementInfo.price : 0;
+        let supplement_price = ZhCalc.sum(_.map(contractSupplements, 'price'));
+        // data.price 汇总入supplement_price并去除原有值
+        supplement_price = ZhCalc.sub(supplement_price, oldPrice);
+        const total_price = ZhCalc.add(node.price, supplement_price);
+        if (parseFloat(total_price) < node.yf_price) {
+            const name = contract_type === contractConst.type.expenses ? '付' : '回';
+            toastr.error('不能删除,因删除后总合同金额汇总会小于累计应' + name + '金额 ' + node.yf_price);
+            return;
+        }
         deleteAfterHint(function () {
             postData(window.location.pathname + '/update', {postType: 'del-contract-supplement', postData: { select: node.id, supplement: csid }}, function (result) {
                 const refreshNode = contractTree.loadPostData(result.node);
@@ -2610,7 +2622,7 @@ $(document).ready(function() {
         $('#cons-add').on('show.bs.modal', function () {
             $('#cons-add input[name="code"]').val('');
             $('#cons-add input[name="name"]').val('');
-            $('#cons-add input[name="total_price"]').val('');
+            $('#cons-add input[name="price"]').val('');
             $('#cons-add input[name="party_a"]').val('');
             $('#cons-add input[name="party_b"]').val('');
             $('#cons-add input[name="sign_date"]').val('');
@@ -2628,7 +2640,7 @@ $(document).ready(function() {
             const data = {
                 code: $('#cons-add input[name="code"]').val(),
                 name: $('#cons-add input[name="name"]').val(),
-                total_price: $('#cons-add input[name="total_price"]').val(),
+                price: $('#cons-add input[name="price"]').val(),
                 party_a: $('#cons-add input[name="party_a"]').val(),
                 party_b: $('#cons-add input[name="party_b"]').val(),
                 sign_date: $('#cons-add input[name="sign_date"]').val() || null,
@@ -2643,12 +2655,12 @@ $(document).ready(function() {
                 toastr.error('请输入合同名称');
                 return;
             }
-            if (!data.total_price) {
+            if (!data.price) {
                 toastr.error('请输入合同金额');
                 return;
             }
             // 金额只能输入数字
-            if (!/^\d+(\.\d+)?$/.test(data.total_price)) {
+            if (!/^\d+(\.\d+)?$/.test(data.price)) {
                 toastr.error('合同金额只能输入数字');
                 return;
             }
@@ -2951,7 +2963,7 @@ $(document).ready(function() {
             address: $('#cons-addsupplement input[name="address"]').val(),
             content: $('#cons-addsupplement textarea[name="content"]').val(),
         }
-        if (!judgeSupplements(data)) {
+        if (!judgeSupplements(data, node)) {
             return;
         }
         console.log(node, data);
@@ -3001,7 +3013,7 @@ $(document).ready(function() {
             address: $('#cons-addsupplement input[name="address"]').val(),
             content: $('#cons-addsupplement textarea[name="content"]').val(),
         }
-        if (!judgeSupplements(data)) {
+        if (!judgeSupplements(data, node)) {
             return;
         }
         console.log(node, data);
@@ -3019,7 +3031,7 @@ $(document).ready(function() {
         })
     });
 
-    function judgeSupplements(data) {
+    function judgeSupplements(data, node) {
         let flag = true;
         if (!data.code) {
             toastr.error('请输入编号');
@@ -3029,7 +3041,6 @@ $(document).ready(function() {
             toastr.error('请输入名称');
             return false;
         }
-        console.log(data.price);
         if (data.price === '') {
             toastr.error('请输入金额');
             return false;
@@ -3039,6 +3050,17 @@ $(document).ready(function() {
             toastr.error('金额只能输入数字');
             return false;
         }
+        const oldPrice = data.id ? (_.find(contractSupplements, { id: data.id }) || {}).price : 0;
+        const newPrice = parseFloat(data.price);
+        let supplement_price = ZhCalc.sum(_.map(contractSupplements, 'price'));
+        // data.price 汇总入supplement_price并去除原有值
+        supplement_price = ZhCalc.add(ZhCalc.sub(supplement_price, oldPrice), newPrice);
+        const total_price = ZhCalc.add(node.price, supplement_price);
+        if (parseFloat(total_price) < node.yf_price) {
+            const name = contract_type === contractConst.type.expenses ? '付' : '回';
+            toastr.error('总合同金额汇总不能小于累计应' + name + '金额 ' + node.yf_price);
+            return;
+        }
         return flag;
     }
 
@@ -3194,24 +3216,24 @@ $(document).ready(function() {
             }
             shenpi_html += !reload ? `<td class="shenpi-td ${auditConst.auditProgressClass[pay.status]}">` : '';
             if ((pay.status === auditConst.status.uncheck || pay.status === auditConst.status.checkNo) && pay.uid === user_id) {
-                shenpi_html += `<a href="javascript:void(0);" node-cid="${pay.cid}" node-cpid="${pay.id}" class="btn ${auditConst.statusButtonClass[pay.status]} btn-sm show-sub-sp">${auditConst.statusButton[pay.status]}</a>`;
+                shenpi_html += `<a href="javascript:void(0);" node-cid="${pay.cid}" node-csid="${pay.id}" class="btn ${auditConst.statusButtonClass[pay.status]} btn-sm show-sub-sp">${auditConst.statusButton[pay.status]}</a>`;
             } else if ((pay.status === auditConst.status.checking || pay.status === auditConst.status.checkNoPre) && pay.curAuditors && pay.curAuditors.findIndex(x => { return x.aid === user_id; }) >= 0) {
-                shenpi_html += `<a href="javascript: void(0);" node-cid="${pay.cid}" node-cpid="${pay.id}" data-operate="checked" data-title="审批通过" class="btn btn-success btn-sm mr-2 show-sp-list">审批通过</a>`;
-                shenpi_html += `<a href="javascript: void(0);" node-cid="${pay.cid}" node-cpid="${pay.id}" data-operate="checkNo" data-title="审批退回" class="btn btn-warning btn-sm show-sp-list">审批退回</a>`;
+                shenpi_html += `<a href="javascript: void(0);" node-cid="${pay.cid}" node-csid="${pay.id}" data-operate="checked" data-title="审批通过" class="btn btn-success btn-sm mr-2 show-sp-list">审批通过</a>`;
+                shenpi_html += `<a href="javascript: void(0);" node-cid="${pay.cid}" node-csid="${pay.id}" data-operate="checkNo" data-title="审批退回" class="btn btn-warning btn-sm show-sp-list">审批退回</a>`;
             } else {
                 if (pay.status === auditConst.status.checked && pay.final_auditor_str) {
-                    shenpi_html += `<a href="javascript:void(0);" node-cid="${pay.cid}" node-cpid="${pay.id}" class="show-sp-list">${pay.final_auditor_str}</a>`;
+                    shenpi_html += `<a href="javascript:void(0);" node-cid="${pay.cid}" node-csid="${pay.id}" class="show-sp-list">${pay.final_auditor_str}</a>`;
                 } else if (pay.status === auditConst.status.checkNo && pay.curAuditors2 && pay.curAuditors2.length > 0) {
                     if (pay.curAuditors2[0].audit_type === auditType.key.common) {
-                        shenpi_html += `<a href="javascript:void(0);" node-cid="${pay.cid}" node-cpid="${pay.id}" class="show-sp-list">${pay.curAuditors2[0].name}${pay.curAuditors2[0].role !== '' && pay.curAuditors2[0].role !== null ? '-' + pay.curAuditors2[0].role : ''}</a>`;
+                        shenpi_html += `<a href="javascript:void(0);" node-cid="${pay.cid}" node-csid="${pay.id}" class="show-sp-list">${pay.curAuditors2[0].name}${pay.curAuditors2[0].role !== '' && pay.curAuditors2[0].role !== null ? '-' + pay.curAuditors2[0].role : ''}</a>`;
                     } else {
-                        shenpi_html += `<a href="javascript:void(0);" node-cid="${pay.cid}" node-cpid="${pay.id}" class="show-sp-list">${transFormToChinese(pay.curAuditors2[0].audit_order) + '审'}</a>`;
+                        shenpi_html += `<a href="javascript:void(0);" node-cid="${pay.cid}" node-csid="${pay.id}" class="show-sp-list">${transFormToChinese(pay.curAuditors2[0].audit_order) + '审'}</a>`;
                     }
                 } else if (pay.curAuditors.length > 0) {
                     if (pay.curAuditors[0].audit_type === auditType.key.common) {
-                        shenpi_html += `<a href="javascript:void(0);" node-cid="${pay.cid}" node-cpid="${pay.id}" class="show-sp-list">${pay.curAuditors[0].name}${pay.curAuditors[0].role !== '' && pay.curAuditors[0].role !== null ? '-' + pay.curAuditors[0].role : ''}</a>`;
+                        shenpi_html += `<a href="javascript:void(0);" node-cid="${pay.cid}" node-csid="${pay.id}" class="show-sp-list">${pay.curAuditors[0].name}${pay.curAuditors[0].role !== '' && pay.curAuditors[0].role !== null ? '-' + pay.curAuditors[0].role : ''}</a>`;
                     } else {
-                        shenpi_html += `<a href="javascript:void(0);" node-cid="${pay.cid}" node-cpid="${pay.id}" class="show-sp-list">${transFormToChinese(pay.curAuditors[0].audit_order) + '审'}</a>`;
+                        shenpi_html += `<a href="javascript:void(0);" node-cid="${pay.cid}" node-csid="${pay.id}" class="show-sp-list">${transFormToChinese(pay.curAuditors[0].audit_order) + '审'}</a>`;
                     }
                 }
                 shenpi_html += ` ${auditConst.auditProgress[pay.status]}`;
@@ -3225,7 +3247,8 @@ $(document).ready(function() {
         $('body').on('click', '.show-sub-sp', function () {
             console.log($(this).attr('node-cid'));
             let node = _.find(contractTree.nodes, { id: $(this).attr('node-cid') });
-            const cpid = $(this).attr('node-cpid');
+            const cpid = $(this).attr('node-cpid') || null;
+            const csid = $(this).attr('node-csid') || null;
             if (cpid) {
                 node = _.find(contractPays, { id: parseInt(cpid) });
                 $('#contract-add-group').hide();
@@ -3233,12 +3256,22 @@ $(document).ready(function() {
                 $('#change-pay-sp-group').val(node.sp_group);
                 $('#start-sp-cid').val(node.cid);
                 $('#start-sp-cpid').val(node.id);
+                $('#start-sp-csid').val('');
+            } else if (csid) {
+                node = _.find(contractSupplements, { id: parseInt(csid) });
+                $('#contract-add-group').show();
+                $('#pay-add-group').hide();
+                $('#change-sp-group').val(node.sp_group);
+                $('#start-sp-cid').val(node.cid);
+                $('#start-sp-csid').val(node.id);
+                $('#start-sp-cpid').val('');
             } else {
                 $('#contract-add-group').show();
                 $('#pay-add-group').hide();
                 $('#change-sp-group').val(node.sp_group);
                 $('#start-sp-cid').val(node.id);
                 $('#start-sp-cpid').val('');
+                $('#start-sp-csid').val('');
             }
             if (!node) {
                 toastr.error('合同数据获取失败');
@@ -3251,14 +3284,22 @@ $(document).ready(function() {
 
         $('body').on('click', '.show-sp-list', function () {
             let node = _.find(contractTree.nodes, { id: $(this).attr('node-cid') });
-            const cpid = $(this).attr('node-cpid');
+            const cpid = $(this).attr('node-cpid') || null;
+            const csid = $(this).attr('node-csid') || null;
             if (cpid) {
                 node = _.find(contractPays, { id: parseInt(cpid) });
                 $('#check-sp-cid').val(node.cid);
                 $('#check-sp-cpid').val(node.id);
+                $('#check-sp-csid').val('');
+            } else if (csid) {
+                node = _.find(contractSupplements, { id: parseInt(csid) });
+                $('#check-sp-cid').val(node.cid);
+                $('#check-sp-csid').val(node.id);
+                $('#check-sp-cpid').val('');
             } else {
                 $('#check-sp-cid').val(node.id);
                 $('#check-sp-cpid').val('');
+                $('#check-sp-csid').val('');
             }
             if (!node) {
                 toastr.error('合同数据获取失败');
@@ -3319,15 +3360,21 @@ $(document).ready(function() {
         function getNode(from = 'start') {
             const cid = $('#'+ from +'-sp-cid').val();
             const cpid = $('#'+ from +'-sp-cpid').val();
+            const csid = $('#'+ from +'-sp-csid').val();
+            let type = 'contract';
             let node = _.find(contractTree.nodes, { id: cid });
             if (cpid) {
                 node = _.find(contractPays, { id: parseInt(cpid) });
+                type = 'pay';
+            } else if (csid) {
+                node = _.find(contractSupplements, { id: parseInt(csid) });
+                type = 'supplement';
             }
             if (!node) {
                 toastr.error('合同数据获取失败');
                 return;
             }
-            return node;
+            return [node, type];
         }
 
         function setStartAuditsHtml(node) {
@@ -3568,20 +3615,20 @@ $(document).ready(function() {
 
         // 添加到审批流程中
         $('.search-user-list').on('click', 'dd', function () {
-            const node = getNode();
+            const [node, type] = getNode();
             const id = parseInt($(this).data('id'));
             if (id) {
-                postData(window.location.pathname + '/check', {postType: 'add-audit', id: node.id, auditorId: id}, function (result) {
+                postData(window.location.pathname + '/check', {postType: 'add-audit', id: node.id, type, auditorId: id}, function (result) {
                     setStartAuditChange(node, result);
                 });
             }
         });
         // 删除审批人
         $('body').on('click', '#auditors li a', function () {
-            const node = getNode();
+            const [node, type] = getNode();
             const li = $(this).parents('li');
             const data = {
-                postType: 'del-audit', id: node.id,
+                postType: 'del-audit', id: node.id, type,
                 auditorId: parseInt(li.attr('auditorId')),
             };
             postData(window.location.pathname + '/check', data, (result) => {
@@ -3590,15 +3637,15 @@ $(document).ready(function() {
         });
 
         $('#change-sp-group').change(function () {
-            const node = getNode();
-            postData(window.location.pathname + '/check', {postType: 'change-sp', id: node.id, sp_group: $(this).val()}, function (result) {
+            const [node, type] = getNode();
+            postData(window.location.pathname + '/check', {postType: 'change-sp', id: node.id, type, sp_group: $(this).val()}, function (result) {
                 setStartAuditChange(node, result);
             });
         });
 
         $('#change-pay-sp-group').change(function () {
-            const node = getNode();
-            postData(window.location.pathname + '/check', {postType: 'change-sp', id: node.id, sp_group: $(this).val()}, function (result) {
+            const [node, type] = getNode();
+            postData(window.location.pathname + '/check', {postType: 'change-sp', id: node.id, type, sp_group: $(this).val()}, function (result) {
                 setStartAuditChange(node, result);
             });
         });
@@ -3614,15 +3661,15 @@ $(document).ready(function() {
         }
 
         $('#start-sp-btn').click(function () {
-            const node = getNode();
-            postData(window.location.pathname + '/check', {postType: 'start', id: node.id}, function (result) {
-                setShenpiChange(node, result);
+            const [node, type] = getNode();
+            postData(window.location.pathname + '/check', {postType: 'start', id: node.id, type}, function (result) {
+                setShenpiChange(node, result, type);
                 $('#sub-sp').modal('hide');
             });
         });
 
         $('body').on('click', '#check-sp-btn', function () {
-            const node = getNode('check');
+            const [node, type] = getNode('check');
             const operate = $('#check-operate').val();
             console.log(node, operate);
             const checkType = operate === 'checked' ? auditConst.status.checked : parseInt($('#audit-list').find('input[name="checkType"]:checked').val());
@@ -3635,17 +3682,18 @@ $(document).ready(function() {
                 postType: 'check',
                 id: node.id,
                 checkType,
+                type,
                 opinion: $('#audit-list').find('textarea').val().replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' '),
             }
             console.log(data);
             postData(window.location.pathname + '/check', data, function (result) {
-                setShenpiChange(node, result);
+                setShenpiChange(node, result, type);
                 $('#sp-list').modal('hide');
             });
         });
 
-        function setShenpiChange(node, result) {
-            if (node.cid) {
+        function setShenpiChange(node, result, type) {
+            if (type === 'pay') {
                 contractPays.splice(contractPays.findIndex(item => item.id === node.id), 1, result);
                 const shenpi_html = setPayShenpiHtml(result, true);
                 $("#htpay-table tbody").find(`tr[data-cpid="${node.id}"]`).children('.shenpi-td').html(shenpi_html);
@@ -3656,6 +3704,17 @@ $(document).ready(function() {
                     $('#htpay-table tbody').find(`tr[data-cpid="${node.id}"]`).find('.pay-edit').show();
                     $('#htpay-table tbody').find(`tr[data-cpid="${node.id}"]`).find('.pay-del').show();
                 }
+            } else if (type === 'supplement') {
+                contractSupplements.splice(contractSupplements.findIndex(item => item.id === node.id), 1, result);
+                const shenpi_html = setSupplementShenpiHtml(result, true);
+                $("#htsupplement-table tbody").find(`tr[data-csid="${node.id}"]`).children('.shenpi-td').html(shenpi_html);
+                $("#htsupplement-table tbody").find(`tr[data-csid="${node.id}"]`).children('.shenpi-td').addClass(auditConst.auditProgressClass[result.status]);
+                $('#htsupplement-table tbody').find(`tr[data-csid="${node.id}"]`).find('.supplement-edit').hide();
+                $('#htsupplement-table tbody').find(`tr[data-csid="${node.id}"]`).find('.supplement-del').hide();
+                if (node.uid === user_id && (result.status === auditConst.status.uncheck || result.status === auditConst.status.checkNo)) {
+                    $('#htsupplement-table tbody').find(`tr[data-csid="${node.id}"]`).find('.supplement-edit').show();
+                    $('#htsupplement-table tbody').find(`tr[data-csid="${node.id}"]`).find('.supplement-del').show();
+                }
             } else {
                 const refreshNode = contractTree.loadPostData({update: result});
                 contractTreeSpreadObj.refreshTree(contractSheet, refreshNode);

+ 23 - 0
app/public/js/cost_stage_book.js

@@ -808,4 +808,27 @@ $(document).ready(function() {
             }, 100);
         });
     })('a[name=showLevel]', billsObj.sheet);
+
+    if (!readOnly) {
+        $('#cost-decimal').on('show.bs.modal', () => {
+            const decimalObjs = [$('#cost-tp'), $('#cost-excl-tax-tp')];
+            for (const obj of decimalObjs) {
+                obj.val(obj.attr('org'));
+            }
+        });
+        $('#cost-decimal-ok').click(() => {
+            const decimal = { tp: parseInt($('#cost-tp').val()), excl_tax_tp: parseInt($('#cost-excl-tax-tp').val()) };
+            postData('decimal', { decimal }, function(result) {
+                if (result.calc) {
+                    const refreshNode = billsObj.tree.loadPostData({ update: result.bills });
+                    billsObj.refreshTree(refreshNode);
+                    detailObj.data.updateDatas(result.detail);
+                    detailObj.reloadDetailData();
+                }
+                $('#cost-tp').attr('org', decimal.tp);
+                $('#cost-excl-tax-tp').attr('org', decimal.excl_tax_tp);
+                $('#cost-decimal').modal('hide');
+            });
+        });
+    }
 });

+ 22 - 0
app/public/js/cost_stage_ledger.js

@@ -1266,6 +1266,28 @@ $(document).ready(function() {
             }
         }
     });
+    if (!readOnly) {
+        $('#cost-decimal').on('show.bs.modal', () => {
+            const decimalObjs = [$('#cost-tp'), $('#cost-excl-tax-tp')];
+            for (const obj of decimalObjs) {
+                obj.val(obj.attr('org'));
+            }
+        });
+        $('#cost-decimal-ok').click(() => {
+            const decimal = { tp: parseInt($('#cost-tp').val()), excl_tax_tp: parseInt($('#cost-excl-tax-tp').val()) };
+            postData('decimal', { decimal }, function(result) {
+                if (result.calc) {
+                    const refreshNode = billsObj.tree.loadPostData({ update: result.bills });
+                    billsObj.refreshTree(refreshNode);
+                    detailObj.data.updateDatas(result.detail);
+                    detailObj.reloadDetailData();
+                }
+                $('#cost-tp').attr('org', decimal.tp);
+                $('#cost-excl-tax-tp').attr('org', decimal.excl_tax_tp);
+                $('#cost-decimal').modal('hide');
+            });
+        });
+    }
 
     const costFile = $.ledger_att({
         selector: '#fujian',

+ 4 - 1
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;
@@ -3499,7 +3502,7 @@ $(document).ready(function() {
             postData('/tender/' + getTenderId() + '/pos/update', data, function (result) {
                 pos.updateDatas(result.pos);
                 if (result.posCalcDetail) posCalcDetail.detail.updateDatas(result.posCalcDetail);
-                const ledgerResult = ledgerTree.loadPostData(data.ledger);
+                const ledgerResult = ledgerTree.loadPostData(result.ledger);
                 treeOperationObj.refreshTree(ledgerSheet, ledgerResult);
                 if (result.ancGcl) ancGcl.updateDatas(result.ancGcl);
                 if (result.ancGclDetail) ancGclDetail.detail.updateDatas(result.ancGclDetail);

+ 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);

+ 1 - 0
app/router.js

@@ -563,6 +563,7 @@ module.exports = app => {
     app.get('/sp/:id/cost/tender/:tid/:stype/:sorder/compare', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, costStageCheck, 'costController.compare');
     app.post('/sp/:id/cost/tender/:tid/:stype/:sorder/load', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, costStageCheck, 'costController.stageLoad');
     app.post('/sp/:id/cost/tender/:tid/:stype/:sorder/update', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, costStageCheck, 'costController.stageUpdate');
+    app.post('/sp/:id/cost/tender/:tid/:stype/:sorder/decimal', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, costStageCheck, 'costController.setDecimal');
     app.post('/sp/:id/cost/tender/:tid/:stype/:sorder/file/upload', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, costStageCheck, 'costController.uploadStageFile');
     app.post('/sp/:id/cost/tender/:tid/:stype/:sorder/file/delete', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, costStageCheck, 'costController.deleteStageFile');
     app.post('/sp/:id/cost/tender/:tid/:stype/:sorder/stage/tag', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, costStageCheck, 'costController.tag');

+ 2 - 2
app/service/budget.js

@@ -263,11 +263,11 @@ module.exports = app => {
             }
         }
 
-        async doFinal(budget, final) {
+        async doFinal(budget, final, stage, contract) {
             const finalObj = new FinalObj(this.ctx);
             let finalData;
             try {
-                finalData = await finalObj.doFinal(budget, final);
+                finalData = await finalObj.doFinal(budget, final, stage, contract);
             } 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;

+ 4 - 3
app/service/contract.js

@@ -55,7 +55,8 @@ module.exports = app => {
                     is_leaf: 1,
                     c_code: data.code,
                     name: data.name,
-                    total_price: data.total_price ? parseFloat(data.total_price) : 0,
+                    total_price: data.price ? parseFloat(data.price) : 0,
+                    price: data.price ? parseFloat(data.price) : 0,
                     party_a: data.party_a,
                     party_b: data.party_b,
                     sign_date: data.sign_date,
@@ -75,7 +76,7 @@ module.exports = app => {
                     await transaction.update(this.ctx.service.contractTree.tableName, { id: node.id, is_leaf: 0 });
                 }
                 this.ctx.service.contractTree._cacheMaxLid(options, maxId + 1);
-                await this.ctx.service.contractSpAudit.makeAudits(transaction, options, insertId, null, this.ctx.session.sessionUser.accountId);
+                await this.ctx.service.contractSpAudit.makeAudits(transaction, options, insertData, 'contract', this.ctx.session.sessionUser.accountId);
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();
@@ -153,7 +154,7 @@ module.exports = app => {
                     // await this.ctx.service.contractSpAudit.loadAuditViewData(l);
                     // await this.ctx.service.contractSpAudit.checkShenpi(l);
                     if (l.status !== auditConst.status.checked || !l.final_auditor_str) {
-                        l.curAuditors2 = await this.ctx.service.contractSpAudit.getAuditorsByStatus(l.id, null, l.status, l.times);
+                        l.curAuditors2 = await this.ctx.service.contractSpAudit.getAuditorsByStatus(l, 'contract', l.status, l.times);
                         if (l.status === auditConst.status.checked && !l.final_auditor_str) {
                             const final_auditor_str = l.curAuditors2[0].audit_type === auditType.key.common
                                 ? l.curAuditors2[0].name + (l.curAuditors2[0].role ? '-' + l.curAuditors2[0].role : '')

+ 9 - 5
app/service/contract_pay.js

@@ -32,16 +32,17 @@ module.exports = app => {
             const pay_shenpi_status = this.ctx.contract_tender ? this.ctx.subProject.page_show.openContractPayTenderShenpi : this.ctx.subProject.page_show.openContractPaySubProjectShenpi;
             if (list.length > 0) {
                 const userList = await this.ctx.service.projectAccount.getAllDataByCondition({ where: { id: list.map(item => item.uid) } });
+                const type = 'pay';
                 for (const l of list) {
                     const userInfo = userList.find(item => item.id === l.uid);
                     l.username = userInfo ? userInfo.name : '';
                     l.files = await this.ctx.service.contractPayAtt.getAtt(l.id);
                     if (pay_shenpi_status) {
-                        await this.ctx.service.contractSpAudit.loadUser(l);
-                        await this.ctx.service.contractSpAudit.loadAuditViewData(l);
-                        await this.ctx.service.contractSpAudit.checkShenpi(l);
+                        await this.ctx.service.contractSpAudit.loadUser(l, type);
+                        await this.ctx.service.contractSpAudit.loadAuditViewData(l, type);
+                        await this.ctx.service.contractSpAudit.checkShenpi(l, type);
                         if (l.status !== auditConst.status.checked || !l.final_auditor_str) {
-                            l.curAuditors2 = await this.ctx.service.contractSpAudit.getAuditorsByStatus(l.cid, l.id, l.status, l.times);
+                            l.curAuditors2 = await this.ctx.service.contractSpAudit.getAuditorsByStatus(l, type, l.status, l.times);
                             if (l.status === auditConst.status.checked && !l.final_auditor_str) {
                                 const final_auditor_str = l.curAuditors2[0].audit_type === auditType.key.common
                                     ? l.curAuditors2[0].name + (l.curAuditors2[0].role ? '-' + l.curAuditors2[0].role : '')
@@ -82,7 +83,8 @@ module.exports = app => {
                 };
                 const result = await transaction.insert(this.tableName, insertData);
                 await this.calcContract(transaction, node);
-                await this.ctx.service.contractSpAudit.makeAudits(transaction, options, cid, result.insertId, this.ctx.session.sessionUser.accountId);
+                insertData.id = result.insertId;
+                await this.ctx.service.contractSpAudit.makeAudits(transaction, options, insertData, 'pay', this.ctx.session.sessionUser.accountId);
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();
@@ -214,6 +216,7 @@ module.exports = app => {
                             tid: p.tid,
                             cid: p.cid,
                             cpid: p.id,
+                            csid: null,
                             aid: fp.uid,
                             order: 0,
                             times: 1,
@@ -234,6 +237,7 @@ module.exports = app => {
                                 tid: p.tid,
                                 cid: p.cid,
                                 cpid: p.id,
+                                csid: null,
                                 aid: a.aid,
                                 order: a.order,
                                 times: 1,

+ 270 - 170
app/service/contract_sp_audit.js

@@ -27,11 +27,16 @@ module.exports = app => {
             this.tableName = 'contract_sp_audit';
         }
 
-        async checkShenpi(contract) {
+        async checkShenpi(contract, type = 'contract') {
             const cid = contract.cid ? contract.cid : contract.id;
-            const cpid = contract.cid ? contract.id : null;
-            const change_type = contract.cid ? contractConst.typeMap[contract.contract_type] : 'contract';
-            const tableName = contract.cid ? this.ctx.service.contractPay : this.ctx.service.contract;
+            const cpid = type === 'pay' ? contract.id : null;
+            const csid = type === 'supplement' ? contract.id : null;
+            const change_type = type === 'contract' || type === 'supplement' ? 'contract' : contractConst.typeMap[contract.contract_type];
+            const tableName = {
+                contract: this.ctx.service.contract,
+                pay: this.ctx.service.contractPay,
+                supplement: this.ctx.service.contractSupplement,
+            }[type];
             const status = auditConst.status;
             let shenpi = '';
             if (this.ctx.contract_tender) {
@@ -43,7 +48,7 @@ module.exports = app => {
             const shenpi_status = shenpi.contract;
             if ((contract.status === status.uncheck || contract.status === status.checkNo) && shenpi_status !== shenpiConst.sp_status.sqspr) {
                 // 进一步比较审批流是否与审批流程设置的相同,不同则替换为固定审批流或固定的终审
-                const auditList = await this.getAllDataByCondition({ where: { cid, cpid, times: contract.times }, orders: [['order', 'asc']] });
+                const auditList = await this.getAllDataByCondition({ where: { cid, cpid, csid, times: contract.times }, orders: [['order', 'asc']] });
                 // auditList.shift();
                 if (shenpi_status === shenpiConst.sp_status.gdspl) {
                     // 判断并获取审批组
@@ -68,9 +73,9 @@ module.exports = app => {
                             }
                         }
                         if (!sameAudit) {
-                            await this.updateNewAuditList(contract, shenpiList);
-                            await this.loadUser(contract);
-                            await this.loadAuditViewData(contract);
+                            await this.updateNewAuditList(contract, type, shenpiList);
+                            await this.loadUser(contract, type);
+                            await this.loadAuditViewData(contract, type);
                         }
                     }
                 } else if (shenpi_status === shenpiConst.sp_status.gdzs) {
@@ -78,7 +83,7 @@ module.exports = app => {
                     // 判断最后一个id是否与固定终审id相同,不同则删除原审批流中如果存在的id和添加终审
                     const lastAuditors = auditList.filter(x => { return x.audit_order === auditList.length - 1; });
                     if (shenpiInfo && (lastAuditors.length === 0 || (lastAuditors.length > 1 || shenpiInfo.audit_id !== lastAuditors[0].aid))) {
-                        await this.updateLastAudit(contract, auditList, shenpiInfo.audit_id);
+                        await this.updateLastAudit(contract, type, auditList, shenpiInfo.audit_id);
                     } else if (!shenpiInfo) {
                         // 不存在终审人的状态下这里恢复为授权审批人
                         shenpi.contract = shenpiConst.sp_status.sqspr;
@@ -87,12 +92,12 @@ module.exports = app => {
             }
         }
 
-        async loadUser(contract) {
+        async loadUser(contract, type = 'contract') {
             const status = auditConst.status;
             const accountId = this.ctx.session.sessionUser.accountId;
 
             contract.user = await this.ctx.service.projectAccount.getAccountInfoById(contract.uid);
-            contract.auditors = await this.getAuditors(contract.cid ? contract.cid : contract.id, contract.cid ? contract.id : null, contract.times); // 全部参与的审批人
+            contract.auditors = await this.getAuditors(contract, type, contract.times); // 全部参与的审批人
             contract.auditorIds = this._.map(contract.auditors, 'aid');
             contract.curAuditors = contract.auditors.filter(x => { return x.status === status.checking; }); // 当前流程中审批中的审批人
             contract.curAuditorIds = this._.map(contract.curAuditors, 'aid');
@@ -105,21 +110,21 @@ module.exports = app => {
             contract.finalAuditorIds = this._.map(contract.userGroups[contract.userGroups.length - 1], 'aid');
         }
 
-        async loadAuditViewData(contract) {
+        async loadAuditViewData(contract, type = 'contract') {
             const times = contract.status === auditConst.status.checkNo ? contract.times - 1 : contract.times;
 
             if (!contract.user) contract.user = await this.ctx.service.projectAccount.getAccountInfoById(contract.uid);
-            contract.auditHistory = await this.getAuditorHistory(contract.cid ? contract.cid : contract.id, contract.cid ? contract.id : null, times);
+            contract.auditHistory = await this.getAuditorHistory(contract, type, times);
             // 获取审批流程中左边列表
             // if ((contract.status === auditConst.status.checkNo) && !(contract.uid === this.ctx.session.sessionUser.accountId || this.ctx.contract_audit_permission.permission_edit_contract || this.ctx.session.sessionUser.is_admin)) {
-            const auditors = await this.getAuditors(contract.cid ? contract.cid : contract.id, contract.cid ? contract.id : null, times); // 全部参与的审批人
+            const auditors = await this.getAuditors(contract, type, times); // 全部参与的审批人
             const auditorGroups = this.ctx.helper.groupAuditors(auditors);
             contract.auditors2 = this.ctx.helper.groupAuditorsUniq(auditorGroups);
             // } else {
             //     contract.auditors2 = contract.userGroups;
             // }
             if (contract.status === auditConst.status.uncheck || contract.status === auditConst.status.checkNo) {
-                contract.auditorList = await this.getAuditors(contract.cid ? contract.cid : contract.id, contract.cid ? contract.id : null, contract.times);
+                contract.auditorList = await this.getAuditors(contract, type, contract.times);
             }
         }
 
@@ -130,7 +135,7 @@ module.exports = app => {
          * @param {Number} times - 第几次审批
          * @return {Promise<*>}
          */
-        async getAuditors(cid, cpid = null, times = 1, order_sort = 'asc', noYB = false) {
+        async getAuditors(contract, type, times = 1, order_sort = 'asc', noYB = false) {
             // const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time`, g.`sort` ' +
             //     'FROM ?? AS la, ?? AS pa, (SELECT t1.`aid`,(@i:=@i+1) as `sort` FROM (SELECT t.`aid`, t.`order` FROM (select `aid`, `order` from ?? WHERE `cpid` = ? AND `times` = ? ORDER BY `order` LIMIT 200) t GROUP BY t.`aid` ORDER BY t.`order`) t1, (select @i:=0) as it) as g ' +
             //     'WHERE la.`cpid` = ? and la.`times` = ? and la.`aid` = pa.`id` and g.`aid` = la.`aid` order by la.`order`';
@@ -142,12 +147,20 @@ module.exports = app => {
             // for (const i in result) {
             //     result[i].max_sort = count.num;
             // }
+            const cid = contract.cid ? contract.cid : contract.id;
+            const cpid = type === 'pay' ? contract.id : null;
+            const csid = type === 'supplement' ? contract.id : null;
+            const cpidSql = type === 'pay'
+                ? ' AND la.cpid = ' + cpid
+                : ' AND la.cpid is null';
+            const csidSql = type === 'supplement'
+                ? ' AND la.csid = ' + csid
+                : ' AND la.csid is null';
             const ybSql = noYB ? ' AND la.audit_order != 0' : '';
-            const cpidSql = cpid ? ' AND la.cpid = ' + cpid : ' AND la.cpid is null';
             const sql = 'SELECT la.id, la.aid, la.times, la.order, la.status, la.opinion, la.begin_time, la.end_time, la.audit_type, la.audit_order,' +
                 '    pa.name, pa.company, pa.role, pa.mobile, pa.telephone' +
                 `  FROM ${this.tableName} la LEFT JOIN ${this.ctx.service.projectAccount.tableName} pa ON la.aid = pa.id` +
-                '  WHERE la.cid = ?' + cpidSql + ' AND la.times = ?' + ybSql +
+                '  WHERE la.cid = ?' + cpidSql + csidSql + ' AND la.times = ?' + ybSql +
                 '  ORDER BY la.order ' + order_sort;
             const sqlParam = [cid, times];
             const result = await this.db.query(sql, sqlParam);
@@ -165,21 +178,37 @@ module.exports = app => {
          * @param {Number} times - 第几次审批
          * @return {Promise<*>}
          */
-        async getCurAuditor(cid, cpid = null, times = 1) {
-            const cpidSql = cpid ? ' AND la.cpid = ' + cpid : ' AND la.cpid is null';
+        async getCurAuditor(contract, type, times = 1) {
+            const cid = contract.cid ? contract.cid : contract.id;
+            const cpid = type === 'pay' ? contract.id : null;
+            const csid = type === 'supplement' ? contract.id : null;
+            const cpidSql = type === 'pay'
+                ? ' AND la.cpid = ' + cpid
+                : ' AND la.cpid is null';
+            const csidSql = type === 'supplement'
+                ? ' AND la.csid = ' + csid
+                : ' AND la.csid is null';
             const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time` ' +
                 '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +
-                '  WHERE la.`cid` = ?' + cpidSql + ' and la.`status` = ? and la.`times` = ? and la.`audit_order` != 0';
+                '  WHERE la.`cid` = ?' + cpidSql + csidSql + ' and la.`status` = ? and la.`times` = ? and la.`audit_order` != 0';
             const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, cid, auditConst.status.checking, times];
             return await this.db.queryOne(sql, sqlParam);
         }
 
-        async getCurAuditors(cid, cpid = null, times = 1) {
-            const cpidSql = cpid ? ' AND la.cpid = ' + cpid : ' AND la.cpid is null';
+        async getCurAuditors(contract, type, times = 1) {
+            const cid = contract.cid ? contract.cid : contract.id;
+            const cpid = type === 'pay' ? contract.id : null;
+            const csid = type === 'supplement' ? contract.id : null;
+            const cpidSql = type === 'pay'
+                ? ' AND la.cpid = ' + cpid
+                : ' AND la.cpid is null';
+            const csidSql = type === 'supplement'
+                ? ' AND la.csid = ' + csid
+                : ' AND la.csid is null';
             const sql =
                 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time`, la.audit_type, la.audit_order ' +
                 '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id`' +
-                '  WHERE la.`cid` = ?' + cpidSql + ' and la.`status` = ? and la.`times` = ? and la.`audit_order` != 0';
+                '  WHERE la.`cid` = ?' + cpidSql + csidSql + ' and la.`status` = ? and la.`times` = ? and la.`audit_order` != 0';
             const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, cid, auditConst.status.checking, times];
             return await this.db.query(sql, sqlParam);
         }
@@ -205,18 +234,21 @@ module.exports = app => {
          * @param auditorId
          * @return {Promise<*>}
          */
-        async getAuditGroupByList(cid, cpid = null, times, transaction = false) {
-            // const sql =
-            //     'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`sid`, la.`aid`, la.`order`, la.`status`' +
-            //     '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +
-            //     '  WHERE la.`sid` = ? and la.`times` = ? and la.`is_old` = 0 GROUP BY la.`aid` ORDER BY la.`order`';
-            // const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, stageId, times];
-            const cpidSql = cpid ? ' AND la.`cpid` = ' + cpid : ' AND la.`cpid` is null';
+        async getAuditGroupByList(contract, type, times, transaction = false) {
+            const cid = contract.cid ? contract.cid : contract.id;
+            const cpid = type === 'pay' ? contract.id : null;
+            const csid = type === 'supplement' ? contract.id : null;
+            const cpidSql = type === 'pay'
+                ? ' AND la.cpid = ' + cpid
+                : ' AND la.cpid is null';
+            const csidSql = type === 'supplement'
+                ? ' AND la.csid = ' + csid
+                : ' AND la.csid is null';
             const sql =
                 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`cid`, la.`cpid`, la.`aid`, la.`order`, la.`status`, la.audit_type, la.audit_order ' +
-                ' FROM (SELECT `aid`, max(`order`) `order` FROM ?? WHERE `cid` = ? and `cpid` ' + (cpid ? '= ' + cpid : 'is null') + ' and `times` = ? and `audit_order` != 0 GROUP BY aid) sa' +
+                ' FROM (SELECT `aid`, max(`order`) `order` FROM ?? WHERE `cid` = ? and `cpid` ' + (cpid ? '= ' + cpid : 'is null') + ' and `csid` ' + (csid ? '= ' + csid : 'is null') + ' and `times` = ? and `audit_order` != 0 GROUP BY aid) sa' +
                 ' LEFT JOIN ?? la ON sa.`aid` = la.`aid` AND sa.`order` = la.`order`' +
-                ' Left JOIN ?? AS pa On la.`aid` = pa.`id` WHERE la.`cid` = ?' + cpidSql + ' and la.`times` = ? and la.`audit_order` != 0 order BY la.`order`';
+                ' Left JOIN ?? AS pa On la.`aid` = pa.`id` WHERE la.`cid` = ?' + cpidSql + csidSql + ' and la.`times` = ? and la.`audit_order` != 0 order BY la.`order`';
             const sqlParam = [this.tableName, cid, times, this.tableName, this.ctx.service.projectAccount.tableName, cid, times];
             return transaction !== false ? await transaction.query(sql, sqlParam) : await this.db.query(sql, sqlParam);
         }
@@ -229,8 +261,16 @@ module.exports = app => {
          * @param {Number} status - 期次数
          * @return {Promise<boolean>}
          */
-        async getAuditorByStatus(cid, cpid = null, status, times = 1) {
-            const cpidSql = cpid ? ' AND la.cpid = ' + cpid : ' AND la.cpid is null';
+        async getAuditorByStatus(contract, type, status, times = 1) {
+            const cid = contract.cid ? contract.cid : contract.id;
+            const cpid = type === 'pay' ? contract.id : null;
+            const csid = type === 'supplement' ? contract.id : null;
+            const cpidSql = type === 'pay'
+                ? ' AND la.cpid = ' + cpid
+                : ' AND la.cpid is null';
+            const csidSql = type === 'supplement'
+                ? ' AND la.csid = ' + csid
+                : ' AND la.csid is null';
             let auditor = null;
             let sql = '';
             let sqlParam = '';
@@ -239,7 +279,7 @@ module.exports = app => {
                 case auditConst.status.checked :
                     sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`cid`, la.`cpid`, la.`aid`, la.`order`, la.`status` ' +
                         '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +
-                        '  WHERE la.`cid` = ?' + cpidSql + ' and la.`status` = ? ' +
+                        '  WHERE la.`cid` = ?' + cpidSql + csidSql + ' and la.`status` = ? ' +
                         '  ORDER BY la.`times` desc, la.`order` desc';
                     sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, cid, status];
                     auditor = await this.db.queryOne(sql, sqlParam);
@@ -247,7 +287,7 @@ module.exports = app => {
                 case auditConst.status.checkNo :
                     sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`cid`, la.`cpid`, la.`aid`, la.`order`, la.`status` ' +
                         '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id`' +
-                        '  WHERE la.`cid` = ?' + cpidSql + ' and la.`status` = ? and la.`times` = ?' +
+                        '  WHERE la.`cid` = ?' + cpidSql + csidSql + ' and la.`status` = ? and la.`times` = ?' +
                         '  ORDER BY la.`times` desc, la.`order` desc';
                     sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, cid, auditConst.status.checkNo, parseInt(times) - 1];
                     auditor = await this.db.queryOne(sql, sqlParam);
@@ -259,8 +299,16 @@ module.exports = app => {
             return auditor;
         }
 
-        async getAuditorsByStatus(cid, cpid = null, status, times = 1) {
-            const cpidSql = cpid ? ' AND la.cpid = ' + cpid : ' AND la.cpid is null';
+        async getAuditorsByStatus(contract, type, status, times = 1) {
+            const cid = contract.cid ? contract.cid : contract.id;
+            const cpid = type === 'pay' ? contract.id : null;
+            const csid = type === 'supplement' ? contract.id : null;
+            const cpidSql = type === 'pay'
+                ? ' AND la.cpid = ' + cpid
+                : ' AND la.cpid is null';
+            const csidSql = type === 'supplement'
+                ? ' AND la.csid = ' + csid
+                : ' AND la.csid is null';
             let auditor = [];
             let sql = '';
             let sqlParam = '';
@@ -269,22 +317,22 @@ module.exports = app => {
                 case auditConst.status.checking :
                 case auditConst.status.checked :
                 case auditConst.status.checkNoPre:
-                    cur = await this.db.queryOne('SELECT * From ?? where cid = ? AND cpid ' + (cpid ? '= ' + cpid : 'is null') + ' AND times = ? AND status = ? AND audit_order != 0 ORDER By times DESC, `order` DESC', [this.tableName, cid, times, status]);
+                    cur = await this.db.queryOne('SELECT * From ?? where cid = ? AND cpid ' + (cpid ? '= ' + cpid : 'is null') + ' AND csid ' + (csid ? '= ' + csid : 'is null') + ' AND times = ? AND status = ? AND audit_order != 0 ORDER By times DESC, `order` DESC', [this.tableName, cid, times, status]);
                     if (!cur) return [];
 
                     sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`cid`, la.`cpid`, la.`order`, la.`status`, la.`audit_order`, la.`audit_type` ' +
                         '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +
-                        '  WHERE la.`cid` = ?' + cpidSql + ' and la.`order` = ? and times = ?';
+                        '  WHERE la.`cid` = ?' + cpidSql + csidSql + ' and la.`order` = ? and times = ?';
                     sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, cid, cur.order, times];
                     auditor = await this.db.query(sql, sqlParam);
                     break;
                 case auditConst.status.checkNo :
-                    cur = await this.db.queryOne('SELECT * From ?? where cid = ? AND cpid ' + (cpid ? '= ' + cpid : 'is null') + ' AND times = ? AND status = ? AND audit_order != 0 ORDER By times DESC, `order` DESC', [this.tableName, cid, parseInt(times) - 1, status]);
+                    cur = await this.db.queryOne('SELECT * From ?? where cid = ? AND cpid ' + (cpid ? '= ' + cpid : 'is null') + ' AND csid ' + (csid ? '= ' + csid : 'is null') + ' AND times = ? AND status = ? AND audit_order != 0 ORDER By times DESC, `order` DESC', [this.tableName, cid, parseInt(times) - 1, status]);
                     if (!cur) return [];
 
                     sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`cid`, la.`cpid`, la.`order`, la.`status`, la.`audit_order`, la.`audit_type` ' +
                         '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +
-                        '  WHERE la.`cid` = ?' + cpidSql + ' and la.`order` = ? and la.`times` = ?';
+                        '  WHERE la.`cid` = ?' + cpidSql + csidSql + ' and la.`order` = ? and la.`times` = ?';
                     sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, cid, cur.order, parseInt(times) - 1];
                     auditor = await this.db.query(sql, sqlParam);
                     break;
@@ -295,9 +343,17 @@ module.exports = app => {
             return auditor;
         }
 
-        async getLastAudit(cid, cpid = null, times, transaction = null) {
-            const cpidSql = cpid ? ' AND cpid = ' + cpid : ' AND cpid is null';
-            const sql = 'SELECT * FROM ?? WHERE `cid` = ?' + cpidSql + ' AND `times` = ? ORDER BY `order` DESC';
+        async getLastAudit(contract, type, times, transaction = null) {
+            const cid = contract.cid ? contract.cid : contract.id;
+            const cpid = type === 'pay' ? contract.id : null;
+            const csid = type === 'supplement' ? contract.id : null;
+            const cpidSql = type === 'pay'
+                ? ' AND cpid = ' + cpid
+                : ' AND cpid is null';
+            const csidSql = type === 'supplement'
+                ? ' AND csid = ' + csid
+                : ' AND csid is null';
+            const sql = 'SELECT * FROM ?? WHERE `cid` = ?' + cpidSql + csidSql + ' AND `times` = ? ORDER BY `order` DESC';
             const sqlParam = [this.tableName, cid, times];
             return transaction ? await transaction.queryOne(sql, sqlParam) : await this.db.queryOne(sql, sqlParam);
         }
@@ -308,9 +364,9 @@ module.exports = app => {
          * @param {Number} times 审核次数
          * @return {Promise<Array>} 查询结果集(包括原报)
          */
-        async getAuditorsWithOwner(cid, cpid = null, times = 1) {
+        async getAuditorsWithOwner(contract, type, times = 1) {
             // const cpidSql = cpid ? ' AND la.cpid = ' + cpid : ' AND la.cpid is null';
-            const result = await this.getAuditGroupByList(cid, cpid, times);
+            const result = await this.getAuditGroupByList(contract, type, times);
             // const sql =
             //     'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As cpid, 0 As `order`' +
             //     '  FROM ' +
@@ -335,20 +391,24 @@ module.exports = app => {
          * @param {Number} times - 第几次审批
          * @return {Promise<number>}
          */
-        async addAuditor(options, cid, cpid = null, auditorId, times = 1, is_gdzs = 0) {
+        async addAuditor(options, contract, type, auditorId, times = 1, is_gdzs = 0) {
+            const cid = contract.cid ? contract.cid : contract.id;
+            const cpid = type === 'pay' ? contract.id : null;
+            const csid = type === 'supplement' ? contract.id : null;
             const transaction = await this.db.beginTransaction();
             let flag = false;
             try {
-                let [newOrder, newAuditOrder] = await this.getNewOrder(cid, cpid, times);
+                let [newOrder, newAuditOrder] = await this.getNewOrder(contract, type, times);
                 // 判断是否存在固定终审,存在则newOrder - 1并使终审order+1
                 newOrder = is_gdzs === 1 ? newOrder - 1 : newOrder;
                 newAuditOrder = is_gdzs === 1 ? newAuditOrder - 1 : newAuditOrder;
-                if (is_gdzs) await this._syncOrderByDelete(transaction, cid, cpid, newOrder, times, '+');
+                if (is_gdzs) await this._syncOrderByDelete(transaction, contract, type, newOrder, times, '+');
                 const data = {
                     spid: options.spid || null,
                     tid: options.tid || null,
                     cid,
                     cpid,
+                    csid,
                     aid: auditorId,
                     times,
                     order: newOrder,
@@ -373,9 +433,17 @@ module.exports = app => {
          * @param {Number} times - 第几次审批
          * @return {Promise<number>}
          */
-        async getNewOrder(cid, cpid = null, times = 1) {
-            const cpidSql = cpid ? ' AND cpid = ' + cpid : ' AND cpid is null';
-            const sql = 'SELECT Max(`order`) As max_order, Max(audit_order) As max_audit_order FROM ?? Where `cid` = ?' + cpidSql + ' and `times` = ?';
+        async getNewOrder(contract, type, times = 1) {
+            const cid = contract.cid ? contract.cid : contract.id;
+            const cpid = type === 'pay' ? contract.id : null;
+            const csid = type === 'supplement' ? contract.id : null;
+            const cpidSql = type === 'pay'
+                ? ' AND cpid = ' + cpid
+                : ' AND cpid is null';
+            const csidSql = type === 'supplement'
+                ? ' AND csid = ' + csid
+                : ' AND csid is null';
+            const sql = 'SELECT Max(`order`) As max_order, Max(audit_order) As max_audit_order FROM ?? Where `cid` = ?' + cpidSql + csidSql + ' and `times` = ?';
             const sqlParam = [this.tableName, cid, times];
             const result = await this.db.queryOne(sql, sqlParam);
             return result && result.max_order ? [result.max_order + 1, result.max_audit_order + 1] : [1, 1];
@@ -389,11 +457,19 @@ module.exports = app => {
          * @param {Number} times - 第几次审批
          * @return {Promise<boolean>}
          */
-        async deleteAuditor(cid, cpid = null, auditorId, times = 1) {
+        async deleteAuditor(contract, type, auditorId, times = 1) {
             const transaction = await this.db.beginTransaction();
             try {
-                const cpidSql = cpid ? ' AND cpid = ' + cpid : ' AND cpid is null';
-                const sql = 'SELECT * FROM ?? WHERE `cid` = ?' + cpidSql + ' and `times` = ? and `aid` = ? and `audit_order` != 0 ORDER BY `order` DESC';
+                const cid = contract.cid ? contract.cid : contract.id;
+                const cpid = type === 'pay' ? contract.id : null;
+                const csid = type === 'supplement' ? contract.id : null;
+                const cpidSql = type === 'pay'
+                    ? ' AND cpid = ' + cpid
+                    : ' AND cpid is null';
+                const csidSql = type === 'supplement'
+                    ? ' AND csid = ' + csid
+                    : ' AND csid is null';
+                const sql = 'SELECT * FROM ?? WHERE `cid` = ?' + cpidSql + csidSql + ' and `times` = ? and `aid` = ? and `audit_order` != 0 ORDER BY `order` DESC';
                 const sqlParam = [this.tableName, cid, times, auditorId];
                 const auditor = await transaction.queryOne(sql, sqlParam);
                 // const condition = { cid, cpid, aid: auditorId, times };
@@ -401,8 +477,8 @@ module.exports = app => {
                 if (!auditor) {
                     throw '该审核人不存在';
                 }
-                await transaction.delete(this.tableName, { cid, cpid, order: auditor.order, times });
-                await this._syncOrderByDelete(transaction, cid, cpid, auditor.order, times);
+                await transaction.delete(this.tableName, { cid, cpid, csid, order: auditor.order, times });
+                await this._syncOrderByDelete(transaction, contract, type, auditor.order, times);
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();
@@ -420,31 +496,11 @@ module.exports = app => {
          * @return {Promise<*>}
          * @private
          */
-        async _syncOrderByDelete(transaction, cid, cpid, order, times, selfOperate = '-') {
-            // this.initSqlBuilder();
-            // this.sqlBuilder.setAndWhere('cid', {
-            //     value: this.db.escape(cid),
-            //     operate: '=',
-            // });
-            // this.sqlBuilder.setAndWhere('order', {
-            //     value: order,
-            //     operate: '>=',
-            // });
-            // this.sqlBuilder.setAndWhere('times', {
-            //     value: times,
-            //     operate: '=',
-            // });
-            // this.sqlBuilder.setUpdateData('order', {
-            //     value: 1,
-            //     selfOperate,
-            // });
-            // this.sqlBuilder.setUpdateData('audit_order', {
-            //     value: 1,
-            //     selfOperate,
-            // });
-            // const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
-            // const data = await transaction.query(sql, sqlParam);
-            const sql = 'UPDATE ?? SET `order` = `order` ' + selfOperate + ' ?, `audit_order` = `audit_order` ' + selfOperate + ' ? WHERE `cid` = ? AND `cpid` ' + (cpid ? '= ' + cpid : 'is null') + ' AND `order` >= ? AND `times` = ?';
+        async _syncOrderByDelete(transaction, contract, type, order, times, selfOperate = '-') {
+            const cid = contract.cid ? contract.cid : contract.id;
+            const cpid = type === 'pay' ? contract.id : null;
+            const csid = type === 'supplement' ? contract.id : null;
+            const sql = 'UPDATE ?? SET `order` = `order` ' + selfOperate + ' ?, `audit_order` = `audit_order` ' + selfOperate + ' ? WHERE `cid` = ? AND `cpid` ' + (cpid ? '= ' + cpid : 'is null') + ' AND `csid` ' + (csid ? '= ' + csid : 'is null') + ' AND `order` >= ? AND `times` = ?';
             const sqlParam = [this.tableName, 1, 1, cid, order, times];
             const data = await transaction.query(sql, sqlParam);
             return data;
@@ -456,8 +512,11 @@ module.exports = app => {
          * @param {Number} times - 第几次审批
          * @return {Promise<boolean>}
          */
-        async start(options, shenpi, cid, cpid = null, times = 1) {
-            const audits = await this.getAllDataByCondition({ where: { cid, cpid, times, order: 1 } });
+        async start(options, shenpi, contract, type, times = 1) {
+            const cid = contract.cid ? contract.cid : contract.id;
+            const cpid = type === 'pay' ? contract.id : null;
+            const csid = type === 'supplement' ? contract.id : null;
+            const audits = await this.getAllDataByCondition({ where: { cid, cpid, csid, times, order: 1 } });
             if (audits.length === 0) {
                 if (shenpi.contract === shenpiConst.sp_status.gdspl) {
                     throw '请联系管理员添加审批人';
@@ -474,6 +533,7 @@ module.exports = app => {
                     tid: options.tid || null,
                     cid,
                     cpid,
+                    csid,
                     aid: this.ctx.session.sessionUser.accountId,
                     times,
                     order: 0,
@@ -489,6 +549,10 @@ module.exports = app => {
                     await transaction.update(this.ctx.service.contractPay.tableName, {
                         id: cpid, status: auditConst.status.checking,
                     });
+                } else if (csid) {
+                    await transaction.update(this.ctx.service.contractSupplement.tableName, {
+                        id: csid, status: auditConst.status.checking,
+                    });
                 } else {
                     await transaction.update(this.ctx.service.contract.tableName, {
                         id: cid, status: auditConst.status.checking,
@@ -589,30 +653,34 @@ module.exports = app => {
          * @param {Number} times - 第几次审批
          * @return {Promise<void>}
          */
-        async check(contract, checkData) {
+        async check(contract, type, checkData) {
             if (!this._.includes([auditConst.status.checked, auditConst.status.checkNo, auditConst.status.checkNoPre], checkData.checkType)) {
                 throw '提交数据错误';
             }
             const pid = this.ctx.session.sessionProject.id;
             switch (checkData.checkType) {
                 case auditConst.status.checked:
-                    await this._checked(pid, contract, checkData);
+                    await this._checked(pid, contract, type, checkData);
                     break;
                 case auditConst.status.checkNo:
-                    await this._checkNo(pid, contract, checkData);
+                    await this._checkNo(pid, contract, type, checkData);
                     break;
                 case auditConst.status.checkNoPre:
-                    await this._checkNoPre(pid, contract, checkData);
+                    await this._checkNoPre(pid, contract, type, checkData);
                     break;
                 default:
                     throw '无效审批操作';
             }
         }
 
-        async _checked(pid, contract, checkData) {
+        async _checked(pid, contract, type, checkData) {
             const accountId = this.ctx.session.sessionUser.accountId;
             const time = new Date();
-            const tableName = contract.cid !== undefined ? this.ctx.service.contractPay.tableName : this.ctx.service.contract.tableName;
+            const tableName = {
+                contract: this.ctx.service.contract.tableName,
+                pay: this.ctx.service.contractPay.tableName,
+                supplement: this.ctx.service.contractSupplement.tableName,
+            }[type];
 
             // 整理当前流程审核人状态更新
             if (contract.curAuditorIds.length === 0) throw '审核数据错误';
@@ -689,18 +757,25 @@ module.exports = app => {
             }
         }
 
-        async _checkNo(pid, contract, checkData) {
+        async _checkNo(pid, contract, type, checkData) {
             const accountId = this.ctx.session.sessionUser.accountId;
             const time = new Date();
-            const tableName = contract.cid !== undefined ? this.ctx.service.contractPay.tableName : this.ctx.service.contract.tableName;
+            const cid = contract.cid ? contract.cid : contract.id;
+            const cpid = type === 'pay' ? contract.id : null;
+            const csid = type === 'supplement' ? contract.id : null;
+            const tableName = {
+                contract: this.ctx.service.contract.tableName,
+                pay: this.ctx.service.contractPay.tableName,
+                supplement: this.ctx.service.contractSupplement.tableName,
+            }[type];
             const audits = contract.curAuditors;
             if (audits.length === 0) throw '审核数据错误';
             const selfAudit = audits.find(x => { return x.aid === accountId; });
             if (!selfAudit) throw '当前标段您无权审批';
-            const auditors = await this.getUniqAuditor(contract.cid || contract.id, contract.cid ? contract.id : null, contract.times, true); // 全部参与的审批人
+            const auditors = await this.getUniqAuditor(contract, type, contract.times, true); // 全部参与的审批人
             const newAuditors = auditors.map(x => {
                 return {
-                    aid: x.aid, spid: contract.spid || null, tid: contract.tid || null, cid: contract.cid ? contract.cid : contract.id, cpid: contract.cid ? contract.id : null,
+                    aid: x.aid, spid: contract.spid || null, tid: contract.tid || null, cid, cpid, csid,
                     times: contract.times + 1, order: x.audit_order, status: auditConst.status.uncheck,
                     audit_type: x.audit_type, audit_order: x.audit_order,
                 };
@@ -745,10 +820,17 @@ module.exports = app => {
          * @param {int} changeData - 变更令的数据
          * @return {void}
          */
-        async _checkNoPre(pid, contract, checkData) {
+        async _checkNoPre(pid, contract, type, checkData) {
             const accountId = this.ctx.session.sessionUser.accountId;
             const time = new Date();
-            const tableName = contract.cid !== undefined ? this.ctx.service.contractPay.tableName : this.ctx.service.contract.tableName;
+            const cid = contract.cid ? contract.cid : contract.id;
+            const cpid = type === 'pay' ? contract.id : null;
+            const csid = type === 'supplement' ? contract.id : null;
+            const tableName = {
+                contract: this.ctx.service.contract.tableName,
+                pay: this.ctx.service.contractPay.tableName,
+                supplement: this.ctx.service.contractSupplement.tableName,
+            }[type];
             // 整理当前流程审核人状态更新
             const audits = contract.curAuditors;
             if (audits.length === 0 || audits[0].order <= 1) throw '审核数据错误';
@@ -758,7 +840,7 @@ module.exports = app => {
 
             // 添加重新审批后,不能用order-1,取groupby值里的上一个才对
             // const preAuditor = await this.getDataByCondition({sid: stageId, times: times, order: audit.order - 1});
-            const auditors2 = await this.getAuditGroupByList(contract.cid || contract.id, contract.cid ? contract.id : null, contract.times);
+            const auditors2 = await this.getAuditGroupByList(contract, type, contract.times);
             const preAuditors = auditors2.filter(x => { return x.audit_order === selfAudit.audit_order - 1});
 
             const transaction = await this.db.beginTransaction();
@@ -802,13 +884,13 @@ module.exports = app => {
                 await transaction.updateRows(this.tableName, updateData);
                 // await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, this._.map(updateData, 'id'));
                 // 顺移其后审核人流程顺序
-                const sql = 'UPDATE ' + this.tableName + ' SET `order` = `order` + 2 WHERE cid = ? AND cpid' + (contract.cid !== undefined ? ' = ' + contract.id : ' is null') + ' AND times = ? AND `order` > ?';
-                await transaction.query(sql, [contract.cid ? contract.cid : contract.id, selfAudit.times, selfAudit.order]);
+                const sql = 'UPDATE ' + this.tableName + ' SET `order` = `order` + 2 WHERE cid = ? AND cpid' + (cpid ? ' = ' + cpid : ' is null') + ' AND csid' + (csid ? ' = ' + csid : ' is null') + ' AND times = ? AND `order` > ?';
+                await transaction.query(sql, [cid, selfAudit.times, selfAudit.order]);
                 // 上一审批人,当前审批人 再次添加至流程
                 const newAuditors = [];
                 preAuditors.forEach(x => {
                     newAuditors.push({
-                        spid: contract.spid || null, tid: contract.tid || null, cid: contract.cid ? contract.cid : contract.id, cpid: contract.cid ? contract.id : null, aid: x.aid,
+                        spid: contract.spid || null, tid: contract.tid || null, cid, cpid, csid, aid: x.aid,
                         times: x.times, order: selfAudit.order + 1,
                         status: auditConst.status.checking, begin_time: time,
                         audit_type: x.audit_type, audit_order: x.audit_order,
@@ -823,7 +905,7 @@ module.exports = app => {
                 const newFlowAuditors = [];
                 flowAudits.forEach(x => {
                     newFlowAuditors.push({
-                        spid: contract.spid || null, tid: contract.tid || null, cid: contract.cid ? contract.cid : contract.id, cpid: contract.cid ? contract.id : null, aid: x.aid,
+                        spid: contract.spid || null, tid: contract.tid || null, cid, cpid, csid, aid: x.aid,
                         times: x.times, order: selfAudit.order + 2,
                         status: auditConst.status.uncheck,
                         audit_type: x.audit_type, audit_order: x.audit_order,
@@ -842,13 +924,13 @@ module.exports = app => {
             }
         }
 
-        async getAuditorGroup(cid, cpid, times) {
-            const auditors = await this.getAuditors(cid, cpid, times); // 全部参与的审批人
+        async getAuditorGroup(contract, type, times) {
+            const auditors = await this.getAuditors(contract, type, times); // 全部参与的审批人
             return this.ctx.helper.groupAuditors(auditors, 'order');
         }
 
-        async getUserGroup(cid, cpid, times) {
-            const group = await this.getAuditorGroup(cid, cpid, times);
+        async getUserGroup(contract, type, times) {
+            const group = await this.getAuditorGroup(contract, type, times);
             // const sql =
             //     'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As cpid, 0 As `order`, 1 As audit_type, 0 As audit_order' +
             //     '  FROM ' + this.ctx.service.changePlan.tableName + ' As s' +
@@ -862,8 +944,8 @@ module.exports = app => {
             return group;
         }
 
-        async getUniqUserGroup(cid, cpid, times) {
-            const group = await this.getAuditorGroup(cid, cpid, times);
+        async getUniqUserGroup(contract, type, times) {
+            const group = await this.getAuditorGroup(contract, type, times);
             // const sql =
             //     'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As cpid, 0 As `order`, 1 As audit_type, 0 As audit_order' +
             //     '  FROM ' + this.ctx.service.changePlan.tableName + ' As s' +
@@ -877,11 +959,11 @@ module.exports = app => {
             return this.ctx.helper.groupAuditorsUniq(group);
         }
 
-        async getAuditorHistory(cid, cpid, times, reverse = false) {
+        async getAuditorHistory(contract, type, times, reverse = false) {
             const history = [];
             if (times >= 1) {
                 for (let i = 1; i <= times; i++) {
-                    const auditors = await this.getAuditors(cid, cpid, i);
+                    const auditors = await this.getAuditors(contract, type, i);
                     const group = this.ctx.helper.groupAuditors(auditors);
                     const historyGroup = [];
                     // 找出group里audit_order最大值
@@ -936,8 +1018,8 @@ module.exports = app => {
             return history;
         }
 
-        async getUniqAuditor(cid, cpid, times, noYb = false) {
-            const auditors = await this.getAuditors(cid, cpid, times, 'asc', noYb); // 全部参与的审批人
+        async getUniqAuditor(contract, type, times, noYb = false) {
+            const auditors = await this.getAuditors(contract, type, times, 'asc', noYb); // 全部参与的审批人
             const result = [];
             auditors.forEach(x => {
                 if (result.findIndex(r => { return x.aid === r.aid && x.audit_order === r.audit_order; }) < 0) {
@@ -947,44 +1029,62 @@ module.exports = app => {
             return result;
         }
 
-        async makeAudits(transaction, options, cid, cpid = null, uid) {
+        async makeAudits(transaction, options, contract, type, uid) {
+            const cid = contract.cid ? contract.cid : contract.id;
+            const cpid = type === 'pay' ? contract.id : null;
+            const csid = type === 'supplement' ? contract.id : null;
             // 创建审批人列表
-            const lastContractInfo = await this.getHaveAuditLastInfo(options, cid, cpid, uid);
+            const lastContractInfo = await this.getHaveAuditLastInfo(options, contract, type, uid);
+            let auditList = [];
             if (lastContractInfo) {
                 // 再获取非原报审批人
-                const auditList = await this.getUniqAuditor(lastContractInfo.cid ? lastContractInfo.cid : lastContractInfo.id, lastContractInfo.cid ? lastContractInfo.id : null, lastContractInfo.times, true); // 全部参与的审批人
-                if (auditList.length > 0) {
-                    const spAudits = [];
-                    for (const x of auditList) {
-                        if (x.audit_order !== 0) {
-                            spAudits.push({
-                                spid: options.spid || null,
-                                tid: options.tid || null,
-                                cid, cpid, aid: x.aid,
-                                times: 1, order: x.audit_order, status: auditConst.status.uncheck,
-                                audit_type: x.audit_type, audit_order: x.audit_order,
-                            });
-                        }
+                auditList = await this.getUniqAuditor(lastContractInfo, type, lastContractInfo.times, true); // 全部参与的审批人
+            } else if ((type === 'supplement' || type === 'pay') && contract.cid) {
+                // 取该合同的审批人列表
+                const contractInfo = await this.ctx.service.contract.getDataById(contract.cid);
+                auditList = await this.getUniqAuditor(contractInfo, 'contract', contractInfo.times, true); // 全部参与的审批人
+            }
+            if (auditList.length > 0) {
+                const spAudits = [];
+                for (const x of auditList) {
+                    if (x.audit_order !== 0) {
+                        spAudits.push({
+                            spid: options.spid || null,
+                            tid: options.tid || null,
+                            cid, cpid, csid, aid: x.aid,
+                            times: 1, order: x.audit_order, status: auditConst.status.uncheck,
+                            audit_type: x.audit_type, audit_order: x.audit_order,
+                        });
                     }
-                    if (spAudits.length > 0) await transaction.insert(this.ctx.service.contractSpAudit.tableName, spAudits);
                 }
+                if (spAudits.length > 0) await transaction.insert(this.ctx.service.contractSpAudit.tableName, spAudits);
             }
         }
 
-        async getHaveAuditLastInfo(options, cid, cpid, uid) {
+        async getHaveAuditLastInfo(options, contract, type, uid) {
             const optionsSql = options.spid ? 'c.spid = "' + options.spid + '"' : 'c.tid = ' + options.tid;
-            const tableName = cpid ? this.ctx.service.contractPay.tableName : this.ctx.service.contract.tableName;
-            const joinSql = cpid ? 'ca.cpid' : 'ca.cid';
-            const cpidSql = cpid ? 'AND ca.`cpid` is not null' : 'AND ca.`cpid` is null';
-            const sql = 'SELECT c.* FROM ?? as c LEFT JOIN ?? as ca ON c.`id` = ' + joinSql + ' WHERE ' + optionsSql + ' AND c.`contract_type` = ? AND c.`uid` = ? ' + cpidSql + ' AND ca.`audit_order` > 0 ORDER BY c.`create_time` DESC';
+            const cpid = type === 'pay' ? contract.id : null;
+            const csid = type === 'supplement' ? contract.id : null;
+            const tableName = {
+                contract: this.ctx.service.contract.tableName,
+                pay: this.ctx.service.contractPay.tableName,
+                supplement: this.ctx.service.contractSupplement.tableName,
+            }[type];
+            const joinSql = cpid ? 'ca.`cpid`' : (csid ? 'ca.`csid`' : 'ca.`cid`');
+            const cpidSql = cpid ? ' AND ca.`cpid` is not null' : ' AND ca.`cpid` is null';
+            const csidSql = cpid ? ' AND ca.`csid` is not null' : ' AND ca.`csid` is null';
+            const sql = 'SELECT c.* FROM ?? as c LEFT JOIN ?? as ca ON c.`id` = ' + joinSql + ' WHERE ' + optionsSql + ' AND c.`contract_type` = ? AND c.`uid` = ?' + cpidSql + csidSql + ' AND ca.`audit_order` > 0 ORDER BY c.`create_time` DESC';
             const sqlParam = [tableName, this.tableName, options.contract_type, uid];
             return await this.db.queryOne(sql, sqlParam);
         }
 
-        async saveAudit(contract, times, sp_group, data) {
+        async saveAudit(contract, type, times, sp_group, data) {
             const transaction = await this.db.beginTransaction();
             try {
-                const auditors = await this.getAuditGroupByList(contract.cid || contract.id, contract.cid ? contract.id : null, times);
+                const cid = contract.cid ? contract.cid : contract.id;
+                const cpid = type === 'pay' ? contract.id : null;
+                const csid = type === 'supplement' ? contract.id : null;
+                const auditors = await this.getAuditGroupByList(contract, type, times);
                 const now_audit = this._.find(auditors, { aid: data.old_aid });
                 if (data.operate !== 'del') {
                     // const exist = await this.getDataByCondition({ cpid: cpId, times, aid: data.new_aid });
@@ -998,13 +1098,13 @@ module.exports = app => {
                         throw '当前人下无法操作新增';
                     }
                     const newAudit = {
-                        spid: contract.spid || null, tid: contract.tid || null, cid: contract.cid || contract.id, cpid: contract.cid ? contract.id : null, aid: data.new_aid,
+                        spid: contract.spid || null, tid: contract.tid || null, cid, cpid, csid, aid: data.new_aid,
                         order: now_audit.order + 1,
                         audit_order: now_audit.audit_order + 1, audit_type: auditType.key.common,
                         times, status: auditConst.status.uncheck,
                     };
                     // order+1
-                    await this._syncOrderByDelete(transaction, contract.cid || contract.id, contract.cid ? contract.id : null, now_audit.order + 1, times, '+');
+                    await this._syncOrderByDelete(transaction, contract, type, now_audit.order + 1, times, '+');
                     await transaction.insert(this.tableName, newAudit);
                     // 更新审批流程页数据,如果存在
                 } else if (data.operate === 'add-sibling') {
@@ -1012,7 +1112,7 @@ module.exports = app => {
                         throw '当前人下无法操作新增';
                     }
                     const newAudit = {
-                        spid: contract.spid || null, tid: contract.tid || null, cid: contract.cid || contract.id, cpid: contract.cid ? contract.id : null, aid: data.new_aid,
+                        spid: contract.spid || null, tid: contract.tid || null, cid, cpid, csid, aid: data.new_aid,
                         order: now_audit.order,
                         audit_order: now_audit.audit_order, audit_type: now_audit.audit_type,
                         times, status: auditConst.status.uncheck,
@@ -1023,8 +1123,8 @@ module.exports = app => {
                         throw '当前人无法操作删除';
                     }
                     const flowAuditors = auditors.filter(x => { return x.audit_order === now_audit.audit_order; });
-                    await transaction.delete(this.tableName, { cid: contract.cid || contract.id, cpid: contract.cid ? contract.id : null, times, aid: now_audit.aid, order: now_audit.order });
-                    if (flowAuditors.length === 1) await this._syncOrderByDelete(transaction, contract.cid || contract.id, contract.cid ? contract.id : null, now_audit.order, times);
+                    await transaction.delete(this.tableName, { cid, cpid, csid, times, aid: now_audit.aid, order: now_audit.order });
+                    if (flowAuditors.length === 1) await this._syncOrderByDelete(transaction, contract, type, now_audit.order, times);
                     // 旧的更新为is_old为1
                     // await transaction.update(this.tableName, { is_old: 1 }, {
                     //     where: {
@@ -1034,7 +1134,7 @@ module.exports = app => {
                     //     }
                     // });
                 } else if (data.operate === 'change') {
-                    const nowAudit = await this.getDataByCondition({ cid: contract.cid || contract.id, cpid: contract.cid ? contract.id : null, times, aid: now_audit.aid, order: now_audit.order });
+                    const nowAudit = await this.getDataByCondition({ cid, cpid, csid, times, aid: now_audit.aid, order: now_audit.order });
                     if (now_audit.status !== auditConst.status.uncheck || !nowAudit) {
                         throw '当前人无法操作替换';
                     }
@@ -1052,24 +1152,24 @@ module.exports = app => {
                 if (this.ctx.contract_tender) {
                     const tenderInfo = await this.ctx.service.tenderInfo.getTenderInfo(this.ctx.contract.id);
                     if (tenderInfo.shenpi.contract === shenpiConst.sp_status.gdspl) {
-                        const newAuditors = await transaction.select(this.tableName, { where: { cid: contract.cid || contract.id, cpid: contract.cid ? contract.id : null, times }, orders: [['order', 'asc']] });
+                        const newAuditors = await transaction.select(this.tableName, { where: { cid, cpid, csid, times }, orders: [['order', 'asc']] });
                         newAuditors.shift();
                         const newAuditorGroup = this.ctx.helper.groupAuditors(newAuditors, 'order');
                         const uniqNewAuditorGroup = this.ctx.helper.groupAuditorsUniq(newAuditorGroup);
                         await this.ctx.service.shenpiAudit.updateAuditListWithAuditType(transaction, this.ctx.contract.id, tenderInfo.shenpi.contract, shenpiConst.sp_type.contract, uniqNewAuditorGroup, sp_group);
                     } else if (tenderInfo.shenpi.contract === shenpiConst.sp_status.gdzs) {
-                        const newAuditors = await this.getAuditGroupByList(contract.cid || contract.id, contract.cid ? contract.id : null, times, transaction);
+                        const newAuditors = await this.getAuditGroupByList(contract, type, times, transaction);
                         await this.ctx.service.shenpiAudit.updateAuditList(transaction, this.ctx.contract.id, tenderInfo.shenpi.contract, shenpiConst.sp_type.contract, this._.map(newAuditors, 'aid'));
                     }
                 } else {
                     if (this.ctx.subProject.shenpi.contract === shenpiConst.sp_status.gdspl) {
-                        const newAuditors = await transaction.select(this.tableName, { where: { cid: contract.cid || contract.id, cpid: contract.cid ? contract.id : null, times }, orders: [['order', 'asc']] });
+                        const newAuditors = await transaction.select(this.tableName, { where: { cid, cpid, csid, times }, orders: [['order', 'asc']] });
                         newAuditors.shift();
                         const newAuditorGroup = this.ctx.helper.groupAuditors(newAuditors, 'order');
                         const uniqNewAuditorGroup = this.ctx.helper.groupAuditorsUniq(newAuditorGroup);
                         await this.ctx.service.shenpiAudit.updateAuditListWithAuditType(transaction, this.ctx.subProject.id, this.ctx.subProject.shenpi.contract, shenpiConst.sp_type.contract, uniqNewAuditorGroup, sp_group);
                     } else if (this.ctx.subProject.shenpi.contract === shenpiConst.sp_status.gdzs) {
-                        const newAuditors = await this.getAuditGroupByList(contract.cid || contract.id, contract.cid ? contract.id : null, times, transaction);
+                        const newAuditors = await this.getAuditGroupByList(contract, type, times, transaction);
                         await this.ctx.service.shenpiAudit.updateAuditList(transaction, this.ctx.subProject.id, this.ctx.subProject.shenpi.contract, shenpiConst.sp_type.contract, this._.map(newAuditors, 'aid'));
                     }
                 }
@@ -1081,7 +1181,7 @@ module.exports = app => {
             }
         }
 
-        async changeSpGroup(contract, sp_group) {
+        async changeSpGroup(contract, type, sp_group) {
             const transaction = await this.db.beginTransaction();
             try {
                 const group = await this.ctx.service.shenpiGroup.getDataById(sp_group);
@@ -1096,11 +1196,13 @@ module.exports = app => {
                 }
                 const shenpiList = await this.ctx.service.shenpiAudit.getAllDataByCondition({ where: condition });
                 // await this.ctx.service.shenpiAudit.noYbShenpiList(change.uid, shenpiList);
-                await this.updateNewAuditors(contract, shenpiList, transaction);
-                if (contract.cid === undefined) {
+                await this.updateNewAuditors(contract, type, shenpiList, transaction);
+                if (type === 'contract') {
                     await transaction.update(this.ctx.service.contract.tableName, { id: contract.id, sp_group: group.id });
-                } else {
+                } else if (type === 'pay') {
                     await transaction.update(this.ctx.service.contractPay.tableName, { id: contract.id, sp_group: group.id });
+                } else if (type === 'supplement') {
+                    await transaction.update(this.ctx.service.contractSupplement.tableName, { id: contract.id, sp_group: group.id });
                 }
                 await transaction.commit();
             } catch (err) {
@@ -1110,10 +1212,10 @@ module.exports = app => {
             return true;
         }
 
-        async updateNewAuditList(contract, newList) {
+        async updateNewAuditList(contract, type, newList) {
             const transaction = await this.db.beginTransaction();
             try {
-                await this.updateNewAuditors(contract, newList, transaction);
+                await this.updateNewAuditors(contract, type, newList, transaction);
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();
@@ -1121,22 +1223,21 @@ module.exports = app => {
             }
         }
 
-        async updateNewAuditors(contract, newList, transaction) {
+        async updateNewAuditors(contract, type, newList, transaction) {
             const condition = { times: contract.times };
-            if (contract.cid !== undefined) {
-                condition.cid = contract.cid;
-                condition.cpid = contract.id;
-            } else {
-                condition.cid = contract.id;
-                condition.cpid = null;
-            }
+            const cid = contract.cid ? contract.cid : contract.id;
+            const cpid = type === 'pay' ? contract.id : null;
+            const csid = type === 'supplement' ? contract.id : null;
+            condition.cid = cid;
+            condition.cpid = cpid;
+            condition.csid = csid;
             // 先删除旧的审批流,再添加新的
             await transaction.delete(this.tableName, condition);
 
             const newAuditors = [];
             for (const auditor of newList) {
                 newAuditors.push({
-                    spid: contract.spid || null, tid: contract.tid || null, cid: contract.cid || contract.id, cpid: contract.cid ? contract.id : null, aid: auditor.audit_id,
+                    spid: contract.spid || null, tid: contract.tid || null, cid, cpid, csid, aid: auditor.audit_id,
                     times: contract.times, order: auditor.audit_order, status: auditConst.status.uncheck,
                     audit_type: auditor.audit_type, audit_order: auditor.audit_order,
                 });
@@ -1156,21 +1257,20 @@ module.exports = app => {
             }
         }
 
-        async updateLastAudit(contract, auditList, lastId) {
+        async updateLastAudit(contract, type, auditList, lastId) {
             const transaction = await this.db.beginTransaction();
             try {
                 // 先判断auditList里的aid是否与lastId相同,相同则删除并重新更新order
                 const existAudit = auditList.find(x => { return x.aid === lastId; });
+                const cid = contract.cid ? contract.cid : contract.id;
+                const cpid = type === 'pay' ? contract.id : null;
+                const csid = type === 'supplement' ? contract.id : null;
                 let order = auditList.length > 0 ? auditList.reduce((rst, a) => { return Math.max(rst, a.order); }, 0) + 1 : 1; // 最大值 + 1
                 if (existAudit) {
                     const condition = { times: contract.times, aid: lastId };
-                    if (contract.cid !== undefined) {
-                        condition.cid = contract.cid;
-                        condition.cpid = contract.id;
-                    } else {
-                        condition.cid = contract.id;
-                        condition.cpid = null;
-                    }
+                    condition.cid = cid;
+                    condition.cpid = cpid;
+                    condition.csid = csid;
                     await transaction.delete(this.tableName, condition);
                     const sameOrder = auditList.filter(x => { return x.order === existAudit.order; });
                     if (sameOrder.length === 1) {
@@ -1187,7 +1287,7 @@ module.exports = app => {
                 }
                 // 添加终审
                 const newAuditor = {
-                    spid: contract.spid || null, tid: contract.tid || null, cid: contract.cid || contract.id, cpid: contract.cid ? contract.id : null, aid: lastId,
+                    spid: contract.spid || null, tid: contract.tid || null, cid, cpid, csid, aid: lastId,
                     times: contract.times, order, status: auditConst.status.uncheck,
                     audit_type: auditType.key.common, audit_order: order,
                 };

+ 10 - 7
app/service/contract_supplement.js

@@ -32,16 +32,17 @@ module.exports = app => {
             const shenpi_status = this.ctx.contract_tender ? this.ctx.subProject.page_show.openContractTenderShenpi : this.ctx.subProject.page_show.openContractSubProjectShenpi;
             if (list.length > 0) {
                 const userList = await this.ctx.service.projectAccount.getAllDataByCondition({ where: { id: list.map(item => item.uid) } });
+                const type = 'supplement';
                 for (const l of list) {
                     const userInfo = userList.find(item => item.id === l.uid);
                     l.username = userInfo ? userInfo.name : '';
                     l.files = await this.ctx.service.contractSupplementAtt.getAtt(l.id);
                     if (shenpi_status) {
-                        await this.ctx.service.contractSpAudit.loadUser(l);
-                        await this.ctx.service.contractSpAudit.loadAuditViewData(l);
-                        await this.ctx.service.contractSpAudit.checkShenpi(l);
+                        await this.ctx.service.contractSpAudit.loadUser(l, type);
+                        await this.ctx.service.contractSpAudit.loadAuditViewData(l, type);
+                        await this.ctx.service.contractSpAudit.checkShenpi(l, type);
                         if (l.status !== auditConst.status.checked || !l.final_auditor_str) {
-                            l.curAuditors2 = await this.ctx.service.contractSpAudit.getAuditorsByStatus(l.cid, l.id, l.status, l.times);
+                            l.curAuditors2 = await this.ctx.service.contractSpAudit.getAuditorsByStatus(l, type, l.status, l.times);
                             if (l.status === auditConst.status.checked && !l.final_auditor_str) {
                                 const final_auditor_str = l.curAuditors2[0].audit_type === auditType.key.common
                                     ? l.curAuditors2[0].name + (l.curAuditors2[0].role ? '-' + l.curAuditors2[0].role : '')
@@ -72,8 +73,8 @@ module.exports = app => {
                     code: data.code,
                     name: data.name,
                     price: data.price || 0,
-                    party_a: data.party_a || 0,
-                    party_b: data.party_b || 0,
+                    party_a: data.party_a,
+                    party_b: data.party_b,
                     sign_time: data.sign_time || null,
                     address: data.address,
                     content: data.content,
@@ -82,7 +83,8 @@ module.exports = app => {
                 };
                 const result = await transaction.insert(this.tableName, insertData);
                 await this.calcContract(transaction, node);
-                await this.ctx.service.contractSpAudit.makeAudits(transaction, options, cid, result.insertId, this.ctx.session.sessionUser.accountId);
+                insertData.id = result.insertId;
+                await this.ctx.service.contractSpAudit.makeAudits(transaction, options, insertData, 'supplement', this.ctx.session.sessionUser.accountId);
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();
@@ -151,6 +153,7 @@ module.exports = app => {
                 supplement_price = this.ctx.helper.add(supplement_price, l.price);
             }
             node.supplement_price = supplement_price;
+            node.total_price = this.ctx.helper.add(node.price, supplement_price);
             node.exist_supplement = supplementsList.length === 0 ? 0 : 1;
             await transaction.update(this.ctx.service.contract.tableName, node);
         }

+ 12 - 4
app/service/contract_tree.js

@@ -381,11 +381,13 @@ module.exports = app => {
                     if (select.uid !== this.ctx.session.sessionUser.accountId && !this.ctx.session.sessionUser.is_admin) throw '当前合同无权删除';
                     const contractPays = await this.ctx.service.contractPay.getDataByCondition({ cid: select.id });
                     if (contractPays) throw '还存在合同支付项,无法删除';
+                    const contractSupplements = await this.ctx.service.contractSupplement.getDataByCondition({ cid: select.id });
+                    if (contractSupplements) throw '还存在补充合同,无法删除';
                     await transaction.delete(this.ctx.service.contract.tableName, { id: select.id });
                     const attList = await this.ctx.service.contractAtt.getAllDataByCondition({ where: { cid: select.id } });
                     await this.ctx.helper.delFiles(attList);
                     await transaction.delete(this.ctx.service.contractAtt.tableName, { cid: select.id });
-                    await transaction.delete(this.ctx.service.contractSpAudit.tableName, { cid: select.id, cpid: null });
+                    await transaction.delete(this.ctx.service.contractSpAudit.tableName, { cid: select.id, cpid: null, csid: null });
                 } else {
                     await transaction.delete(this.tableName, { id: select.id });
                     const delOptions = this._.cloneDeep(options);
@@ -399,10 +401,12 @@ module.exports = app => {
                         if (contractUids.length > 1 || !(contractUids[0] === this.ctx.session.sessionUser.accountId || this.ctx.session.sessionUser.is_admin)) throw '存在合同你无权删除';
                         const contractPays = await transaction.select(this.ctx.service.contractPay.tableName, { where: { cid: this.ctx.helper._.map(contracts, 'id') } });
                         if (contractPays.length > 0) throw '还存在合同支付项,无法删除';
+                        const contractSupplements = await this.ctx.service.contractSupplement.getDataByCondition({ cid: this.ctx.helper._.map(contracts, 'id') });
+                        if (contractSupplements) throw '还存在补充合同,无法删除';
                         const attList = await this.ctx.service.contractAtt.getAllDataByCondition({ where: { cid: this.ctx.helper._.map(contracts, 'id') } });
                         await this.ctx.helper.delFiles(attList);
                         await transaction.delete(this.ctx.service.contractAtt.tableName, { cid: this.ctx.helper._.map(contracts, 'id') });
-                        await transaction.delete(this.ctx.service.contractSpAudit.tableName, { cid: this.ctx.helper._.map(contracts, 'id'), cpid: null });
+                        await transaction.delete(this.ctx.service.contractSpAudit.tableName, { cid: this.ctx.helper._.map(contracts, 'id'), cpid: null, csid: null });
                     }
                     const operate = await this._deletePosterity(options, select, transaction);
                 }
@@ -462,11 +466,13 @@ module.exports = app => {
                         if (s.uid !== this.ctx.session.sessionUser.accountId && !this.ctx.session.sessionUser.is_admin) throw '存在合同你无权删除';
                         const contractPays = await this.ctx.service.contractPay.getDataByCondition({ cid: s.id });
                         if (contractPays) throw '部分合同还存在合同支付项,无法删除';
+                        const contractSupplements = await this.ctx.service.contractSupplement.getDataByCondition({ cid: s.id });
+                        if (contractSupplements) throw '部分合同还存在补充合同,无法删除';
                         await transaction.delete(this.ctx.service.contract.tableName, { id: s.id });
                         const attList = await this.ctx.service.contractAtt.getAllDataByCondition({ where: { cid: s.id } });
                         await this.ctx.helper.delFiles(attList);
                         await transaction.delete(this.ctx.service.contractAtt.tableName, { cid: s.id });
-                        await transaction.delete(this.ctx.service.contractSpAudit.tableName, { cid: s.id, cpid: null });
+                        await transaction.delete(this.ctx.service.contractSpAudit.tableName, { cid: s.id, cpid: null, csid: null });
                     } else {
                         await transaction.delete(this.tableName, { id: s.id });
                         const contracts = _.filter(deleteData, function (item) {
@@ -477,10 +483,12 @@ module.exports = app => {
                             if (contractUids.length > 1 || !(contractUids[0] === this.ctx.session.sessionUser.accountId || this.ctx.session.sessionUser.is_admin)) throw '存在合同你无权删除';
                             const contractPays = await transaction.select(this.ctx.service.contractPay.tableName, { where: { cid: _.map(contracts, 'id') } });
                             if (contractPays.length > 0) throw '还存在合同支付项,无法删除';
+                            const contractSupplements = await this.ctx.service.contractSupplement.getDataByCondition({ cid: _.map(contracts, 'id') });
+                            if (contractSupplements) throw '部分合同还存在补充合同,无法删除';
                             const attList = await this.ctx.service.contractAtt.getAllDataByCondition({ where: { cid: _.map(contracts, 'id') } });
                             await this.ctx.helper.delFiles(attList);
                             await transaction.delete(this.ctx.service.contractAtt.tableName, { cid: _.map(contracts, 'id') });
-                            await transaction.delete(this.ctx.service.contractSpAudit.tableName, { cid: _.map(contracts, 'id'), cpid: null });
+                            await transaction.delete(this.ctx.service.contractSpAudit.tableName, { cid: _.map(contracts, 'id'), cpid: null, csid: null });
                         }
                     }
                     const operate = await this._deletePosterity(options, s, transaction);

+ 21 - 4
app/service/cost_stage.js

@@ -25,9 +25,9 @@ module.exports = app => {
             super(ctx);
             this.tableName = 'cost_stage';
             this.stageType = {
-                ledger: { key: 'ledger', name: '成本报审', decimal: { tp: 6, tax: 2 }, shenpi_status: 'cost_stage_ledger', push_type: 'costStageLedger', dataService: 'costStageLedger', detailService: 'costStageDetail' },
-                book: { key: 'book', name: '财务账面', decimal: { tp: 6 }, shenpi_status: 'cost_stage_book', push_type: 'costStageBook', dataService: 'costStageBook', detailService: 'costStageBookDetail' },
-                analysis: { key: 'analysis', name: '成本分析', decimal: { tp: 6 }, shenpi_status: 'cost_stage_analysis', push_type: 'costStageAnalysis', dataService: 'costStageAnalysis', detailService: 'costStageAnalysisDetail' },
+                ledger: { key: 'ledger', name: '成本报审', decimal: { tp: 6, tax: 2, excl_tax_tp: 6 }, shenpi_status: 'cost_stage_ledger', push_type: 'costStageLedger', dataService: 'costStageLedger', detailService: 'costStageDetail' },
+                book: { key: 'book', name: '财务账面', decimal: { tp: 6, tax: 2, excl_tax_tp: 6 }, shenpi_status: 'cost_stage_book', push_type: 'costStageBook', dataService: 'costStageBook', detailService: 'costStageBookDetail' },
+                analysis: { key: 'analysis', name: '成本分析', decimal: { tp: 6, tax: 2, excl_tax_tp: 6 }, shenpi_status: 'cost_stage_analysis', push_type: 'costStageAnalysis', dataService: 'costStageAnalysis', detailService: 'costStageAnalysisDetail' },
             };
         }
 
@@ -329,7 +329,24 @@ module.exports = app => {
                 }
             }
         }
+
+        async getLastCheckedStage(tid, stage_type) {
+            const lastCheckedStage = await this.ctx.service.costStage.getAllDataByCondition({
+                where: { tid, stage_type, audit_status: audit.status.checked },
+                orders: [['stage_order', 'desc']],
+            });
+            if (lastCheckedStage && lastCheckedStage.length > 0) {
+                this._analysisstage(lastCheckedStage[0]);
+                return lastCheckedStage[0];
+            }
+            return null;
+        }
+
+        async getNumByChecked(tenderId, stage_type) {
+            const num = await this.db.queryOne(`SELECT COUNT(*) as num From ${this.tableName} WHERE tid = ? and stage_type = ? and audit_status = ?`, [tenderId, stage_type, audit.status.checked]);
+            return num ? num.num : 0;
+        }
     }
 
     return CostStage;
-};
+};

+ 0 - 33
app/service/cost_stage_analysis.js

@@ -664,39 +664,6 @@ module.exports = app => {
             if (updateData.length > 0) await transaction.updateRows(this.tableName, updateData);
         }
 
-        async setDecimal(decimal) {
-            if (!this.ctx.costStage) throw '读取数据错误';
-            if (this.ctx.costStage.stage_order !== this.ctx.costStage.latestOrder) throw '往期不可修改小数位数';
-            if (this.ctx.costStage.audit_status !== auditConst.status.uncheck && this.ctx.costStage.audit_status !== auditConst.status.checkNo) throw '仅原报可修改小数位数';
-            const orgDecimal = this.ctx.costStage.decimal;
-
-            const calcTp = decimal.tp < orgDecimal.tp;
-            this.ctx.costStage.decimal = { tp: decimal.tp, tax: decimal.tax || this.ctx.costStage.decimal.tax };
-
-            const updateData = [];
-            if (calcTp) {
-                const calcData = await this.getAllDataByCondition({ where: {stage_id: this.ctx.costStage.id, tree_is_leaf: 1 } });
-                for (const cd of calcData) {
-                    const nd = { id: cd.id, tree_id: cd.tree_id };
-                    for (const prop of costFields.calcFields) {
-                        nd[prop] = this.ctx.helper.round(nd[prop], decimal.tp);
-                    }
-                    updateData.push(nd);
-                }
-            }
-            const conn = await this.db.beginTransaction();
-            try {
-                await conn.update(this.ctx.service.costStage.tableName, { id: this.ctx.costStage.id, decimal: JSON.stringify(this.ctx.costStage.decimal)});
-                if (updateData.length > 0) await conn.updateRows(this.tableName, updateData);
-                await conn.commit();
-                return { calc: calcTp, update: updateData };
-            } catch(err) {
-                await conn.rollback();
-                return { calc: false, update: [] };
-            }
-
-        }
-
         async getSum(stage) {
             const sumCol = stage.calcTemplate.col_set.find(x => { return x.sum; });
             const field = sumCol.field;

+ 71 - 2
app/service/cost_stage_book.js

@@ -19,6 +19,7 @@ costFields.calcFields = [...costFields.curFields];
 costFields.editQueryFields = [...costFields.baseFields, ...costFields.textFields, ...costFields.preFields, ...costFields.curFields];
 costFields.readQueryFields = [...costFields.baseFields, ...costFields.textFields, ...costFields.preFields, ...costFields.readFields];
 costFields.compareQueryFields = [...costFields.baseFields, ...costFields.textFields, ...costFields.preFields, ...costFields.curFields, 'calc_his'];
+const auditConst = require('../const/audit').costStage;
 
 module.exports = app => {
     class CostStageBook extends app.BaseService {
@@ -129,9 +130,9 @@ module.exports = app => {
                 if (d.in_tp !== undefined ) {
                     nd.in_tp = d.in_tp !== undefined ? this.ctx.helper.round(d.in_tp || 0, this.ctx.costStage.decimal.tp) : (od.in_tp ? od.in_tp : 0);
                     const divNum = this.ctx.helper.add(1, this.ctx.helper.div(cl.tax, 100));
-                    nd.in_excl_tax_tp = this.ctx.helper.div(nd.in_tp, divNum, this.ctx.costStage.decimal.tp);
+                    nd.in_excl_tax_tp = this.ctx.helper.div(nd.in_tp, divNum, this.ctx.costStage.decimal.excl_tax_tp);
                 }
-                if (d.in_excl_tax_tp !== undefined) nd.in_excl_tax_tp = this.ctx.helper.round(d.in_excl_tax_tp, this.ctx.costStage.decimal.tp);
+                if (d.in_excl_tax_tp !== undefined) nd.in_excl_tax_tp = this.ctx.helper.round(d.in_excl_tax_tp, this.ctx.costStage.decimal.excl_tax_tp);
                 if (od) {
                     updateDetail.push(nd);
                 } else {
@@ -198,6 +199,74 @@ module.exports = app => {
             const result = await this.db.queryOne(`SELECT ${sumFields.join(', ')} FROM ${this.tableName} where stage_id = ?`, [stage.id]);
             return result;
         }
+
+        async setDecimal(decimal) {
+            if (!this.ctx.costStage) throw '读取数据错误';
+            if (this.ctx.costStage.stage_order !== this.ctx.costStage.latestOrder) throw '往期不可修改小数位数';
+            if (this.ctx.costStage.audit_status !== auditConst.status.uncheck && this.ctx.costStage.audit_status !== auditConst.status.checkNo) throw '仅原报可修改小数位数';
+            const orgDecimal = this.ctx.costStage.decimal;
+
+            const calcTp = decimal.tp < orgDecimal.tp || decimal.excl_tax_tp !== orgDecimal.excl_tax_tp;
+            this.ctx.costStage.decimal = this.ctx.helper._.assignIn(this.ctx.costStage.typeInfo.decimal, decimal);
+
+            const updateData = [], detailUpdateData = [];
+            let orgLedger, orgDetail;
+            if (calcTp) {
+                const detailCalcData = await this.ctx.service.costStageBookDetail.getAllDataByCondition({ where: { stage_id: this.ctx.costStage.id }});
+                orgDetail = await this.ctx.service.costStageDetail.getAllDataByCondition({ where: { stage_id: this.ctx.costStage.relaStage.id }});
+                for (const cd of detailCalcData) {
+                    const nd = { id: cd.id, ledger_id: cd.ledger_id, detail_id: cd.detail_id };
+                    const cl = orgLedger.find(x => { return x.id === cd.ledger_id });
+                    nd.in_tp = this.ctx.helper.round(cd.in_tp, decimal.tp);
+                    const divNum = cl.tax ? this.ctx.helper.add(1, this.ctx.helper.div(cl.tax, 100)) : 1;
+                    nd.in_excl_tax_tp = this.ctx.helper.div(nd.in_tp !== undefined ? nd.in_tp : nd.in_tp, divNum, decimal.excl_tax_tp);
+                    detailUpdateData.push(nd);
+                }
+                const calcData = await this.getAllDataByCondition({ where: {stage_id: this.ctx.costStage.id } });
+                orgLedger = await this.ctx.service.costStageLedger.getAllDataByCondition({ where: { stage_id: this.ctx.costStage.relaStage.id }});
+                for (const cd of calcData) {
+                    const nd = { id: cd.id, ledger_id: cd.ledger_id };
+                    const relaDetail = detailUpdateData.filter(x => { return x.ledger_id === cd.ledger_id; });
+                    if (relaDetail && relaDetail.length > 0) {
+                        for (const rd of relaDetail) {
+                            for (const prop of costFields.calcFields) {
+                                nd[prop] = this.ctx.helper.add(nd[prop], rd[prop]);
+                            }
+                        }
+                    } else {
+                        const cl = orgLedger.find(x => { return x.id === cd.ledger_id });
+                        nd.in_tp = this.ctx.helper.round(cd.in_tp, decimal.tp);
+                        const divNum = cl.tax ? this.ctx.helper.add(1, this.ctx.helper.div(cl.tax, 100)) : 1;
+                        nd.in_excl_tax_tp = this.ctx.helper.div(nd.in_tp !== undefined ? nd.in_tp : nd.in_tp, divNum, decimal.excl_tax_tp);
+                    }
+                    updateData.push(nd);
+                }
+            }
+
+            const conn = await this.db.beginTransaction();
+            try {
+                await conn.update(this.ctx.service.costStage.tableName, { id: this.ctx.costStage.id, decimal: JSON.stringify(this.ctx.costStage.decimal)});
+                if (updateData.length > 0) await conn.updateRows(this.tableName, updateData);
+                if (detailUpdateData.length > 0) await conn.updateRows(this.ctx.service.costStageBookDetail.tableName, detailUpdateData);
+                await conn.commit();
+            } catch(err) {
+                await conn.rollback();
+                return { calc: false, bills: [], detail: [] };
+            }
+            updateData.forEach(i => {
+                i.book_id = i.id;
+                delete i.id;
+                const cl = orgLedger.find(x => { return x.id === i.ledger_id });
+                i.tree_id = cl.tree_id;
+            });
+            detailUpdateData.forEach(u => {
+                u.book_id = u.id;
+                delete u.id;
+                const cl = orgDetail.find(x => { return x.id === od.detail_id; });
+                i.id = cl.id;
+            });
+            return { calc: calcTp, bills: updateData, detail: detailUpdateData };
+        }
     }
 
     return CostStageBook;

+ 39 - 10
app/service/cost_stage_ledger.js

@@ -461,8 +461,8 @@ module.exports = app => {
                 if (row.tax !== undefined || nData.sf_tp !== undefined || nData.yf_tp !== undefined) {
                     nData.tax = row.tax !== undefined ? helper.round(row.tax || 0, decimal.tax) : oData.tax;
                     const divNum = nData.tax ? helper.add(1, helper.div(nData.tax, 100)) : 1;
-                    nData.yf_excl_tax_tp = helper.div(nData.yf_tp !== undefined ? nData.yf_tp : oData.yf_tp, divNum, decimal.tp);
-                    nData.sf_excl_tax_tp = helper.div(nData.sf_tp !== undefined ? nData.sf_tp : oData.sf_tp, divNum, decimal.tp);
+                    nData.yf_excl_tax_tp = helper.div(nData.yf_tp !== undefined ? nData.yf_tp : oData.yf_tp, divNum, decimal.excl_tax_tp);
+                    nData.sf_excl_tax_tp = helper.div(nData.sf_tp !== undefined ? nData.sf_tp : oData.sf_tp, divNum, decimal.excl_tax_tp);
                 }
                 for (const field of costFields.textFields) {
                     if (row[field] !== undefined) nData[field] = row[field] || '';
@@ -509,31 +509,60 @@ module.exports = app => {
             if (this.ctx.costStage.audit_status !== auditConst.status.uncheck && this.ctx.costStage.audit_status !== auditConst.status.checkNo) throw '仅原报可修改小数位数';
             const orgDecimal = this.ctx.costStage.decimal;
 
-            const calcTp = decimal.tp < orgDecimal.tp;
-            this.ctx.costStage.decimal = { up: decimal.up, tp: decimal.tp, qty: decimal.qty };
+            const calcTp = decimal.tp < orgDecimal.tp || decimal.excl_tax_tp !== orgDecimal.excl_tax_tp;
+            this.ctx.costStage.decimal = this.ctx.helper._.assignIn(this.ctx.costStage.typeInfo.decimal, decimal);
 
-            const updateData = [];
+            const updateData = [], detailUpdateData = [];
             if (calcTp) {
+                const detailCalcData = await this.ctx.service.costStageDetail.getAllDataByCondition({ where: { stage_id: this.ctx.costStage.id }});
+                for (const cd of detailCalcData) {
+                    const nd = { id: cd.id, ledger_id: cd.ledger_id };
+                    nd.tax = this.ctx.helper.round(cd.tax, decimal.tax);
+                    nd.pay_tp = this.ctx.helper.round(cd.pay_tp, decimal.tp);
+                    nd.cut_tp = this.ctx.helper.round(cd.cut_tp, decimal.tp);
+                    nd.yf_tp = this.ctx.helper.round(cd.yf_tp, decimal.tp);
+                    nd.sf_tp = this.ctx.helper.round(cd.sf_tp, decimal.tp);
+                    const divNum = nd.tax ? this.ctx.helper.add(1, this.ctx.helper.div(nd.tax, 100)) : 1;
+                    nd.yf_excl_tax_tp = this.ctx.helper.div(nd.yf_tp !== undefined ? nd.yf_tp : nd.yf_tp, divNum, decimal.excl_tax_tp);
+                    nd.sf_excl_tax_tp = this.ctx.helper.div(nd.sf_tp !== undefined ? nd.sf_tp : nd.sf_tp, divNum, decimal.excl_tax_tp);
+                    detailUpdateData.push(nd);
+                }
                 const calcData = await this.getAllDataByCondition({ where: {stage_id: this.ctx.costStage.id, tree_is_leaf: 1 } });
                 for (const cd of calcData) {
                     const nd = { id: cd.id, tree_id: cd.tree_id };
-                    for (const prop of costFields.curFields) {
-                        nd[prop] = this.ctx.helper.round(nd[prop]);
+                    const relaDetail = detailUpdateData.filter(x => { return x.ledger_id === cd.id; });
+                    if (relaDetail && relaDetail.length > 0) {
+                        for (const rd of relaDetail) {
+                            for (const prop of costFields.calcFields) {
+                                if (prop === 'tax') continue;
+                                nd[prop] = this.ctx.helper.add(nd[prop], rd[prop]);
+                            }
+                        }
+                    } else {
+                        nd.tax = this.ctx.helper.round(cd.tax, decimal.tax);
+                        nd.pay_tp = this.ctx.helper.round(cd.pay_tp, decimal.tp);
+                        nd.cut_tp = this.ctx.helper.round(cd.cut_tp, decimal.tp);
+                        nd.yf_tp = this.ctx.helper.round(cd.yf_tp, decimal.tp);
+                        nd.sf_tp = this.ctx.helper.round(cd.sf_tp, decimal.tp);
+                        const divNum = nd.tax ? this.ctx.helper.add(1, this.ctx.helper.div(nd.tax, 100)) : 1;
+                        nd.yf_excl_tax_tp = this.ctx.helper.div(nd.yf_tp !== undefined ? nd.yf_tp : nd.yf_tp, divNum, decimal.excl_tax_tp);
+                        nd.sf_excl_tax_tp = this.ctx.helper.div(nd.sf_tp !== undefined ? nd.sf_tp : nd.sf_tp, divNum, decimal.excl_tax_tp);
                     }
                     updateData.push(nd);
                 }
             }
+
             const conn = await this.db.beginTransaction();
             try {
                 await conn.update(this.ctx.service.costStage.tableName, { id: this.ctx.costStage.id, decimal: JSON.stringify(this.ctx.costStage.decimal)});
                 if (updateData.length > 0) await conn.updateRows(this.tableName, updateData);
+                if (detailUpdateData.length > 0) await conn.updateRows(this.ctx.service.costStageDetail.tableName, detailUpdateData);
                 await conn.commit();
-                return { calc: calcTp, update: updateData };
+                return { calc: calcTp, bills: updateData, detail: detailUpdateData };
             } catch(err) {
                 await conn.rollback();
-                return { calc: false, update: [] };
+                return { calc: false, bills: [], detail: [] };
             }
-
         }
 
         async getSum(stage) {

+ 9 - 8
app/service/pos.js

@@ -258,26 +258,27 @@ module.exports = app => {
             let needUpdateBills;
             for (const d of data) {
                 if (d.sgfh_qty !== undefined || d.sjcl_qty !== undefined || d.qtcl_qty !== undefined || d.ex_qty1 !== undefined) {
+                    const od = billsPos.find(x => { return x.id === d.id; });
                     if (d.sgfh_qty !== undefined) {
                         d.sgfh_qty = this.round(d.sgfh_qty, precision.value);
-                    } else if (op) {
-                        d.sgfh_qty = op.sgfh_qty;
+                    } else if (od) {
+                        d.sgfh_qty = od.sgfh_qty;
                     }
                     if (d.sjcl_qty !== undefined) {
                         d.sjcl_qty = this.round(d.sjcl_qty, precision.value);
-                    } else if (op) {
-                        d.sjcl_qty = op.sjcl_qty;
+                    } else if (od) {
+                        d.sjcl_qty = od.sjcl_qty;
                     }
                     if (d.qtcl_qty !== undefined) {
                         d.qtcl_qty = this.round(d.qtcl_qty, precision.value);
-                    } else if (op) {
-                        d.qtcl_qty = op.qtcl_qty;
+                    } else if (od) {
+                        d.qtcl_qty = od.qtcl_qty;
                     }
                     d.quantity = this.ctx.helper.sum([d.sgfh_qty, d.qtcl_qty, d.sjcl_qty]);
                     if (d.ex_qty1 !== undefined) {
                         d.ex_qty1 = this.round(d.ex_qty1, precision.value);
-                    } else if (op) {
-                        d.ex_qty1 = op.ex_qty1;
+                    } else if (od) {
+                        d.ex_qty1 = od.ex_qty1;
                     }
                     needUpdateBills = true;
                 }

+ 3 - 1
app/service/shenpi_audit.js

@@ -413,7 +413,9 @@ module.exports = app => {
                     return;
                 }
                 // 更新固定审批流
-                await transaction.delete(this.tableName, { tid: tenderId, sp_type, sp_status });
+                const delCondition = { sp_type, sp_status };
+                this._.assign(delCondition, tidCondition);
+                await transaction.delete(this.tableName, delCondition);
                 const insertDatas = [];
                 for (const [i, id] of ids.entries()) {
                     const oneInsertData = {

+ 5 - 0
app/service/stage.js

@@ -357,6 +357,11 @@ module.exports = app => {
             return this.db.query(`SELECT * From ${this.tableName} WHERE tid = ? and status <> ?`, [tenderId, auditConst.stage.status.checked]);
         }
 
+        async getNumByChecked(tenderId) {
+            const num = await this.db.queryOne(`SELECT COUNT(*) as num From ${this.tableName} WHERE tid = ? and status = ?`, [tenderId, auditConst.stage.status.checked]);
+            return num ? num.num : 0;
+        }
+
         /**
          * 获取 最新一期 审批完成的 期计量
          * @param tenderId

+ 1 - 0
app/view/budget/compare.ejs

@@ -117,4 +117,5 @@
     const category = JSON.parse(unescape('<%- escape(JSON.stringify(categoryData)) %>'));
     const tenderList = JSON.parse(unescape('<%- escape(JSON.stringify(tenderList)) %>'));
     const ckColSetting = 'budget-col-visible-1.0.0';
+    const contract = JSON.parse(unescape('<%- escape(JSON.stringify(contract)) %>'));
 </script>

+ 54 - 2
app/view/budget/compare_modal.ejs

@@ -5,8 +5,60 @@
                 <h5 class="modal-title">选择决算标段</h5>
             </div>
             <div class="modal-body">
-                <h5>可选标段</h5>
-                <div id="sf-spread" style="height: 300px"></div>
+                <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>
+                <div>
+                    <ul class="nav nav-tabs">
+                        <li class="nav-item">
+                            <a class="nav-link active" data-toggle="tab" href="#sf-tender" role="tab" name="sf-tab">可选标段</a>
+                        </li>
+                        <li class="nav-item">
+                            <a class="nav-link" data-toggle="tab" href="#sf-contract" role="tab" name="sf-tab">可选合同</a>
+                        </li>
+                    </ul>
+                </div>
+                <div class="tab-content">
+                    <div id="sf-tender" class="tab-pane active">
+                        <div id="sf-tender-spread" style="height: 300px"></div>
+                    </div>
+                    <div id="sf-contract" class="tab-pane">
+                        <div id="sf-contract-spread" style="height: 300px"></div>
+                    </div>
+                </div>
             </div>
             <div class="modal-footer" style="justify-content: space-between">
                 <div class="d-flex">

+ 16 - 12
app/view/contract/detail.ejs

@@ -79,9 +79,9 @@
                             <li class="nav-item">
                                 <a class="nav-link " data-toggle="tab" href="#htfile" role="tab">合同文件</a>
                             </li>
-<!--                            <li class="nav-item">-->
-<!--                                <a class="nav-link " data-toggle="tab" href="#htsupplement" role="tab">补充合同</a>-->
-<!--                            </li>-->
+                            <li class="nav-item">
+                                <a class="nav-link " data-toggle="tab" href="#htsupplement" role="tab">补充合同</a>
+                            </li>
                             <li class="ml-auto">
                                 <!-- 结算合同所有tab可见 ,结算后见解锁合同,2者互斥-->
                                 <a href="#cons-unlock" data-toggle="modal" data-target="#cons-unlock" style="display: none;" class="btn btn-success btn-sm pull-right mr-2">解锁合同</a>
@@ -96,7 +96,7 @@
                                 <a href="javascript:void(0);" id="save_contract_btn" style="display: none" class="btn btn-primary btn-sm pull-right mr-2">确定</a>
                                 <a href="javascript:void(0);" id="edit_contract_btn" style="display: none" class="btn btn-primary btn-sm pull-right mr-2">编辑合同</a>
                                 <a href="javascript:void(0);" id="add_contract_supplement_btn" style="display: none;" class="btn btn-primary btn-sm pull-right mr-2">新增补充合同</a>
-                            <% if (shenpi_status) { %>
+                                <% if (shenpi_status) { %>
                                     <span class="pull-right mr-2" id="shenpi_btn">
                                     <a href="#sub-sp" data-toggle="modal" data-target="#sub-sp" class="btn btn-primary btn-sm">待上报</a>
                                 </span>
@@ -117,20 +117,28 @@
                                         <td width="20%" class="change-input-td" id="htdetail_tax"></td>
                                     </tr>
                                     <tr>
+                                        <th class="text-center align-middle">原合同金额</th>
+                                        <td class="change-input-td" id="htdetail_price"></td>
+                                        <th class="text-center align-middle">补充合同金额</th>
+                                        <td class="" id="htdetail_supplement_price"></td>
                                         <th class="text-center align-middle">合同金额</th>
-                                        <td class="change-input-td" id="htdetail_total_price"></td>
+                                        <td class="" id="htdetail_total_price"></td>
+                                    </tr>
+                                    <tr>
                                         <th class="text-center align-middle"><% if (ctx.contract_type === contractConst.type.income) { %>累计回款(P)<% } else if (ctx.contract_type === contractConst.type.expenses) { %>累计付款(P)<% } %></th>
                                         <td id="htdetail_pay_price"></td>
                                         <th class="text-center align-middle">累计扣款(K)</th>
                                         <td id="htdetail_debit_price"></td>
-                                    </tr>
-                                    <tr>
                                         <th class="text-center align-middle"><% if (ctx.contract_type === contractConst.type.income) { %>累计应回(S=P-K)<% } else if (ctx.contract_type === contractConst.type.expenses) { %>累计应付(S=P-K)<% } %></th>
                                         <td id="htdetail_yf_price"></td>
+                                    </tr>
+                                    <tr>
                                         <th class="text-center align-middle"><% if (ctx.contract_type === contractConst.type.income) { %>累计实回(A)<% } else if (ctx.contract_type === contractConst.type.expenses) { %>累计实付(A)<% } %></th>
                                         <td id="htdetail_sf_price"></td>
                                         <th class="text-center align-middle"><% if (ctx.contract_type === contractConst.type.income) { %>待回款(S-A)<% } else if (ctx.contract_type === contractConst.type.expenses) { %>待支付(S-A)<% } %></th>
                                         <td id="htdetail_df_price"></td>
+                                        <th class="text-center align-middle">创建时间</th>
+                                        <td id="htdetail_create_time"></td>
                                     </tr>
                                     <tr>
                                         <th class="text-center align-middle">甲方</th>
@@ -161,10 +169,6 @@
                                     <tr>
                                         <th class="text-center align-middle">结算书编号</th>
                                         <td id="htdetail_settle_code"></td>
-                                        <th class="text-center align-middle">创建时间</th>
-                                        <td id="htdetail_create_time"></td>
-                                    </tr>
-                                    <tr>
                                         <th class="text-center align-middle">备注</th>
                                         <td class="change-input-td" colspan="5" id="htdetail_remark"></td>
                                     </tr>
@@ -267,7 +271,7 @@
                                 <table class="table table-sm table-bordered" id="htsupplement-table" style="display: none;">
                                     <thead>
                                     <tr class="text-center" id="supplement-header">
-                                        <th width="3%">序号</th><th width="5%" sort="code">编号</th><th width="10%">名称</th><th width="7%">金额</th><th width="10%">甲方</th><th width="10%">乙方</th><th width="5%">签订时间</th><th width="5%">签订地点</th><th width="5%">创建人</th><th width="9%" sort="create_time">创建时间</th><th width="15%">补充原因及内容</th><th width="4%">附件</th>
+                                        <th width="3%">序号</th><th width="5%" sort="code">编号</th><th width="8%">名称</th><th width="7%">金额</th><th width="8%">甲方</th><th width="8%">乙方</th><th width="5%">签订时间</th><th width="5%">签订地点</th><th width="5%">创建人</th><th width="9%" sort="create_time">创建时间</th><th width="10%">补充原因及内容</th><th width="4%">附件</th>
                                         <% if (shenpi_status) { %>
                                             <th width="15%">审批进度</th>
                                         <% } %>

+ 3 - 1
app/view/contract/detail_modal.ejs

@@ -19,7 +19,7 @@
                 </div>
                 <div class="form-group">
                     <label>合同金额<b class="text-danger">*</b></label>
-                    <input class="form-control form-control-sm" placeholder="请输入合同金额" type="text" name="total_price">
+                    <input class="form-control form-control-sm" placeholder="请输入合同金额" type="text" name="price">
                     <div class="invalid-feedback">合同金额不能为0。</div>
                 </div>
                 <div class="form-group">
@@ -505,6 +505,7 @@
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
                 <input type="hidden" id="start-sp-cid" />
                 <input type="hidden" id="start-sp-cpid" />
+                <input type="hidden" id="start-sp-csid" />
                 <button class="btn btn-primary btn-sm" type="button" id="start-sp-btn">确认上报</button>
             </div>
         </div>
@@ -536,6 +537,7 @@
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
                 <input type="hidden" id="check-sp-cid" />
                 <input type="hidden" id="check-sp-cpid" />
+                <input type="hidden" id="check-sp-csid" />
                 <input type="hidden" id="check-operate" />
                 <span id="check-sp-btn-html">
                 </span>

+ 5 - 0
app/view/cost/book.ejs

@@ -19,6 +19,11 @@
                     </div>
                 </div>
             </div>
+            <% if (ctx.costStage.create_user_id === ctx.session.sessionUser.accountId) { %>
+            <div class="d-inline-block">
+                <a href="" class="btn btn-sm btn-light text-primary" data-toggle="modal" data-placement="bottom" title="设置" data-target="#cost-decimal"><i class="fa fa-cog" aria-hidden="true"></i></a>
+            </div>
+            <% } %>
             <div class="ml-auto">
             </div>
         </div>

+ 2 - 1
app/view/cost/book_modal.ejs

@@ -1,4 +1,5 @@
 <% include ../shares/delete_hint_modal.ejs %>
 <% include ./audit_modal.ejs %>
 <% include ../shares/upload_att.ejs %>
-<% include ../shares/new_tag_modal.ejs %>
+<% include ../shares/new_tag_modal.ejs %>
+<% include ./cost_decimal.ejs %>

+ 66 - 0
app/view/cost/cost_decimal.ejs

@@ -0,0 +1,66 @@
+
+<div class="modal fade show" id="cost-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-6">
+                            <div class="input-group input-group-sm">
+                                <div class="input-group-prepend">
+                                    <span class="input-group-text">金额</span>
+                                </div>
+                                <input type="number" id="cost-tp" class="form-control" value="<%- ctx.costStage.decimal.tp %>" org="<%- ctx.costStage.decimal.tp %>" min="0" max="6" maxlength="1" oninput="limitDecimal(this)" <% if (ctx.costStage.readOnly) { %>disabled<% } %>>
+                            </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="number" id="cost-excl-tax-tp" class="form-control" value="<%- ctx.costStage.decimal.excl_tax_tp %>" org="<%- ctx.costStage.decimal.excl_tax_tp %>" min="0" max="6" maxlength="1" oninput="limitDecimal(this)" <% if (ctx.costStage.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.costStage.readOnly) { %>
+                <button type="button" class="btn btn-sm btn-primary" id="cost-decimal-ok">确认</button>
+                <% } %>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    // 根据Min Max限制Input输入
+    function limitInputMinMax (obj) {
+        if (_.toNumber(obj.value) > _.toNumber(obj.max)) {
+            obj.value = obj.max;
+        }
+        if(_.toNumber(obj.value) < _.toNumber(obj.min)) {
+            obj.value = obj.min;
+        }
+    }
+    // 根据Maxlength限制input输入
+    function limitMaxLength (obj) {
+        if (obj.value.length > obj.maxLength) {
+            obj.value = obj.value.substr(0, obj.maxLength);
+        }
+    }
+    // 根据正则限制输入
+    function limitReg(obj, reg) {
+        obj.value = obj.value.replace(reg, '');
+    }
+    // 小数位数 input 输入限制
+    function limitDecimal(obj) {
+        limitReg(obj, /[^\d]/g);
+        limitMaxLength(obj);
+        limitInputMinMax(obj);
+    }
+</script>

+ 3 - 0
app/view/cost/ledger.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="#cost-decimal"><i class="fa fa-cog" aria-hidden="true"></i></a>
+                </div>
                 <% } %>
             </div>
             <div class="ml-auto">

+ 1 - 0
app/view/cost/ledger_modal.ejs

@@ -2,6 +2,7 @@
 <% include ./audit_modal.ejs %>
 <% include ../shares/upload_att.ejs %>
 <% include ../shares/new_tag_modal.ejs %>
+<% include ./cost_decimal.ejs %>
 <div class="modal" id="import-deal-type" data-backdrop="static" aria-modal="true" role="dialog">
     <div class="modal-dialog" role="document">
         <div class="modal-content">

+ 3 - 1
app/view/datacollect/index.ejs

@@ -43,8 +43,10 @@
                     <button type="button" class="btn btn-sm btn-outline-dark text-white dropdown-toggle" data-toggle="dropdown" id="zhankai">决策大屏<span><%- ctx.subProject.data_collect %></span></button>
                     <div class="dropdown-menu" aria-labelledby="zhankai">
                         <% for (const i of ctx.subProject.data_collect_pages) { %>
-                        <% if (i === '6') { %>
+                        <% if (['6', '7'].includes(i)) { %>
                             <a class="dropdown-item" href="/sp/<%- ctx.subProject.id %>/datacollect/index/<%- i %>">决策大屏<%- i %></a>
+                        <% } else if (['8'].includes(i)) { %>
+                            <a class="dropdown-item" target="_blank" href="<%- shj_url %>">决策大屏<%- i %></a>
                         <% } else { %>
                             <a class="dropdown-item change-collect" href="javascript:void(0)" data-collect="<%- i %>">决策大屏<%- i %></a>
                         <% } %>

+ 5 - 1
app/view/datacollect/index4GY18Y.ejs

@@ -61,7 +61,11 @@
                     <button type="button" class="btn btn-sm btn-outline-dark text-white dropdown-toggle" data-toggle="dropdown" id="zhankai">决策大屏<span>6</span></button>
                     <div class="dropdown-menu" aria-labelledby="zhankai">
                         <% for (const i of ctx.subProject.data_collect_pages) { %>
-                            <a class="dropdown-item" href="/sp/<%- ctx.subProject.id %>/datacollect/index/<%- i %>">决策大屏<%- i %></a>
+                            <% if (['8'].includes(i)) { %>
+                                <a class="dropdown-item" target="_blank" href="<%- shj_url %>">决策大屏<%- i %></a>
+                            <% } else { %>
+                                <a class="dropdown-item" href="/sp/<%- ctx.subProject.id %>/datacollect/index/<%- i %>">决策大屏<%- i %></a>
+                            <% } %>
                         <% } %>
                     </div>
                 </div>

File diff ditekan karena terlalu besar
+ 1131 - 0
app/view/datacollect/index_cost.ejs


+ 7 - 0
app/view/layout/menu.ejs

@@ -37,10 +37,17 @@
             <% } %>
             <% } else { %>
             <li <% if((ctx.controllerName === menu.controller && (!menu.notIncludedUrl || !menu.notIncludedUrl.some(item => ctx.url.includes(item)))) || (!!menu.controllers && menu.controllers.indexOf(ctx.controllerName) >= 0 && (!menu.includedUrl || !menu.includedUrl[ctx.controllerName] || menu.includedUrl[ctx.controllerName].some(item => ctx.url.includes(item)))) || (menu.url && menu.url === ctx.url)) { %>class="active"<% } %>>
+                <% if (menu.controller === 'datacollect' && ctx.subProject.data_collect_pages.includes('8') && ctx.subProject.data_collect === 8) { %>
+                <a href="<%- menu.url %>" target="_blank" id="<%- 'nav_' + index%>" data-toggle="tooltip" data-placement="right" title="" data-original-title="<%- menu.name %>">
+                    <i class="fa <%- menu.icon %>"></i>
+                    <% if (menu.caption) { %><span class="d-inline-block" style="vertical-align: text-bottom;"><%- menu.caption %></span><% } %>
+                </a>
+                <% } else { %>
                 <a href="<%- menu.url %>" id="<%- 'nav_' + index%>" data-toggle="tooltip" data-placement="right" title="" data-original-title="<%- menu.name %>">
                     <i class="fa <%- menu.icon %>"></i>
                     <% if (menu.caption) { %><span class="d-inline-block" style="vertical-align: text-bottom;"><%- menu.caption %></span><% } %>
                 </a>
+                <% } %>
             </li>
             <% } %>
             <% } %>

+ 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>

+ 25 - 0
app/view/sp_setting/datacollect.ejs

@@ -24,6 +24,10 @@
                         <a class="nav-item nav-link<% if (ctx.subProject.data_collect === is_dz2) { %> active<% } %>" data-datacollect="<%- is_dz2 %>" data-toggle="tab" href="#shujudaping-<%- is_dz2 %>" role="tab">
                             决策大屏<%- ctx.helper.transFormToChinese(is_dz2) %><% if (ctx.helper._.indexOf(ctx.subProject.data_collect_pages, is_dz2.toString()) !== -1) { %>(已开启<% if (ctx.subProject.data_collect === is_dz2) { %>、默认<% } %>)<% } %></a>
                     <% } %>
+                    <a class="nav-item nav-link<% if (ctx.subProject.data_collect === 7) { %> active<% } %>" data-datacollect="7" data-toggle="tab" href="#shujudaping-7" role="tab">
+                        决策大屏<%- ctx.helper.transFormToChinese(7) %><% if (ctx.helper._.indexOf(ctx.subProject.data_collect_pages, '7') !== -1) { %>(已开启<% if (ctx.subProject.data_collect === 7) { %>、默认<% } %>)<% } %></a>
+                    <a class="nav-item nav-link<% if (ctx.subProject.data_collect === is_dz3) { %> active<% } %>" data-datacollect="<%- is_dz3 %>" data-toggle="tab" href="#shujudaping-<%- is_dz3 %>" role="tab">
+                        决策大屏<%- ctx.helper.transFormToChinese(is_dz3) %><% if (ctx.helper._.indexOf(ctx.subProject.data_collect_pages, is_dz3.toString()) !== -1) { %>(已开启<% if (ctx.subProject.data_collect === is_dz3) { %>、默认<% } %>)<% } %></a>
                     <div class="ml-auto">
                         <!--<div class="form-check form-check-inline">-->
                             <!--<input class="form-check-input" type="checkbox" id="show-datacollect" value="option1" <% if (ctx.subProject.data_collect) { %>checked<% } %>>-->
@@ -57,6 +61,14 @@
                             <span>四川广元路桥集团计量内控管理云平台定制(GY18Y)</span><a class="nav-link" style="display: inline-block;" data-toggle="modal" href="#dpsix-set" data-target="#dpsix-set" title="决策大屏六设置"><i class="fa fa-cog"></i></a><img src="/public/images/juecedaping0<%- is_dz2 %>.png" width="100%">
                         </div>
                     <% } %>
+                    <div id="shujudaping-7" class="tab-pane<% if (ctx.subProject.data_collect === 7) { %> active<% } %>">
+                        成本管理决策大屏数据汇总<img src="/public/images/juecedaping07.png" width="100%">
+                    </div>
+                    <% if (is_dz3) { %>
+                    <div id="shujudaping-<%- is_dz3 %>" class="tab-pane<% if (ctx.subProject.data_collect === is_dz3) { %> active<% } %>">
+                        山海鲸数据大屏平台定制(会打开新标签页跳转)<img src="/public/images/juecedaping0<%- is_dz3 %>.png" width="100%">
+                    </div>
+                    <% } %>
                 </div>
             </div>
         </div>
@@ -79,6 +91,7 @@
     let addDataCollect = JSON.parse(unescape('<%- escape(JSON.stringify(ctx.subProject.page_show.addDataCollect)) %>'));
     let is_dz1 = <% if (is_dz1) { %>parseInt('<%- is_dz1 %>');<% } else { %>false;<% } %>
     let is_dz2 = <% if (is_dz2) { %>parseInt('<%- is_dz2 %>');<% } else { %>false;<% } %>
+    let is_dz3 = <% if (is_dz3) { %>parseInt('<%- is_dz3 %>');<% } else { %>false;<% } %>
     $(function () {
         autoFlashHeight();
         // 开启及关闭展示
@@ -155,6 +168,18 @@
                 }
                 $('#tablist a').eq(is_dz2-3).text('决策大屏' + transFormToChinese(is_dz2) + msg);
             }
+            let msg = '';
+            if (_.indexOf(dataCollectPages, '7') !== -1) {
+                msg = '(已开启' + (7 === dataCollect ? '、默认' : '') + ')' ;
+            }
+            $('#tablist a').eq(7-3).text('决策大屏' + transFormToChinese(7) + msg);
+            if (is_dz3) {
+                let msg = '';
+                if (_.indexOf(dataCollectPages, is_dz3.toString()) !== -1) {
+                    msg = '(已开启' + (is_dz3=== dataCollect ? '、默认' : '') + ')' ;
+                }
+                $('#tablist a').eq(is_dz3-3).text('决策大屏' + transFormToChinese(is_dz3) + msg);
+            }
         }
         $('#tablist a').click(function () {
             const page = parseInt($(this).data('datacollect'));

+ 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',

+ 8 - 1
sql/update.sql

@@ -11,10 +11,17 @@
 -- 表结构
 ------------------------------------
 
+ALTER TABLE `zh_contract`
+ADD COLUMN `price` decimal(30, 8) NULL DEFAULT 0 COMMENT '合同金额(可编辑,和补充合同金额汇总得出总金额)' AFTER `total_price`;
+
+ALTER TABLE `zh_contract_sp_audit`
+ADD COLUMN `csid` int NULL DEFAULT NULL COMMENT '补充合同id' AFTER `cpid`;
+
 ALTER TABLE `zh_sub_project`
 ADD COLUMN `cost_ledger_col_set` json NULL COMMENT '成本报审-列设置' AFTER `cost_calc_template`;
 
 
 ------------------------------------
 -- 表数据
-------------------------------------
+------------------------------------
+update zh_contract set price = total_price;