Просмотр исходного кода

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

Tony Kang 15 часов назад
Родитель
Сommit
fc4de27387
42 измененных файлов с 1465 добавлено и 326 удалено
  1. 1 0
      .gitignore
  2. 61 16
      app/base/base_bills_service.js
  3. 10 0
      app/const/contract.js
  4. 14 1
      app/controller/contract_controller.js
  5. 6 3
      app/controller/cost_controller.js
  6. 42 26
      app/controller/ledger_controller.js
  7. 1 1
      app/lib/sum_load.js
  8. 95 24
      app/public/js/contract_detail.js
  9. 94 64
      app/public/js/contract_setting.js
  10. 1 1
      app/public/js/cost_stage_book.js
  11. 34 38
      app/public/js/cost_stage_ledger.js
  12. 253 82
      app/public/js/ledger.js
  13. 4 5
      app/public/js/path_tree.js
  14. 1 0
      app/public/js/shares/ct_preview.js
  15. 1 1
      app/public/js/shares/new_tag.js
  16. 2 0
      app/public/js/shenpi.js
  17. 3 0
      app/public/js/zh_calc.js
  18. 3 2
      app/router.js
  19. 121 13
      app/service/ancillary_gcl.js
  20. 296 0
      app/service/ancillary_gcl_detail.js
  21. 8 5
      app/service/calc_tmpl.js
  22. 1 0
      app/service/contract.js
  23. 34 2
      app/service/contract_pay.js
  24. 34 0
      app/service/contract_sp_audit.js
  25. 9 0
      app/service/cost_stage.js
  26. 11 11
      app/service/cost_stage_detail.js
  27. 1 0
      app/service/cost_stage_file.js
  28. 1 1
      app/service/cost_stage_ledger.js
  29. 10 5
      app/service/ledger.js
  30. 41 1
      app/service/pos.js
  31. 23 1
      app/service/pos_calc_detail.js
  32. 3 1
      app/service/stage_change.js
  33. 2 2
      app/service/stage_stash.js
  34. 2 2
      app/view/contract/detail.ejs
  35. 9 0
      app/view/contract/detail_modal.ejs
  36. 165 10
      app/view/contract/setting.ejs
  37. 1 0
      app/view/cost/analysis.ejs
  38. 1 0
      app/view/cost/book.ejs
  39. 1 0
      app/view/cost/ledger.ejs
  40. 8 8
      app/view/ledger/explode.ejs
  41. 8 0
      app/view/ledger/explode_modal.ejs
  42. 49 0
      sql/update.sql

+ 1 - 0
.gitignore

@@ -20,3 +20,4 @@ temp
 /app/public/deal_bills/uploads
 /app/public/js/webCache
 views
+/test/check

+ 61 - 16
app/base/base_bills_service.js

@@ -21,10 +21,11 @@ const billsUtils = require('../lib/bills_utils');
 
 class BaseBillsSerivce extends TreeService {
 
-    constructor (ctx, setting, relaPosName, relaAncGclName, relaExtraName, relaPosDetailName) {
+    constructor (ctx, setting, relaPosName, relaAncGclName, relaAncGclDetailName, relaExtraName, relaPosDetailName) {
         super(ctx, setting);
         this.relaPosService = relaPosName;
         this.relaAncGclService = relaAncGclName;
+        this.relaAncGclDetailService = relaAncGclDetailName;
         this.relaExtraService = relaExtraName;
         this.relaPosDetailService = relaPosDetailName;
     }
@@ -50,6 +51,13 @@ class BaseBillsSerivce extends TreeService {
         return this._ancGclName ? this.ctx.service[this._ancGclName] : undefined;
     }
 
+    set relaAncGclDetailService(ancGclDetailName) {
+        this._ancGclDetailName = ancGclDetailName;
+    }
+    get relaAncGclDetailService() {
+        return this._ancGclDetailName ? this.ctx.service[this._ancGclDetailName] : undefined;
+    }
+
     set relaExtraService(extraName) {
         this._extraName = extraName
     }
@@ -657,12 +665,13 @@ class BaseBillsSerivce extends TreeService {
             if (pd[0].ledger_pid !== pasteData[0][0].ledger_pid) throw '复制数据错误:仅可操作同层节点';
         }
         const userId = this.ctx.session.sessionUser.accountId;
+        const qtyDecimal = this.ctx.tender.info.decimal.qty;
         this.newBills = false;
         const selectData = await this.getDataByKid(tid, sid);
         if (!selectData) throw '粘贴数据错误';
         const newParentPath = selectData.full_path.replace(selectData.ledger_id, '');
 
-        const pasteBillsData = [], pastePosData = [], pasteAncGclData = [], pasteBillsExtraData = [], pastePosDetailData = [], leafBillsId = [];
+        const pasteBillsData = [], pastePosData = [], pasteAncGclData = [], pasteAncGclDetailData = [], pasteBillsExtraData = [], pastePosDetailData = [], leafBillsId = [];
         const tpDecimal = this.ctx.tender.info.decimal.tp;
         let maxId = await this._getMaxLid(this.ctx.tender.id);
         const calcTemplate = await this.ctx.service.calcTmpl.getAllTemplateDetail(this.ctx.session.sessionProject.id, 'posCalc');
@@ -794,6 +803,40 @@ class BaseBillsSerivce extends TreeService {
                         newBills.qtcl_qty = this.ctx.helper.add(newBills.qtcl_qty, newPos.qtcl_qty);
                         newBills.ex_qty1 = this.ctx.helper.add(newBills.ex_qty1, newPos.ex_qty1);
                         if (defaultData) this.ctx.helper._.assignIn(newPos, defaultData);
+
+                        for (const ag of pos.ancGcl) {
+                            const nig = {
+                                id: this.uuid.v4(), tid: this.ctx.tender.id,
+                                add_user_id: userId, update_user_id: userId,
+                                lid: newPos.lid, pid: newPos.id, g_order: ag.g_order,
+                                is_aux: ag.is_aux || 0, name: ag.name || '', unit: ag.unit || '',
+                                drawing_code: ag.drawing_code || '', memo: ag.memo || '',
+                                quantity: ag.quantity || 0, expr: ag.expr || '', calc_template: ag.calc_template || ''
+                            };
+                            pasteAncGclData.push(nig);
+                            if (!ag.calc_template) continue;
+
+                            let sumQty = 0;
+                            for (const cd of ag.calcDetail) {
+                                const newDetail = {
+                                    id: this.uuid.v4(), tid: this.ctx.tender.id, lid: newPos.lid, pid: newPos.id, ag_id: nig.id,
+                                    create_user_id: userId, update_user_id: userId,
+                                    agd_order: cd.agd_order,
+                                    str1 : cd.str1 || '', str2 : cd.str1 || '', str3 : cd.str1 || '', str4 : cd.str1 || '',
+                                    num_a : cd.num_a || 0, num_b : cd.num_b || 0, num_c : cd.num_c || 0, num_d : cd.num_d || 0,
+                                    num_e : cd.num_e || 0, num_f : cd.num_f || 0, num_g : cd.num_g || 0, num_h : cd.num_h || 0,
+                                    num_i : cd.num_i || 0, num_j : cd.num_j || 0, num_k : cd.num_k || 0, num_l : cd.num_l || 0,
+                                    num_m : cd.num_m || 0, num_n : cd.num_n || 0, num_o : cd.num_o || 0, num_p : cd.num_p || 0,
+                                    num_q : cd.num_q || 0, num_r : cd.num_r || 0, num_s : cd.num_s || 0, num_t : cd.num_t || 0,
+                                    num_u : cd.num_u || 0,
+                                    qty: cd.qty || 0, expr: cd.expr || 0, spec: cd.spec || ''
+                                };
+                                sumQty = this.ctx.helper.add(sumQty, newDetail.qty);
+                                pasteAncGclDetailData.push(newDetail);
+                            }
+                            nig.quantity = this.ctx.helper.round(sumQty, qtyDecimal);
+                        }
+
                         pastePosData.push(newPos);
                     }
                 } else {
@@ -811,19 +854,19 @@ class BaseBillsSerivce extends TreeService {
                 newBills.deal_tp = this.ctx.helper.mul(newBills.deal_qty, newBills.unit_price, tpDecimal);
                 newBills.ex_tp1 = this.ctx.helper.mul(newBills.ex_qty1, newBills.unit_price, tpDecimal);
                 if (defaultData) this.ctx.helper._.assignIn(newBills, defaultData);
-                if (d.ancGcl && d.ancGcl.length > 0) {
-                    for (const gcl of d.ancGcl) {
-                        const newAncGcl = {
-                            id: this.uuid.v4(), tid: tid, lid: newBills.id,
-                            add_user_id: userId, update_user_id: userId,
-                            name: gcl.name, unit: gcl.unit, g_order: gcl.g_order, is_aux: gcl.is_aux,
-                            quantity: gcl.quantity, expr: gcl.expr,
-                            drawing_code: gcl.drawing_code, memo: gcl.memo,
-                        };
-                        if (defaultData) this.ctx.helper._.assignIn(newAncGcl, defaultData);
-                        pasteAncGclData.push(newAncGcl);
-                    }
-                }
+                // if (d.ancGcl && d.ancGcl.length > 0) {
+                //     for (const gcl of d.ancGcl) {
+                //         const newAncGcl = {
+                //             id: this.uuid.v4(), tid: tid, lid: newBills.id,
+                //             add_user_id: userId, update_user_id: userId,
+                //             name: gcl.name, unit: gcl.unit, g_order: gcl.g_order, is_aux: gcl.is_aux,
+                //             quantity: gcl.quantity, expr: gcl.expr,
+                //             drawing_code: gcl.drawing_code, memo: gcl.memo,
+                //         };
+                //         if (defaultData) this.ctx.helper._.assignIn(newAncGcl, defaultData);
+                //         pasteAncGclData.push(newAncGcl);
+                //     }
+                // }
                 pbd.push(newBills);
             }
             for (const d of pbd) {
@@ -852,6 +895,7 @@ class BaseBillsSerivce extends TreeService {
                 await this.transaction.insert(this.relaPosService.tableName, pastePosData);
             }
             if (pasteAncGclData.length > 0 && this.relaAncGclService) await this.transaction.insert(this.relaAncGclService.tableName, pasteAncGclData);
+            if (pasteAncGclDetailData.length > 0 && this.relaAncGclDetailService) await this.transaction.insert(this.relaAncGclDetailService.tableName, pasteAncGclDetailData);
             if (pasteBillsExtraData.length > 0 && this.relaExtraService) await this.transaction.insert(this.relaExtraService.tableName, pasteBillsExtraData);
             if (pastePosDetailData.length > 0 && this.relaPosDetailService) await this.transaction.insert(this.relaPosDetailService.tableName, pastePosDetailData);
             await this.transaction.commit();
@@ -863,6 +907,7 @@ class BaseBillsSerivce extends TreeService {
         // 查询应返回的结果
         const updateData = await this.getNextsData(selectData.tender_id, selectData.ledger_pid, selectData.order + pasteData.length);
         const ancGcl = this.relaAncGclService ? { add: pasteAncGclData } : undefined;
+        const ancGclDetail = this.relaAncGclDetailSerivce ? { add: pasteAncGclDetailData } : undefined;
         const posCalcDetail = this.relaPosDetailService ? { add: pastePosDetailData } : undefined;
         if (pasteBillsExtraData.length > 0) {
             for (const be of pasteBillsExtraData) {
@@ -872,7 +917,7 @@ class BaseBillsSerivce extends TreeService {
         }
         return {
             ledger: { create: pasteBillsData, update: updateData },
-            pos: pastePosData, ancGcl, posCalcDetail,
+            pos: pastePosData, ancGcl, ancGclDetail, posCalcDetail,
         };
     }
 

+ 10 - 0
app/const/contract.js

@@ -73,6 +73,16 @@ const defaultColSet = {
     ],
 };
 
+const defaultAttribute = [
+    { name: '税率(%)', field: 'type', fixed: ['alias'], type: 'int' },
+    { name: '数值', field: 'num_val', fixed: [], type: 'int' },
+    { name: '丙方', field: 'third', fixed: [], type: 'text' },
+    { name: '丁方', field: 'fourth', fixed: [], type: 'text' },
+    { name: '文本', field: 'text', fixed: [], type: 'text' },
+    { name: '合同内容', field: 'contract_content', fixed: [], type: 'long_text', tip: '独占一行,上限1000' },
+    { name: '支付条件', field: 'pay_condition', fixed: [], type: 'long_text', tip: '独占一行,上限1000' },
+];
+
 module.exports = {
     type,
     typeMap,

+ 14 - 1
app/controller/contract_controller.js

@@ -209,7 +209,14 @@ module.exports = app => {
                         responseData.data = commonJson && commonJson[(data.is_tender ? 'tender_' : '') + 'contract_type'] ? commonJson[(data.is_tender ? 'tender_' : '') + 'contract_type'] : [];
                         break;
                     case 'set-contract-type':
-                        result = await ctx.service.subProject.saveCommonJsons(ctx.subProject.id, ['contract_type', 'tender_contract_type'], data.contract_type);
+                        result = await ctx.service.subProject.saveCommonJsons(ctx.subProject.id, ['contract_', 'tender_contract_type'], data.contract_type);
+                        break;
+                    case 'get-contract-used':
+                        const commonJson2 = ctx.subProject.common_json ? JSON.parse(ctx.subProject.common_json) : null;
+                        responseData.data = commonJson2 && commonJson2[(data.is_tender ? 'tender_' : '') + 'contract_used'] ? commonJson2[(data.is_tender ? 'tender_' : '') + 'contract_used'] : [];
+                        break;
+                    case 'set-contract-used':
+                        result = await ctx.service.subProject.saveCommonJsons(ctx.subProject.id, ['contract_used', 'tender_contract_used'], data.contract_used);
                         break;
                     default: throw '参数有误';
                 }
@@ -278,6 +285,7 @@ module.exports = app => {
                     ctx.contract.valuation, ctx.contract.measure_type) : await ctx.service.budgetStd.getStdList(ctx.subProject.std_id, 'yu');
                 const commonJson = ctx.subProject.common_json ? JSON.parse(ctx.subProject.common_json) : null;
                 const types = commonJson && commonJson[(ctx.contract_tender ? 'tender_' : '') + 'contract_type'] ? commonJson[(ctx.contract_tender ? 'tender_' : '') + 'contract_type'] : [];
+                const used = commonJson && commonJson[(ctx.contract_tender ? 'tender_' : '') + 'contract_used'] ? commonJson[(ctx.contract_tender ? 'tender_' : '') + 'contract_used'] : [];
                 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;
                 const renderData = {
@@ -289,6 +297,7 @@ module.exports = app => {
                     contractConst,
                     whiteList,
                     types,
+                    used,
                     thisUrl: `/sp/${ctx.subProject.id}` + (ctx.contract_tender ? `/contract/tender/${ctx.contract.id}/detail` : '/contract/detail'),
                     stdChapters,
                     is_setting: false,
@@ -846,10 +855,14 @@ module.exports = app => {
                 const commonJson = ctx.subProject.common_json ? JSON.parse(ctx.subProject.common_json) : null;
                 const types = commonJson && commonJson.contract_type ? commonJson.contract_type : [];
                 const tender_types = commonJson && commonJson.tender_contract_type ? commonJson.tender_contract_type : [];
+                const used = commonJson && commonJson.contract_used ? commonJson.contract_used : [];
+                const tender_used = commonJson && commonJson.tender_contract_used ? commonJson.tender_contract_used : [];
                 const renderData = {
                     types_from,
                     types,
                     tender_types,
+                    used,
+                    tender_used,
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.contract.setting),
                     thisUrl: `/sp/${ctx.subProject.id}/contract${types_from === 'tender' ? '/tender' : ''}/setting`,
                     is_setting: true,

+ 6 - 3
app/controller/cost_controller.js

@@ -94,6 +94,8 @@ module.exports = app => {
                 if (s.audit_status !== audit.common.status.checked) {
                     await this.ctx.service.costStage.loadUser(s);
                     s.is_join = s.userIds.indexOf(this.ctx.session.sessionUser.accountId) >= 0;
+                    // 实时计算 // 暂不开放
+                    // await this.ctx.service.costStage.calcRealtime(s);
                 } else {
                     const auditors = await this.ctx.service.costStageAudit.getAuditors(s.id, s.audit_times);
                     s.is_join = auditors.findIndex(a => { return a.audit_id === this.ctx.session.sessionUser.accountId }) >= 0;
@@ -216,13 +218,13 @@ module.exports = app => {
                 if (stage.id !== stages[0].id) throw '非最新一期,不可删除';
                 if (stage.stage_type === 'ledger') {
                     const bookStages = await ctx.service.costStage.getAllStages(ctx.tender.id, 'book', 'DESC');
-                    if (bookStages.findIndex(x => { return x.rela_stage && x.rela_stage.sid === stage.id; })) throw '该期已关联财务账面,请勿删除';
+                    if (bookStages.findIndex(x => { return x.rela_stage && x.rela_stage.sid === stage.id; }) >= 0) throw '该期已关联财务账面,请勿删除';
                     const analysisStages = await ctx.service.costStage.getAllStages(ctx.tender.id, 'analysis', 'DESC');
-                    if (analysisStages.findIndex(x => { return x.rela_stage && x.rela_stage.sid === stage.id; })) throw '该期已关联成本分析,请勿删除';
+                    if (analysisStages.findIndex(x => { return x.rela_stage && x.rela_stage.sid === stage.id; }) >= 0) throw '该期已关联成本分析,请勿删除';
                 }
                 if (stage.stage_type === 'book') {
                     const analysisStages = await ctx.service.costStage.getAllStages(ctx.tender.id, 'analysis', 'DESC');
-                    if (analysisStages.findIndex(x => { return x.rela_stage && x.rela_stage.sid === stage.id; })) throw '该期已关联成本分析,请勿删除';
+                    if (analysisStages.findIndex(x => { return x.rela_stage && x.rela_stage.sid === stage.id; }) >= 0) throw '该期已关联成本分析,请勿删除';
                 }
 
                 await ctx.service.costStage.delete(stage_id);
@@ -629,6 +631,7 @@ module.exports = app => {
                         fileext: fileInfo.ext,
                         filesize: Array.isArray(parts.field.size) ? parts.field.size[index] : parts.field.size,
                         filepath,
+                        extra_upload: ctx.costStage.audit_status === audit.common.status.checked,
                     });
                     ++index;
                     if (Array.isArray(parts.field.size) && index < parts.field.size.length) {

+ 42 - 26
app/controller/ledger_controller.js

@@ -366,7 +366,7 @@ module.exports = app => {
 
                 ctx.body = responseData;
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 ctx.body = this.ajaxErrorBody(err);
             }
 
@@ -394,7 +394,7 @@ module.exports = app => {
                 }
                 responseData.data = await ctx.service.ledger.locateNode(tenderId, data.id);
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 responseData.err = 1;
                 responseData.msg = err;
             }
@@ -426,7 +426,7 @@ module.exports = app => {
                 const expandData = await ctx.service.ledger.getPosterityByParentId(tenderId, data.id);
                 responseData.data = { expand: expandData };
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 responseData.err = 1;
                 responseData.msg = err;
             }
@@ -458,6 +458,9 @@ module.exports = app => {
                 const ancillaryGclData = this.ctx.tender.data.measure_type === measureType.tz.value
                     ? await ctx.service.ancillaryGcl.getAllDataByCondition({ where: { tid: ctx.tender.id } })
                     : [];
+                const ancGclDetailData = this.ctx.tender.data.measure_type === measureType.tz.value
+                    ? await ctx.service.ancillaryGclDetail.getAllDataByCondition({ where: { tid: ctx.tender.id } })
+                    : [];
                 if (this.ctx.tender.data.measure_type === measureType.tz.value) {
                     const calcTemplate = await ctx.service.ledgerExtra.getCalcTemplateData(ctx.tender.id);
                     this.ctx.helper.assignRelaData(ledgerData, [
@@ -471,12 +474,12 @@ module.exports = app => {
                 const ledgerTemplates = await this.ctx.service.ledgerTemplate.getAllTemplate(this.ctx.session.sessionProject.id);
                 ctx.body = {
                     err: 0, msg: '', data: {
-                        bills: ledgerData, pos: posData, ancGcl: ancillaryGclData, posCalcDetail: posCalcDetailData,
+                        bills: ledgerData, pos: posData, ancGcl: ancillaryGclData, posCalcDetail: posCalcDetailData, ancGclDetail: ancGclDetailData,
                         tags: ledgerTags, ledgerTemplates,
                     }
                 };
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 ctx.body = { err: 1, msg: err.toString(), data: [] };
             }
         }
@@ -501,7 +504,7 @@ module.exports = app => {
                 ], this.ctx.tender.info.decimal);
                 ctx.body = { err: 0, msg: '', data: checkData.checkResult };
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 ctx.body = this.ajaxErrorBody(err, '检查数据错误');
             }
         }
@@ -513,7 +516,20 @@ module.exports = app => {
                 const responseData = await ctx.service.ancillaryGcl.updateDatas(data);
                 ctx.body = { err: 0, msg: '', data: responseData };
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
+
+        async ancGclDetailUpdate(ctx) {
+            try {
+                await this.checkMeasureType(measureType.tz.value);
+                const data = JSON.parse(ctx.request.body.data);;
+                const responseData = await ctx.service.ancillaryGclDetail.updateDatas(data);
+                ctx.body = { err: 0, msg: '', data: responseData };
+            } catch (err) {
+                ctx.log(err);
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
@@ -525,7 +541,7 @@ module.exports = app => {
                 const responseData = await ctx.service.posCalcDetail.updateDatas(data);
                 ctx.body = { err: 0, msg: '', data: responseData };
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
@@ -543,7 +559,7 @@ module.exports = app => {
                 const responseData = await ctx.service.pos.savePosData(data, ctx.tender.id);
                 ctx.body = { err: 0, msg: '', data: responseData };
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
@@ -560,7 +576,7 @@ module.exports = app => {
                 const result = await ctx.service.pos.pastePosData(data, ctx.tender.id);
                 ctx.body = { err: 0, msg: '', data: result };
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
@@ -590,7 +606,7 @@ module.exports = app => {
                 }
                 ctx.body = responseData;
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
@@ -676,7 +692,7 @@ module.exports = app => {
         //         }
         //         ctx.body = responseData;
         //     } catch (err) {
-        //         this.log(err);
+        //         ctx.log(err);
         //         // 失败需要消耗掉stream 以防卡死
         //         if (stream) await sendToWormhole(stream);
         //         ctx.body = {err: 1, msg: err.toString(), data: null};
@@ -699,7 +715,7 @@ module.exports = app => {
                 const ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
                 ctx.body = { err: 0, msg: '', data: { bills: ledgerData } };
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
@@ -713,7 +729,7 @@ module.exports = app => {
                 const ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
                 ctx.body = { err: 0, msg: '', data: { bills: ledgerData } };
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
@@ -741,7 +757,7 @@ module.exports = app => {
                         fs.unlinkSync(fileName);
                     }
                 } catch (err) {
-                    this.log(err);
+                    ctx.log(err);
                     this.setMessage(err.toString(), this.messageType.ERROR);
                 }
             }
@@ -764,7 +780,7 @@ module.exports = app => {
                 };
                 await this.layout('ledger/bwtz.ejs', renderData);
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 ctx.redirect(ctx.request.header.referer);
             }
         }
@@ -786,7 +802,7 @@ module.exports = app => {
                 // }
                 ctx.body = { err: 0, msg: '', data: result };
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 ctx.body = this.ajaxErrorBody(err, '加载合同支付数据错误');
             }
         }
@@ -806,7 +822,7 @@ module.exports = app => {
 
                 await this.layout('ledger/gather.ejs', renderData);
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 await this.redirect(ctx.request.header.referer);
             }
         }
@@ -850,7 +866,7 @@ module.exports = app => {
                     bills: billsData, pos: posData, ancGcl, dealBills, change, spec: {zlj: zlj, jrg: stdConst.jrg},
                 }};
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 ctx.body = { err: 1, msg: err.toString(), data: [] };
             }
         }
@@ -878,7 +894,7 @@ module.exports = app => {
                 await ctx.service.ledgerAudit.loadLedgerAuditViewData(tender);
                 ctx.body = {err: 0, msg: '', data: tender};
             } catch (error) {
-                this.log(error);
+                ctx.log(error);
                 ctx.body = { err: 1, msg: error.toString(), data: null };
             }
         }
@@ -934,7 +950,7 @@ module.exports = app => {
                 }
                 responseData.data = files;
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 // 失败需要消耗掉stream 以防卡死
                 if (stream) {
                     await sendToWormhole(stream);
@@ -979,7 +995,7 @@ module.exports = app => {
                         throw '不存在该文件';
                     }
                 } catch (err) {
-                    this.log(err);
+                    ctx.log(err);
                     this.setMessage(err.toString(), this.messageType.ERROR);
                 }
             }
@@ -1007,7 +1023,7 @@ module.exports = app => {
                         fileInfo.filepath && (responseData.data = { filepath });
                     }
                 } catch (error) {
-                    this.log(error);
+                    ctx.log(error);
                     this.setMessage(error.toString(), this.messageType.ERROR);
                     responseData.err = 1;
                     responseData.msg = error.toString();
@@ -1099,7 +1115,7 @@ module.exports = app => {
                 attData.in_time = moment(attData.in_time * 1000).format('YYYY-MM-DD');
                 responseData.data = attData;
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 // 失败需要消耗掉stream 以防卡死
                 if (stream) {
                     await sendToWormhole(stream);
@@ -1151,7 +1167,7 @@ module.exports = app => {
                 });
                 // fs的错误不能被try catch捕捉
                 readStream.on('error', err => {
-                    this.log(err);
+                    ctx.log(err);
                     if (fs.existsSync(path.resolve(this.app.baseDir, zipPath))) {
                         fs.unlinkSync(path.resolve(this.app.baseDir, zipPath));
                     }
@@ -1160,7 +1176,7 @@ module.exports = app => {
                     ctx.body = responseData;
                 });
             } catch (err) {
-                this.log(err);
+                ctx.log(err);
                 if (fs.existsSync(path.resolve(this.app.baseDir, zipPath))) {
                     fs.unlinkSync(path.resolve(this.app.baseDir, zipPath));
                 }

+ 1 - 1
app/lib/sum_load.js

@@ -411,7 +411,7 @@ class gatherStageGclTree extends loadGclBaseTree {
             let end_contract_qty = node.contract_qty;
             activeQty = this.ctx.helper.add(activeQty, node.pre_qc_minus_qty);
             end_contract_qty = this.ctx.helper.add(end_contract_qty, node.pre_contract_qty);
-            const end_contract_tp = activeQty ? node.total_price : this.ctx.helper.mul(this.ctx.helper.div(end_contract_qty, activeQty), node.total_price, info.decimal.tp);
+            const end_contract_tp = activeQty ? this.ctx.helper.mul(this.ctx.helper.div(end_contract_qty, activeQty), node.total_price, info.decimal.tp) : node.total_price;
             return this.ctx.helper.sub(end_contract_tp, node.pre_contract_tp);
         } else if (info.calc_type === 'up') {
             if (correct) {

+ 95 - 24
app/public/js/contract_detail.js

@@ -369,6 +369,10 @@ $(document).ready(function() {
                             if (cp.uid === user_id && (!pay_shenpi_status || (pay_shenpi_status && (cp.status === auditConst.status.uncheck || cp.status === auditConst.status.checkNo)))) {
                                 $('#htpay-table tbody tr').eq(i).find('.pay-edit').show();
                                 $('#htpay-table tbody tr').eq(i).find('.pay-del').show();
+                            } else if (pay_shenpi_status && cp.status === auditConst.status.checked && is_admin) {
+                                $('#htpay-table tbody tr').eq(i).find('.pay-del').show();
+                            } else if (pay_shenpi_status && (cp.status === auditConst.status.checking || cp.status === auditConst.status.checkNoPre) && cp.curAuditorIds && cp.curAuditorIds.length > 0 && cp.curAuditorIds.indexOf(user_id) !== -1) {
+                                $('#htpay-table tbody tr').eq(i).find('.pay-edit').show();
                             }
                         }
                     }
@@ -509,18 +513,25 @@ $(document).ready(function() {
             let paysHtml = '';
             const newPays = pays.map(pay => {
                 let showEdit = false;
+                let showDel = false;
                 if (pay.uid === user_id && !node.settle_code && (!pay_shenpi_status || (pay_shenpi_status && (pay.status === auditConst.status.uncheck || pay.status === auditConst.status.checkNo)))) {
-                    showEdit = true
-                }
-                return {...pay, showEdit}
+                    showEdit = true;
+                    showDel = true;
+                } else if (!node.settle_code && pay_shenpi_status && pay.status === auditConst.status.checked && is_admin) {
+                    showDel = true;
+                } else if (!node.settle_code && pay_shenpi_status && (pay.status === auditConst.status.checking || pay.status === auditConst.status.checkNoPre) && pay.curAuditorIds && pay.curAuditorIds.length > 0 && pay.curAuditorIds.indexOf(user_id) !== -1) {
+                    showEdit = true;
+                }
+                return {...pay, showEdit, showDel}
             })
             console.log(pays);
             newPays.forEach((pay, idx) => {
-                const shenpi_html = setPayShenpiHtml(pay);
-                const operationHtml = !pay.fpcid ? `<a href="javascript:void(0);" class="text-primary pay-edit" data-id="${pay.id}" ${!pay.showEdit ? `style="display:none"` : ''}>编辑</a> <a href="javascript:void(0);" class="text-danger pay-del" data-id="${pay.id}" ${!pay.showEdit ? `style="display:none"` : ''}>删除</a>` : '';
+                const shenpi_html = setPayShenpiHtml(pay, false, node);
+                const operationHtml = !pay.fpcid ? `<a href="javascript:void(0);" class="text-primary pay-edit" data-id="${pay.id}" ${!pay.showEdit ? `style="display:none"` : ''}>编辑</a> <a href="javascript:void(0);" class="text-danger pay-del" data-id="${pay.id}" ${!pay.showDel ? `style="display:none"` : ''}>删除</a>` : '';
                 paysHtml += `<tr class="text-center" data-cpid="${pay.id}">
                                         <td style="position: relative">${idx + 1}${ pay.fpcid ? '<a href="javascript:void(0);" style="position: absolute;right: 2px;top:50%;transform: translate(-50%, -50%);"><i class="fa fa-cny"></i></a>' : ''}</td>
                                         <td>${moment(pay.pay_time).format('YYYY-MM-DD')}</td>
+                                        <td>${pay.used}</td>
                                         <td>${pay.pay_price}</td>
                                         <td>${pay.debit_price}</td>
                                         <td>${pay.yf_price}</td>
@@ -536,7 +547,7 @@ $(document).ready(function() {
             });
             $('#htpay-table tbody').html(paysHtml);
         },
-        setContract: function (sheet) {
+        setContract: function (sheet, needPost = true) {
             const node = SpreadJsObj.getSelectObject(sheet);
 
             if (node && node.c_code) {
@@ -554,13 +565,15 @@ $(document).ready(function() {
                     $('#htdetail_' + c).text(node[c] || '');
                 }
                 $('#htdetail_df_price').text(ZhCalc.sub(node.yf_price, node.sf_price) || '');
-                postData(window.location.pathname + '/update', {postType: 'get-contract', postData: node.id}, function (result) {
-                    const refreshNode = contractTree.loadPostData({ update: result.contract });
-                    contractTreeSpreadObj.refreshTree(contractSheet, refreshNode);
-                    contractTreeSpreadObj.setContractPays(result.pays, node);
-                    contractTreeSpreadObj.setContractFiles(result.files, node.id);
-                    contractTreeSpreadObj.setContractBtn(result.contract);
-                });
+                if (needPost) {
+                    postData(window.location.pathname + '/update', {postType: 'get-contract', postData: node.id}, function (result) {
+                        const refreshNode = contractTree.loadPostData({ update: result.contract });
+                        contractTreeSpreadObj.refreshTree(contractSheet, refreshNode);
+                        contractTreeSpreadObj.setContractPays(result.pays, node);
+                        contractTreeSpreadObj.setContractFiles(result.files, node.id);
+                        contractTreeSpreadObj.setContractBtn(result.contract);
+                    });
+                }
             } else {
                 $('#htdetail-table').hide();
                 $('#htpay-table').hide();
@@ -1761,6 +1774,7 @@ $(document).ready(function() {
                 contractTreeSpreadObj.refreshTree(contractSheet, refreshNode);
                 contractTreeSpreadObj.setContractPays(result.pays, node);
                 contractTreeSpreadObj.checkCloseStatus(node);
+                contractTreeSpreadObj.setContract(contractSheet, false);
             })
         }, '确认删除该合同' + contractConst.typeName[contract_type] + '?');
     });
@@ -1796,17 +1810,26 @@ $(document).ready(function() {
             toastr.error('未找到该合同' + contractConst.typeName[contract_type]);
             return
         }
+        if (node.settle_code) {
+            toastr.warning('已结算的合同不能编辑合同' + contractConst.typeName[contract_type]);
+            return;
+        }
+        let only_sf_edit = false;
+        if (pay_shenpi_status && (cpInfo.status === auditConst.status.checking || cpInfo.status === auditConst.status.checkNoPre) && cpInfo.curAuditorIds && cpInfo.curAuditorIds.length > 0 && cpInfo.curAuditorIds.indexOf(user_id) > -1) {
+            only_sf_edit = true;
+        }
         $('#cons-addpay .modal-title').text('编辑' + contractConst.typeName[contract_type]);
         $('#cons-addpay input[name="cpid"]').val(cpid);
         $('#add-contract-pay').hide();
         $('#save-contract-pay').show();
-        setPayModalInfo(cpInfo);
+        setPayModalInfo(cpInfo, only_sf_edit);
         $('#cons-addpay').modal('show');
     });
 
-    function setPayModalInfo(cpInfo = null) {
+    function setPayModalInfo(cpInfo = null, only_sf_edit = false) {
         $('#cons-addpay input[name="pay_time"]').val(cpInfo ? moment(cpInfo.pay_time).format('YYYY-MM-DD') : '');
         payTime.selectDate(cpInfo ? new Date(cpInfo.pay_time) : '');
+        $('#cons-addpay select[name="used"]').val(cpInfo ? cpInfo.used : '合同');
         $('#cons-addpay input[name="pay_price"]').val(cpInfo ? cpInfo.pay_price : '');
         $('#cons-addpay input[name="debit_price"]').val(cpInfo ? cpInfo.debit_price : '');
         $('#cons-addpay input[name="yf_price"]').val(cpInfo ? cpInfo.yf_price : '');
@@ -1815,7 +1838,24 @@ $(document).ready(function() {
         $('#cons-addpay textarea[name="remark"]').val(cpInfo ? cpInfo.remark : '');
         $('#cons-addpay .yf-tips').text('');
         $('#cons-addpay .sf-tips').text('');
-
+        if (only_sf_edit) {
+            // 用readOnly
+            $('#cons-addpay input[name="pay_time"]').attr('readonly', true);
+            $('#cons-addpay select[name="used"]').attr('disabled', true);
+            $('#cons-addpay input[name="pay_price"]').attr('readonly', true);
+            $('#cons-addpay input[name="debit_price"]').attr('readonly', true);
+            $('#cons-addpay input[name="yf_price"]').attr('readonly', true);
+            $('#cons-addpay select[name="pay_type"]').attr('disabled', true);
+            $('#cons-addpay textarea[name="remark"]').attr('readonly', true);
+        } else {
+            $('#cons-addpay input[name="pay_time"]').removeAttr('readonly');
+            $('#cons-addpay select[name="used"]').removeAttr('readonly');
+            $('#cons-addpay input[name="pay_price"]').removeAttr('readonly');
+            $('#cons-addpay input[name="debit_price"]').removeAttr('readonly');
+            $('#cons-addpay input[name="yf_price"]').removeAttr('readonly');
+            $('#cons-addpay select[name="pay_type"]').removeAttr('readonly');
+            $('#cons-addpay textarea[name="remark"]').removeAttr('readonly');
+        }
     }
 
     // 上传附件
@@ -2212,10 +2252,6 @@ $(document).ready(function() {
             toastr.error('请选择一个合同节点');
             return;
         }
-        if (!(node.uid === user_id || permission_add_pay)) {
-            toastr.error('没有权限添加合同' + contractConst.typeName[contract_type]);
-            return;
-        }
         if (node.settle_code) {
             toastr.error('该合同已结算,不能修改');
             return;
@@ -2228,6 +2264,18 @@ $(document).ready(function() {
         const cpid = $('#cons-addpay input[name="cpid"]').val();
         if (cpid) {
             const cpInfo = _.find(contractPays, { id: parseInt(cpid) });
+            if (!cpInfo) {
+                toastr.error('未找到' + contractConst.typeName[contract_type] + '信息');
+                return;
+            }
+            let only_sf_edit = false;
+            if (pay_shenpi_status && (cpInfo.status === auditConst.status.checking || cpInfo.status === auditConst.status.checkNoPre) && cpInfo.curAuditorIds && cpInfo.curAuditorIds.length > 0 && cpInfo.curAuditorIds.indexOf(user_id) > -1) {
+                only_sf_edit = true;
+            }
+            if (!(node.uid === user_id || permission_add_pay || only_sf_edit)) {
+                toastr.error('没有权限编辑合同' + contractConst.typeName[contract_type]);
+                return;
+            }
             const newNodePayPrice = ZhCalc.add(ZhCalc.sub(node.pay_price, cpInfo.pay_price), parseFloat(pay_price));
             const newNodeDebitPrice = ZhCalc.add(ZhCalc.sub(node.debit_price, cpInfo.debit_price), parseFloat(debit_price));
             const newNodeYfPrice = ZhCalc.sub(newNodePayPrice, newNodeDebitPrice);
@@ -2242,6 +2290,10 @@ $(document).ready(function() {
                 $('#cons-addpay .yf-tips').text('');
             }
         } else {
+            if (!(node.uid === user_id || permission_add_pay)) {
+                toastr.error('没有权限添加合同' + contractConst.typeName[contract_type]);
+                return;
+            }
             if (name !== 'sf_price') {
                 $('#cons-addpay input[name="sf_price"]').val(yf_price);
                 sf_price = yf_price;
@@ -2282,6 +2334,7 @@ $(document).ready(function() {
         }
         const data = {
             pay_time: $('#cons-addpay input[name="pay_time"]').val(),
+            used: $('#cons-addpay select[name="used"]').val(),
             pay_price: $('#cons-addpay input[name="pay_price"]').val() || 0,
             debit_price: $('#cons-addpay input[name="debit_price"]').val() || 0,
             yf_price: $('#cons-addpay input[name="yf_price"]').val() || 0,
@@ -2299,6 +2352,7 @@ $(document).ready(function() {
             contractTreeSpreadObj.refreshTree(contractSheet, refreshNode);
             contractTreeSpreadObj.setContractPays(result.pays, node);
             contractTreeSpreadObj.checkCloseStatus(node);
+            contractTreeSpreadObj.setContract(contractSheet, false);
             // const selection = contractSheet.getSelections();
             // const sel = selection ? selection[0] : contractSheet.getSelections()[0];
             // const row = sel ? sel.row : -1;
@@ -2323,13 +2377,18 @@ $(document).ready(function() {
             toastr.error('未找到该合同' + contractConst.typeName[contract_type]);
             return
         }
-        if (cpInfo.uid !== user_id) {
-            toastr.error('只能编辑自己的合同' + contractConst.typeName[contract_type]);
+        let only_sf_edit = false;
+        if (pay_shenpi_status && (cpInfo.status === auditConst.status.checking || cpInfo.status === auditConst.status.checkNoPre) && cpInfo.curAuditorIds && cpInfo.curAuditorIds.length > 0 && cpInfo.curAuditorIds.indexOf(user_id) > -1) {
+            only_sf_edit = true;
+        }
+        if (!(cpInfo.uid === user_id || only_sf_edit)) {
+            toastr.error('只能编辑自己创建的合同' + contractConst.typeName[contract_type]);
             return;
         }
         const data = {
             id: cpInfo.id,
             pay_time: $('#cons-addpay input[name="pay_time"]').val(),
+            used: $('#cons-addpay select[name="used"]').val(),
             pay_price: $('#cons-addpay input[name="pay_price"]').val() || 0,
             debit_price: $('#cons-addpay input[name="debit_price"]').val() || 0,
             yf_price: $('#cons-addpay input[name="yf_price"]').val() || 0,
@@ -2346,6 +2405,7 @@ $(document).ready(function() {
             const refreshNode = contractTree.loadPostData(result.node);
             contractTreeSpreadObj.refreshTree(contractSheet, refreshNode);
             contractTreeSpreadObj.setContractPays(result.pays, node);
+            contractTreeSpreadObj.setContract(contractSheet, false);
             // const selection = contractSheet.getSelections();
             // const sel = selection ? selection[0] : contractSheet.getSelections()[0];
             // const row = sel ? sel.row : -1;
@@ -2378,7 +2438,7 @@ $(document).ready(function() {
             return false;
         }
         if (data.sf_price && !/^\d+(\.\d+)?$/.test(data.sf_price)) {
-            toastr.error('实'+ name +'金额只能输入数字');
+            toastr.error('实'+ name +'金额汇总需要大于等于0');
             return false;
         }
         if (cpInfo) {
@@ -2522,9 +2582,13 @@ $(document).ready(function() {
         });
     });
 
-    function setPayShenpiHtml(pay, reload = false) {
+    function setPayShenpiHtml(pay, reload = false, contract = null) {
         let shenpi_html = '';
         if (pay_shenpi_status) {
+            if (shenpi_status && contract && contract.status !== auditConst.status.checked) {
+                shenpi_html += `<td class="text-secondary">合同未完成审批</td>`;
+                return shenpi_html;
+            }
             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>`;
@@ -2929,6 +2993,13 @@ $(document).ready(function() {
             });
         });
 
+        $('#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) {
+                setStartAuditChange(node, result);
+            });
+        });
+
         function setStartAuditChange(node, result) {
             if (node.cid) {
                 contractPays.splice(contractPays.findIndex(item => item.id === node.id), 1, result);

+ 94 - 64
app/public/js/contract_setting.js

@@ -7,66 +7,109 @@
  * @date 2019/3/19
  * @version
  */
-function setTypeTable(types, type) {
-    $('#'+ type + '-type-table').empty();
+function setTypeTable(types, type, fun = 'type') {
+    $('#'+ type + '-'+ fun +'-table').empty();
+    if (fun === 'used') {
+        $('#' + type + '-' + fun + '-table').append(`<tr>
+                                                <td>合同</td>
+                                                <td></td>
+                                            </tr>`);
+    }
     types.forEach(t => {
         const typeRow = `<tr>
                                     <td><input class="form-control form-control-sm" name="value" placeholder="请输入值" value="${t}"></td>
                                     <td>
-                                        <a href="javascript:void(0);" class="btn btn-sm text-danger remove-type-btn"><i class="fa fa-remove"></i></a>
+                                        <a href="javascript:void(0);" class="btn btn-sm text-danger remove-${fun}-btn"><i class="fa fa-remove"></i></a>
                                     </td>
                                 </tr>`;
-        $('#'+ type + '-type-table').append(typeRow);
+        $('#'+ type + '-'+ fun +'-table').append(typeRow);
     });
 }
 $(document).ready(() => {
     autoFlashHeight();
-    // setTypeTable(types_from === 'subProject' ? subProject_types : tender_types);
-    // $('#bd-set-1').on('show.bs.modal', function () {
-    //     $('#type-table').empty();
-    //     // 看url上是否带有tender
-    //     const is_tender = window.location.pathname.includes('tender') ? 1 : 0;
-    //     console.log(window.location, is_tender);
-    //     postData(`/sp/${spid}/contract/audit/save`, { type: 'get-contract-type', is_tender }, function (types) {
-    //         setTypeTable(types);
-    //     });
-    // });
-    $('#addType').click(function(){
-        const type = $('#types-tabs a.active').attr('type');
-        const newType = `<tr>
+    const funs = ['type', 'used'];
+    for (const fun of funs) {
+        $('#add-' + fun + '-btn').click(function(){
+            const type = $('#' + fun + '-tabs a.active').attr('type');
+            const newType = `<tr>
                                 <td><input class="form-control form-control-sm" name="value" placeholder="请输入值" value=""></td>
                                 <td>
-                                    <a href="javascript:void(0);" class="btn btn-sm text-danger remove-type-btn"><i class="fa fa-remove"></i></a>
+                                    <a href="javascript:void(0);" class="btn btn-sm text-danger remove-${fun}-btn"><i class="fa fa-remove"></i></a>
                                 </td>
                             </tr>`;
-        $('#'+ type + '-type-table').append(newType);
-    });
+            $('#'+ type + '-' + fun + '-table').append(newType);
+        });
 
-    $('body').on('click', '.remove-type-btn', function() {
-        const input = $(this).parents('td').siblings('td').eq(0).children('input');
-        input.attr('disabled', true);
-        // 文字加删除线并移除foucs
-        if (input.val() === '') {
-            input.removeAttr('placeholder');
-        }
-        input.css('text-decoration', 'line-through');
-        input.blur();
-        $(this).remove();
-        checkAndShowTypesBtn();
-    });
+        $('body').on('click', '.remove-' + fun + '-btn', function() {
+            const input = $(this).parents('td').siblings('td').eq(0).children('input');
+            input.attr('disabled', true);
+            // 文字加删除线并移除foucs
+            if (input.val() === '') {
+                input.removeAttr('placeholder');
+            }
+            input.css('text-decoration', 'line-through');
+            input.blur();
+            $(this).remove();
+            checkAndShowTypesBtn(fun);
+        });
 
-    $('body').on('blur', '#contract-types-set input[name="value"]', function() {
-        checkAndShowTypesBtn();
-    });
+        $('body').on('blur', '#contract-' + fun + '-set input[name="value"]', function() {
+            checkAndShowTypesBtn(fun);
+        });
 
-    function checkAndShowTypesBtn(return_type = false) {
+        $('#set-' + fun + '-btn').click(function() {
+            const { new_subProject_types, new_tender_types } = checkAndShowTypesBtn(fun, true);
+            const data = {
+                type: 'set-contract-' + fun,
+            }
+            if (fun === 'type') {
+                data.contract_type = {
+                    contract_type: new_subProject_types,
+                    tender_contract_type: new_tender_types
+                };
+            } else if (fun === 'used') {
+                data.contract_used = {
+                    contract_used: new_subProject_types,
+                    tender_contract_used: new_tender_types
+                }
+            }
+            postData(`/sp/${spid}/contract/audit/save`, data, function (res) {
+                toastr.success('设置成功');
+                if (fun === 'type') {
+                    tender_type = new_tender_types;
+                    subProject_type = new_subProject_types;
+                } else if (fun === 'used') {
+                    tender_used = new_tender_types;
+                    subProject_used = new_subProject_types;
+                }
+                setTypeTable(new_tender_types, 'tender', fun);
+                setTypeTable(new_subProject_types, 'subProject', fun);
+                checkAndShowTypesBtn(fun);
+            });
+        });
+
+        $('#cancel-' + fun + '-btn').click(function() {
+            toastr.warning('已取消改动');
+            if (fun === 'type') {
+                setTypeTable(tender_type, 'tender', fun);
+                setTypeTable(subProject_type, 'subProject', fun);
+            } else if (fun === 'used') {
+                setTypeTable(tender_used, 'tender', fun);
+                setTypeTable(subProject_used, 'subProject', fun);
+            }
+            checkAndShowTypesBtn(fun);
+        });
+    }
+
+    function checkAndShowTypesBtn(fun, return_type = false) {
         const new_subProject_types = [];
         const new_tender_types = [];
         const type = ['subProject', 'tender'];
         for (const t of type) {
-            $('#'+ t + '-type-table tr').each(function () {
+            $('#'+ t + '-' + fun + '-table tr').each(function () {
                 const input = $(this).find('input[name="value"]');
-                if (!input.prop('disabled')) {
+                console.log(input.val());
+                if (input && input.val() !== undefined && !input.prop('disabled')) {
                     const value = input.val().trim();
                     if (value) {
                         if (t === 'subProject') {
@@ -78,12 +121,19 @@ $(document).ready(() => {
                 }
             });
         }
-        console.log(new_tender_types, tender_types, new_subProject_types, subProject_types);
-        // 用lodash对比数组内容是否相同
-        if (_.isEqual(new_tender_types.sort(), tender_types.sort()) && _.isEqual(new_subProject_types.sort(), subProject_types.sort())) {
-            $('#show-type-btn').hide();
-        } else {
-            $('#show-type-btn').show();
+        if (fun === 'type') {
+            // 判断数组是否相同,但别改变原数组顺序
+            if (_.isEqual(_.sortBy(new_tender_types), _.sortBy(tender_type)) && _.isEqual(_.sortBy(new_subProject_types), _.sortBy(subProject_type))) {
+                $('#show-'+ fun +'-btn').hide();
+            } else {
+                $('#show-' + fun + '-btn').show();
+            }
+        } else if (fun === 'used') {
+            if (_.isEqual(_.sortBy(new_tender_types), _.sortBy(tender_used)) && _.isEqual(_.sortBy(new_subProject_types), _.sortBy(subProject_used))) {
+                $('#show-'+ fun +'-btn').hide();
+            } else {
+                $('#show-' + fun + '-btn').show();
+            }
         }
         if (return_type) {
             return {
@@ -93,26 +143,6 @@ $(document).ready(() => {
         }
     }
 
-    $('#set-type-btn').click(function() {
-        const { new_subProject_types, new_tender_types } = checkAndShowTypesBtn(true);
-        postData(`/sp/${spid}/contract/audit/save`, { type: 'set-contract-type', contract_type: { contract_type: new_subProject_types, tender_contract_type: new_tender_types } }, function (res) {
-            toastr.success('设置成功');
-            tender_types = new_tender_types;
-            subProject_types = new_subProject_types;
-            setTypeTable(tender_types, 'tender');
-            setTypeTable(subProject_types, 'subProject');
-            checkAndShowTypesBtn();
-        });
-    });
-
-    $('#cancel-type-btn').click(function() {
-        toastr.warning('已取消改动');
-        // const is_tender = $('#types-tabs a.active').attr('type') === 'subPorjct' ? 0 : 1;
-        setTypeTable(tender_types, 'tender');
-        setTypeTable(subProject_types, 'subProject');
-        checkAndShowTypesBtn();
-    });
-
     $('#contract-shenpi-set input[type="checkbox"]').change(function() {
         postData(`/sp/${spid}/contract/setting/update`, { type: 'page_show',
             openContractSubProjectShenpi: $('#openContractSubProjectShenpi')[0].checked,

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

@@ -626,7 +626,7 @@ $(document).ready(function() {
         subMasterKey: 'rela_sub_id',
         uploadUrl: 'file/upload',
         deleteUrl: 'file/delete',
-        checked: false,
+        checked: costStageComplete,
         zipName: `附件.zip`,
         readOnly: false,
         fileIdType: 'string',

+ 34 - 38
app/public/js/cost_stage_ledger.js

@@ -22,7 +22,7 @@ $(document).ready(function() {
                 isLeaf: 'tree_is_leaf',
                 fullPath: 'tree_full_path',
                 rootId: -1,
-                calcFields: ['cur_tp', 'pre_tp', 'end_tp'],
+                calcFields: ['yf_tp', 'sf_tp', 'pay_tp', 'cut_tp'],
                 keys: ['id', 'stage_id', 'tree_id'],
             };
             this.tree = createNewPathTree('ledger', this.treeSetting);
@@ -335,13 +335,13 @@ $(document).ready(function() {
                     if (['up-move', 'down-move'].indexOf(type) > -1) {
                         if (sel) {
                             sheet.setSelection(tree.nodes.indexOf(node), sel.col, sel.rowCount, sel.colCount);
-                            // SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, tree.nodes.indexOf(node)]);
+                            SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, tree.nodes.indexOf(node)]);
                         }
                     } else if (type === 'add') {
                         const sel = sheet.getSelections()[0];
                         if (sel) {
                             sheet.setSelection(tree.nodes.indexOf(refreshData.create[0]), sel.col, sel.rowCount, sel.colCount);
-                            // SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, tree.nodes.indexOf(refreshData.create[0])]);
+                            SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, tree.nodes.indexOf(refreshData.create[0])]);
                         }
                     }
                     self.refreshOperationValid();
@@ -512,6 +512,7 @@ $(document).ready(function() {
                     {title: '合同编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 160, formatter: '@', readOnly: true},
                     {title: '合同名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 160, formatter: '@', readOnly: true},
                     {title: '乙方', colSpan: '1', rowSpan: '1', field: 'party_b', hAlign: 0, width: 150, formatter: '@', readOnly: true},
+                    {title: '支付年月', colSpan: '1', rowSpan: '1', field: 'pay_date', hAlign: 1, width: 120, formatter: '@', readOnly: true},
                     {title: '合同金额', colSpan: '1', rowSpan: '1', field: 'deal_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true},
                     {title: '税率(%)', colSpan: '1', rowSpan: '1', field: 'tax', hAlign: 2, width: 80, type: 'Number', readOnly: true},
                     {title: '付款金额', colSpan: '1', rowSpan: '1', field: 'pay_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true},
@@ -549,7 +550,6 @@ $(document).ready(function() {
                             getTagHtml: function (index, data) {
                                 if (!data) return;
                                 const getHtml = function (list) {
-                                    console.log(list);
                                     if (!list || list.length === 0) return '';
                                     const html = [];
                                     for (const l of list) {
@@ -800,6 +800,13 @@ $(document).ready(function() {
         clipboardPasting(e, info) {
             info.cancel = true;
 
+            const relaBills = detailObj.billsNode;
+            if (!relaBills) {
+                toastr.error('数据错误,请选择台账节点后再试');
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                return;
+            }
+
             const pasteData = info.pasteData.html
                 ? SpreadJsObj.analysisPasteHtml(info.pasteData.html)
                 : (info.pasteData.text === ''
@@ -808,30 +815,20 @@ $(document).ready(function() {
             const hint = {
                 invalidExpr: {type: 'warning', msg: '粘贴的表达式非法'},
             };
-            const datas = [], filterNodes = [];
+            const updateDatas = [], insertDatas = [], filterNodes = [], order = info.sheet.zh_data.length > 0 ? info.sheet.zh_data[info.sheet.zh_data.length - 1].d_order : 0;
 
-            // todo
-            let level, filterRow = 0;
             for (let iRow = 0; iRow < info.cellRange.rowCount; iRow ++) {
                 const curRow = info.cellRange.row + iRow;
-                const node = tree.nodes[curRow];
-                if (!node) continue;
+                const node = info.sheet.zh_data[curRow];
+                if (!info.sheet.zh_setting.emptyRows && !node) continue;
 
                 let bPaste = false;
-                const data = info.sheet.zh_tree.getNodeKeyData(node);
+                const data = node ? { id: node.id } : { ledger_id: relaBills.id, cost_id: relaBills.cost_id, d_order: order + insertDatas.length + 1 };
                 for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
                     const curCol = info.cellRange.col + iCol;
                     const colSetting = info.sheet.zh_setting.cols[curCol];
-                    const value = trimInvalidChar(pasteData[iRow-filterRow][iCol]);
-                    if (node.children && node.children.length > 0 && invalidFields.parent.indexOf(colSetting.field) >= 0) {
-                        toastMessageUniq(hint.parent);
-                        continue;
-                    }
+                    const value = trimInvalidChar(pasteData[iRow][iCol]);
 
-                    if (billsObj.checkNodeUsed(tree, node) && colSetting.field === 'unit_price') {
-                        toastMessageUniq (hint.usedUp);
-                        continue;
-                    }
                     if (colSetting.type === 'Number') {
                         const num = _.toNumber(value);
                         if (num) {
@@ -846,22 +843,22 @@ $(document).ready(function() {
                             }
                         }
                     } else {
-                        if (node.used && (colSetting.field ==='b_code') && data[colSetting.field] !== '' && value === '') {
-                            toastMessageUniq(hint.usedCode);
-                            continue;
-                        }
                         data[colSetting.field] = value;
                     }
                     bPaste = true;
                 }
                 if (bPaste) {
-                    datas.push(data);
+                    if (node) {
+                        updateDatas.push(data)
+                    } else {
+                        insertDatas.push(data);
+                    }
                 } else {
                     filterNodes.push(node);
                 }
             }
-            if (datas.length > 0) {
-                postData('update', {target: 'detail', update: datas}, function (result) {
+            if (updateDatas.length > 0 || insertDatas.length > 0) {
+                postData('update', {target: 'detail', update: updateDatas, add: insertDatas.length > 0 ? insertDatas : undefined }, function (result) {
                     detailObj.data.updateDatas(result.detail);
                     detailObj.refreshSheet();
                 }, function () {
@@ -962,6 +959,13 @@ $(document).ready(function() {
             });
             const typePayDate =  $('#type-pay-date').datepicker({}).data('datepicker');
             const selectPayDate =  $('#type-pay-date').datepicker({}).data('datepicker');
+            const afterImport = function(result) {
+                detailObj.data.updateDatas(result.detail);
+                result.ledger.tree_id = curNode.tree_id;
+                const refreshNode = billsObj.tree.loadPostData({ update: result.ledger });
+                billsObj.refreshTree(refreshNode);
+                detailObj.loadDetailData(curNode);
+            };
             $('#import-deal-type-ok').click(function() {
                 const updateData = { target: 'importContract', ledger_id: curNode.id, cost_id: curNode.cost_id, types: $('[name=contract_type]').val(), months: $('#type-pay-date').val() };
                 if (updateData.types.length === 0) {
@@ -973,11 +977,7 @@ $(document).ready(function() {
                     return;
                 }
                 postData('update', updateData, function(result) {
-                    detailObj.data.updateDatas(result.detail);
-                    detailObj.reloadDetailData();
-                    result.ledger.tree_id = curNode.tree_id;
-                    const refreshNode = billsObj.tree.loadPostData({ update: result.ledger });
-                    billsObj.refreshTree(refreshNode);
+                    afterImport(result);
                     $('#import-deal-type').modal('hide');
                 });
             });
@@ -1012,11 +1012,7 @@ $(document).ready(function() {
                     return;
                 }
                 postData('update', updateData, function(result) {
-                    detailObj.data.updateDatas(result.detail);
-                    detailObj.refreshSheet();
-                    result.ledger.tree_id = curNode.tree_id;
-                    const refreshNode = billsObj.tree.loadPostData({ update: result.ledger });
-                    billsObj.refreshTree(refreshNode);
+                    afterImport(result);
                     $('#import-deal-select').modal('hide');
                 });
             });
@@ -1232,7 +1228,7 @@ $(document).ready(function() {
         subMasterKey: 'rela_sub_id',
         uploadUrl: 'file/upload',
         deleteUrl: 'file/delete',
-        checked: false,
+        checked: costStageComplete,
         zipName: `附件.zip`,
         readOnly: false,
         fileIdType: 'string',
@@ -1283,7 +1279,7 @@ $(document).ready(function() {
             billsObj.loadRelaData();
             if (cur.pid) {
                 const pIndex = detailObj.sheet.zh_data.findIndex(x => { return x.id === cur.pid; });
-                SpreadJsObj.locateRow(detailObj.sheet, pIndex);
+                if (pIndex >= 0) SpreadJsObj.locateRow(detailObj.sheet, pIndex);
             }
         },
     });

+ 253 - 82
app/public/js/ledger.js

@@ -128,7 +128,7 @@ $(document).ready(function() {
     const posSheet = posSpread.getActiveSheet();
 
     // 初始化 附属工程量
-    const ancGcl = createAncillaryGcl({ id: 'id', masterId: 'lid', sort: [['g_order', 'asc']] });
+    const ancGcl = createAncillaryGcl({ id: 'id', masterId: 'pid', topId: 'lid', sort: [['g_order', 'asc']] });
     const ancGclSpread = SpreadJsObj.createNewSpread($('#anc-gcl-spread')[0]);
     const ancGclSheet = ancGclSpread.getActiveSheet();
 
@@ -531,7 +531,7 @@ $(document).ready(function() {
         };
         const loadCurDetailData = function() {
             const curAncGcl = SpreadJsObj.getSelectObject(ancGclSheet);
-            template = node ? posCalcTemplate.find(x => { return x.id === node.calc_template }) : null;
+            template = curAncGcl ? posCalcTemplate.find(x => { return x.id === curAncGcl.calc_template }) : null;
             if (template) {
                 const specCol = template.spread_cache.cols.find(x => { return x.field === 'spec'; });
                 if (specCol) {
@@ -553,8 +553,8 @@ $(document).ready(function() {
                 detail.updateDatas(result.detail);
                 reloadCurDetailData();
                 if (result.ancGcl) {
-                    ancGcl.updateDatas(result.ancGcl);
-                    // todo 刷新附属工程量
+                    ancGcl.updateDatas({ update: result.ancGcl });
+                    ancGclObj.loadCurAncillaryGcl();
                 }
             },
             baseOpr: function (type, addCount = 1) {
@@ -569,10 +569,10 @@ $(document).ready(function() {
                 const row = sel[0].row, count = sel[0].rowCount;
                 const first = detailRange[row];
                 if (type === 'insert') {
-                    const node = SpreadJsObj.getSelectObject(posSheet);
+                    const node = SpreadJsObj.getSelectObject(ancGclSheet);
                     data.add = [];
                     for (let i = 1; i <= addCount; i++) {
-                        data.add.push({ lid: node.lid, pid: node.id, pcd_order: detailRange.length + i });
+                        data.add.push({ lid: node.lid, pid: node.pid, ag_id: node.id, agd_order: detailRange.length + i });
                     }
                     if (first) data.select = first.id;
                 } else if (type === 'delete') {
@@ -617,7 +617,7 @@ $(document).ready(function() {
                     if (data.update <= 1) return;
                 }
 
-                postData('/tender/' + getTenderId() + '/anc-gcl-calc/update', data, function(result) {
+                postData('/tender/' + getTenderId() + '/anc-gcl-detail/update', data, function(result) {
                     ctrlObj.afterPostData(result);
                     if (type !== 'delete') SpreadJsObj.locateData(sheet, first);
                 });
@@ -636,9 +636,9 @@ $(document).ready(function() {
                 const orgText = detailData ? detailData[col.field] : '', newText = trimInvalidChar(info.editingText);
                 if (orgText === newText || (!orgText && !newText)) return;
 
-                const pos = ctrlObj.posNode;
-                if (!pos) {
-                    toastr.error('数据错误,请选择计量单元后再试');
+                const ancGcl = ctrlObj.ancGclNode;
+                if (!ancGcl) {
+                    toastr.error('数据错误,请选择附属工程量后再试');
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
                     return;
                 }
@@ -666,7 +666,7 @@ $(document).ready(function() {
                 } else {
                     const sortData = info.sheet.zh_data;
                     const order = (!sortData || sortData.length === 0) ? 1 : Math.max(sortData[sortData.length - 1].pcd_order + 1, sortData.length + 1);
-                    const addData = { lid: pos.lid, pid: pos.id, pcd_order: order };
+                    const addData = { lid: ancGcl.lid, pid: ancGcl.pid, ag_id: ancGcl.id, agd_order: order };
                     if (col.type === 'Number') {
                         const num = _.toNumber(newText);
                         if (!_.isFinite(num)) {
@@ -685,7 +685,7 @@ $(document).ready(function() {
                     }
                     data.add = [addData];
                 }
-                postData('/tender/' + getTenderId() + '/pos-calc/update', data, function (result) {
+                postData('/tender/' + getTenderId() + '/anc-gcl-detail/update', data, function (result) {
                     ctrlObj.afterPostData(result);
                 }, function () {
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -718,7 +718,7 @@ $(document).ready(function() {
                 }
                 if (data.update.length === 0) return;
 
-                postData('/tender/' + getTenderId() + '/pos-calc/update', data, function (result) {
+                postData('/tender/' + getTenderId() + '/anc-gcl-detail/update', data, function (result) {
                     ctrlObj.afterPostData(result);
                 }, function () {
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -726,8 +726,8 @@ $(document).ready(function() {
             },
             clipboardPasting: function(e, info) {
                 info.cancel = true;
-                const relaPos = SpreadJsObj.getSelectObject(posSheet);
-                if (!relaPos) {
+                const relaAncGcl = SpreadJsObj.getSelectObject(ancGclSheet);
+                if (!relaAncGcl) {
                     toastr.error('数据错误,请选择计量单元后再试');
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
                     return;
@@ -771,14 +771,14 @@ $(document).ready(function() {
                         data.update.push(updateData);
                     } else {
                         if (!data.add) data.add = [];
-                        const addData = { lid: relaPos.lid, pid: relaPos.id, pcd_order: curRow + 1};
+                        const addData = { lid: relaAncGcl.lid, pid: relaAncGcl.pid, ag_id: relaAncGcl.id, agd_order: curRow + 1};
                         analysisData(pasteData[iRow], addData);
                         data.add.push(addData);
                     }
                 }
                 if ((!data.update || data.update.length === 0) && (!data.add || data.add.length === 0)) return;
 
-                postData('/tender/' + getTenderId() + '/pos-calc/update', data, function (result) {
+                postData('/tender/' + getTenderId() + '/anc-gcl-detail/update', data, function (result) {
                     ctrlObj.afterPostData(result);
                 }, function () {
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -791,7 +791,7 @@ $(document).ready(function() {
             spread.bind(spreadNS.Events.EditEnded, ctrlObj.editEnded);
             spread.bind(spreadNS.Events.ClipboardPasting, ctrlObj.clipboardPasting);
             $.contextMenu({
-                selector: '#pos-detail-spread',
+                selector: '#anc-gcl-detail-spread',
                 build: function ($trigger, e) {
                     const target = SpreadJsObj.safeRightClickSelection($trigger, e, spread);
                     return target.hitTestType === GC.Spread.Sheets.SheetArea.viewport || target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
@@ -888,7 +888,6 @@ $(document).ready(function() {
         },
         afterLocated:  function (lid, pos_id) {
             posOperationObj.loadCurPosData();
-            ancGclObj.loadCurAncillaryGcl();
             if (pos_id) {
                 const posSheet = posSpread.getActiveSheet();
                 const relaPos = posSheet.zh_data.find(x => { return x.id === pos_id; });
@@ -910,7 +909,6 @@ $(document).ready(function() {
         storeKey: 'ledger-error-' + getTenderId(),
         afterLocated:  function () {
             posOperationObj.loadCurPosData();
-            ancGclObj.loadCurAncillaryGcl();
         },
         afterShow: function () {
             ledgerSpread.refresh();
@@ -929,7 +927,6 @@ $(document).ready(function() {
         checkType: getCheckType(checkOption),
         afterLocated:  function () {
             posOperationObj.loadCurPosData();
-            ancGclObj.loadCurAncillaryGcl();
         },
         afterShow: function () {
             ledgerSpread.refresh();
@@ -1190,7 +1187,6 @@ $(document).ready(function() {
                         self.refreshTree(sheet, refreshNode);
                         self.refreshOperationValid(sheet);
                         posOperationObj.loadCurPosData();
-                        ancGclObj.loadCurAncillaryGcl();
                         billsTag.afterDeleteBills(refreshNode.delete);
                     });
                 });
@@ -1710,7 +1706,6 @@ $(document).ready(function() {
         },
         loadRelaData: function () {
             posOperationObj.loadCurPosData();
-            ancGclObj.loadCurAncillaryGcl();
             posSearch.search($('#pos-keyword').val());
             treeOperationObj.loadExprToInput(ledgerSpread.getActiveSheet());
         },
@@ -1939,22 +1934,41 @@ $(document).ready(function() {
                 for (const b of cbl) {
                     const posRange = pos.getLedgerPos(b.id);
                     if (posRange && posRange.length > 0) b.pos = posRange;
-                    if (b.calc_template) {
-                        const template = posCalcTemplate.find(x => { return x.id === b.calc_template; });
-                        b.calc_template_str = template ? template.name : '';
-                        if (b.pos) {
+
+                    if (b.pos) {
+                        for (p of b.pos) {
+                            p.ancGcl = ancGcl.getPartData(p.id) || [];
+                            for (const ag of p.ancGcl) {
+                                if (ag.calc_template) ag.calcDetail = ancGclDetail.detail.getPartData(ag.id);
+                            }
+                        }
+                        if (b.calc_template) {
+                            const template = posCalcTemplate.find(x => { return x.id === b.calc_template; });
+                            b.calc_template_str = template ? template.name : '';
                             for (const p of b.pos) {
                                 const detailRange = posCalcDetail.detail.getPartData(p.id);
                                 p.calcDetail = detailRange;
                             }
                         }
                     }
-                    const gclRange = ancGcl.getPartData(b.id);
-                    if (gclRange && gclRange.length > 0) b.ancGcl = gclRange;
+
                 }
             }
             return copyBlockList;
-        }
+        },
+        removeCalcTemplate: function(node) {
+            const billsUpdateData = {postType: 'extra', postData: { id: node.id, tender_id: node.tender_id, ledger_id: node.ledger_id, calc_template: '' }};
+            postData(window.location.pathname + '/update', billsUpdateData, function (result) {
+                const refreshNode = ledgerTree.loadPostData(result);
+                treeOperationObj.refreshTree(ledgerSheet, refreshNode);
+                if (result.pos) {
+                    pos.updateDatas(result.pos);
+                    posOperationObj.loadCurPosData(false);
+                }
+                posCalcDetail.detail.removeDatasByTopId(node.id);
+                posCalcDetail.loadCurDetailData();
+            });
+        },
     };
     const ledgerTemplate = $.ledger_template({
         selector: '#ledger-template',
@@ -2165,7 +2179,7 @@ $(document).ready(function() {
     }
 
     const calcTemplateSelect = (function(){
-        let ctrlBills = {}, first = 1;
+        let ctrlNode = {}, first = 1, nodeType = 'bills';
         const tree = createNewPathTree('gather', {
             id: 'tree_id', pid: 'tree_pid', order: 'tree_order',
             level: 'tree_level', isLeaf: 'tree_is_leaf', fullPath: 'tree_full_path',
@@ -2200,12 +2214,13 @@ $(document).ready(function() {
                 return !data.is_calc_template ? defaultFont : 'bold ' + defaultFont;
             },
             getColor: function(sheet, data, row, col, defaultColor) {
-                return data && data.id === ctrlBills.calc_template ? spreadColor.common.invalid : defaultColor;
+                return data && data.id === ctrlNode.calc_template ? spreadColor.common.invalid : defaultColor;
             }
         });
         SpreadJsObj.loadSheetData(sheet, SpreadJsObj.DataType.Tree, tree);
-        const select = function(node) {
-            ctrlBills = node;
+        const select = function(node, type) {
+            nodeType = type || 'bills';
+            ctrlNode = node;
             SpreadJsObj.reloadRowBackColor(sheet, 0, tree.nodes.length);
             $('#select-calc-template').modal('show');
         };
@@ -2216,23 +2231,79 @@ $(document).ready(function() {
                 toastr.warning('请勿选择文件夹');
                 return;
             }
-            if (ct.id === ctrlBills.calc_template) {
+            if (ct.id === ctrlNode.calc_template) {
                 toastr.warning('选择的计算模板与之前相同');
                 return;
             }
-            const updateData = {postType: 'extra', postData: { id: ctrlBills.id, tender_id: ctrlBills.tender_id, ledger_id: ctrlBills.ledger_id, calc_template: ct.id }};
-            postData(window.location.pathname + '/update', updateData, function (result) {
-                const refreshNode = ledgerTree.loadPostData(result);
-                treeOperationObj.refreshTree(ledgerSheet, refreshNode);
-                posCalcDetail.loadCurDetailData();
-                $('#select-calc-template').modal('hide');
-            });
+            switch (nodeType) {
+                case 'ancGcl':
+                    const ancGclUpdateData = { calcTmpl: { id: ctrlNode.id, calc_template: ct.id } };
+                    postData('/tender/' + getTenderId() + '/anc-gcl/update', ancGclUpdateData, function(result) {
+                        ancGcl.updateDatas(result);
+                        ancGclObj.loadCurAncillaryGcl();
+                        ancGclDetail.detail.updateDatas({ update: result.detail });
+                        ancGclDetail.reloadCurDetailData();
+                        $('#select-calc-template').modal('hide');
+                    });
+                    break;
+                case 'bills':
+                default:
+                    const billsUpdateData = {postType: 'extra', postData: { id: ctrlNode.id, tender_id: ctrlNode.tender_id, ledger_id: ctrlNode.ledger_id, calc_template: ct.id }};
+                    postData(window.location.pathname + '/update', billsUpdateData, function (result) {
+                        const refreshNode = ledgerTree.loadPostData(result);
+                        treeOperationObj.refreshTree(ledgerSheet, refreshNode);
+                        posCalcDetail.loadCurDetailData();
+                        $('#select-calc-template').modal('hide');
+                    });
+                    break;
+            }
         });
         $('#select-calc-template').on('shown.bs.modal', function() {
             if (first) {
                 spread.refresh();
                 first = 0;
             }
+            searchObj.clear();
+        });
+        const searchObj = {
+            result: [],
+            cur: 0,
+            search: function() {
+                const keyword = $(`#select-calc-template-sk`).val();
+                searchObj.result = keyword ? tree.nodes.filter(x => {
+                    return x.name.indexOf(keyword) >= 0;
+                }) : [];
+                $(`#select-calc-template-sr`)[0].innerText = `结果:${searchObj.result.length}`;
+                searchObj.cur = 0;
+                if (searchObj.result.length > 0) {
+                    SpreadJsObj.locateTreeNode(sheet, tree.getNodeKey(searchObj.result[searchObj.cur]));
+                }
+            },
+            searchPre: function () {
+                if (searchObj.result.length === 0) return;
+                searchObj.cur = searchObj.cur === 0 ? searchObj.result.length - 1 : this.cur - 1;
+                SpreadJsObj.locateTreeNode(sheet, tree.getNodeKey(searchObj.result[searchObj.cur]), true);
+            },
+            searchNext: function () {
+                if (searchObj.result.length === 0) return;
+                searchObj.cur = searchObj.cur === searchObj.result.length - 1 ? 0 : searchObj.cur + 1;
+                SpreadJsObj.locateTreeNode(sheet, tree.getNodeKey(searchObj.result[searchObj.cur]), true);
+            },
+            clear: function () {
+                $(`#select-calc-template-sk`).val('');
+                $(`#select-calc-template-sr`)[0].innerText = `结果:${0}`;
+                searchObj.result = [];
+                searchObj.cur = 0;
+            }
+        };
+        $('#select-calc-template-sk').change(function() {
+            searchObj.search();
+        });
+        $('#select-calc-template-sp').click(function() {
+            searchObj.searchPre();
+        });
+        $('#select-calc-template-sn').click(function() {
+            searchObj.searchNext();
         });
         return { select }
     })();
@@ -2372,11 +2443,20 @@ $(document).ready(function() {
             disabled: function(key, opt) {
                 const node = SpreadJsObj.getSelectObject(ledgerSheet);
                 return !node || !node.b_code;
+            }
+        };
+        billsContextMenuOptions.items.removeCalcTmpl = {
+            name: '移除计算模板',
+            callback: function(key, opt) {
+                const node = SpreadJsObj.getSelectObject(ledgerSheet);
+                treeOperationObj.removeCalcTemplate(node);
             },
-            visible: function(key, opt) {
-                return !readOnly;
+            disabled: function(key, opt) {
+                const node = SpreadJsObj.getSelectObject(ledgerSheet);
+                return !node || !node.b_code || !node.calc_template;
             }
         };
+        billsContextMenuOptions.items.calcTmplSpr = '----';
         billsContextMenuOptions.items.batchInsert = {
             name: '批量插入',
             type: 'batchInsert',
@@ -2673,7 +2753,6 @@ $(document).ready(function() {
                             SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), 'tree', ledgerTree);
                             pos.loadDatas(result.pos);
                             posOperationObj.loadCurPosData();
-                            ancGclObj.loadCurAncillaryGcl();
                             checkShowLast(result.bills.length);
                         }, null);
                     },
@@ -2984,6 +3063,7 @@ $(document).ready(function() {
             posOperationObj.loadExprToInput();
             posOperationObj.refreshOperationValid(posSpread.getActiveSheet());
             if (refreshDetail) posCalcDetail.loadCurDetailData();
+            ancGclObj.loadCurAncillaryGcl();
         },
         baseOpr: function (sheet, type) {
             const data = {
@@ -3034,6 +3114,7 @@ $(document).ready(function() {
                         const loadResult = ledgerTree.loadPostData(result.ledger);
                         treeOperationObj.refreshTree(ledgerSpread.getActiveSheet(), loadResult);
                         treeOperationObj.refreshOperationValid(ledgerSpread.getActiveSheet());
+                        posOperationObj.loadCurPosData();
                     } else {
                         const updateRst = pos.updateDatas(result.pos);
                         billsTag.refreshPosTagView(updateRst.update);
@@ -3230,6 +3311,7 @@ $(document).ready(function() {
                     const loadResult = ledgerTree.loadPostData(result.ledger);
                     treeOperationObj.refreshTree(ledgerSpread.getActiveSheet(), loadResult);
                     treeOperationObj.refreshOperationValid(ledgerSpread.getActiveSheet());
+                    posOperationObj.loadCurPosData();
                 });
             }
         },
@@ -3358,6 +3440,7 @@ $(document).ready(function() {
             posOperationObj.loadExprToInput();
             posOperationObj.refreshOperationValid(posSpread.getActiveSheet());
             posCalcDetail.loadCurDetailData();
+            ancGclObj.loadCurAncillaryGcl();
         },
         addPegs: function (pegs) {
             if (!pegs || pegs.length <= 0) return;
@@ -3389,6 +3472,10 @@ $(document).ready(function() {
                     const detailRange = posCalcDetail.detail.getPartData(p.id);
                     p.calcDetail = detailRange;
                 }
+                p.ancGcl = ancGcl.getPartData(p.id) || [];
+                for (const ag of p.ancGcl) {
+                    if (ag.calc_template) ag.calcDetail = ancGclDetail.detail.getPartData(ag.id);
+                }
                 iRow++;
             }
             return copyBlockList;
@@ -3414,6 +3501,8 @@ $(document).ready(function() {
                 if (result.posCalcDetail) posCalcDetail.detail.updateDatas(result.posCalcDetail);
                 const ledgerResult = ledgerTree.loadPostData(data.ledger);
                 treeOperationObj.refreshTree(ledgerSheet, ledgerResult);
+                if (result.ancGcl) ancGcl.updateDatas(result.ancGcl);
+                if (result.ancGclDetail) ancGclDetail.detail.updateDatas(result.ancGclDetail);
                 removeLocalCache(copyBlockTag);
                 posOperationObj.loadCurPosData();
             });
@@ -3591,6 +3680,10 @@ $(document).ready(function() {
             {title: '设计量', colSpan: '1', rowSpan: '1', field: 'quantity', hAlign: 2, width: 60, type: 'Number'},
             {title: '设计量公式', colSpan: '1', rowSpan: '1', field: 'expr', hAlign: 0, width: 80, formatter: '@'},
             {title: '图册号', colSpan: '1', rowSpan: '1', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@'},
+            {title: '计算模板', colSpan: '1', rowSpan: '1', field: 'calc_template', hAlign: 1, width: 80, formatter: '@', readOnly: true, getValue: function(data) {
+                const ct = data ? posCalcTemplate.find(x => { return x.id === data.calc_template }) : null;
+                return ct ? ct.name : '';
+            }},
             {title: '备注', colSpan: '1', rowSpan: '1', field: 'memo', hAlign: 0, width: 80, formatter: '@'},
         ],
         emptyRows: 3,
@@ -3605,9 +3698,10 @@ $(document).ready(function() {
     SpreadJsObj.initSheet(ancGclSheet, ancGclSpreadSetting);
     const ancGclObj = {
         loadCurAncillaryGcl: function () {
-            const node = treeOperationObj.getSelectNode(ledgerSheet);
-            const gclData = node ? ancGcl.getPartData(node.id) || [] : [];
+            const posNode = SpreadJsObj.getSelectObject(posSheet);
+            const gclData = posNode ? ancGcl.getPartData(posNode.id) || [] : [];
             SpreadJsObj.loadSheetData(ancGclSheet, SpreadJsObj.DataType.Data, gclData);
+            ancGclDetail.loadCurDetailData();
         },
         baseOpr: function (type) {
             const data = {};
@@ -3621,8 +3715,8 @@ $(document).ready(function() {
             const row = sel[0].row, count = sel[0].rowCount;
             const first = gclRange[row];
             if (type === 'insert') {
-                const node = SpreadJsObj.getSelectObject(ledgerSheet);
-                data.add = [{ lid: node.id, g_order: gclRange.length + 1 }];
+                const posNode = SpreadJsObj.getSelectObject(posSheet);
+                data.add = [{ lid: posNode.lid, pid: posNode.id, g_order: gclRange.length + 1 }];
             } else if (type === 'delete') {
                 data.del = [];
                 for (let iRow = 0; iRow < count; iRow++) {
@@ -3673,6 +3767,7 @@ $(document).ready(function() {
         },
         editStarting: function (e, info) {
             ancGclObj.ledgerTreeNode = SpreadJsObj.getSelectObject(ledgerSheet);
+            ancGclObj.posNode = SpreadJsObj.getSelectObject(posSheet);
         },
         editEnded: function (e, info) {
             const setting = info.sheet.zh_setting;
@@ -3683,19 +3778,9 @@ $(document).ready(function() {
             const orgText = gclData ? gclData[col.field] : '', newText = trimInvalidChar(info.editingText);
             if (orgText === newText || (!orgText && !newText)) return;
 
-            const node = ancGclObj.ledgerTreeNode;
-            if (!node) {
-                toastr.error('数据错误,请选择台账节点后再试');
-                SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                return;
-            }
-            if (!!newText && node.children && node.children.length > 0) {
-                toastr.error('父节点不可添加附属工程量');
-                SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                return;
-            }
-            if (!!newText && (!node.b_code || node.b_code === '')) {
-                toastr.error('项目节不可插入附属工程量');
+            const posNode = ancGclObj.posNode;
+            if (!posNode) {
+                toastr.error('数据错误,请选择计量单元后再试');
                 SpreadJsObj.reLoadRowData(info.sheet, info.row);
                 return;
             }
@@ -3728,7 +3813,7 @@ $(document).ready(function() {
             } else {
                 const sortData = info.sheet.zh_data;
                 const order = (!sortData || sortData.length === 0) ? 1 : Math.max(sortData[sortData.length - 1].g_order + 1, sortData.length + 1);
-                const addData = { lid: node.id, g_order: order };
+                const addData = { lid: posNode.lid, pid: posNode.id, g_order: order };
                 if (col.type === 'Number') {
                     const num = _.toNumber(newText);
                     if (!_.isFinite(num)) {
@@ -3801,19 +3886,9 @@ $(document).ready(function() {
         },
         clipboardPasting: function(e, info) {
             info.cancel = true;
-            const node = SpreadJsObj.getSelectObject(ledgerSheet);
-            if (!node) {
-                toastr.error('数据错误,请选择台账节点后再试');
-                SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                return;
-            }
-            if (node.children && node.children.length > 0) {
-                toastr.error('父节点不可添加附属工程量');
-                SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                return;
-            }
-            if ((!node.b_code || node.b_code === '')) {
-                toastr.error('项目节不可插入附属工程量');
+            const posNode = ancGclObj.posNode;
+            if (!posNode) {
+                toastr.error('数据错误,请选择计量单元后再试');
                 SpreadJsObj.reLoadRowData(info.sheet, info.row);
                 return;
             }
@@ -3861,7 +3936,7 @@ $(document).ready(function() {
                     data.update.push(updateData);
                 } else {
                     if (!data.add) data.add = [];
-                    const addData = { lid: node.id, g_order: curRow + 1};
+                    const addData = { lid: posNode.lid, pid: posNode.id, g_order: curRow + 1};
                     analysisData(pasteData[iRow], addData);
                     data.add.push(addData);
                 }
@@ -3896,7 +3971,51 @@ $(document).ready(function() {
                 SpreadJsObj.reLoadRowData(info.sheet, info.row);
             });
         },
+        selectionChanged: function(e, info) {
+            ancGclDetail.loadCurDetailData();
+        },
+        removeCalcTemplate: function(node) {
+            const ancGclUpdateData = { calcTmpl: { id: node.id, calc_template: '' } };
+            postData('/tender/' + getTenderId() + '/anc-gcl/update', ancGclUpdateData, function(result) {
+                ancGcl.updateDatas(result);
+                ancGclObj.loadCurAncillaryGcl();
+                ancGclDetail.detail.updateDatas({ update: result.detail });
+                ancGclDetail.reloadCurDetailData();
+            });
+        },
+        getBlockData: function() {
+            const copyBlockList = [];
+            const sheet = ancGclSheet;
+            const sel = sheet.getSelections()[0];
+            let iRow = sel.row;
+            while (iRow < sel.row + sel.rowCount) {
+                const p = sheet.zh_data[iRow];
+                if (!p) break;
+                copyBlockList.push(p);
+                if (p.calc_template) {
+                    const detailRange = ancGclDetail.detail.getPartData(p.id);
+                    p.calcDetail = detailRange;
+                }
+                iRow++;
+            }
+            return copyBlockList;
+        },
+        pasteBlock: function(copyInfo) {
+            const posNode = SpreadJsObj.getSelectObject(posSheet);
+            if (!posNode) {
+                toastr.error('数据错误,请选择计量单元后再试');
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                return;
+            }
+            const data = { pasteBlock: { pid: posNode.id, tid: copyInfo.tid, block: copyInfo.block } };
+            postData('/tender/' + getTenderId() + '/anc-gcl/update', data, function (result) {
+                ancGcl.updateDatas(result);
+                if (result.details) ancGclDetail.detail.updateDatas(result.details);
+                ancGclObj.loadCurAncillaryGcl();
+            });
+        }
     };
+    ancGclSpread.bind(spreadNS.Events.SelectionChanged, ancGclObj.selectionChanged);
     if (!readOnly) {
         SpreadJsObj.addDeleteBind(ancGclSpread, ancGclObj.deletePress);
         ancGclSpread.bind(spreadNS.Events.EditStarting, ancGclObj.editStarting);
@@ -3960,6 +4079,60 @@ $(document).ready(function() {
                         ancGclObj.baseOpr('up-move');
                     }
                 },
+                editSpr: '----',
+                selectCalcTemplate: {
+                    name: '选择计算模板',
+                    callback: function(key, opt) {
+                        const node = SpreadJsObj.getSelectObject(ancGclSheet);
+                        calcTemplateSelect.select(node, 'ancGcl');
+                    },
+                    disabled: function(key, opt) {
+                        const node = SpreadJsObj.getSelectObject(ancGclSheet);
+                        return !node;
+                    }
+                },
+                removeCalcTemplate: {
+                    name: '移除计算模板',
+                    callback: function(key, opt) {
+                        const node = SpreadJsObj.getSelectObject(ancGclSheet);
+                        ancGclObj.removeCalcTemplate(node);
+                    },
+                    disabled: function(key, opt) {
+                        const node = SpreadJsObj.getSelectObject(ancGclSheet);
+                        return !node || !node.calc_template;
+                    }
+                },
+                calcTmplSpr: '----',
+                copyBlock: {
+                    name: '复制整块',
+                    callback: function(key, opt) {
+                        ancGclObj.block = [];
+                        const copyBlockList = ancGclObj.getBlockData();
+                        setLocalCache(copyBlockTag, JSON.stringify({ block: copyBlockList, tag: 'ancGcl' }));
+                    },
+                    disabled: function(key, opt) {
+                        const node = SpreadJsObj.getSelectObject(ancGclSheet);
+                        return !node;
+                    }
+                },
+                pasteBlock: {
+                    name: '粘贴整块',
+                    callback: function(key, opt) {
+                        const copyInfo = JSON.parse(getLocalCache(copyBlockTag));
+                        if (copyInfo.block.length > 0) {
+                            ancGclObj.pasteBlock(copyInfo);
+                        } else {
+                            document.execCommand('paste');
+                        }
+                    },
+                    disabled: function(key, opt) {
+                        const pos = SpreadJsObj.getSelectObject(posSheet);
+                        if (!pos) return true;
+
+                        const copyInfo = JSON.parse(getLocalCache(copyBlockTag));
+                        return !(copyInfo && copyInfo.block && copyInfo.block.length > 0 && copyInfo.tag === 'ancGcl');
+                    }
+                }
             }
         });
     }
@@ -4001,6 +4174,9 @@ $(document).ready(function() {
 
         ancGclObj.loadCurAncillaryGcl();
         SpreadJsObj.resetTopAndSelect(ancGclSheet);
+        ancGclDetail.detail.loadDatas(data.ancGclDetail);
+        ancGclDetail.loadCurDetailData();
+        SpreadJsObj.resetTopAndSelect(ancGclDetail.sheet);
 
         posCalcDetail.detail.loadDatas(data.posCalcDetail);
         posCalcDetail.loadCurDetailData();
@@ -4507,7 +4683,6 @@ $(document).ready(function() {
                         treeOperationObj.refreshOperationValid(mainSheet);
                         ledgerSpread.focus();
                         posOperationObj.loadCurPosData();
-                        ancGclObj.loadCurAncillaryGcl();
                     });
                 });
             }
@@ -4926,7 +5101,6 @@ $(document).ready(function() {
                             SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, result.create[0].index]);
                             treeOperationObj.refreshOperationValid(sheet);
                             posOperationObj.loadCurPosData();
-                            ancGclObj.loadCurAncillaryGcl();
                             self.obj.modal('hide');
                         }, null, true);
                     } else {
@@ -5823,7 +5997,6 @@ $(document).ready(function() {
             SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), 'tree', ledgerTree);
             pos.loadDatas(result.pos);
             posOperationObj.loadCurPosData();
-            ancGclObj.loadCurAncillaryGcl();
             checkShowLast(result.bills.length);
         });
     });
@@ -5835,7 +6008,6 @@ $(document).ready(function() {
             const loadResult = ledgerTree.loadPostData({update: data.source.bills});
             treeOperationObj.refreshTree(ledgerSpread.getActiveSheet(), loadResult);
             posOperationObj.loadCurPosData();
-            ancGclObj.loadCurAncillaryGcl();
             for (const e of data.error) {
                 e.serialNo = ledgerTree.getNodeIndex(ledgerTree.getItems(e.ledger_id)) + 1;
             }
@@ -6089,7 +6261,6 @@ $(document).ready(function() {
           if (Object.keys(att).length) {
               SpreadJsObj.locateTreeNode(ledgerSpread.getActiveSheet(), att.ledger_id, true);
               posOperationObj.loadCurPosData();
-              ancGclObj.loadCurAncillaryGcl();
           }
       }
   });

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

@@ -352,9 +352,8 @@ const createNewPathTree = function (type, setting) {
                 }
             };
             this.nodes = [];
-            if (!isResort) {
-                this.children = this.getChildren();
-            } else {
+            this.children = this.getChildren();
+            if (isResort) {
                 this.sortByOrder(this.children);
             }
             addSortNodes(this.children);
@@ -813,8 +812,8 @@ const createNewPathTree = function (type, setting) {
                     for (const prop in data) {
                         if (data[prop] !== undefined && data[prop] !== node[prop]) {
                             if (prop === this.setting.pid) {
-                                loadedData.push(this.getItems(node[this.setting.pid]));
-                                loadedData.push(this.getItems(data[this.setting.pid]));
+                                if (node[this.setting.pid] !== this.setting.rootId) loadedData.push(this.getItems(node[this.setting.pid]));
+                                if (data[this.setting.pid] !== this.setting.rootId) loadedData.push(this.getItems(data[this.setting.pid]));
                             }
                             // if (prop === this.setting.order) {
                             //     loadedData = loadedData.concat(this.getPosterity(node));

+ 1 - 0
app/public/js/shares/ct_preview.js

@@ -16,6 +16,7 @@ const calcTemplatePreview = (function(){
                 try {
                     pd[pc.field] = ZhCalc.round(ZhCalc.mathCalcExpr(calcExpr.replace(new RegExp('%', 'gm'), '/100')), pc.decimal);
                 } catch(err) {
+                    console.log(err);
                     pd[pc.field] = 0;
                 }
             }

+ 1 - 1
app/public/js/shares/new_tag.js

@@ -34,7 +34,7 @@ const newTag = function (setting) {
             }
             billsTag.updateDatasAndShow(result);
             billsSheet.repaint();
-            posSheet.repaint();
+            if (posSheet) posSheet.repaint();
             $('#addtag').modal('hide');
         });
     });

+ 2 - 0
app/public/js/shenpi.js

@@ -645,6 +645,8 @@ $(document).ready(function () {
                 auditData.audit_group = removeMultiData ? removeMultiData.audit_group : '';
                 auditData.audit_group_order = removeMultiData ? removeMultiData.audit_group_order : 0;
                 auditData.audit_group_limit = removeMultiData ? removeMultiData.audit_group_limit : 0;
+                auditData.audit_checkno_valid = removeMultiData ? removeMultiData.audit_checkno_valid : 0;
+                auditData.audit_group_need = removeMultiData ? removeMultiData.audit_group_need : 0;
                 auditList.push(auditData);
                 aidList.push(parseInt(remove.getAttribute('data-id')));
             }

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

@@ -291,6 +291,9 @@ const ZhCalc = (function () {
             math && math.config({
                 number: 'BigNumber',
             });
+            math.import({
+                if: function(con, a, b) { return con ? a : b; },
+            });
             mathConfig = true;
         }
     }

+ 3 - 2
app/router.js

@@ -556,7 +556,7 @@ module.exports = app => {
     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/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/tag', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, costStageCheck, 'costController.tag');
+    app.post('/sp/:id/cost/tender/:tid/:stype/:sorder/stage/tag', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, costStageCheck, 'costController.tag');
     app.get('/sp/:id/cost/tender/:tid/flow', sessionAuth, subProjectCheck, tenderCheck, projectManagerCheck, 'tenderController.shenpiSet');
     app.post('/sp/:id/cost/tender/:tid/:stype/:sorder/audit/add', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, costStageCheck, 'costController.addStageAudit');
     app.post('/sp/:id/cost/tender/:tid/:stype/:sorder/audit/delete', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, costStageCheck, 'costController.deleteStageAudit');
@@ -681,9 +681,10 @@ module.exports = app => {
     app.post('/tender/:id/ledger/dsk', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.importDsk');
     app.get('/tender/:id/ledger/download/:file', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.download');
     app.post('/tender/:id/anc-gcl/update', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.ancGclUpdate');
-    app.post('/tender/:id/pos-calc/update', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.posCalcUpdate');
+    app.post('/tender/:id/anc-gcl-detail/update', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.ancGclDetailUpdate');
     app.post('/tender/:id/pos/update', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.posUpdate');
     app.post('/tender/:id/pos/paste', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.posPaste');
+    app.post('/tender/:id/pos-calc/update', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.posCalcUpdate');
     app.post('/tender/:id/ledger/deal2sgfh', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.deal2sgfh');
     app.post('/tender/:id/ledger/sgfh2deal', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.sgfh2deal');
     app.post('/tender/:id/ledger/check', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.check');

+ 121 - 13
app/service/ancillary_gcl.js

@@ -29,11 +29,11 @@ module.exports = app => {
             const datas = data instanceof Array ? data : [data];
             const insertData = [];
             for (const d of datas) {
-                if (!d.lid || !d.g_order) throw '新增其他数据,提交的数据错误';
+                if (!d.lid || !d.pid || !d.g_order) throw '新增其他数据,提交的数据错误';
                 const nd = {
                     id: this.uuid.v4(), tid: this.ctx.tender.id,
                     add_user_id: user_id, update_user_id: user_id,
-                    lid: d.lid, g_order: d.g_order,
+                    lid: d.lid, pid: d.pid, g_order: d.g_order,
                 };
                 if (d.is_aux !== undefined) nd.is_aux = d.is_aux;
                 if (d.name) nd.name = d.name || '';
@@ -49,23 +49,22 @@ module.exports = app => {
                 where: { id: this.ctx.helper._.map(insertData, 'id') }
             });
         }
-
         async _delDatas (data) {
             if (!data || data.length === 0) throw '提交数据错误';
             const orgDatas = await this.getAllDataByCondition({ where: { id: data } });
 
             if (!orgDatas || orgDatas.length === 0) throw '删除的辅助工程量不存在';
 
-            const bills = await this.ctx.service.ledger.getDataById(orgDatas[0].lid);
-            let billsAnc = await this.getAllDataByCondition({ where: { tid: bills.tid, lid: bills.id } });
+            const pos = await this.ctx.service.pos.getDataById(orgDatas[0].pid);
+            let posAnc = await this.getAllDataByCondition({ where: { tid: pos.tid, pid: pos.id } });
 
-            billsAnc = billsAnc.filter(ba => {
+            posAnc = posAnc.filter(ba => {
                 return data.indexOf(ba.id) < 0;
             });
-            billsAnc.sort((x, y) => { return x.g_order - y.g_order; });
+            posAnc.sort((x, y) => { return x.g_order - y.g_order; });
 
             const updateData = [];
-            billsAnc.forEach((x, i) => {
+            posAnc.forEach((x, i) => {
                 if (x.g_order !== i + 1) updateData.push({ id: x.id, g_order: i + 1});
             });
 
@@ -73,6 +72,7 @@ module.exports = app => {
             try {
                 await conn.delete(this.tableName, { id: data });
                 if (updateData.length > 0) await conn.updateRows(this.tableName, updateData);
+                await this.ctx.service.ancillaryGclDetail.deleteAncGclPartData(conn, orgDatas[0].tid, data);
                 await conn.commit();
                 return [data, updateData];
             } catch (err) {
@@ -80,7 +80,6 @@ module.exports = app => {
                 throw err;
             }
         }
-
         async _updateDatas (data) {
             if (!data || data.length === 0) throw '提交数据错误';
             const qtyDecimal = this.ctx.tender.info.decimal.qty;
@@ -102,8 +101,10 @@ module.exports = app => {
                 if (d.name !== undefined) nd.name = d.name;
                 if (d.g_order !== undefined) nd.g_order = d.g_order;
                 if (d.unit !== undefined) nd.unit = d.unit;
-                if (d.quantity !== undefined) nd.quantity = this.ctx.helper.round(d.quantity, qtyDecimal);
-                if (d.expr !== undefined) nd.expr = d.expr || '';
+                if (!od.calc_template) {
+                    if (d.quantity !== undefined) nd.quantity = this.ctx.helper.round(d.quantity, qtyDecimal);
+                    if (d.expr !== undefined) nd.expr = d.expr || '';
+                }
                 if (d.drawing_code !== undefined) nd.drawing_code = d.drawing_code;
                 if (d.memo !== undefined) nd.memo = d.memo || '';
                 uDatas.push(nd);
@@ -115,9 +116,101 @@ module.exports = app => {
                 return [];
             }
         }
+        async _setCalcTemplate(data) {
+            if (!data || data.length === 0) throw '提交数据错误';
+            const qtyDecimal = this.ctx.tender.info.decimal.qty;
+            const user_id = this.ctx.session.sessionUser.accountId;
+
+            const datas = data instanceof Array ? data : [data];
+            const orgDatas = await this.getAllDataByCondition({
+                where: { id: this.ctx.helper._.map(datas, 'id') }
+            });
+            if (!orgDatas || orgDatas.length === 0) throw '修改的辅助工程量不存在';
+
+            const uDatas = [], detailUpdate = [];
+            for (const d of datas) {
+                const od = orgDatas.find(x => { return x.id === d.id; });
+                if (!od) continue;
+
+                const nd = { id: od.id, update_user_id: user_id, calc_template: d.calc_template, expr: '', quantity: 0 };
+                uDatas.push(nd);
+            }
+            const conn = await this.db.beginTransaction();
+            try {
+                if (datas[0].calc_template) {
+                    for (const ud of uDatas) {
+                        const [du, quantity] = await this.ctx.service.ancillaryGclDetail.resetAncGclCalcTemplate(conn, ud.id, ud.calc_template);
+                        detailUpdate.push(...du);
+                        ud.quantity = this.ctx.helper.round(quantity, qtyDecimal);
+                    }
+                } else {
+                    await this.ctx.service.ancillaryGclDetail.deleteAncGclPartData(conn, orgDatas[0].tid, uDatas.map(x => { return x.id; }));
+                }
+                await conn.updateRows(this.tableName, uDatas);
+                await conn.commit();
+                return [uDatas, detailUpdate];
+            } catch (err) {
+                this.ctx.log(err);
+                await conn.rollback();
+                throw '修改计算模板失败';
+            }
+        }
+        async _pasteBlock(data) {
+            if (!(data.block instanceof Array)) throw '提交数据错误';
+
+            const qtyDecimal = this.ctx.tender.info.decimal.qty;
+            const user_id = this.ctx.session.sessionUser.accountId;
+            const pos = await this.ctx.service.pos.getDataById(data.pid);
+            const existAncGcl = await this.getAllDataByCondition({ where : { pid: data.pid }, orders: [['g_order', 'DESC']]});
+            const maxOrder = existAncGcl.length > 0 ? existAncGcl[0].g_order : 0;
+
+            const insertAncGcl = [], insertDetail = [];
+            for (const [i, b] of data.block.entries()) {
+                const nig = {
+                    id: this.uuid.v4(), tid: this.ctx.tender.id,
+                    add_user_id: user_id, update_user_id: user_id,
+                    lid: pos.lid, pid: pos.id, g_order: maxOrder + i + 1,
+                    is_aux: b.is_aux || 0, name: b.name || '', unit: b.unit || '',
+                    drawing_code: b.drawing_code || '', memo: b.memo || '',
+                    quantity: b.quantity || 0, expr: b.expr || '', calc_template: b.calc_template || ''
+                };
+                insertAncGcl.push(nig);
+                if (!b.calc_template) continue;
+
+                let sumQty = 0;
+                for (const cd of b.calcDetail) {
+                    const newDetail = {
+                        id: this.uuid.v4(), tid: this.ctx.tender.id, lid: pos.lid, pid: pos.id, ag_id: nig.id,
+                        create_user_id: user_id, update_user_id: user_id,
+                        agd_order: cd.agd_order,
+                        str1 : cd.str1 || '', str2 : cd.str1 || '', str3 : cd.str1 || '', str4 : cd.str1 || '',
+                        num_a : cd.num_a || 0, num_b : cd.num_b || 0, num_c : cd.num_c || 0, num_d : cd.num_d || 0,
+                        num_e : cd.num_e || 0, num_f : cd.num_f || 0, num_g : cd.num_g || 0, num_h : cd.num_h || 0,
+                        num_i : cd.num_i || 0, num_j : cd.num_j || 0, num_k : cd.num_k || 0, num_l : cd.num_l || 0,
+                        num_m : cd.num_m || 0, num_n : cd.num_n || 0, num_o : cd.num_o || 0, num_p : cd.num_p || 0,
+                        num_q : cd.num_q || 0, num_r : cd.num_r || 0, num_s : cd.num_s || 0, num_t : cd.num_t || 0,
+                        num_u : cd.num_u || 0,
+                        qty: cd.qty || 0, expr: cd.expr || 0, spec: cd.spec || ''
+                    };
+                    sumQty = this.ctx.helper.add(sumQty, newDetail.qty);
+                    insertDetail.push(newDetail);
+                }
+                nig.quantity = this.ctx.helper.round(sumQty, qtyDecimal);
+            }
 
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.insert(this.tableName, insertAncGcl);
+                if (insertDetail.length > 0) await transaction.insert(this.ctx.service.ancillaryGclDetail.tableName, insertDetail);
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+            return [ insertAncGcl, { add: insertDetail }];
+        }
         async updateDatas(data) {
-            const result = {add: [], del: [], update: []};
+            const result = { add: [], del: [], update: [], details: null };
             try {
                 if (data.add) {
                     result.add = await this._addDatas(data.add);
@@ -128,6 +221,12 @@ module.exports = app => {
                 if (data.del) {
                     [result.del, result.update] = await this._delDatas(data.del);
                 }
+                if (data.calcTmpl) {
+                    [result.update, result.details] = await this._setCalcTemplate(data.calcTmpl);
+                }
+                if (data.pasteBlock) {
+                    [result.add, result.details] = await this._pasteBlock(data.pasteBlock);
+                }
                 return result;
             } catch (err) {
                 if (err.stack) {
@@ -139,9 +238,18 @@ module.exports = app => {
             }
         }
 
-        async deletePartData(transaction, tid, lid) {
+        async deleteBillsPartData(transaction, tid, lid) {
             await transaction.delete(this.tableName, { tid: tid, lid: lid });
         }
+        async deletePosPartData(transaction, tid, pid) {
+            await transaction.delete(this.tableName, { tid: tid, pid: pid });
+        }
+
+        async getUsedCalcTemplate(tid) {
+            const sql = `SELECT calc_template, count(id) as count FROM ${this.tableName} WHERE tid = ? and calc_template <> '' GROUP BY calc_template`;
+            const result = await this.db.query(sql, [tid]);
+            return result;
+        }
     }
 
     return AncillaryGcl;

+ 296 - 0
app/service/ancillary_gcl_detail.js

@@ -0,0 +1,296 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+const strField = ['str1', 'str2', 'str3', 'str4'];
+const numField = ['num_a', 'num_b', 'num_c', 'num_d', 'num_e', 'num_f', 'num_g', 'num_h', 'num_i', 'num_j', 'num_k', 'num_l', 'num_m', 'num_n', 'num_o', 'num_p', 'num_q', 'num_r', 'num_s', 'num_t', 'num_u'];
+const specialField = ['spec', 'qty', 'expr'];
+const math = require('mathjs');
+math.config({
+    number: 'BigNumber',
+});
+
+module.exports = app => {
+
+    class AncillaryGclDetail extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'ancillary_gcl_detail';
+        }
+
+        // ---------- 校验&计算数据相关 ------------------
+        getDecimal(decimal, field) {
+            return decimal[field] !== undefined ? decimal[field] || 0 : decimal.def;
+        }
+        _calcExpr(formula) {
+            const percentReg = /((\d+)|((\d+)(\.\d+)))%/g;
+            const percent = formula.match(percentReg);
+            if (percent) {
+                for (const p of percent) {
+                    const v = math.eval(p.replace(new RegExp('%', 'gm'), '/100'));
+                    formula = formula.replace(p, v);
+                }
+            }
+            try {
+                // 使用mathjs计算 math.eval('17259401.95*0.9') = 15533461.754999999,正确应为 15533461.755
+                // const value = math.eval(formula);
+                // 使用逆波兰法四则运算,可防止出现误差,但是只支持四则运算,不支持科学运算
+                // const value = this.ctx.helper.calcExprStrRpn(formula);
+                // 使用mathjs的大数运算,可支持所有
+                const value = parseFloat(math.eval(formula));
+                return Number.isFinite(value) ? value : 0;
+            } catch(err) {
+                return 0;
+            }
+        }
+        _reCalcQty(data, template) {
+            const qtyExpr = template.calc_expr.find(x => { return x.field === 'qty'; });
+            const result = { id: data.id, expr: qtyExpr ? qtyExpr.expr : '' };
+            for (const nf of numField) {
+                result[nf] = this.ctx.helper.round(data[nf] || 0, this.getDecimal(template.decimal, nf));
+                result.expr = result.expr.replace(new RegExp(nf, 'gm'), data[nf] || 0);
+            }
+            result.qty = this.ctx.helper.round(this._calcExpr(result.expr), this.getDecimal(template.decimal, 'qty'));
+            return result;
+        }
+        // 后端计算
+        _loadDataAndCalc(data, updateData, orgData, template) {
+            let calc = false;
+            for (const sf of strField) {
+                if (updateData[sf] !== undefined) data[sf] = updateData[sf] || '';
+            }
+            if (updateData.spec !== undefined) {
+                calc = true;
+                data.spec = updateData.spec;
+                if (template.spec_rela) {
+                    const sv = template.specValue.find(x => { return x.spec === data.spec; });
+                    data[template.spec_rela] = sv ? sv.value : 0;
+                }
+            }
+            if (updateData[template.spec_rela] !== undefined) delete updateData[template.spec_rela];
+            this.ctx.service.calcTmpl.calcByTemplate(data, updateData, orgData, template.calc_expr, { qty: 'expr' });
+            return calc || (data.qty !== undefined && orgData.qty !== data.qty);
+        }
+        // 依赖前端计算
+        _loadDataResult(data, updateData) {
+            for (const sf of strField) {
+                if (updateData[sf] !== undefined) data[sf] = updateData[sf] || '';
+            }
+            if (updateData.spec !== undefined) data.spec = updateData.spec;
+            if (updateData.qty !== undefined) data.qty = updateData.qty;
+            if (updateData.expr !== undefined) data.expr = updateData.expr;
+            for (const nf of numField) {
+                if (updateData[nf] !== undefined) data[nf] = updateData[nf];
+            }
+        }
+        // --------------------------------------------
+
+        async _getAncGclUpdateData(data, ag_id, calc = true) {
+            if (!calc) return null;
+            try {
+                const detailDatas = await this.getAllDataByCondition({ columns: ['id', 'ag_id', 'qty'], where: { ag_id } });
+
+                const ancGclUpdate = { id: ag_id, quantity: 0 };
+                for (const d of data) {
+                    ancGclUpdate.quantity = this.ctx.helper.add(d.qty, ancGclUpdate.quantity);
+                }
+                for (const d of detailDatas) {
+                    if (data.findIndex(x => { return x.id === d.id; }) >= 0) continue;
+                    ancGclUpdate.quantity = this.ctx.helper.add(d.qty, ancGclUpdate.quantity);
+                }
+                return ancGclUpdate;
+            } catch (err) {
+                return null;
+            }
+        }
+
+        async _addDatas(data, selectId) {
+            const user_id = this.ctx.session.sessionUser.accountId;
+
+            const datas = data instanceof Array ? data : [data];
+            const ancGcl = await this.ctx.service.ancillaryGcl.getDataById(datas[0].ag_id);
+            if (!ancGcl.calc_template) throw '未定义计算模板,请先在附属工程量选择计算模板';
+            const calcTemplate = await this.ctx.service.calcTmpl.getTemplate(ancGcl.calc_template);
+            if (!calcTemplate) throw '计算模板不存在';
+            const details = await this.getAllDataByCondition({ where: { tid: this.ctx.tender.id, pid: datas[0].pid }, orders: [['agd_order', 'ASC']] });
+            const selectIndex = selectId ? details.findIndex(x => { return x.id === selectId; }) : -1;
+            if (selectId && selectIndex < 0) throw '选择的数据不存在';
+            const startOrder = selectId
+                ? (selectIndex > 0 ? details[selectIndex - 1].agd_order : 0)
+                : (details.length > 0 ? details[details.length - 1].agd_order : 0);
+
+            const insertData = [], updateData = [];
+            let isCalc = false;
+            for (const [i, d] of datas.entries()) {
+                if (!d.lid || !d.pid || !d.ag_id || !d.agd_order) throw '新增其他数据,提交的数据错误';
+                const nd = {
+                    id: this.uuid.v4(), tid: this.ctx.tender.id,
+                    create_user_id: user_id, update_user_id: user_id,
+                    lid: d.lid, pid: d.pid, ag_id: d.ag_id, agd_order: startOrder + i + 1,
+                };
+                if (this._loadDataAndCalc(nd, d, {}, calcTemplate)) isCalc = true;
+                insertData.push(nd);
+            }
+            const gclUpdate = await this._getAncGclUpdateData(insertData, insertData[0].ag_id, isCalc);
+            if (selectIndex >= 0) {
+                const maxOrder = insertData[insertData.length - 1].agd_order;
+                for (const p of details) {
+                    if (p.agd_order > startOrder) {
+                        updateData.push({ id: p.id, agd_order: maxOrder + updateData.length + 1 });
+                    }
+                }
+            }
+
+            const conn = await this.db.beginTransaction();
+            try {
+                await conn.insert(this.tableName, insertData);
+                if (updateData.length > 0) await conn.updateRows(this.tableName, updateData);
+                if (gclUpdate) await conn.update(this.ctx.service.ancillaryGcl.tableName, gclUpdate);
+                await conn.commit();
+            } catch(err) {
+                await conn.rollback();
+                throw err;
+            }
+            const addData = await this.getAllDataByCondition({
+                where: { id: this.ctx.helper._.map(insertData, 'id') }
+            });
+            const gclUpdateData = isCalc ? await this.ctx.service.ancillaryGcl.getDataById(addData[0].ag_id) : null;
+            return [addData, updateData, gclUpdateData]
+        }
+        async _delDatas (data) {
+            if (!data || data.length === 0) throw '提交数据错误';
+            const orgDatas = await this.getAllDataByCondition({ where: { id: data } });
+
+            if (!orgDatas || orgDatas.length === 0) throw '删除的明细不存在';
+
+            let details = await this.getAllDataByCondition({ where: { tid: orgDatas[0].tid, pid: orgDatas[0].pid } });
+            details = details.filter(pa => {
+                return data.indexOf(pa.id) < 0;
+            });
+            details.sort((x, y) => { return x.agd_order - y.agd_order; });
+
+            const updateData = [];
+            details.forEach((x, i) => {
+                if (x.agd_order !== i + 1) updateData.push({ id: x.id, agd_order: i + 1});
+            });
+            const gclUpdate = await this._getAncGclUpdateData(data.map(x => { return { id: x, qty: 0 }; }), orgDatas[0].ag_id);
+
+            const conn = await this.db.beginTransaction();
+            try {
+                await conn.delete(this.tableName, { id: data });
+                if (updateData.length > 0) await conn.updateRows(this.tableName, updateData);
+                if (gclUpdate) await conn.update(this.ctx.service.ancillaryGcl.tableName, gclUpdate);
+                await conn.commit();
+            } catch (err) {
+                await conn.rollback();
+                throw err;
+            }
+            const ancGclData = await this.ctx.service.ancillaryGcl.getDataById(orgDatas[0].ag_id);
+            return [data, updateData, ancGclData];
+        }
+        async _updateDatas (data) {
+            if (!data || data.length === 0) throw '提交数据错误';
+            const user_id = this.ctx.session.sessionUser.accountId;
+
+            const datas = data instanceof Array ? data : [data];
+            const orgDatas = await this.getAllDataByCondition({
+                where: { id: this.ctx.helper._.map(datas, 'id') }
+            });
+            if (!orgDatas || orgDatas.length === 0) throw '修改的明细不存在';
+
+            const ancGcl = await this.ctx.service.ancillaryGcl.getDataById(orgDatas[0].ag_id);
+            if (!ancGcl.calc_template) throw '未定义计算模板,请先在附属工程量选择计算模板';
+            const calcTemplate = await this.ctx.service.calcTmpl.getTemplate(ancGcl.calc_template);
+            if (!calcTemplate) throw '计算模板不存在';
+
+            const uDatas = [];
+            let isCalc = false;
+            for (const d of datas) {
+                const od = orgDatas.find(x => { return x.id === d.id; });
+                if (!od) continue;
+
+                const nd = { id: od.id, update_user_id: user_id };
+                if (d.agd_order) nd.agd_order = d.agd_order;
+                if (this._loadDataAndCalc(nd, d, od, calcTemplate)) isCalc = true;
+                uDatas.push(nd);
+            }
+            const gclUpdate = await this._getAncGclUpdateData(uDatas, orgDatas[0].ag_id, isCalc);
+
+            if (uDatas.length > 0) {
+                const conn = await this.db.beginTransaction();
+                try {
+                    await conn.updateRows(this.tableName, uDatas);
+                    if (gclUpdate) await conn.update(this.ctx.service.ancillaryGcl.tableName, gclUpdate);
+                    await conn.commit();
+                } catch (err) {
+                    await conn.rollback();
+                    throw err;
+                }
+                return [uDatas, gclUpdate];
+            } else {
+                return [];
+            }
+        }
+        async updateDatas(data) {
+            const result = { detail: {add: [], del: [], update: []}, ancGcl: null};
+            try {
+                if (data.add) {
+                    [result.detail.add, result.detail.update, result.ancGcl] = await this._addDatas(data.add, data.select);
+                }
+                if (data.update) {
+                    const orgUpdate = result.detail.update;
+                    [result.detail.update, result.ancGcl] = await this._updateDatas(data.update);
+                    if (orgUpdate.length > 0) result.detail.update.push(...orgUpdate);
+                }
+                if (data.del) {
+                    [result.detail.del, result.detail.update, result.ancGcl] = await this._delDatas(data.del);
+                }
+                return result;
+            } catch (err) {
+                throw err;
+            }
+        }
+
+        async resetAncGclCalcTemplate(transaction, ag_id, templateId) {
+            const user_id = this.ctx.session.sessionUser.accountId;
+            const calcTemplate = await this.ctx.service.calcTmpl.getTemplate(templateId);
+            if (!calcTemplate) throw '计算模板不存在';
+
+            const detailDatas = await this.getAllDataByCondition({ where: { ag_id: ag_id } });
+            const detailUpdateDatas = [];
+            let qty = 0;
+            for (const pd of detailDatas) {
+                const ud = this._reCalcQty(pd, calcTemplate);
+                ud.update_user_id = user_id;
+                detailUpdateDatas.push(ud);
+                qty = this.ctx.helper.add(ud.qty, qty);
+            }
+
+            if (detailUpdateDatas.length > 0) await transaction.updateRows(this.tableName, detailUpdateDatas);
+            return [detailUpdateDatas, qty];
+        }
+
+        async deleteBillsPartData(transaction, tid, lid) {
+            await transaction.delete(this.tableName, { tid: tid, lid: lid });
+        }
+        async deletePosPartData(transaction, tid, pid) {
+            await transaction.delete(this.tableName, { tid: tid, lid: pid });
+        }
+        async deleteAncGclPartData(transaction, tid, ag_id) {
+            await transaction.delete(this.tableName, { tid: tid, ag_id: ag_id });
+        }
+    }
+
+    return AncillaryGclDetail;
+};

+ 8 - 5
app/service/calc_tmpl.js

@@ -14,6 +14,9 @@ const math = require('mathjs');
 math.config({
     number: 'BigNumber',
 });
+math.import({
+    if: function(con, a, b) { return con ? a : b; },
+});
 const ValidTemplateType = ['posCalc', 'cost'];
 const PosCalc = (function(){
     const EmptySpreadCache = {
@@ -355,12 +358,12 @@ module.exports = app => {
             const tender = await this.ctx.service.tender.getAllDataByCondition({ columns: ['id'], where: { project_id: templates[0].pid }});
             for (const t of tender) {
                 const used = await this.ctx.service.ledgerExtra.getUsedCalcTemplate(t.id);
+                const ancGclUsed = await this.ctx.service.ancillaryGcl.getUsedCalcTemplate(t.id);
                 templates.forEach(x => {
-                    const u = used.find(ut => { return ut.calc_template === x.id; });
-                    if (u) {
-                        x.used.push(t.id);
-                        x.used_count = this.ctx.helper.add(x.used_count + u.count);
-                    }
+                    const u = used.find(ut => { return ut.calc_template === x.id; }) || { count: 0 };
+                    const u2 = ancGclUsed.find(ut => { return ut.calc_template === x.id; }) || { count: 0 };
+                    if (u.count > 0 || u2.count > 0) x.used.push(t.id);
+                    x.used_count = this.ctx.helper.sum([ x.used_count, u.count, u2.count ]);
                 });
             }
         }

+ 1 - 0
app/service/contract.js

@@ -73,6 +73,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 transaction.commit();
             } catch (err) {
                 await transaction.rollback();

+ 34 - 2
app/service/contract_pay.js

@@ -70,6 +70,7 @@ module.exports = app => {
                     cid,
                     uid: this.ctx.session.sessionUser.accountId,
                     pay_time: data.pay_time,
+                    used: data.used || '合同',
                     pay_price: data.pay_price,
                     debit_price: data.debit_price,
                     yf_price: data.yf_price,
@@ -79,8 +80,9 @@ module.exports = app => {
                     create_time: new Date(),
                     need_shenpi: options.spid ? this.ctx.subProject.page_show.openContractPaySubProjectShenpi : this.ctx.subProject.page_show.openContractPayTenderShenpi,
                 };
-                await transaction.insert(this.tableName, insertData);
+                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);
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();
@@ -180,6 +182,7 @@ module.exports = app => {
                         fpid,
                         fpcid: p.id,
                         pay_time: times,
+                        used: p.used || '合同',
                         pay_price: p.pay_price || 0,
                         debit_price: 0,
                         yf_price: p.pay_price || 0,
@@ -191,15 +194,44 @@ module.exports = app => {
                 }
             }
             if (addPays.length > 0) {
-                await transaction.insert(this.tableName, addPays);
+                const result = await transaction.insert(this.tableName, addPays);
                 for (const c of contracts) {
                     await this.calcContract(transaction, c);
                 }
+                // 获取刚批量添加的所有list
+                for (let j = 0; j < addPays.length; j++) {
+                    addPays[j].id = result.insertId + j;
+                }
+                for (const p of addPays) {
+                    await this.ctx.service.contractSpAudit.makeAudits(transaction, p, p.cid, p.id, uid);
+                }
+                const commonJson = this.ctx.subProject.common_json ? JSON.parse(this.ctx.subProject.common_json) : {};
+                const used = commonJson && commonJson.tender_contract_used ? commonJson.tender_contract_used : [];
+                const addUsed = this._.uniq(this._.map(pays, 'used'));
+                // 还要判断里面有叫"合同"的值,如果有则去除
+                const index = addUsed.indexOf('合同');
+                if (index > -1) {
+                    addUsed.splice(index, 1);
+                }
+                // 判断addUsed和used对比是否有新增的值,如果有则更新分项目的common_json
+                if (this._.difference(addUsed, used).length > 0) {
+                    const newUsed = this._.uniq([...used, ...addUsed]);
+                    commonJson.tender_contract_used = newUsed;
+                    await transaction.update(this.ctx.service.subProject.tableName, { id: this.ctx.subProject.id, common_json: JSON.stringify(commonJson) });
+                }
             }
         }
 
         async removeContractPays(transaction, fpid, pays) {
             await transaction.delete(this.tableName, { fpid });
+            // 删除合同附件, 删除审批人列表
+            for (const p of pays) {
+                // 删除合同附件
+                const attList = await this.ctx.service.contractPayAtt.getAllDataByCondition({ where: { cpid: p.id } });
+                await this.ctx.helper.delFiles(attList);
+                await transaction.delete(this.ctx.service.contractPayAtt.tableName, { cpid: p.id });
+                await transaction.delete(this.ctx.service.contractSpAudit.tableName, { cpid: p.id });
+            }
             const contracts = await transaction.select(this.ctx.service.contract.tableName, { where: { id: this._.uniq(this._.map(pays, 'cid')) } });
             if (contracts.length > 0) {
                 for (const c of contracts) {

+ 34 - 0
app/service/contract_sp_audit.js

@@ -947,6 +947,40 @@ module.exports = app => {
             return result;
         }
 
+        async makeAudits(transaction, options, cid, cpid = null, uid) {
+            // 创建审批人列表
+            const lastContractInfo = await this.getHaveAuditLastInfo(options, cid, cpid, uid);
+            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,
+                            });
+                        }
+                    }
+                    if (spAudits.length > 0) await transaction.insert(this.ctx.service.contractSpAudit.tableName, spAudits);
+                }
+            }
+        }
+
+        async getHaveAuditLastInfo(options, cid, cpid, 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 sqlParam = [tableName, this.tableName, options.contract_type, uid];
+            return await this.db.queryOne(sql, sqlParam);
+        }
+
         async saveAudit(contract, times, sp_group, data) {
             const transaction = await this.db.beginTransaction();
             try {

+ 9 - 0
app/service/cost_stage.js

@@ -53,6 +53,15 @@ module.exports = app => {
                 s.rela_stage = s.rela_stage ? JSON.parse(s.rela_stage) : null;
             });
         }
+        async calcRealtime(stage) {
+            const typeInfo = this.stageType[stage.stage_type];
+            if (!typeInfo) return;
+
+            stage.stage_tp = await this.ctx.service[typeInfo.dataService].getSum(stage);
+            for (const prop in stage.stage_tp) {
+                stage.stage_end_tp[prop] = this.ctx.helper.add(stage.stage_tp[prop], stage.stage_pre_tp[prop]);
+            }
+        }
         /**
          * 获取全部期
          * @param tid

+ 11 - 11
app/service/cost_stage_detail.js

@@ -9,7 +9,7 @@
  */
 
 const costFields = {
-    textFields: ['code', 'name', 'party_b', 'postil', 'memo'],
+    textFields: ['code', 'name', 'party_b', 'pay_date', 'postil', 'memo'],
     curFields: ['pay_tp', 'cut_tp', 'yf_tp', 'sf_tp', 'yf_excl_tax_tp', 'sf_excl_tax_tp'],
     readFields: ['read_pay_tp', 'read_cut_tp', 'read_yf_tp', 'read_sf_tp', 'read_yf_excl_tax_tp', 'read_sf_excl_tax_tp'],
     taxFields: ['tax'],
@@ -291,15 +291,15 @@ module.exports = app => {
                         stage_order: this.ctx.costStage.stage_order, is_deal: 1,
                         ledger_id: ledgerId, cost_id: costId, d_order: insertDetails.length + 1, source_cid: pay.cid,
                         add_user_id: user_id, update_user_id: user_id,
-                        code: pay.c_code, name: pay.name, party_b: pay.party_b,
+                        code: pay.c_code, name: pay.name, party_b: pay.party_b, pay_date: months || '',
                     };
                     insertDetails.push(idetail);
                 }
                 idetail.tax = pay.tax || 0;
                 idetail.pay_tp = this.ctx.helper.add(idetail.pay_tp, pay.pay_price);
                 idetail.cut_tp = this.ctx.helper.add(idetail.cut_tp, pay.debit_price);
-                idetail.yf_tp = this.ctx.helper.add(idetail.sf_tp, pay.yf_price);
-                idetail.sf_tp = this.ctx.helper.add(idetail.yf_tp, pay.sf_price);
+                idetail.yf_tp = this.ctx.helper.add(idetail.yf_tp, pay.yf_price);
+                idetail.sf_tp = this.ctx.helper.add(idetail.sf_tp, pay.sf_price);
                 const divNum = idetail.tax ? this.ctx.helper.add(1, this.ctx.helper.div(idetail.tax, 100)) : 1;
                 idetail.yf_excl_tax_tp = this.ctx.helper.add(idetail.yf_excl_tax_tp, this.ctx.helper.div(pay.yf_price, divNum, this.ctx.costStage.decimal.tp));
                 idetail.sf_excl_tax_tp = this.ctx.helper.add(idetail.sf_excl_tax_tp, this.ctx.helper.div(pay.sf_price, divNum, this.ctx.costStage.decimal.tp));
@@ -310,17 +310,17 @@ module.exports = app => {
             const conn = await this.db.beginTransaction();
             try {
                 await conn.delete(this.tableName, { stage_id: this.ctx.costStage.id, ledger_id: ledgerId });
-                await conn.insert(this.tableName, insertDetails);
+                if (insertDetails.length > 0) await conn.insert(this.tableName, insertDetails);
                 await conn.update(this.ctx.service.costStageLedger.tableName, billsUpdate);
                 await conn.commit();
             } catch(err) {
                 await conn.rollback();
                 throw err;
             }
-            const addData = await this.getAllDataByCondition({
-                where: { id: this.ctx.helper._.map(insertDetails, 'id') }
-            });
-            const ledgerData = await this.ctx.service.costStageLedger.getDataById(addData[0].ledger_id);
+            const addData = insertDetails.length > 0
+                ? await this.getAllDataByCondition({ where: { id: this.ctx.helper._.map(insertDetails, 'id') }})
+                : [];
+            const ledgerData = await this.ctx.service.costStageLedger.getDataById(ledgerId);
             return { detail: { add: addData, del: detailDatas.map(x => { return x.id; }) }, ledger: ledgerData };
         }
         async _importContractByTypes(ledgerId, costId, types, months) {
@@ -328,8 +328,8 @@ module.exports = app => {
             return await this._importContractByIds(ledgerId, costId, contracts.map(x => { return x.id; }), months);
         }
         async importContract(data) {
-            if (data.types) return this._importContractByTypes(data.ledger_id, data.cost_id, data.types, data.months || [this.ctx.costStage.stage_date]);
-            if (data.ids) return this._importContractByIds(data.ledger_id, data.cost_id, data.ids, data.months || [this.ctx.costStage.stage_date]);
+            if (data.types) return this._importContractByTypes(data.ledger_id, data.cost_id, data.types, data.months || this.ctx.costStage.stage_date);
+            if (data.ids) return this._importContractByIds(data.ledger_id, data.cost_id, data.ids, data.months || this.ctx.costStage.stage_date);
         }
 
         async deletePartData(transaction, tender_id, ledger_id) {

+ 1 - 0
app/service/cost_stage_file.js

@@ -60,6 +60,7 @@ module.exports = app => {
                         rela_id: x.rela_id, rela_sub_id: x.rela_sub_id,
                         user_id: user.id, user_name: user.name, user_company: user.company, user_role: user.role,
                         filename: x.filename, fileext: x.fileext, filesize: x.filesize, filepath: x.filepath,
+                        extra_upload: x.extra_upload,
                     };
                 });
                 await conn.insert(this.tableName, insertData);

+ 1 - 1
app/service/cost_stage_ledger.js

@@ -439,7 +439,7 @@ module.exports = app => {
                     nData.pay_tp = row.pay_tp !== undefined ? helper.round(row.pay_tp || 0, decimal.tp) : oData.pay_tp || 0;
                     nData.cut_tp = row.cut_tp !== undefined ? helper.round(row.cut_tp || 0, decimal.tp) : oData.cut_tp || 0;
                     nData.yf_tp = helper.sub(nData.pay_tp, nData.cut_tp);
-                    if (od.yf_tp === od.sf_tp) nData.sf_tp = nData.yf_tp;
+                    if (oData.yf_tp === oData.sf_tp) nData.sf_tp = nData.yf_tp;
                 }
                 if (row.sf_tp !== undefined) nData.sf_tp = helper.round(row.sf_tp || 0, decimal.tp);
                 if (row.tax !== undefined || nData.sf_tp !== undefined || nData.yf_tp !== undefined) {

+ 10 - 5
app/service/ledger.js

@@ -51,7 +51,7 @@ module.exports = app => {
                 fullPath: 'full_path',
                 keyPre: 'ledger_bills_maxLid:',
                 uuid: true,
-            }, 'pos', 'ancillaryGcl', 'ledgerExtra', 'posCalcDetail');
+            }, 'pos', 'ancillaryGcl', 'ancillaryGclDetail', 'ledgerExtra', 'posCalcDetail');
             this.depart = ctx.app.config.table_depart.heavy;
             this.tableName = 'ledger';
         }
@@ -290,10 +290,11 @@ module.exports = app => {
          * @private
          */
         async _deleteRelaData(mid, deleteData) {
-            await this.ctx.service.pos.deletePosData(this.transaction, mid, this._.map(deleteData, 'id'));
-            await this.ctx.service.ancillaryGcl.deletePartData(this.transaction, mid, this._.map(deleteData, 'id'));
             await this.transaction.delete(this.ctx.service.ledgerExtra.tableName, { tid: mid, id: this._.map(deleteData, 'id') });
+            await this.ctx.service.pos.deletePosData(this.transaction, mid, this._.map(deleteData, 'id'));
             await this.ctx.service.posCalcDetail.deleteBillsPartData(this.transaction, mid, this._.map(deleteData, 'id'));
+            await this.ctx.service.ancillaryGcl.deleteBillsPartData(this.transaction, mid, this._.map(deleteData, 'id'));
+            await this.ctx.service.ancillaryGclDetail.deleteBillsPartData(this.transaction, mid, this._.map(deleteData, 'id'));
         }
 
         _checkField(data, field) {
@@ -880,8 +881,12 @@ module.exports = app => {
                 } else {
                     await conn.insert(this.service.ledgerExtra.tableName, { id: data.id, tid, calc_template: data.calc_template });
                 }
-                // 重算所有数据
-                const calc = await this.ctx.service.posCalcDetail.resetBillsCalcTemplate(conn, data.id, data.calc_template);
+                if (!data.calc_template) {
+                    await this.ctx.service.posCalcDetail.deleteBillsPartData(conn, tid, data.id);
+                    await this.ctx.service.posCalcDetail.removeBillsCalcTemplate(conn, data.id);
+                } else {
+                    await this.ctx.service.posCalcDetail.resetBillsCalcTemplate(conn, data.id, data.calc_template);
+                }
                 await conn.commit();
             } catch (err) {
                 await conn.rollback();

+ 41 - 1
app/service/pos.js

@@ -357,6 +357,8 @@ module.exports = app => {
                 await transaction.delete(this.tableName, {tid: tid, id: data});
                 await transaction.update(this.ctx.service.ledger.tableName, updateBills);
                 await this.ctx.service.posCalcDetail.deletePosPartData(transaction, tid, data);
+                await this.ctx.service.ancillaryGcl.deletePosPartData(transaction, tid, data);
+                await this.ctx.service.ancillaryGclDetail.deletePosPartData(transaction, tid, data);
                 await transaction.commit();
                 updateBills.ledger_id = bills.ledger_id;
                 return { ledger: { update: [updateBills] }, pos: data };
@@ -427,6 +429,8 @@ module.exports = app => {
             if (!(data.block instanceof Array)) throw '提交数据错误';
 
             const result = { ledger: {}, pos: null };
+            const qtyDecimal = this.ctx.tender.info.decimal.qty;
+            const user_id = this.ctx.session.sessionUser.accountId;
 
             const bills = await this.ctx.service.ledger.getDataById(data.lid);
             const le = await this.ctx.service.ledgerExtra.getDataById(data.lid);
@@ -438,7 +442,7 @@ module.exports = app => {
                 ? { id: bills.id, sgfh_qty: bills.sgfh_qty, sjcl_qty: bills.sjcl_qty, qtcl_qty: bills.qtcl_qty, quantity: bills.quantity, ex_qty1: bills.ex_qty1 }
                 : { id: bills.id, sgfh_qty: 0, sjcl_qty: 0, qtcl_qty: 0, quantity: 0, ex_qty1: 0 };
 
-            const insertPos = [], insertDetail = [];
+            const insertPos = [], insertDetail = [], insertAncGcl = [], insertAncGclDetail = [];
             for (const [i, b] of data.block.entries()) {
                 const nip = {};
                 insertPos.push(nip);
@@ -506,6 +510,38 @@ module.exports = app => {
                 this._calcExpr(nip, 'qtcl_qty', b.qtcl_expr, b.qtcl_qty, precision);
                 nip.quantity = this.ctx.helper.add(nip.sgfh_qty, this.ctx.helper.add(nip.sjcl_qty, nip.qtcl_qty));
                 nip.ex_qty1 = this.ctx.helper.round(b.ex_qty1, precision.value);
+                for (const ag of b.ancGcl) {
+                    const nig = {
+                        id: this.uuid.v4(), tid: this.ctx.tender.id,
+                        add_user_id: user_id, update_user_id: user_id,
+                        lid: nip.lid, pid: nip.id, g_order: ag.g_order,
+                        is_aux: ag.is_aux || 0, name: ag.name || '', unit: ag.unit || '',
+                        drawing_code: ag.drawing_code || '', memo: ag.memo || '',
+                        quantity: ag.quantity || 0, expr: ag.expr || '', calc_template: ag.calc_template || ''
+                    };
+                    insertAncGcl.push(nig);
+                    if (!ag.calc_template) continue;
+
+                    let sumQty = 0;
+                    for (const cd of ag.calcDetail) {
+                        const newDetail = {
+                            id: this.uuid.v4(), tid: this.ctx.tender.id, lid: nip.lid, pid: nip.id, ag_id: nig.id,
+                            create_user_id: user_id, update_user_id: user_id,
+                            agd_order: cd.agd_order,
+                            str1 : cd.str1 || '', str2 : cd.str1 || '', str3 : cd.str1 || '', str4 : cd.str1 || '',
+                            num_a : cd.num_a || 0, num_b : cd.num_b || 0, num_c : cd.num_c || 0, num_d : cd.num_d || 0,
+                            num_e : cd.num_e || 0, num_f : cd.num_f || 0, num_g : cd.num_g || 0, num_h : cd.num_h || 0,
+                            num_i : cd.num_i || 0, num_j : cd.num_j || 0, num_k : cd.num_k || 0, num_l : cd.num_l || 0,
+                            num_m : cd.num_m || 0, num_n : cd.num_n || 0, num_o : cd.num_o || 0, num_p : cd.num_p || 0,
+                            num_q : cd.num_q || 0, num_r : cd.num_r || 0, num_s : cd.num_s || 0, num_t : cd.num_t || 0,
+                            num_u : cd.num_u || 0,
+                            qty: cd.qty || 0, expr: cd.expr || 0, spec: cd.spec || ''
+                        };
+                        sumQty = this.ctx.helper.add(sumQty, newDetail.qty);
+                        insertAncGclDetail.push(newDetail);
+                    }
+                    nig.quantity = this.ctx.helper.round(sumQty, qtyDecimal);
+                }
 
                 updateBills.sgfh_qty = this.ctx.helper.add(updateBills.sgfh_qty, nip.sgfh_qty);
                 updateBills.sjcl_qty = this.ctx.helper.add(updateBills.sjcl_qty, nip.sjcl_qty);
@@ -525,6 +561,8 @@ module.exports = app => {
             try {
                 await transaction.insert(this.tableName, insertPos);
                 if (insertDetail.length > 0) await transaction.insert(this.ctx.service.posCalcDetail.tableName, insertDetail);
+                if (insertAncGcl.length > 0) await transaction.insert(this.ctx.service.ancillaryGcl.tableName, insertAncGcl);
+                if (insertAncGclDetail.length > 0) await transaction.insert(this.ctx.service.ancillaryGclDetail.tableName, insertAncGclDetail);
                 await transaction.update(this.ctx.service.ledger.tableName, updateBills);
                 await transaction.commit();
             } catch (err) {
@@ -535,6 +573,8 @@ module.exports = app => {
             updateBills.ledger_id = bills.ledger_id;
             result.ledger.update = [updateBills];
             result.posCalcDetail = { add: insertDetail };
+            result.ancGcl = { add: insertAncGcl };
+            result.ancGclDetail = { add: insertAncGclDetail };
             return result;
         }
 

+ 23 - 1
app/service/pos_calc_detail.js

@@ -8,7 +8,7 @@
  * @version
  */
 const strField = ['str1', 'str2', 'str3', 'str4'];
-const numField = ['num_a', 'num_b', 'num_c', 'num_d', 'num_e', 'num_f', 'num_g', 'num_h', 'num_i'];
+const numField = ['num_a', 'num_b', 'num_c', 'num_d', 'num_e', 'num_f', 'num_g', 'num_h', 'num_i', 'num_j', 'num_k', 'num_l', 'num_m', 'num_n', 'num_o', 'num_p', 'num_q', 'num_r', 'num_s', 'num_t', 'num_u'];
 const specialField = ['spec', 'qty', 'expr'];
 const math = require('mathjs');
 math.config({
@@ -331,6 +331,28 @@ module.exports = app => {
             await transaction.update(this.ctx.service.ledger.tableName, ub);
             return detailUpdateDatas.length > 0 || posUpdateData.length > 0;
         }
+        async removeBillsCalcTemplate(transaction, lid) {
+            const user_id = this.ctx.session.sessionUser.accountId;
+
+            const ledgerData = await this.ctx.service.ledger.getDataById(lid);
+            const posDatas = await this.ctx.service.pos.getAllDataByCondition({ where: { lid } });
+
+            const posUpdateData = [];
+            let bQty = 0;
+            for (const p of posDatas) {
+                const up = { id: p.id, sgfh_qty: 0, sgfh_expr: '' };
+                up.quantity = this.ctx.helper.sum([up.sgfh_qty, p.sjcl_qty, p.qtcl_qty]);
+                posUpdateData.push(up);
+                bQty = this.ctx.helper.add(bQty, up.sgfh_qty);
+            }
+            const ub = { id: lid, sgfh_qty: bQty };
+            ub.sgfh_tp = this.ctx.helper.mul(ub.sgfh_qty, ledgerData.unit_price, this.ctx.tender.info.decimal.tp);
+            ub.quantity = this.ctx.helper.sum([ub.sgfh_qty, ledgerData.sjcl_qty, ledgerData.qtcl_qty]);
+            ub.total_price = this.ctx.helper.mul(ub.quantity, ledgerData.unit_price, this.ctx.tender.info.decimal.tp);
+
+            if (posUpdateData.length > 0) await transaction.updateRows(this.ctx.service.pos.tableName, posUpdateData);
+            await transaction.update(this.ctx.service.ledger.tableName, ub);
+        }
 
         async deleteBillsPartData(transaction, tid, lid) {
             await transaction.delete(this.tableName, { tid: tid, lid: lid });

+ 3 - 1
app/service/stage_change.js

@@ -166,7 +166,7 @@ class autoUseChange {
                 activeQty = this.helper.add(activeQty, preSb.qc_minus_qty);
                 end_contract_qty = this.helper.add(end_contract_qty, preSb.contract_qty);
             }
-            const end_contract_tp = activeQty ? bills.total_price : this.helper.mul(this.helper.div(end_contract_qty, activeQty), bills.total_price, this.decimal.tp);
+            const end_contract_tp = activeQty ? this.helper.mul(this.helper.div(end_contract_qty, activeQty), bills.total_price, this.decimal.tp) : bills.total_price;
             return preSb ? this.helper.sub(end_contract_tp, preSb.contract_tp) : end_contract_tp;
         } else if (info.calc_type === 'up') {
             if (this.correct) {
@@ -250,9 +250,11 @@ class autoUseChange {
 
             if (sb) {
                 const contract_tp = qc_minus_qty ? this.calcContractTp(this.info, cb.bills, sb.contract_qty || 0, qc_minus_qty) : sb.contract_tp;
+                console.log(qc_minus_qty, contract_tp);
                 this.updateBills.push({ id: sb.id, qc_qty, qc_tp, positive_qc_qty, positive_qc_tp, negative_qc_qty, negative_qc_tp, qc_minus_qty, contract_tp });
             } else {
                 const contract_tp = qc_minus_qty ? this.calcContractTp(this.info, cb.bills, 0, qc_minus_qty) : 0;
+                console.log(qc_minus_qty, contract_tp);
                 this.insertBills.push({
                     tid: this.default.tid, sid: this.default.sid, said: this.default.said,
                     lid, contract_tp, qc_qty, qc_tp, positive_qc_qty, positive_qc_tp, negative_qc_qty, negative_qc_tp, qc_minus_qty, times: 1, order: 0

+ 2 - 2
app/service/stage_stash.js

@@ -72,7 +72,7 @@ class loadStageExcelTree {
                 activeQty = this.ctx.helper.add(activeQty, preSb.qc_minus_qty);
                 end_contract_qty = this.ctx.helper.add(end_contract_qty, preSb.contract_qty);
             }
-            const end_contract_tp = activeQty ? node.total_price : this.ctx.helper.mul(this.ctx.helper.div(end_contract_qty, activeQty), node.total_price, this.decimal.tp);
+            const end_contract_tp = activeQty ? this.ctx.helper.mul(this.ctx.helper.div(end_contract_qty, activeQty), node.total_price, this.decimal.tp) : node.total_price;
             return preSb ? this.ctx.helper.sub(end_contract_tp, preSb.contract_tp) : end_contract_tp;
         } else if (this.info.calc_type === 'up') {
             if (correct) {
@@ -289,7 +289,7 @@ module.exports = app => {
                     let end_contract_qty = stageBills.contract_qty;
                     activeQty = helper.add(activeQty, bills.pre_qc_minus_qty);
                     end_contract_qty = helper.add(end_contract_qty, bills.pre_contract_qty);
-                    const end_contract_tp = activeQty ? bills.total_price : helper.mul(helper.div(end_contract_qty, activeQty), bills.total_price, decimal.tp);
+                    const end_contract_tp = activeQty ? helper.mul(helper.div(end_contract_qty, activeQty), bills.total_price, decimal.tp) : bills.total_price;
                     return helper.sub(end_contract_tp, bills.pre_contract_tp);
                 } else if (info.calc_type === 'up') {
                     if (correct) {

+ 2 - 2
app/view/contract/detail.ejs

@@ -176,13 +176,13 @@
                                     <thead>
                                     <tr class="text-center">
                                         <% if (ctx.contract_type === contractConst.type.income) { %>
-                                            <th width="4%">序号</th><th width="5%">回款日期</th><th width="8%">回款金额</th><th width="8%">扣款金额</th><th width="8%">应回金额</th><th width="8%">实回金额</th><th width="5%">回款方式</th><th width="5%">创建人</th><th width="9%">创建时间</th><th width="10%">备注</th><th width="5%">附件</th>
+                                            <th width="4%">序号</th><th width="5%">回款日期</th><th width="5%">用途</th><th width="7%">回款金额</th><th width="7%">扣款金额</th><th width="7%">应回金额</th><th width="7%">实回金额</th><th width="5%">回款方式</th><th width="5%">创建人</th><th width="9%">创建时间</th><th width="10%">备注</th><th width="4%">附件</th>
                                             <% if (pay_shenpi_status) { %>
                                             <th width="15%">审批进度</th>
                                             <% } %>
                                             <th width="10%">操作</th>
                                         <% } else if (ctx.contract_type === contractConst.type.expenses) { %>
-                                            <th width="4%">序号</th><th width="5%">支付日期</th><th width="8%">付款金额</th><th width="8%">扣款金额</th><th width="8%">应付金额</th><th width="8%">实付金额</th><th width="5%">支付方式</th><th width="5%">创建人</th><th width="9%">创建时间</th><th width="10%">备注</th><th width="5%">附件</th>
+                                            <th width="4%">序号</th><th width="5%">支付日期</th><th width="5%">用途</th><th width="7%">付款金额</th><th width="7%">扣款金额</th><th width="7%">应付金额</th><th width="7%">实付金额</th><th width="5%">支付方式</th><th width="5%">创建人</th><th width="9%">创建时间</th><th width="10%">备注</th><th width="4%">附件</th>
                                             <% if (pay_shenpi_status) { %>
                                             <th width="15%">审批进度</th>
                                             <% } %>

+ 9 - 0
app/view/contract/detail_modal.ejs

@@ -161,6 +161,15 @@
                     <input class="datepicker-here form-control form-control-sm" name="pay_time" placeholder="点击选择时间" data-date-format="yyyy-MM-dd" data-language="zh" type="text" autocomplete="off">
                 </div>
                 <div class="form-group">
+                    <label>用途</label>
+                    <select class="form-control form-control-sm" name="used">
+                        <option>合同</option>
+                        <% for (u of used) { %>
+                        <option><%= u %></option>
+                        <% } %>
+                    </select>
+                </div>
+                <div class="form-group">
                     <label><% if (ctx.contract_type === contractConst.type.expenses) { %>付<% } else if (ctx.contract_type === contractConst.type.income) { %>回<% } %>款金额<b class="text-danger">*</b></label>
                     <input class="form-control form-control-sm" name="pay_price" placeholder="请输入<% if (ctx.contract_type === contractConst.type.expenses) { %>付<% } else if (ctx.contract_type === contractConst.type.income) { %>回<% } %>款金额" type="number">
                 </div>

+ 165 - 10
app/view/contract/setting.ejs

@@ -9,7 +9,7 @@
         <div class="c-body">
             <div class="sjs-height-0">
                 <div class="row m-0 mt-3">
-                    <div class="col-8">
+                    <div class="col-6">
                         <div class="card mb-3">
                             <div class="card-header">
                                 <div class="float-left">合同类型</div>
@@ -18,16 +18,16 @@
                                     <button id="set-type-btn" class="btn btn-sm btn-success">保存</button>
                                 </div>
                             </div>
-                            <div class="card-body" id="contract-types-set">
-                                <nav class="nav nav-tabs" id="types-tabs">
-                                    <a class="nav-link nav-item <% if (types_from === 'subProject') { %>active<% } %>" data-toggle="tab" data-tab="subProject-types-tab" href="#subProject-types-tab" role="tab" type="subProject">项目</a>
-                                    <a class="nav-link nav-item <% if (types_from !== 'subProject') { %>active<% } %>" data-toggle="tab" data-tab="tender-types-tab" href="#tender-types-tab" role="tab" type="tender">标段</a>
+                            <div class="card-body" id="contract-type-set">
+                                <nav class="nav nav-tabs" id="type-tabs">
+                                    <a class="nav-link nav-item <% if (types_from === 'subProject') { %>active<% } %>" data-toggle="tab" data-tab="subProject-type-tab" href="#subProject-type-tab" role="tab" type="subProject">项目</a>
+                                    <a class="nav-link nav-item <% if (types_from !== 'subProject') { %>active<% } %>" data-toggle="tab" data-tab="tender-type-tab" href="#tender-type-tab" role="tab" type="tender">标段</a>
                                     <div class="ml-auto">
-                                        <a href="javascript:void(0);" style="vertical-align: sub;" id="addType">新增类型</a>
+                                        <a href="javascript:void(0);" style="vertical-align: sub;" id="add-type-btn">新增类型</a>
                                     </div>
                                 </nav>
                                 <div class="tab-content my-2">
-                                    <div class="tab-pane <% if (types_from === 'subProject') { %>active<% } %>" id="subProject-types-tab" style="max-height: 400px;overflow: auto;">
+                                    <div class="tab-pane <% if (types_from === 'subProject') { %>active<% } %>" id="subProject-type-tab" style="max-height: 400px;overflow: auto;">
                                         <table class="table table-bordered">
                                             <thead><tr class="text-center">
                                                 <th width="60%">名称</th>
@@ -46,7 +46,7 @@
                                             </tbody>
                                         </table>
                                     </div>
-                                    <div class="tab-pane <% if (types_from !== 'subProject') { %>active<% } %>" id="tender-types-tab" style="max-height: 400px;overflow: auto;">
+                                    <div class="tab-pane <% if (types_from !== 'subProject') { %>active<% } %>" id="tender-type-tab" style="max-height: 400px;overflow: auto;">
                                         <table class="table table-bordered">
                                             <thead><tr class="text-center">
                                                 <th width="60%">名称</th>
@@ -68,6 +68,159 @@
                                 </div>
                             </div>
                         </div>
+                    </div>
+                    <div class="col-6">
+                        <div class="card mb-3">
+                            <div class="card-header">
+                                <div class="float-left">资金用途</div>
+                                <div class="float-right" id="show-used-btn" style="display: none">
+                                    <button id="cancel-used-btn" class="btn btn-sm btn-secondary">取消</button>
+                                    <button id="set-used-btn" class="btn btn-sm btn-success">保存</button>
+                                </div>
+                            </div>
+                            <div class="card-body" id="contract-used-set">
+                                <nav class="nav nav-tabs" id="used-tabs">
+                                    <a class="nav-link nav-item <% if (types_from === 'subProject') { %>active<% } %>" data-toggle="tab" data-tab="subProject-used-tab" href="#subProject-used-tab" role="tab" type="subProject">项目</a>
+                                    <a class="nav-link nav-item <% if (types_from !== 'subProject') { %>active<% } %>" data-toggle="tab" data-tab="tender-used-tab" href="#tender-used-tab" role="tab" type="tender">标段</a>
+                                    <div class="ml-auto">
+                                        <a href="javascript:void(0);" style="vertical-align: sub;" id="add-used-btn">新增用途</a>
+                                    </div>
+                                </nav>
+                                <div class="tab-content my-2">
+                                    <div class="tab-pane <% if (types_from === 'subProject') { %>active<% } %>" id="subProject-used-tab" style="max-height: 400px;overflow: auto;">
+                                        <table class="table table-bordered">
+                                            <thead><tr class="text-center">
+                                                <th width="60%">名称</th>
+                                                <th>操作</th>
+                                            </tr>
+                                            </thead>
+                                            <tbody class="text-center" id="subProject-used-table">
+                                            <tr>
+                                                <td>合同</td>
+                                                <td></td>
+                                            </tr>
+                                            <% for (const type of used) { %>
+                                                <tr>
+                                                    <td><input class="form-control form-control-sm" name="value" placeholder="请输入值" value="<%- type %>"></td>
+                                                    <td>
+                                                        <a href="javascript:void(0);" class="btn btn-sm text-danger remove-used-btn"><i class="fa fa-remove"></i></a>
+                                                    </td>
+                                                </tr>
+                                            <% } %>
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                    <div class="tab-pane <% if (types_from !== 'subProject') { %>active<% } %>" id="tender-used-tab" style="max-height: 400px;overflow: auto;">
+                                        <table class="table table-bordered">
+                                            <thead><tr class="text-center">
+                                                <th width="60%">名称</th>
+                                                <th>操作</th>
+                                            </tr>
+                                            </thead>
+                                            <tbody class="text-center" id="tender-used-table">
+                                            <tr>
+                                                <td>合同</td>
+                                                <td></td>
+                                            </tr>
+                                            <% for (const type of tender_used) { %>
+                                                <tr>
+                                                    <td><input class="form-control form-control-sm" name="value" placeholder="请输入值" value="<%- type %>"></td>
+                                                    <td>
+                                                        <a href="javascript:void(0);" class="btn btn-sm text-danger remove-used-btn"><i class="fa fa-remove"></i></a>
+                                                    </td>
+                                                </tr>
+                                            <% } %>
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="col-8">
+                        <div class="card mb-3">
+                            <div class="card-header d-flex justify-content-between">
+                                <div class="card-title mb-0">合同附加属性(未开发完,勿点!)</div>
+                                <span class="pull-right">项目/标段合同同步生效</span>
+                            </div>
+                            <div class="card-body">
+                                <table class="table">
+                                    <tr class="text-center">
+                                        <th width="15%">名称</th>
+                                        <th width="15%">类型</th>
+                                        <th width="15%">别名</th>
+                                        <th width="10%">显示</th>
+                                        <th width="">说明</th>
+                                        <th width="15%">操作</th>
+                                    </tr>
+                                    <tbody class="text-center">
+                                    <tr>
+                                        <td>税率(%)</td><td>数字</td><td>-</td>
+                                        <td><input type="checkbox" class=""></td>
+                                        <td></td>
+                                        <td>
+                                            <a href="#" class="mx-2 text-muted"><i class="fa fa-arrow-up"></i></a>
+                                            <a href="#" class="mx-2"><i class="fa fa-arrow-down"></i></a>
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>数值</td><td>数字</td><td><input type="text" class="form-control form-control-sm"></td>
+                                        <td><input type="checkbox" class=""></td>
+                                        <td></td>
+                                        <td>
+                                            <a href="#" class="mx-2"><i class="fa fa-arrow-up"></i></a>
+                                            <a href="#" class="mx-2"><i class="fa fa-arrow-down"></i></a>
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>丙方</td><td>文本</td><td><input type="text" class="form-control form-control-sm"></td>
+                                        <td><input type="checkbox" class=""></td>
+                                        <td></td>
+                                        <td>
+                                            <a href="#" class="mx-2"><i class="fa fa-arrow-up"></i></a>
+                                            <a href="#" class="mx-2"><i class="fa fa-arrow-down"></i></a>
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>丁方</td><td>文本</td><td><input type="text" class="form-control form-control-sm"></td>
+                                        <td><input type="checkbox" class=""></td>
+                                        <td></td>
+                                        <td>
+                                            <a href="#" class="mx-2"><i class="fa fa-arrow-up"></i></a>
+                                            <a href="#" class="mx-2"><i class="fa fa-arrow-down"></i></a>
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>文本</td><td>文本</td><td><input type="text" class="form-control form-control-sm"></td>
+                                        <td><input type="checkbox" class=""></td>
+                                        <td></td>
+                                        <td>
+                                            <a href="#" class="mx-2"><i class="fa fa-arrow-up"></i></a>
+                                            <a href="#" class="mx-2"><i class="fa fa-arrow-down"></i></a>
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>合同内容</td><td>长文本</td><td><input type="text" class="form-control form-control-sm"></td>
+                                        <td><input type="checkbox" class=""></td>
+                                        <td>独占一行,上限1000</td>
+                                        <td>
+                                            <a href="#" class="mx-2"><i class="fa fa-arrow-up"></i></a>
+                                            <a href="#" class="mx-2"><i class="fa fa-arrow-down"></i></a>
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <td>支付条件</td><td>长文本</td><td><input type="text" class="form-control form-control-sm"></td>
+                                        <td><input type="checkbox" class=""></td>
+                                        <td>独占一行,上限1000</td>
+                                        <td>
+                                            <a href="#" class="mx-2"><i class="fa fa-arrow-up"></i></a>
+                                            <a href="#" class="mx-2 text-muted"><i class="fa fa-arrow-down"></i></a>
+                                        </td>
+                                    </tr>
+                                    </tbody>
+                                </table>
+                            </div>
+                        </div>
                         <div class="card mb-3">
                             <div class="card-header">
                                 <div class="float-left">审批设置</div>
@@ -105,6 +258,8 @@
 </div>
 <script>
     const types_from = JSON.parse(unescape('<%- escape(JSON.stringify(types_from)) %>'));
-    let subProject_types = JSON.parse(unescape('<%- escape(JSON.stringify(types)) %>'));
-    let tender_types = JSON.parse(unescape('<%- escape(JSON.stringify(tender_types)) %>'));
+    let subProject_type = JSON.parse(unescape('<%- escape(JSON.stringify(types)) %>'));
+    let tender_type = JSON.parse(unescape('<%- escape(JSON.stringify(tender_types)) %>'));
+    let subProject_used = JSON.parse(unescape('<%- escape(JSON.stringify(used)) %>'));
+    let tender_used = JSON.parse(unescape('<%- escape(JSON.stringify(tender_used)) %>'));
 </script>

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

@@ -81,4 +81,5 @@
     const bllsSpreadSetting = JSON.parse(unescape('<%- escape(JSON.stringify( ctx.costStage.calcTemplate.spread_cache )) %>'));
     const dealSpreadSetting = JSON.parse(unescape('<%- escape(JSON.stringify( ctx.costStage.calcTemplate.sub_spread_cache.deal )) %>'));
     const commonSpreadSetting = JSON.parse(unescape('<%- escape(JSON.stringify( ctx.costStage.calcTemplate.sub_spread_cache.common )) %>'));
+    const costStageComplete = <%- ctx.costStage.audit_status === auditConst.status.checked %>;
 </script>

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

@@ -72,4 +72,5 @@
     const readOnly = <%- ctx.costStage.readOnly %>;
     const tenderId = parseInt('<%- ctx.tender.id %>');
     const stageId = parseInt('<%- ctx.costStage.id %>');
+    const costStageComplete = <%- ctx.costStage.audit_status === auditConst.status.checked %>;
 </script>

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

@@ -88,4 +88,5 @@
     const tenderId = parseInt('<%- ctx.tender.id %>');
     const stageId = parseInt('<%- ctx.costStage.id %>');
     const stageDate = '<%- ctx.costStage.stage_date %>';
+    const costStageComplete = <%- ctx.costStage.audit_status === auditConst.status.checked %>;
 </script>

+ 8 - 8
app/view/ledger/explode.ejs

@@ -153,21 +153,21 @@
                         </div>
                     </div>
                     <div class="sp-wrap" style="display: flex; flex-wrap: wrap;">
-                        <div class="c-body" id="pos-spread" style="width: 100%">
+                        <div class="c-body h-100" id="pos-spread" style="width: 100%">
                         </div>
-                        <div class="c-body" id="pos-right" style="display: none; width: 40%;">
+                        <div class="c-body h-100" id="pos-right" style="display: none; width: 40%;">
                             <div class="resize-x" id="pos-right-spr" r-Type="width" div1="#pos-spread" div2="#pos-right" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
-                            <div class="tab-content sp-wrap">
-                                <div id="anc-gcl" class="tab-pane table-select-show">
-                                    <div id="anc-gcl-spread" style="height: 235px;">
+                            <div class="tab-content h-100">
+                                <div id="anc-gcl" class="tab-pane table-select-show h-100">
+                                    <div id="anc-gcl-spread" class="h-50">
                                     </div>
-                                    <div id="anc-gcl-detail" style="height: 235px;">
+                                    <div id="anc-gcl-detail" class="h-50">
                                         <div id="anc-gcl-spr" class="resize-y" r-Type="height" div1="#anc-gcl-spread" div2="#anc-gcl-detail" r-parent="div2" title="调整大小"><!--调整上下高度条--></div>
                                         <div id="anc-gcl-detail-spread" class="w-100 h-100"></div>
                                     </div>
                                 </div>
-                                <div id="pos-detail" class="tab-pane table-select-show">
-                                    <div class="sp-wrap" id="pos-detail-spread">
+                                <div id="pos-detail" class="tab-pane table-select-show h-100">
+                                    <div class="h-100" id="pos-detail-spread">
                                     </div>
                                 </div>
                             </div>

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

@@ -128,6 +128,14 @@
                 </div>
             </div>
             <div class="modal-body">
+                <div class="input-group input-group-sm mr-2 mb-2">
+                    <input type="text" class="form-control" placeholder="输入编号/名称查找" id="select-calc-template-sk">
+                    <div class="input-group-append"><span class="input-group-text" id="select-calc-template-sr">结果:0</span></div>
+                    <div class="input-group-append">
+                        <button class="btn btn-outline-secondary" type="button" title="上一个" id="select-calc-template-sp"><i class="fa fa-angle-double-left"></i></button>
+                        <button class="btn btn-outline-secondary" type="button" title="下一个" id="select-calc-template-sn"><i class="fa fa-angle-double-right"></i></button>
+                    </div>
+                </div>
                 <div id="sct-spread" class="modal-height-500"></div>
             </div>
             <div class="modal-footer">

+ 49 - 0
sql/update.sql

@@ -25,6 +25,7 @@ ADD COLUMN `need_shenpi` tinyint(1) NULL COMMENT '是否需要审批' AFTER `fpc
 ADD COLUMN `status` tinyint(2) NULL DEFAULT 1 COMMENT '审批状态' AFTER `need_shenpi`,
 ADD COLUMN `times` tinyint(3) NULL DEFAULT 1 COMMENT '审批次数' AFTER `status`,
 ADD COLUMN `sp_group` int(11) NULL DEFAULT 0 COMMENT '固定审批组id' AFTER `times`,
+ADD COLUMN `used` varchar(255) NULL DEFAULT '合同' COMMENT '资金用途' AFTER `pay_time`,
 ADD COLUMN `final_auditor_str` varchar(50) NOT NULL DEFAULT '' COMMENT '终审人相关(cache)' AFTER `sp_group`;
 
 ALTER TABLE `zh_contract_tree`
@@ -121,6 +122,7 @@ CREATE TABLE `zh_cost_stage_file`  (
   `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
   `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
   `is_deleted` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否删除',
+  `extra_upload` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否为审批完成后追加',
   PRIMARY KEY (`id`) USING BTREE
 ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
 
@@ -182,6 +184,7 @@ CREATE TABLE `zh_cost_stage_detail`  (
   `code` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '编号',
   `name` varchar(255) CHARACTER SET utf16 NOT NULL DEFAULT '' COMMENT '名称',
   `party_b` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '乙方',
+  `pay_date` varchar(50) NOT NULL DEFAULT '' COMMENT '支付年月',
   `pre_pay_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '截止上期-付款',
   `pre_cut_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '截止上期-扣款',
   `pre_yf_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '截止上期-应付',
@@ -482,6 +485,52 @@ ALTER TABLE `zh_ancillary_gcl`
 ADD COLUMN `pid` varchar(36) NOT NULL DEFAULT '' COMMENT '计量单元id' AFTER `lid`,
 ADD COLUMN `calc_template` varchar(36) NOT NULL DEFAULT '' COMMENT '明细计算模板' AFTER `update_time`;
 
+CREATE TABLE `zh_ancillary_gcl_detail`  (
+  `id` varchar(36) NOT NULL COMMENT 'uuid',
+  `tid` int(11) NOT NULL COMMENT '标段id(zh_tender.id)',
+  `lid` varchar(36) NOT NULL COMMENT '台账id(zh_ledger.id)',
+  `pid` varchar(36) NOT NULL COMMENT '计量单元id(zh_pos.id)',
+  `ag_id` varchar(36) NOT NULL COMMENT '附属工程量id(zh_ancillary_gcl.id)',
+  `agd_order` int(11) NOT NULL COMMENT '排序',
+  `create_user_id` int(11) NOT NULL COMMENT '新增用户id(zh_project_account.id)',
+  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '新增时间',
+  `update_user_id` int(11) NOT NULL COMMENT '最后修改用户id(zh_project_account.id)',
+  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
+  `qty` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数量',
+  `expr` varchar(255) NOT NULL DEFAULT '' COMMENT '数量计算式',
+  `str1` varchar(255) NOT NULL DEFAULT '' COMMENT '文本1',
+  `str2` varchar(255) NOT NULL DEFAULT '' COMMENT '文本2',
+  `str3` varchar(255) NOT NULL DEFAULT '' COMMENT '文本3',
+  `str4` varchar(255) NOT NULL DEFAULT '' COMMENT '文本4',
+  `spec` varchar(50) NOT NULL DEFAULT '' COMMENT '规格',
+  `num_a` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值a',
+  `num_b` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值b',
+  `num_c` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值c',
+  `num_d` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值d',
+  `num_e` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值e',
+  `num_f` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值f',
+  `num_g` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值g',
+  `num_h` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值h',
+  `num_i` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值i',
+  `num_j` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值j',
+  `num_k` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值k',
+  `num_l` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值l',
+  `num_m` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值m',
+  `num_n` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值n',
+  `num_o` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值o',
+  `num_p` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值p',
+  `num_q` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值q',
+  `num_r` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值r',
+  `num_s` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值s',
+  `num_t` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值t',
+  `num_u` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '数值u',
+  PRIMARY KEY (`id`)
+);
+
+ALTER TABLE `zh_tender_cache`
+MODIFY COLUMN `stage_flow_cur_info` varchar(5000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '期-当前流程信息(json)' AFTER `stage_flow_cur_uid`,
+MODIFY COLUMN `stage_flow_pre_info` varchar(5000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '期-上一流程信息(json)' AFTER `stage_flow_pre_uid`;
+
 ------------------------------------
 -- 表数据
 ------------------------------------