Kaynağa Gözat

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

Tony Kang 2 gün önce
ebeveyn
işleme
3079cac726

+ 1 - 1
app/const/contract.js

@@ -82,7 +82,7 @@ const defaultColSet = {
 };
 
 const attributeSet = [
-    { name: '数值', field: 'num_val', fixed: [], type: 'int', type_name: '数字' },
+    { name: '数值', field: 'num_val', fixed: [], type: 'number', type_name: '数字' },
     { name: '丙方', field: 'party_c', fixed: [], type: 'text', type_name: '文本' },
     { name: '丁方', field: 'party_d', fixed: [], type: 'text', type_name: '文本' },
     { name: '文本', field: 'text', fixed: [], type: 'text', type_name: '文本' },

+ 3 - 0
app/controller/contract_controller.js

@@ -332,6 +332,9 @@ module.exports = app => {
                     const groupList = accountList.filter(item1 => item1.company === item.name);
                     return { groupName: item.name, groupList };
                 }).filter(x => { return x.groupList.length > 0; });
+                const contractAttributeSet = commonJson && commonJson.contract_attribute ? commonJson.contract_attribute : contractConst.attributeSet;
+                const attributeSet = ctx.service.contractColSet.analysisColSetWithDefine(contractConst.attributeSet, contractAttributeSet, contractConst.defaultAttributeSet);
+                renderData.attributeSet = ctx.helper._.filter(attributeSet, { show: 1 });
                 await this.layout('contract/detail.ejs', renderData, 'contract/detail_modal.ejs');
             } catch (err) {
                 ctx.log(err);

+ 8 - 1
app/controller/cost_controller.js

@@ -128,11 +128,14 @@ module.exports = app => {
                 const stage_type = this.ctx.service.costStage.stageType.book.key;
                 const stages = await this.getTypeStages(ctx, stage_type);
                 const validLedgerStages = await this.ctx.service.costStage.getAllCheckedStages(ctx.tender.id, 'ledger', 'DESC');
+                for (const vls of validLedgerStages) {
+                    vls.beenRela = stages.findIndex(x => { return x.rela_stage.sid === vls.id; }) >= 0;
+                }
                 const renderData = {
                     stage_type,
                     auditType: audit.auditType,
                     stages,
-                    validLedgerStages,
+                    validLedgerStages: validLedgerStages.filter(x => { return !x.beenRela; }),
                     auditConst: audit.common,
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.cost.cost_stage)
                 };
@@ -254,6 +257,9 @@ module.exports = app => {
                 const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
                 const commonJson = ctx.subProject.common_json ? JSON.parse(ctx.subProject.common_json) : null;
                 const contractTypes = commonJson && commonJson.tender_contract_type ? commonJson.tender_contract_type : [];
+                const contractUsed = commonJson && commonJson.tender_contract_used ? commonJson.tender_contract_used : [];
+                contractUsed.unshift('合同');
+                contractUsed.unshift('全部');
                 let payStages = [];
                 if (ctx.subProject.page_show.phasePay) {
                     const pstages = await this.ctx.service.phasePay.getAllDataByCondition({
@@ -281,6 +287,7 @@ module.exports = app => {
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.cost[`cost_stage_${stageTypeInfo.key}`]),
                     shenpi_status: ctx.tender.info.shenpi[stageTypeInfo.shenpi_status] || 1,
                     contractTypes,
+                    contractUsed,
                 };
                 await this.layout(`cost/${stageTypeInfo.key}.ejs`, renderData, `cost/${stageTypeInfo.key}_modal.ejs`);
             } catch (err) {

+ 1 - 1
app/lib/rm/tender_material.js

@@ -452,7 +452,7 @@ class ReportMemoryMaterial {
         if (!material) return [];
 
         const materials = await this.ctx.service.material.getAllDataByCondition({
-            where: {tid: this.ctx.material.tid},
+            where: {tid: material.tid},
             orders: [['order', 'desc']],
         });
         if (materials.length === 0) return [];

+ 5 - 0
app/middleware/stage_check.js

@@ -98,6 +98,11 @@ module.exports = options => {
                 stage.readOnly = true;
                 stage.curTimes = stage.times;
                 stage.curOrder = _.max(_.map(stage.auditors, 'order'));
+            } else if (stage.status === status.checking && stage.curAuditors[0].audit_type === auditType.key.multi) {
+                stage.curOrder = stage.curAuditors[0].order;
+                stage.curTimes = stage.times;
+                const ass = stage.auditAssists.find(x => { return stage.curAuditorIds.indexOf(x.user_id) >= 0 && x.ass_user_id === accountId; });
+                stage.readOnly = stage.curAuditorIds.indexOf(accountId) < 0 && !ass;
             } else {
                 const ass = stage.auditAssists.find(x => { return stage.flowAuditorIds.indexOf(x.user_id) >= 0 && x.ass_user_id === accountId; });
                 stage.readOnly = stage.flowAuditorIds.indexOf(accountId) < 0 && !ass;

+ 46 - 5
app/public/js/contract_detail.js

@@ -11,6 +11,7 @@ $(document).ready(function() {
             {title: '创建人', colSpan: '1', rowSpan: '2', field: 'username', hAlign: 1, width: 80, formatter: '@', readOnly: true},
             {title: '合同金额', colSpan: '1', rowSpan: '2', field: 'total_price', hAlign: 2, width: 120, formatter: '@', readOnly: true},
             {title: '合同类型', colSpan: '1', rowSpan: '2', field: 'type', hAlign: 0, width: 120, readOnly: 'readOnly.type', cellType: 'customizeCombo', comboItems: types, cellTypeKey: 1},
+            {title: '合同状态', colSpan: '1', rowSpan: '2', field: 'status', hAlign: 1, width: 100, formatter: '@', readOnly: true, getValue:'getValue.status', foreColor:'foreColor.status'},
             {title: '甲方', colSpan: '1', rowSpan: '2', field: 'party_a', hAlign: 1, width: 80, formatter: '@', readOnly: true},
             {title: '乙方', colSpan: '1', rowSpan: '2', field: 'party_b', hAlign: 1, width: 80, formatter: '@', readOnly: true},
             {title: '备注', colSpan: '1', rowSpan: '2', field: 'remark', hAlign: 0, width: 120, formatter: '@', cellType: 'ellipsisAutoTip', scrollHeightClass: '.sjs-height-1'},
@@ -351,6 +352,11 @@ $(document).ready(function() {
                         $('#htdetail_' + c).text(node[c] || '');
                     }
                     $('#htdetail_df_price').text(ZhCalc.sub(node.yf_price, node.sf_price) || '');
+                    const attrInfo = node.attribute_json ? JSON.parse(node.attribute_json) : null;
+                    for (const attr of attributeSet) {
+                        $('#htdetail_' + attr.field).text(attrInfo && attrInfo[attr.field] ? attrInfo[attr.field] : '');
+                    }
+
                 }
             } else if ($('.bc-bar .nav li .active').attr('href') === '#htpay') {
                 $('#edit_contract_btn').hide();
@@ -553,6 +559,7 @@ $(document).ready(function() {
 
             if (node && node.c_code) {
                 $('#htdetail-table').show();
+                $('#htdetail-attr').show();
                 $('#htpay-table').show();
                 $('#htfile-table').show();
                 for (const c of contractDetail) {
@@ -565,6 +572,10 @@ $(document).ready(function() {
                     }
                     $('#htdetail_' + c).text(node[c] || '');
                 }
+                const attrInfo = node.attribute_json ? JSON.parse(node.attribute_json) : null;
+                for (const attr of attributeSet) {
+                    $('#htdetail_' + attr.field).text(attrInfo && attrInfo[attr.field] ? attrInfo[attr.field] : '');
+                }
                 $('#htdetail_df_price').text(ZhCalc.sub(node.yf_price, node.sf_price) || '');
                 if (needPost) {
                     postData(window.location.pathname + '/update', {postType: 'get-contract', postData: node.id}, function (result) {
@@ -577,6 +588,7 @@ $(document).ready(function() {
                 }
             } else {
                 $('#htdetail-table').hide();
+                $('#htdetail-attr').hide();
                 $('#htpay-table').hide();
                 $('#htfile-table').hide();
                 $('#edit_contract_btn').hide();
@@ -1147,6 +1159,7 @@ $(document).ready(function() {
             }];
             if (oldParentNode.children.length === 1) {
                 oldParentNode.is_leaf = 1;
+                oldParentNode.total_price = null;
                 data.push({
                     id: oldParentNode.id,
                     is_leaf: 1,
@@ -1176,8 +1189,8 @@ $(document).ready(function() {
                 toastr.success('合同已移动成功');
                 const refreshNode = contractTree.loadPostData(result);
                 contractTreeSpreadObj.refreshTree(contractSheet, refreshNode);
-                const newNode = SpreadJsObj.getSelectObject(contractSheet);
-                contractTreeSpreadObj.changeContractTab(newNode, true);
+                contractTreeSpreadObj.setContract(contractSheet);
+                contractTreeSpreadObj.refreshOperationValid(contractSheet);
                 $('#cons-move').modal('hide');
             });
         });
@@ -1267,6 +1280,7 @@ $(document).ready(function() {
             }
             if (node.children.length === data.length) {
                 node.is_leaf = 1;
+                node.total_price = null;
                 data.push({
                     id: node.id,
                     is_leaf: 1,
@@ -1297,8 +1311,10 @@ $(document).ready(function() {
                 toastr.success('合同已移动成功');
                 const refreshNode = contractTree.loadPostData(result);
                 contractTreeSpreadObj.refreshTree(contractSheet, refreshNode);
-                const newNode = SpreadJsObj.getSelectObject(contractSheet);
-                contractTreeSpreadObj.changeContractTab(newNode, true);
+                contractTreeSpreadObj.setContract(contractSheet);
+                contractTreeSpreadObj.refreshOperationValid(contractSheet);
+                // const newNode = SpreadJsObj.getSelectObject(contractSheet);
+                // contractTreeSpreadObj.changeContractTab(newNode, true);
                 $('#cons-move2').modal('hide');
             });
         });
@@ -1650,6 +1666,14 @@ $(document).ready(function() {
                     $('#htdetail_' + c).html('<input type="text" class="form-control form-control-sm" value="' + node[c] + '">');
                 }
             }
+            const attrInfo = node.attribute_json ? JSON.parse(node.attribute_json) : null;
+            for (const attr of attributeSet) {
+                if (attr.type === 'long_text') {
+                    $('#htdetail_' + attr.field).html('<textarea class="form-control form-control-sm" rows="3">' + (attrInfo && attrInfo[attr.field] ? attrInfo[attr.field] : '') + '</textarea>');
+                    continue;
+                }
+                $('#htdetail_' + attr.field).html('<input type="' + attr.type + '" class="form-control form-control-sm" value="' + (attrInfo && attrInfo[attr.field] ? attrInfo[attr.field] : '') + '">');
+            }
         }
     });
 
@@ -1671,6 +1695,15 @@ $(document).ready(function() {
                     data[c] = $('#htdetail_' + c + ' input').val();
                 }
             }
+            const attrInfo = node.attribute_json ? JSON.parse(node.attribute_json) : {};
+            for (const attr of attributeSet) {
+                if (attr.type === 'long_text') {
+                    attrInfo[attr.field] = $('#htdetail_' + attr.field + ' textarea').val();
+                    continue;
+                }
+                attrInfo[attr.field] = $('#htdetail_' + attr.field + ' input').val();
+            }
+            data.attribute_json = JSON.stringify(attrInfo);
             if (data.c_code === '' || data.name === '') {
                 toastr.warning('合同编号和合同名称不能为空');
                 return;
@@ -1726,6 +1759,14 @@ $(document).ready(function() {
                     }
                 }
             }
+            const attrInfo = node.attribute_json ? JSON.parse(node.attribute_json) : {};
+            for (const attr of attributeSet) {
+                if (attr.type === 'long_text') {
+                    $('#htdetail_' + attr.field).text(attrInfo && attrInfo[attr.field] ? attrInfo[attr.field] : '');
+                    continue;
+                }
+                $('#htdetail_' + attr.field).text(attrInfo && attrInfo[attr.field] ? attrInfo[attr.field] : '');
+            }
         }
     });
 
@@ -2229,7 +2270,7 @@ $(document).ready(function() {
                 total_price: $('#cons-add input[name="total_price"]').val(),
                 party_a: $('#cons-add input[name="party_a"]').val(),
                 party_b: $('#cons-add input[name="party_b"]').val(),
-                sign_date: $('#cons-add input[name="sign_date"]').val(),
+                sign_date: $('#cons-add input[name="sign_date"]').val() || null,
                 remark: $('#cons-add textarea[name="remark"]').val(),
                 type: $('#cons-add select[name="type"]').val(),
             }

+ 1 - 0
app/public/js/cost_stage_analysis.js

@@ -896,6 +896,7 @@ $(document).ready(function() {
         callback: function () {
             billsObj.spread.refresh();
             detailObj.spread.refresh();
+            searchObj.spread.refresh();
         }
     });
     // 导航Menu

+ 8 - 4
app/public/js/cost_stage_book.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: ['sf_tp', 'in_tp', 'in_excl_tax_tp'],
                 keys: ['id', 'stage_id', 'tree_id'],
             };
             this.tree = createNewPathTree('ledger', this.treeSetting);
@@ -32,6 +32,7 @@ $(document).ready(function() {
                     {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 185, formatter: '@', cellType: 'autoTip', readOnly: true},
                     {title: '单位', colSpan: '1', rowSpan: '1', field: 'unit', hAlign: 1, width: 50, formatter: '@', cellType: 'unit', readOnly: true},
                     {title: '税率(%)', colSpan: '1', rowSpan: '1', field: 'tax', hAlign: 2, width: 80, type: 'Number', readOnly: true},
+                    {title: '实付金额', colSpan: '1', rowSpan: '1', field: 'sf_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true},
                     {title: '入账金额', colSpan: '1', rowSpan: '1', field: 'in_tp', hAlign: 2, width: 80, type: 'Number'},
                     {title: '入账金额(不含税)', colSpan: '1', rowSpan: '1', field: 'in_excl_tax_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true},
                     {title: '本期批注', colSpan: '1', rowSpan: '1', field: 'postil', hAlign: 0, width: 100, formatter: '@'},
@@ -521,9 +522,11 @@ $(document).ready(function() {
             postData('update', data, function (result) {
                 detailObj.data.updateDatas(result.detail);
                 detailObj.refreshSheet();
-                result.ledger.tree_id = relaBills.tree_id;
-                const refreshNode = billsObj.tree.loadPostData({ update: result.ledger });
-                billsObj.refreshTree(refreshNode);
+                if (result.ledger) {
+                    result.ledger.tree_id = relaBills.tree_id;
+                    const refreshNode = billsObj.tree.loadPostData({ update: result.ledger });
+                    billsObj.refreshTree(refreshNode);
+                }
             }, function () {
                 SpreadJsObj.reLoadRowData(info.sheet, info.row, 1);
             });
@@ -732,6 +735,7 @@ $(document).ready(function() {
         callback: function () {
             billsObj.spread.refresh();
             detailObj.spread.refresh();
+            searchObj.spread.refresh();
         }
     });
     // 导航Menu

+ 13 - 3
app/public/js/cost_stage_ledger.js

@@ -513,7 +513,6 @@ $(document).ready(function() {
                     {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},
                     {title: '扣款金额', colSpan: '1', rowSpan: '1', field: 'cut_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true},
@@ -967,7 +966,12 @@ $(document).ready(function() {
                 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() };
+                const updateData = {
+                    target: 'importContract', ledger_id: curNode.id, cost_id: curNode.cost_id,
+                    types: $('[name=contract_type]').val(),
+                    months: $('#type-pay-date').val(),
+                    used: $('#type-contract-used').val(),
+                };
                 if (updateData.types.length === 0) {
                     toastr.warning('请至少选择一种类型');
                     return;
@@ -1002,7 +1006,12 @@ $(document).ready(function() {
                 contractSelectSpread.refresh();
             });
             $('#import-deal-select-ok').click(function() {
-                const updateData = { target: 'importContract', ledger_id: curNode.id, cost_id: curNode.cost_id, ids: getSelectContractId(), months: $('#select-pay-date').val() };
+                const updateData = {
+                    target: 'importContract', ledger_id: curNode.id, cost_id: curNode.cost_id,
+                    ids: getSelectContractId(),
+                    months: $('#select-pay-date').val(),
+                    used: $('#select-contract-used').val(),
+                };
                 if (updateData.ids.length === 0) {
                     toastr.warning('请至少选择一项合同数据');
                     return;
@@ -1342,6 +1351,7 @@ $(document).ready(function() {
         callback: function () {
             billsObj.spread.refresh();
             detailObj.spread.refresh();
+            searchObj.spread.refresh();
         }
     });
     // 导航Menu

+ 4 - 4
app/public/js/cost_tmpl.js

@@ -297,11 +297,11 @@ $(document).ready(() => {
                         icon: 'fa-plus',
                         callback: function (key, opt) {
                             const select = SpreadJsObj.getSelectObject(self.sheet);
-                            self.addCol('in_excl_tax_tp', select);
+                            self.addCol('sf_excl_tax_tp', select);
                         },
                     },
                     'add_sf_percent': {
-                        name: '新增实付(不含税)列',
+                        name: '新增实付比例列',
                         icon: 'fa-plus',
                         callback: function (key, opt) {
                             const select = SpreadJsObj.getSelectObject(self.sheet);
@@ -471,13 +471,13 @@ $(document).ready(() => {
             return this.colSetData;
         }
         export() {
-            window.open(`/sp/${spid}/template/ctd?tid=${this.template.id}`);
+            window.open(`template/ctd?tid=${this.template.id}`);
         }
         import() {
             const self = this;
             BaseImportFile.show({
                 validList: ['.ctd'],
-                url: `/sp/${spid}/template/ctd/load?tid=${this.template.id}`,
+                url: `/template/ctd/load?tid=${this.template.id}`,
                 afterImport: function (result) {
                     self.template.col_set = result.update.col_set;
                     self.template.multi_header = result.update.multi_header;

+ 2 - 2
app/service/contract_sp_audit.js

@@ -444,8 +444,8 @@ module.exports = app => {
             // });
             // const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
             // const data = await transaction.query(sql, sqlParam);
-            const sql = 'UPDATE ?? SET `order` = `order` ' + selfOperate + ' ?, audit_order = audit_order ' + selfOperate + ' ? WHERE `cid` = ? AND `cpid` ' + (cpid ? '= ' + cpid : 'is null') + ' AND `order` >= ? AND `times` = ?';
-            const sqlParam = [this.tableName, 1, 1, cid, cpid, order, times];
+            const sql = 'UPDATE ?? SET `order` = `order` ' + selfOperate + ' ?, `audit_order` = `audit_order` ' + selfOperate + ' ? WHERE `cid` = ? AND `cpid` ' + (cpid ? '= ' + cpid : 'is null') + ' AND `order` >= ? AND `times` = ?';
+            const sqlParam = [this.tableName, 1, 1, cid, order, times];
             const data = await transaction.query(sql, sqlParam);
             return data;
         }

+ 22 - 14
app/service/cost_stage_book_detail.js

@@ -131,6 +131,7 @@ module.exports = app => {
             const orgDatas = orgDataIds.length > 0 ? await this.getAllDataByCondition({ where: { id: orgDataIds } }) : [];
 
             const updateDetail = [], insertDetail = [];
+            let calc = false;
             for (const d of datas) {
                 const od = orgDatas.find(x => { return x.id === d.book_id; });
                 const cd = orgDetail.find(x => { return x.id === d.detail_id });
@@ -143,6 +144,7 @@ module.exports = app => {
                     if (d[prop] !== undefined) nd[prop] = d[prop];
                 }
                 if (d.in_tp !== undefined ) {
+                    calc = true;
                     nd.in_tp = d.in_tp !== undefined ? this.ctx.helper.round(d.in_tp || 0, this.ctx.costStage.decimal.tp) : (od.in_tp ? od.in_tp : 0);
                     const divNum = this.ctx.helper.add(1, this.ctx.helper.div(cd.tax, 100));
                     nd.in_excl_tax_tp = this.ctx.helper.div(nd.in_tp, divNum, this.ctx.costStage.decimal.tp);
@@ -153,25 +155,29 @@ module.exports = app => {
                     insertDetail.push(nd);
                 }
             }
-            const bookUpdate = await this._getBookUpdateData([...insertDetail, ...updateDetail], orgDetail[0].ledger_id);
+            const bookUpdate = calc ? await this._getBookUpdateData([...insertDetail, ...updateDetail], orgDetail[0].ledger_id) : null;
             const orgBook = await this.ctx.service.costStageBook.getDataByCondition({ ledger_id: orgDetail[0].ledger_id });
             const orgLedger = await this.ctx.service.costStageLedger.getDataById(orgDetail[0].ledger_id);
-            if (!orgBook) {
-                bookUpdate.tender_id = this.ctx.costStage.tid;
-                bookUpdate.stage_id = this.ctx.costStage.id;
-                bookUpdate.ledger_id = orgLedger.id;
-                bookUpdate.cost_id = orgLedger.cost_id;
-                bookUpdate.add_user_id = user_id;
+            if (bookUpdate) {
+                if (!orgBook) {
+                    bookUpdate.tender_id = this.ctx.costStage.tid;
+                    bookUpdate.stage_id = this.ctx.costStage.id;
+                    bookUpdate.ledger_id = orgLedger.id;
+                    bookUpdate.cost_id = orgLedger.cost_id;
+                    bookUpdate.add_user_id = user_id;
+                }
             }
 
             const conn = await this.db.beginTransaction();
             try {
                 if (updateDetail.length > 0) await conn.updateRows(this.tableName, updateDetail);
                 if (insertDetail.length > 0) await conn.insert(this.tableName, insertDetail);
-                if (orgBook) {
-                    await conn.update(this.ctx.service.costStageBook.tableName, bookUpdate);
-                } else {
-                    await conn.insert(this.ctx.service.costStageBook.tableName, bookUpdate);
+                if (bookUpdate) {
+                    if (orgBook) {
+                        await conn.update(this.ctx.service.costStageBook.tableName, bookUpdate);
+                    } else {
+                        await conn.insert(this.ctx.service.costStageBook.tableName, bookUpdate);
+                    }
                 }
                 await conn.commit();
             } catch (err) {
@@ -190,9 +196,11 @@ module.exports = app => {
                 const cl = orgDetail.find(x => { return x.id === od.detail_id; });
                 u.id = cl.id;
             });
-            bookUpdate.book_id = bookUpdate.id;
-            delete bookUpdate.id;
-            bookUpdate.tree_id = orgLedger.tree_id;
+            if (bookUpdate) {
+                bookUpdate.book_id = bookUpdate.id;
+                delete bookUpdate.id;
+                bookUpdate.tree_id = orgLedger.tree_id;
+            }
             return { detail: { update: [...insertDetail, ...updateDetail] }, ledger: bookUpdate };
         }
 

+ 8 - 7
app/service/cost_stage_detail.js

@@ -270,7 +270,7 @@ module.exports = app => {
             }
         }
 
-        async _importContractByIds(ledgerId, costId, ids, months) {
+        async _importContractByIds(ledgerId, costId, ids, months, used) {
             if (!ledgerId || !costId) throw '导入明细数据,提交的数据错误';
             const user_id = this.ctx.session.sessionUser.accountId;
             const times = months.split(' ~ ');
@@ -278,8 +278,9 @@ module.exports = app => {
             const beginTime = this.ctx.moment(times[0], 'YYYY-MM').startOf('month').format('YYYY-MM-DD HH:mm:ss');
             const endTime = this.ctx.moment(times[1] || times[0], 'YYYY-MM').endOf('month').format('YYYY-MM-DD HH:mm:ss');
 
-            const paySql = `SELECT cp.*, c.c_code, c.name, c.party_b FROM ${this.ctx.service.contractPay.tableName} cp LEFT JOIN ${this.ctx.service.contract.tableName} c ON cp.cid = c.id ` +
-                           `    WHERE cp.cid IN(${ids.map(x => { return `'${x}'`}).join(', ')}) AND cp.pay_time >= ? AND cp.pay_time <= ?`;
+            const usedFilter = !used || used === '全部' ? '' : this.db.format(` AND cp.used = ?`, [used]);
+            const paySql = `SELECT cp.*, c.c_code, c.name, c.party_b, c.tax FROM ${this.ctx.service.contractPay.tableName} cp LEFT JOIN ${this.ctx.service.contract.tableName} c ON cp.cid = c.id ` +
+                           `    WHERE cp.cid IN(${ids.map(x => { return `'${x}'`}).join(', ')}) AND cp.pay_time >= ? AND cp.pay_time <= ?` + usedFilter;
             const validPays = await this.db.query(paySql, [beginTime, endTime]);
 
             const insertDetails = [];
@@ -323,13 +324,13 @@ module.exports = app => {
             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) {
+        async _importContractByTypes(ledgerId, costId, types, months, used) {
             const contracts = await this.ctx.service.contract.getAllDataByCondition({ where: { tid: this.ctx.costStage.tid, contract_type: 1, type: types } });
-            return await this._importContractByIds(ledgerId, costId, contracts.map(x => { return x.id; }), months);
+            return await this._importContractByIds(ledgerId, costId, contracts.map(x => { return x.id; }), months, used);
         }
         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, data.used);
+            if (data.ids) return this._importContractByIds(data.ledger_id, data.cost_id, data.ids, data.months || this.ctx.costStage.stage_date, data.used);
         }
 
         async deletePartData(transaction, tender_id, ledger_id) {

+ 1 - 1
app/service/pos.js

@@ -434,7 +434,7 @@ module.exports = app => {
 
             const bills = await this.ctx.service.ledger.getDataById(data.lid);
             const le = await this.ctx.service.ledgerExtra.getDataById(data.lid);
-            const calcTemplate = le.calc_template ? await this.ctx.service.calcTmpl.getTemplate(le.calc_template) : null;
+            const calcTemplate = le && le.calc_template ? await this.ctx.service.calcTmpl.getTemplate(le.calc_template) : null;
             const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
             const existPos = await this.ctx.service.pos.getAllDataByCondition({ where : { lid: bills.id }, orders: [['porder', 'DESC']]});
             const maxOrder = existPos.length > 0 ? existPos[0].porder : 0;

+ 12 - 10
app/service/shenpi_audit.js

@@ -207,6 +207,7 @@ module.exports = app => {
         }
 
         async copyAudit2otherTender(data, tid = this.ctx.tender.id) {
+            const sp_type = shenpiConst.sp_type[data.code] || shenpiConst.sp_other_type[data.code] || shenpiConst.cost_sp_type[data.code];
             const transaction = await this.db.beginTransaction();
             try {
                 const shenpi_status = parseInt(data.status);
@@ -235,11 +236,11 @@ module.exports = app => {
                 let is_group = false;
                 let groupList = [];
                 if (shenpi_status === shenpiConst.sp_status.gdspl) {
-                    groupList = await this.ctx.service.shenpiGroup.getGroupList(tid, shenpiConst.sp_type[data.code]);
+                    groupList = await this.ctx.service.shenpiGroup.getGroupList(tid, sp_type);
                     if (groupList.length > 0) {
                         is_group = true;
                         for (const g of groupList) {
-                            g.auditList = await this.getAllDataByCondition({ where: { tid, sp_type: shenpiConst.sp_type[data.code], sp_status: shenpi_status, sp_group: g.id } });
+                            g.auditList = await this.getAllDataByCondition({ where: { tid, sp_type, sp_status: shenpi_status, sp_group: g.id } });
                         }
                     }
                 }
@@ -247,15 +248,15 @@ module.exports = app => {
                     if (shenpi_status !== shenpiConst.sp_status.sqspr) {
                         if (shenpi_status === shenpiConst.sp_status.gdspl) {
                             await transaction.delete(this.ctx.service.shenpiGroup.tableName, {
-                                tid: t.id, sp_type: shenpiConst.sp_type[data.code],
+                                tid: t.id, sp_type,
                             });
                         }
-                        await transaction.delete(this.tableName, { tid: t.id, sp_type: shenpiConst.sp_type[data.code] || shenpiConst.sp_other_type[data.code], sp_status: shenpi_status });
+                        await transaction.delete(this.tableName, { tid: t.id, sp_type, sp_status: shenpi_status });
                         if (is_group) {
                             for (const g of groupList) {
                                 const result = await transaction.insert(this.ctx.service.shenpiGroup.tableName, {
                                     tid: t.id,
-                                    sp_type: shenpiConst.sp_type[data.code],
+                                    sp_type,
                                     name: g.name,
                                     is_select: g.is_select,
                                     change_type: g.change_type,
@@ -264,7 +265,7 @@ module.exports = app => {
                                 for (const s of g.auditList) {
                                     insertList.push({
                                         tid: t.id,
-                                        sp_type: shenpiConst.sp_type[data.code],
+                                        sp_type,
                                         sp_status: shenpi_status,
                                         audit_id: s.audit_id,
                                         audit_type: s.audit_type,
@@ -289,7 +290,7 @@ module.exports = app => {
                                 }
                                 insertList.push({
                                     tid: t.id,
-                                    sp_type: shenpiConst.sp_type[data.code] || shenpiConst.sp_other_type[data.code],
+                                    sp_type,
                                     sp_status: shenpi_status,
                                     audit_id: s.audit_id,
                                     audit_type: s.audit_type,
@@ -316,6 +317,7 @@ module.exports = app => {
         }
 
         async copyAudit2otherShenpi(data) {
+            const sp_type = shenpiConst.sp_type[data.code] || shenpiConst.sp_other_type[data.code] || shenpiConst.cost_sp_type[data.code];
             const transaction = await this.db.beginTransaction();
             try {
                 const shenpi_status = parseInt(data.status);
@@ -332,12 +334,12 @@ module.exports = app => {
                     }
                     if (shenpiInfo[code] !== shenpiConst.sp_status.sqspr) {
                         if (shenpiInfo[code] === shenpiConst.sp_status.gdspl) {
-                            const group = await this.ctx.service.shenpiGroup.getAllDataByCondition({ where: { tid: this.ctx.tender.id, sp_type: shenpiConst.sp_type[code] } });
+                            const group = await this.ctx.service.shenpiGroup.getAllDataByCondition({ where: { tid: this.ctx.tender.id, sp_type } });
                             if (group && group.length > 0) {
                                 throw '选择同步的流程中存在审批组,无法设置同步';
                             }
                         }
-                        await transaction.delete(this.tableName, { tid: this.ctx.tender.id, sp_type: shenpiConst.sp_type[code], sp_status: shenpiInfo[code] });
+                        await transaction.delete(this.tableName, { tid: this.ctx.tender.id, sp_type, sp_status: shenpiInfo[code] });
                         const flows = data.flowList.split(';');
                         let audit_order = 1;
                         for (const f of flows) {
@@ -349,7 +351,7 @@ module.exports = app => {
                                 if (aid !== this.ctx.tender.data.user_id || needYB.indexOf(code) >= 0) {
                                     insertList.push({
                                         tid: this.ctx.tender.id,
-                                        sp_type: shenpiConst.sp_type[code], sp_status: shenpi_status,
+                                        sp_type, sp_status: shenpi_status,
                                         audit_id: aid,
                                         audit_type: auditTypeValid ? audit_type : auditType.key.common,
                                         audit_order: audit_order,

+ 79 - 25
app/view/contract/detail.ejs

@@ -15,7 +15,7 @@
                         <a class="dropdown-item" name="showLevel" tag="4" href="javascript: void(0);">第四层</a>
                         <a class="dropdown-item" name="showLevel" tag="5" href="javascript: void(0);">第五层</a>
                         <a class="dropdown-item" name="showLevel" tag="last" href="javascript: void(0);">最底层</a>
-<!--                        <a class="dropdown-item" name="showLevel" tag="leafXmj" href="javascript: void(0);">只显示项目节</a>-->
+                        <!--                        <a class="dropdown-item" name="showLevel" tag="leafXmj" href="javascript: void(0);">只显示项目节</a>-->
                     </div>
                 </div>
             </div>
@@ -28,20 +28,20 @@
                     <a href="javascript: void(0);" name="base-opr" type="down-level" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="降级"><i class="fa fa-arrow-right" aria-hidden="true"></i></a>
                     <a href="javascript: void(0);" name="base-opr" type="down-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
                     <a href="javascript: void(0);" name="base-opr" type="up-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
-<!--                    <a href="javascript: void(0);" name="cpc" type="copy" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="复制"><i class="fa fa-files-o" aria-hidden="true"></i></a>-->
-<!--                    <a href="javascript: void(0);" name="cpc" type="cut" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="剪切"><i class="fa fa-scissors" aria-hidden="true"></i></a>-->
-<!--                    <a href="javascript: void(0);" name="cpc" type="paste" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="粘贴"><i class="fa fa-clipboard" aria-hidden="true"></i></a>-->
+                    <!--                    <a href="javascript: void(0);" name="cpc" type="copy" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="复制"><i class="fa fa-files-o" aria-hidden="true"></i></a>-->
+                    <!--                    <a href="javascript: void(0);" name="cpc" type="cut" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="剪切"><i class="fa fa-scissors" aria-hidden="true"></i></a>-->
+                    <!--                    <a href="javascript: void(0);" name="cpc" type="paste" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="粘贴"><i class="fa fa-clipboard" aria-hidden="true"></i></a>-->
                 </div>
             <% } %>
             <div class="ml-auto">
                 <% if (ctx.session.sessionUser.is_admin) { %>
-                  <a href="#col-set" data-toggle="modal" data-target="#col-set" class="btn btn-sm btn-primary mr-2">列设置</a>
-                <a href="javascript:void(0);" data-stid="<%- ctx.contract_tender ? ctx.contractOptions.tid : '' %>" class="btn btn-sm btn-primary get-audits mr-2">成员管理</a>
-                <a href="#empower" data-toggle="modal" data-target="#empower" class="btn btn-sm btn-primary mr-2">节点授权</a>
+                    <a href="#col-set" data-toggle="modal" data-target="#col-set" class="btn btn-sm btn-primary mr-2">列设置</a>
+                    <a href="javascript:void(0);" data-stid="<%- ctx.contract_tender ? ctx.contractOptions.tid : '' %>" class="btn btn-sm btn-primary get-audits mr-2">成员管理</a>
+                    <a href="#empower" data-toggle="modal" data-target="#empower" class="btn btn-sm btn-primary mr-2">节点授权</a>
                 <% } %>
-<!--                <a href="#cons-relat" data-toggle="modal" data-target="#cons-relat" class="btn btn-primary btn-sm pull-right">关联合同</a>-->
+                <!--                <a href="#cons-relat" data-toggle="modal" data-target="#cons-relat" class="btn btn-primary btn-sm pull-right">关联合同</a>-->
                 <% if (ctx.session.sessionUser.is_admin || audit_permission.permission_add) { %>
-                <a href="#cons-add" data-toggle="modal" data-target="#cons-add" class="btn btn-primary btn-sm pull-right mr-2" id="add-cons-btn" style="display: none">新增合同</a>
+                    <a href="#cons-add" data-toggle="modal" data-target="#cons-add" class="btn btn-primary btn-sm pull-right mr-2" id="add-cons-btn" style="display: none">新增合同</a>
                 <% } %>
             </div>
         </div>
@@ -86,14 +86,14 @@
                                 <!-- 合同文件的按钮 -->
                                 <a href="#cons-upfile" data-toggle="modal" data-target="#cons-upfile" style="display: none;" class="btn btn-primary btn-sm pull-right mr-2">上传文件</a>
                                 <!-- 合同支付的按钮 -->
-<!--                                <a href="#cons-addpay2" data-toggle="modal" data-target="#cons-addpay2" class="btn btn-primary btn-sm pull-right mr-2">添加回款2</a>-->
+                                <!--                                <a href="#cons-addpay2" data-toggle="modal" data-target="#cons-addpay2" class="btn btn-primary btn-sm pull-right mr-2">添加回款2</a>-->
                                 <a href="javascript:void(0);" id="add_contract_pay_btn" style="display: none;" class="btn btn-primary btn-sm pull-right mr-2">添加<% if (ctx.contract_type === contractConst.type.expenses) { %>支付<% } else if (ctx.contract_type === contractConst.type.income) { %>回款<% } %></a>
                                 <!-- 合同详情的按钮,点击编辑出现确定和取消按钮 -->
                                 <a href="javascript:void(0);" id="cancel_contract_btn" style="display: none" class="btn btn-secondary btn-sm pull-right mr-2">取消</a>
                                 <a href="javascript:void(0);" id="save_contract_btn" style="display: none" class="btn btn-primary btn-sm pull-right mr-2">确定</a>
                                 <a href="javascript:void(0);" id="edit_contract_btn" style="display: none" class="btn btn-primary btn-sm pull-right mr-2">编辑合同</a>
                                 <% if (shenpi_status) { %>
-                                <span class="pull-right mr-2" id="shenpi_btn">
+                                    <span class="pull-right mr-2" id="shenpi_btn">
                                     <a href="#sub-sp" data-toggle="modal" data-target="#sub-sp" class="btn btn-primary btn-sm">待上报</a>
                                 </span>
                                 <% } %>
@@ -103,7 +103,7 @@
                     <div class="tab-content">
                         <div class="tab-pane active" id="htdetail">
                             <div class="sp-wrap col-12" style="overflow: auto;">
-                                <table class="table table-sm table-bordered" id="htdetail-table" style="display: none;">
+                                <table class="table table-sm table-bordered mb-1" id="htdetail-table" style="display: none;">
                                     <tr>
                                         <th class="text-center align-middle" width="10%">合同编号</th>
                                         <td width="20%" class="change-input-td" id="htdetail_c_code"></td>
@@ -145,14 +145,14 @@
                                         <td class="change-input-td" id="htdetail_address"></td>
                                     </tr>
                                     <% if (ctx.contract_type === contractConst.type.expenses) { %>
-                                    <tr class="contract-expenses">
-                                        <th class="text-center align-middle">收款单位</th>
-                                        <td class="change-input-td" id="htdetail_entity"></td>
-                                        <th class="text-center align-middle">收款开户行</th>
-                                        <td class="change-input-td" id="htdetail_bank"></td>
-                                        <th class="text-center align-middle">收款账号</th>
-                                        <td class="change-input-td" id="htdetail_bank_account"></td>
-                                    </tr>
+                                        <tr class="contract-expenses">
+                                            <th class="text-center align-middle">收款单位</th>
+                                            <td class="change-input-td" id="htdetail_entity"></td>
+                                            <th class="text-center align-middle">收款开户行</th>
+                                            <td class="change-input-td" id="htdetail_bank"></td>
+                                            <th class="text-center align-middle">收款账号</th>
+                                            <td class="change-input-td" id="htdetail_bank_account"></td>
+                                        </tr>
                                     <% } %>
                                     <tr>
                                         <th class="text-center align-middle">结算书编号</th>
@@ -165,6 +165,59 @@
                                         <td class="change-input-td" colspan="5" id="htdetail_remark"></td>
                                     </tr>
                                 </table>
+                                <% if (attributeSet.length > 0) { %>
+                                    <div class="mb-1">附加属性</div>
+                                    <%
+                                        // 按顺序分组:遇到 long_text 就切断,形成 segments
+                                        // segments 结构:[{ type: 'inline', items: [...] }, { type: 'long', item: {...} }, ...]
+                                        const segments = [];
+                                        let buffer = [];
+
+                                        for (const attr of attributeSet) {
+                                            if (attr.type === 'long_text') {
+                                                if (buffer.length) {
+                                                    segments.push({ type: 'inline', items: [...buffer] });
+                                                    buffer = [];
+                                                }
+                                                segments.push({ type: 'long', item: attr });
+                                            } else {
+                                                buffer.push(attr);
+                                            }
+                                        }
+                                        if (buffer.length) segments.push({ type: 'inline', items: [...buffer] });
+
+                                        // 每行3列对应的 td 宽度
+                                        const tdWidths = ['20%', '30%', '20%'];
+                                    %>
+                                    <table class="table table-sm table-bordered" id="htdetail-attr" style="display: none;">
+                                        <% for (const seg of segments) { %>
+                                            <% if (seg.type === 'long') { %>
+                                                <tr>
+                                                    <th class="text-center align-middle" width="10%"><%- seg.item.alias %></th>
+                                                    <td class="change-input-td" colspan="5"
+                                                        data-type="<%- seg.item.field %>"
+                                                        id="htdetail_<%- seg.item.field %>"></td>
+                                                </tr>
+                                            <% } else { %>
+                                                <% for (let i = 0; i < seg.items.length; i += 3) { %>
+                                                    <% const row = seg.items.slice(i, i + 3) %>
+                                                    <tr>
+                                                        <% for (let k = 0; k < row.length; k++) { %>
+                                                            <th class="text-center align-middle" width="10%"><%- row[k].alias %></th>
+                                                            <td width="<%= tdWidths[k] %>" class="change-input-td"
+                                                                data-type="<%- row[k].field %>"
+                                                                id="htdetail_<%- row[k].field %>"></td>
+                                                        <% } %>
+                                                        <% if (row.length < 3) { %>
+                                                            <% const remainColspan = (3 - row.length) * 2 %>
+                                                            <td colspan="<%= remainColspan %>"></td>
+                                                        <% } %>
+                                                    </tr>
+                                                <% } %>
+                                            <% } %>
+                                        <% } %>
+                                    </table>
+                                <% } %>
                             </div>
                         </div>
                         <div class="tab-pane" id="htpay">
@@ -175,13 +228,13 @@
                                         <% if (ctx.contract_type === contractConst.type.income) { %>
                                             <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="15%">审批进度</th>
                                             <% } %>
                                             <th width="10%">操作</th>
                                         <% } else if (ctx.contract_type === contractConst.type.expenses) { %>
                                             <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="15%">审批进度</th>
                                             <% } %>
                                             <th width="10%">操作</th>
                                         <% } %>
@@ -196,9 +249,9 @@
                             <div class="sp-wrap" style="overflow: auto">
                                 <table class="table table-sm table-bordered" id="htfile-table" style="display: none;">
                                     <thead>
-                                        <tr class="text-center">
-                                            <th width="5%">序号</th><th>名称</th><th width="5%">上传人</th><th width="15%">上传时间</th><th width="15%">操作</th>
-                                        </tr>
+                                    <tr class="text-center">
+                                        <th width="5%">序号</th><th>名称</th><th width="5%">上传人</th><th width="15%">上传时间</th><th width="15%">操作</th>
+                                    </tr>
                                     </thead>
                                     <tbody>
                                     </tbody>
@@ -240,6 +293,7 @@
     const auditType = JSON.parse(unescape('<%- escape(JSON.stringify(auditType)) %>'));
     const accountGroup3 = JSON.parse(unescape('<%- escape(JSON.stringify(accountGroup)) %>'));
     const accountList3 = JSON.parse(unescape('<%- escape(JSON.stringify(accountList)) %>'));
+    const attributeSet = JSON.parse(unescape('<%- escape(JSON.stringify(attributeSet)) %>'));
 
     const thisUrl = JSON.parse(unescape('<%- escape(JSON.stringify(thisUrl)) %>'));
     const stdChapters = JSON.parse(unescape('<%- escape(JSON.stringify(stdChapters)) %>'));

+ 6 - 0
app/view/cost/book_list.ejs

@@ -21,7 +21,10 @@
                     <tr class="text-center">
                         <th width="80px">期数</th>
                         <th width="70px">报审月份</th>
+                        <th width="70px">关联成本</th>
                         <th width="70px">创建人</th>
+                        <th>本期入账金额</th>
+                        <th>截止本期入账金额</th>
                         <th>本期入账金额(不含税)</th>
                         <th>截止本期入账金额(不含税)</th>
                         <th width="180px">审批进度</th>
@@ -35,7 +38,10 @@
                             <a href="/sp/<%- ctx.subProject.id %>/cost/tender/<%- ctx.tender.id %>/book/<%- s.stage_order %>/stage" target="_blank">第 <%- s.stage_order %> 期</a>
                         </td>
                         <td class="text-center"><%- s.stage_date %></td>
+                        <td class="text-center">第<%- s.rela_stage.sorder %>期</td>
                         <td class="text-center"><%- s.user_name %></td>
+                        <td class="text-right"><%- s.stage_tp.in_tp %></td>
+                        <td class="text-right"><%- s.stage_end_tp.in_tp %></td>
                         <td class="text-right"><%- s.stage_tp.in_excl_tax_tp %></td>
                         <td class="text-right"><%- s.stage_end_tp.in_excl_tax_tp %></td>
                         <td class="<%- auditConst.info[s.audit_status].class %>">

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

@@ -21,6 +21,14 @@
                     <label>支付年月</label>
                     <input class="datepicker-here form-control form-control-sm" readonly autocomplete="off" id="type-pay-date" placeholder="点击选择年月" data-view="months" data-range="true"  data-multiple-dates-separator=" ~ " data-min-view="months" data-date-format="yyyy-MM" data-language="zh" type="text">
                 </div>
+                <div class="form-group form-group-sm">
+                    <label>选择用户</label>
+                    <select class="form-control form-control-sm" id="type-contract-used">
+                        <% for (const cu of contractUsed) { %>
+                        <option value="<%- cu %>"><%- cu %></option>
+                        <% } %>
+                    </select>
+                </div>
             </div>
             <div class="modal-footer">
                 <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>" />
@@ -42,6 +50,14 @@
                     <label>支付年月</label>
                     <input class="datepicker-here form-control form-control-sm" readonly autocomplete="off" id="select-pay-date" placeholder="点击选择年月" data-view="months" data-range="true"  data-multiple-dates-separator=" ~ " data-min-view="months" data-date-format="yyyy-MM" data-language="zh" type="text">
                 </div>
+                <div class="form-group form-group-sm">
+                    <label>选择用户</label>
+                    <select class="form-control form-control-sm" id="select-contract-used">
+                        <% for (const cu of contractUsed) { %>
+                        <option value="<%- cu %>"><%- cu %></option>
+                        <% } %>
+                    </select>
+                </div>
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>