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

排序功能,及补充合同部分功能(隐藏了)

ellisran 19 часов назад
Родитель
Сommit
147cb9848b

+ 10 - 0
app/controller/contract_controller.js

@@ -588,6 +588,7 @@ module.exports = app => {
                         responseData.data.contract = contract;
                         responseData.data.pays = await ctx.service.contractPay.getPays(options, data.postData);
                         responseData.data.files = await ctx.service.contractAtt.getAtt(data.postData);
+                        responseData.data.supplements = await ctx.service.contractSupplement.getSupplements(options, data.postData);
                         break;
                     case 'add-contract-pay':
                         responseData.data = await ctx.service.contractPay.add(options, data.postData.select, data.postData.pay);
@@ -598,6 +599,15 @@ module.exports = app => {
                     case 'del-contract-pay':
                         responseData.data = await ctx.service.contractPay.del(options, data.postData.select, data.postData.pay);
                         break;
+                    case 'add-contract-supplement':
+                        responseData.data = await ctx.service.contractSupplement.add(options, data.postData.select, data.postData.supplement);
+                        break;
+                    case 'save-contract-supplement':
+                        responseData.data = await ctx.service.contractSupplement.save(options, data.postData.select, data.postData.supplement);
+                        break;
+                    case 'del-contract-supplement':
+                        responseData.data = await ctx.service.contractSupplement.del(options, data.postData.select, data.postData.supplement);
+                        break;
                     default:
                         throw '未知操作';
                 }

+ 526 - 6
app/public/js/contract_detail.js

@@ -339,6 +339,7 @@ $(document).ready(function() {
                 $('#save_contract_btn').hide();
                 $('#cancel_contract_btn').hide();
                 $('#add_contract_pay_btn').hide();
+                $('#add_contract_supplement_btn').hide();
                 $('a[href*="#cons-upfile"]').hide();
                 if (need) {
                     for (const c of contractDetail) {
@@ -363,6 +364,7 @@ $(document).ready(function() {
                 $('#save_contract_btn').hide();
                 $('#cancel_contract_btn').hide();
                 $('#add_contract_pay_btn').hide();
+                $('#add_contract_supplement_btn').hide();
                 if (node.settle_code) {
                     $('#add_contract_pay_btn').hide();
                     if (need) {
@@ -390,12 +392,44 @@ $(document).ready(function() {
                 $('#save_contract_btn').hide();
                 $('#cancel_contract_btn').hide();
                 $('#add_contract_pay_btn').hide();
+                $('#add_contract_supplement_btn').hide();
                 $('a[href*="#cons-upfile"]').show();
+            } else if ($('.bc-bar .nav li .active').attr('href') === '#htsupplement') {
+                $('#edit_contract_btn').hide();
+                $('#save_contract_btn').hide();
+                $('#cancel_contract_btn').hide();
+                $('#add_contract_pay_btn').hide();
+                $('#add_contract_supplement_btn').hide();
+                if (node.settle_code) {
+                    $('#add_contract_supplement_btn').hide();
+                    if (need) {
+                        $('#htsupplement-table tbody').find('.pay-edit').hide();
+                        $('#htsupplement-table tbody').find('.pay-del').hide();
+                    }
+                } else {
+                    if ((node.uid === user_id || permission_edit_contract) && (!shenpi_status || (shenpi_status && node.status === auditConst.status.checked))) $('#add_contract_supplement_btn').show();
+                    if (need && contractSupplements && contractSupplements.length > 0) {
+                        for (const [i, cp] of contractSupplements.entries()) {
+                            if (cp.uid === user_id && (!shenpi_status || (shenpi_status && (cp.status === auditConst.status.uncheck || cp.status === auditConst.status.checkNo)))) {
+                                $('#htsupplement-table tbody tr').eq(i).find('.pay-edit').show();
+                                $('#htsupplement-table tbody tr').eq(i).find('.pay-del').show();
+                            } else if (shenpi_status && cp.status === auditConst.status.checked && is_admin) {
+                                $('#htsupplement-table tbody tr').eq(i).find('.pay-del').show();
+                            } else if (shenpi_status && (cp.status === auditConst.status.checking || cp.status === auditConst.status.checkNoPre) && cp.curAuditorIds && cp.curAuditorIds.length > 0 && cp.curAuditorIds.indexOf(user_id) !== -1) {
+                                $('#htsupplement-table tbody tr').eq(i).find('.pay-edit').show();
+                            }
+                        }
+                    }
+                }
+                $('a[href*="#cons-upfile"]').hide();
             }
         },
         openContractPayFiles: function (pay) {
             this.setContractFiles(pay.files, pay.cid, pay.id, '#cons-pay-file table tbody');
         },
+        openContractSupplementFiles: function (supplement) {
+            this.setContractFiles(supplement.files, supplement.cid, supplement.id, '#cons-supplement-file table tbody');
+        },
         checkCloseStatus: function (node) {
             if (pay_shenpi_status) {
                 let have_uncheck = false;
@@ -467,6 +501,11 @@ $(document).ready(function() {
                     } else {
                         $('#add_contract_pay_btn').hide();
                     }
+                    if (node && node.c_code && $('.bc-bar .nav li .active').attr('href') === '#htsupplement' && (node.uid === user_id || permission_edit_contract) && node.status === auditConst.status.checked) {
+                        $('#add_contract_supplement_btn').show();
+                    } else {
+                        $('#add_contract_supplement_btn').hide();
+                    }
                 }
             } else if ((node.uid === user_id || permission_edit_contract)) {
                 if (node.settle_code) {
@@ -490,6 +529,11 @@ $(document).ready(function() {
                 } else {
                     $('#add_contract_pay_btn').hide();
                 }
+                if (node && node.c_code && $('.bc-bar .nav li .active').attr('href') === '#htsupplement' && (node.uid === user_id || permission_edit_contract)) {
+                    $('#add_contract_supplement_btn').show();
+                } else {
+                    $('#add_contract_supplement_btn').hide();
+                }
             }
         },
         setContractFiles: function (files, cid, cpid = null, _this = '#htfile-table tbody') {
@@ -515,6 +559,9 @@ $(document).ready(function() {
             });
             $(_this).html(filesHtml);
         },
+        setContractListHtml: function (type, node, list) {
+            reOrderTenders(type, node, list);
+        },
         setContractPays: function (pays, node) {
             contractPays = pays;
             let paysHtml = '';
@@ -554,6 +601,44 @@ $(document).ready(function() {
             });
             $('#htpay-table tbody').html(paysHtml);
         },
+        setContractSupplements: function (supplements, node) {
+            contractSupplements = supplements;
+            let supplementsHtml = '';
+            const newSupplements = supplements.map(supplement => {
+                let showEdit = false;
+                let showDel = false;
+                if (supplement.uid === user_id && !node.settle_code && (!shenpi_status || (shenpi_status && (supplement.status === auditConst.status.uncheck || supplement.status === auditConst.status.checkNo)))) {
+                    showEdit = true;
+                    showDel = true;
+                } else if (!node.settle_code && shenpi_status && supplement.status === auditConst.status.checked && is_admin) {
+                    showDel = true;
+                } else if (!node.settle_code && shenpi_status && (supplement.status === auditConst.status.checking || supplement.status === auditConst.status.checkNoPre) && supplement.curAuditorIds && supplement.curAuditorIds.length > 0 && supplement.curAuditorIds.indexOf(user_id) !== -1) {
+                    showEdit = true;
+                }
+                return {...supplement, showEdit, showDel}
+            })
+            console.log(supplements);
+            newSupplements.forEach((supplement, idx) => {
+                const shenpi_html = setSupplementShenpiHtml(supplement, false, node);
+                const operationHtml = `<a href="javascript:void(0);" class="text-primary supplement-edit" data-id="${supplement.id}" ${!supplement.showEdit ? `style="display:none"` : ''}>编辑</a> <a href="javascript:void(0);" class="text-danger supplement-del" data-id="${supplement.id}" ${!supplement.showDel ? `style="display:none"` : ''}>删除</a>`;
+                supplementsHtml += `<tr class="text-center" data-cpid="${supplement.id}">
+                                        <td>${supplement.code}</td>
+                                        <td>${supplement.name}</td>
+                                        <td>${supplement.price}</td>
+                                        <td>${supplement.party_a}</td>
+                                        <td>${supplement.party_b}</td>
+                                        <td>${supplement.sign_time ? moment(supplement.sign_time).format('YYYY-MM-DD') : ''}</td>
+                                        <td>${supplement.address}</td>
+                                        <td>${supplement.username}</td>
+                                        <td>${moment(supplement.create_time).format('YYYY-MM-DD HH:mm:ss')}</td>
+                                        <td>${supplement.content}</td>
+                                        <td><a href="javascript:void(0);" class="text-primary open-supplement-files" data-csid="${supplement.id}"><i class="fa fa-paperclip fa-rotate-90"></i></a> <span class="files-num">${supplement.files.length > 0 ? supplement.files.length : ''}</span></td>
+                                        ${shenpi_html}
+                                        <td>${operationHtml}</td>
+                                    </tr>`;
+            });
+            $('#htsupplement-table tbody').html(supplementsHtml);
+        },
         setContract: function (sheet, needPost = true) {
             const node = SpreadJsObj.getSelectObject(sheet);
 
@@ -562,6 +647,7 @@ $(document).ready(function() {
                 $('#htdetail-attr').show();
                 $('#htpay-table').show();
                 $('#htfile-table').show();
+                $('#htsupplement-table').show();
                 for (const c of contractDetail) {
                     if (c === 'create_time') {
                         $('#htdetail_' + c).text(node[c] ? moment(node[c]).format('YYYY-MM-DD HH:mm:ss') : '');
@@ -581,8 +667,11 @@ $(document).ready(function() {
                     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.setContractPays(result.pays, node);
+                        contractTreeSpreadObj.setContractListHtml('pay', node, result.pays);
                         contractTreeSpreadObj.setContractFiles(result.files, node.id);
+                        // contractTreeSpreadObj.setContractSupplements(result.supplements, node.id);
+                        contractTreeSpreadObj.setContractListHtml('supplement', node, result.supplements);
                         contractTreeSpreadObj.setContractBtn(result.contract);
                     });
                 }
@@ -591,6 +680,7 @@ $(document).ready(function() {
                 $('#htdetail-attr').hide();
                 $('#htpay-table').hide();
                 $('#htfile-table').hide();
+                $('#htsupplement-table').hide();
                 $('#edit_contract_btn').hide();
                 $('#save_contract_btn').hide();
                 $('#cancel_contract_btn').hide();
@@ -599,6 +689,7 @@ $(document).ready(function() {
                 $('a[href*="#cons-upfile"]').hide();
                 if (node && node.c_code && $('.bc-bar .nav li .active').attr('href') === '#htfile' && permission_att) $('a[href*="#cons-upfile"]').show();
                 $('#add_contract_pay_btn').hide();
+                $('#add_contract_supplement_btn').hide();
                 $('#shenpi_btn').html('');
             }
         },
@@ -961,7 +1052,7 @@ $(document).ready(function() {
             if (select.c_code) {
                 if (!(select.uid === user_id || is_admin)) {
                     canDelete = false;
-                } else if (select.exist_pay) {
+                } else if (select.exist_pay || select.exist_supplement) {
                     canDelete = false;
                 }
             } else {
@@ -974,7 +1065,7 @@ $(document).ready(function() {
                         if (!(sc.uid === user_id || is_admin)) {
                             canDelete = false;
                             break;
-                        } else if (sc.exist_pay) {
+                        } else if (sc.exist_pay || sc.exist_supplement) {
                             canDelete = false;
                             break;
                         }
@@ -986,6 +1077,93 @@ $(document).ready(function() {
     };
     contractSpread.bind(spreadNS.Events.SelectionChanged, contractTreeSpreadObj.selectionChanged);
     contractSpread.bind(spreadNS.Events.topRowChanged, contractTreeSpreadObj.topRowChanged);
+
+    // 支付/回款,补充合同列表排序及切换排序
+    let payOrderSetting = getLocalCache('jl-contract-pay-list-order-' + spid);
+    if (!payOrderSetting) payOrderSetting = 'create_time|down';
+
+    let supplementOrderSetting = getLocalCache('jl-contract-supplement-list-order-' + spid);
+    if (!supplementOrderSetting) supplementOrderSetting = 'create_time|down';
+    const pinyin = new PinYinOrder();
+    function CompareStr (x, y) {
+        return pinyin.compareWord(x, y);
+    }
+
+    function reOrderTenders (type, node = null, ts = null, orderStr = '') {
+        let orders = [];
+        if (type === 'pay') {
+            if (orderStr) {
+                payOrderSetting = orderStr;
+                setLocalCache('jl-contract-pay-list-order-' + spid, orderStr);
+            }
+            if (!ts) ts = contractPays;
+            orders = payOrderSetting.split('|');
+        } else if (type === 'supplement') {
+            if (orderStr) {
+                supplementOrderSetting = orderStr;
+                setLocalCache('jl-contract-supplement-list-order-' + spid, orderStr);
+            }
+            if (!ts) ts = contractSupplements;
+            orders = supplementOrderSetting.split('|');
+        }
+        if (orders[0] === 'code') {
+            ts.sort(function (a, b) {
+                // return orders[1] === 'up'
+                //     ? a[orders[0]].localeCompare(b[orders[0]], 'zh')
+                //     : -a[orders[0]].localeCompare(b[orders[0]], 'zh');
+                return orders[1] === 'up'
+                    ? CompareStr(a[orders[0]], b[orders[0]])
+                    : CompareStr(b[orders[0]], a[orders[0]]);
+            });
+        } else if (orders[0] === 'pay_time') {
+            ts.sort(function (a, b){
+                return orders[1] === 'up'
+                    ? Date.parse(a[orders[0]]) - Date.parse(b[orders[0]])
+                    : Date.parse(b[orders[0]]) - Date.parse(a[orders[0]]);
+            });
+        } else if (orders[0] === 'create_time') {
+            ts.sort(function (a, b){
+                return orders[1] === 'up'
+                    ? Date.parse(a[orders[0]]) - Date.parse(b[orders[0]])
+                    : Date.parse(b[orders[0]]) - Date.parse(a[orders[0]]);
+            })
+        }
+        if (!node) node = SpreadJsObj.getSelectObject(contractSheet);
+        setHeaderOrderButtonHtml(type);
+        if (type === 'pay') {
+            contractTreeSpreadObj.setContractPays(ts, node);
+        } else if (type === 'supplement') {
+            contractTreeSpreadObj.setContractSupplements(ts, node);
+        }
+    }
+    function setHeaderOrderButtonHtml(type) {
+        if (type === 'pay') {
+            $('#pay-header th[sort="pay_time"]').html((contract_type === contractConst.type.expenses ? '支付' : '回款') + '日期' + getOrderButton('pay_time', 'pay') + '</th>');
+            $('#pay-header th[sort="create_time"]').html('创建时间' + getOrderButton('create_time', 'pay') + '</th>');
+        } else if (type === 'supplement') {
+            $('#supplement-header th[sort="code"]').html('编号' + getOrderButton('code', 'supplement') + '</th>');
+            $('#supplement-header th[sort="create_time"]').html('创建时间' + getOrderButton('create_time', 'supplement') + '</th>');
+        }
+    }
+    function getOrderButton(field, type) {
+        let orders = [];
+        if (type === 'pay') {
+            orders = payOrderSetting.split('|');
+        } else {
+            orders = supplementOrderSetting.split('|');
+        }
+        const button = field === orders[0]
+            ? (orders[1] === 'up'
+                ? `<i class="fa fa-sort-amount-asc" aria-hidden="true" data-filed="${field + '|down'}" data-type="${type}"></i>`
+                : `<i class="fa fa-sort-amount-desc" aria-hidden="true" data-filed="${field + '|up'}" data-type="${type}"></i>`)
+            : `<i class="fa fa-sort" aria-hidden="true" data-filed="${field + '|up'}" data-type="${type}"></i>`;
+        return '<a href="javascript:void(0)" class="btn btn-sm ml-1">' + button + '</a>';
+    }
+    $('body').on('click', '#pay-header th[sort] a, #supplement-header th[sort] a', function (e) {
+        const field = $(this).find('i').data('filed');
+        const type = $(this).find('i').data('type');
+        reOrderTenders(type, null, null, field);
+    });
     // 右键菜单
     let batchInsertObj;
     $.contextMenu.types.batchInsert = function (item, opt, root) {
@@ -1637,9 +1815,11 @@ $(document).ready(function() {
         const node = SpreadJsObj.getSelectObject(contractSheet);
         $('a[href*="#cons-upfile"]').hide();
         $('#add_contract_pay_btn').hide();
+        $('#add_contract_supplement_btn').hide();
         if (node && node.c_code) {
             if ($('.bc-bar .nav li .active').attr('href') === '#htfile' && permission_att) $('a[href*="#cons-upfile"]').show();
             if ($('.bc-bar .nav li .active').attr('href') === '#htpay' && !node.settle_code && permission_add_pay && (!shenpi_status || (shenpi_status && node.status === auditConst.status.checked))) $('#add_contract_pay_btn').show();
+            if ($('.bc-bar .nav li .active').attr('href') === '#htsupplement' && !node.settle_code && permission_edit_contract && (!shenpi_status || (shenpi_status && node.status === auditConst.status.checked))) $('#add_contract_supplement_btn').show();
             if ((node.uid === user_id || permission_edit_contract)) contractTreeSpreadObj.changeContractTab(node, true);
         }
     });
@@ -1845,7 +2025,8 @@ $(document).ready(function() {
             postData(window.location.pathname + '/update', {postType: 'del-contract-pay', postData: { select: node.id, pay: cpid }}, function (result) {
                 const refreshNode = contractTree.loadPostData(result.node);
                 contractTreeSpreadObj.refreshTree(contractSheet, refreshNode);
-                contractTreeSpreadObj.setContractPays(result.pays, node);
+                // contractTreeSpreadObj.setContractPays(result.pays, node);
+                contractTreeSpreadObj.setContractListHtml('pay', node, result.pays);
                 contractTreeSpreadObj.checkCloseStatus(node);
                 contractTreeSpreadObj.setContract(contractSheet, false);
             })
@@ -1991,6 +2172,162 @@ $(document).ready(function() {
         }, '确认删除该文件?');
     });
 
+    $('body').on('click', '#htsupplement .supplement-del', function () {
+        const node = SpreadJsObj.getSelectObject(contractSheet);
+        const csid = $(this).data('id');
+        if (!node || !node.c_code) {
+            toastr.warning('请选择合同节点');
+            return;
+        }
+        if (node.settle_code) {
+            toastr.warning('已结算的合同不能删除补充合同');
+            return;
+        }
+        deleteAfterHint(function () {
+            postData(window.location.pathname + '/update', {postType: 'del-contract-supplement', postData: { select: node.id, supplement: csid }}, function (result) {
+                const refreshNode = contractTree.loadPostData(result.node);
+                contractTreeSpreadObj.refreshTree(contractSheet, refreshNode);
+                // contractTreeSpreadObj.setContractSupplements(result.supplements, node);
+                contractTreeSpreadObj.setContractListHtml('supplement', node, result.supplements);
+                contractTreeSpreadObj.checkCloseStatus(node);
+                contractTreeSpreadObj.setContract(contractSheet, false);
+            })
+        }, '确认删除该补充合同?');
+    });
+
+    const signTime = $('#cons-addsupplement input[name="sign_time"]').datepicker().data('datepicker');
+
+    $('#add_contract_supplement_btn').on('click', function () {
+        const node = SpreadJsObj.getSelectObject(contractSheet);
+        if (!node || !node.c_code) {
+            toastr.error('请选择一个合同节点');
+            return;
+        }
+        $('#cons-addsupplement .modal-title').text('新增补充合同');
+        $('#cons-addsupplement input[name="csid"]').val('');
+        $('#add-contract-supplement').show();
+        $('#save-contract-supplement').hide();
+        setSupplementModalInfo();
+        $('#cons-addsupplement').modal('show');
+    });
+
+    $('body').on('click', '#htsupplement .supplement-edit', function () {
+        const node = SpreadJsObj.getSelectObject(contractSheet);
+        if (!node || !node.c_code) {
+            toastr.error('请选择一个合同节点');
+            return;
+        }
+        const csid = $(this).data('id');
+        const csInfo = _.find(contractSupplements, { id: parseInt(csid) });
+        if (!csInfo) {
+            toastr.error('未找到该补充合同');
+            return
+        }
+        if (node.settle_code) {
+            toastr.warning('已结算的合同不能编辑补充合同');
+            return;
+        }
+        let only_sf_edit = false;
+        if (shenpi_status && (csInfo.status === auditConst.status.checking || csInfo.status === auditConst.status.checkNoPre) && csInfo.curAuditorIds && csInfo.curAuditorIds.length > 0 && csInfo.curAuditorIds.indexOf(user_id) > -1) {
+            only_sf_edit = true;
+        }
+        $('#cons-addsupplement .modal-title').text('编辑补充合同');
+        $('#cons-addsupplement input[name="csid"]').val(csid);
+        $('#add-contract-supplement').hide();
+        $('#save-contract-supplement').show();
+        setSupplementModalInfo(csInfo, only_sf_edit);
+        $('#cons-addsupplement').modal('show');
+    });
+
+    function setSupplementModalInfo(csInfo = null, only_sf_edit = false) {
+        $('#cons-addsupplement input[name="code"]').val(csInfo ? csInfo.code : '');
+        $('#cons-addsupplement input[name="name"]').val(csInfo ? csInfo.name : '');
+        $('#cons-addsupplement input[name="price"]').val(csInfo ? csInfo.price : '');
+        $('#cons-addsupplement input[name="party_a"]').val(csInfo ? csInfo.party_a : '');
+        $('#cons-addsupplement input[name="party_b"]').val(csInfo ? csInfo.party_b : '');
+        $('#cons-addsupplement input[name="sign_time"]').val(csInfo ? moment(csInfo.sign_time).format('YYYY-MM-DD') : '');
+        signTime.selectDate(csInfo ? new Date(csInfo.sign_time) : '');
+        $('#cons-addsupplement textarea[name="content"]').val(csInfo ? csInfo.content : '');
+        if (only_sf_edit) {
+            // 用readOnly
+            $('#cons-addsupplement input[name="code"]').attr('readonly', true);
+            $('#cons-addsupplement input[name="name"]').attr('readonly', true);
+            $('#cons-addsupplement input[name="party_a"]').attr('readonly', true);
+            $('#cons-addsupplement input[name="party_b"]').attr('readonly', true);
+            $('#cons-addsupplement input[name="sign_time"]').attr('readonly', true);
+            $('#cons-addsupplement input[name="address"]').attr('readonly', true);
+            $('#cons-addsupplement textarea[name="content"]').attr('readonly', true);
+        } else {
+            $('#cons-addsupplement input[name="code"]').removeAttr('readonly');
+            $('#cons-addsupplement input[name="name"]').removeAttr('readonly');
+            $('#cons-addsupplement input[name="party_a"]').removeAttr('readonly');
+            $('#cons-addsupplement input[name="party_b"]').removeAttr('readonly');
+            $('#cons-addsupplement input[name="sign_time"]').removeAttr('readonly');
+            $('#cons-addsupplement input[name="address"]').removeAttr('readonly');
+            $('#cons-addsupplement textarea[name="content"]').removeAttr('readonly');
+        }
+    }
+
+    // 上传附件
+    $('#cons-supplement-file input[type="file"]').change(function () {
+        const files = Array.from(this.files);
+        console.log(files);
+        const valiData = files.map(v => {
+            const ext = v.name.substring(v.name.lastIndexOf('.') + 1)
+            return {
+                size: v.size,
+                ext
+            }
+        })
+        const node = SpreadJsObj.getSelectObject(contractSheet);
+        if (!node || !node.c_code) {
+            toastr.warning('请选择合同再上传文件');
+            $('#cons-supplement-file input[type="file"]').val('');
+            return;
+        }
+        const csid = $('#cons-supplement-file input[name="csid"]').val();
+        const csInfo = _.find(contractsupplements, { id: parseInt(csid) });
+        if (!csInfo) {
+            toastr.warning('请选择补充合同再上传文件');
+            $('#cons-supplement-file input[type="file"]').val('');
+            return;
+        }
+        if (validateFiles(valiData)) {
+            if (files.length) {
+                const formData = new FormData()
+                files.forEach(file => {
+                    formData.append('name', file.name)
+                    formData.append('size', file.size)
+                    formData.append('file', file)
+                })
+                postDataWithFile(`${thisUrl}/${contractConst.typeMap[contract_type]}/${node.id}/supplement/${csInfo.id}/file/upload`, formData, function (result) {
+                    csInfo.files = result;
+                    contractTreeSpreadObj.openContractSupplementFiles(csInfo);
+                    $('#htsupplement-table tbody').find('tr[data-csid="' + csInfo.id + '"]').find('.files-num').text(result.length || '');
+                });
+            }
+        }
+        $('#cons-supplement-file input[type="file"]').val('');
+    });
+
+    $('body').on('click', '#cons-supplement-file .file-del', function () {
+        const node = SpreadJsObj.getSelectObject(contractSheet);
+        const csid = $('#cons-supplement-file input[name="csid"]').val();
+        const csInfo = _.find(contractSupplements, { id: parseInt(csid) });
+        if (!csInfo) {
+            toastr.warning('未选择补充合同');
+            return;
+        }
+        const fid = $(this).data('id');
+        deleteAfterHint(function () {
+            postData(`${thisUrl}/${contractConst.typeMap[contract_type]}/${node.id}/supplement/${csInfo.id}/file/delete`, { id: fid }, function (result) {
+                csInfo.files = result;
+                contractTreeSpreadObj.openContractSupplementFiles(csInfo);
+                $('#htsupplement-table tbody').find('tr[data-csid="' + csInfo.id + '"]').find('.files-num').text(result.length || '');
+            });
+        }, '确认删除该文件?');
+    });
+
     postData(window.location.pathname + '/load', {}, function (result) {
         const datas = result.contractTree.concat(result.contractList);
         for (const t of datas) {
@@ -2428,7 +2765,8 @@ $(document).ready(function() {
         postData(window.location.pathname + '/update', {postType: 'add-contract-pay', postData: { select: node.id, pay: data }}, function (result) {
             const refreshNode = contractTree.loadPostData(result.node);
             contractTreeSpreadObj.refreshTree(contractSheet, refreshNode);
-            contractTreeSpreadObj.setContractPays(result.pays, node);
+            // contractTreeSpreadObj.setContractPays(result.pays, node);
+            contractTreeSpreadObj.setContractListHtml('pay', node, result.pays);
             contractTreeSpreadObj.checkCloseStatus(node);
             contractTreeSpreadObj.setContract(contractSheet, false);
             // const selection = contractSheet.getSelections();
@@ -2482,7 +2820,8 @@ $(document).ready(function() {
         postData(window.location.pathname + '/update', {postType: 'save-contract-pay', postData: { select: node.id, pay: data }}, function (result) {
             const refreshNode = contractTree.loadPostData(result.node);
             contractTreeSpreadObj.refreshTree(contractSheet, refreshNode);
-            contractTreeSpreadObj.setContractPays(result.pays, node);
+            // contractTreeSpreadObj.setContractPays(result.pays, node);
+            contractTreeSpreadObj.setContractListHtml('pay', node, result.pays);
             contractTreeSpreadObj.setContract(contractSheet, false);
             // const selection = contractSheet.getSelections();
             // const sel = selection ? selection[0] : contractSheet.getSelections()[0];
@@ -2579,6 +2918,151 @@ $(document).ready(function() {
         contractTreeSpreadObj.openContractPayFiles(pay);
     });
 
+    $('#add-contract-supplement').click(function () {
+        const node = SpreadJsObj.getSelectObject(contractSheet);
+        if (!node || !node.c_code) {
+            toastr.error('请选择一个合同节点');
+            return;
+        }
+        if (!(node.uid === user_id || permission_edit_contract)) {
+            toastr.error('没有权限新增补充合同');
+            return;
+        }
+        if (!(!shenpi_status || (shenpi_status && node.status === auditConst.status.checked))) {
+            toastr.error('该合同审批状态未通过,不能添加');
+            return;
+        }
+        if (node.settle_code) {
+            toastr.error('该合同已结算,不能添加');
+            return;
+        }
+        const data = {
+            code: $('#cons-addsupplement input[name="code"]').val(),
+            name: $('#cons-addsupplement input[name="name"]').val(),
+            price: $('#cons-addsupplement input[name="price"]').val(),
+            party_a: $('#cons-addsupplement input[name="party_a"]').val(),
+            party_b: $('#cons-addsupplement input[name="party_b"]').val(),
+            sign_time: $('#cons-addsupplement input[name="sign_time"]').val() || null,
+            address: $('#cons-addsupplement input[name="address"]').val(),
+            content: $('#cons-addsupplement textarea[name="content"]').val(),
+        }
+        if (!judgeSupplements(data)) {
+            return;
+        }
+        console.log(node, data);
+        postData(window.location.pathname + '/update', {postType: 'add-contract-supplement', postData: { select: node.id, supplement: data }}, function (result) {
+            const refreshNode = contractTree.loadPostData(result.node);
+            contractTreeSpreadObj.refreshTree(contractSheet, refreshNode);
+            // contractTreeSpreadObj.setContractSupplements(result.supplements, node);
+            contractTreeSpreadObj.setContractListHtml('supplement', node, result.supplements);
+            contractTreeSpreadObj.checkCloseStatus(node);
+            contractTreeSpreadObj.setContract(contractSheet, false);
+            $('#cons-addsupplement').modal('hide');
+        })
+    });
+
+    $('#save-contract-supplement').click(function () {
+        const node = SpreadJsObj.getSelectObject(contractSheet);
+        if (!node || !node.c_code) {
+            toastr.error('请选择一个合同节点');
+            return;
+        }
+        if (node.settle_code) {
+            toastr.error('该合同已结算,不能修改');
+            return;
+        }
+        const csid = $('#cons-addsupplement input[name="csid"]').val();
+        const csInfo = _.find(contractSupplements, { id: parseInt(csid) });
+        if (!csInfo) {
+            toastr.error('未找到该补充合同');
+            return
+        }
+        let only_sf_edit = false;
+        if (shenpi_status && (csInfo.status === auditConst.status.checking || csInfo.status === auditConst.status.checkNoPre) && csInfo.curAuditorIds && csInfo.curAuditorIds.length > 0 && csInfo.curAuditorIds.indexOf(user_id) > -1) {
+            only_sf_edit = true;
+        }
+        if (!(csInfo.uid === user_id || only_sf_edit)) {
+            toastr.error('只能编辑自己创建的补充合同');
+            return;
+        }
+        const data = {
+            id: csInfo.id,
+            code: $('#cons-addsupplement input[name="code"]').val(),
+            name: $('#cons-addsupplement input[name="name"]').val(),
+            price: $('#cons-addsupplement input[name="price"]').val(),
+            party_a: $('#cons-addsupplement input[name="party_a"]').val(),
+            party_b: $('#cons-addsupplement input[name="party_b"]').val(),
+            sign_time: $('#cons-addsupplement input[name="sign_time"]').val() || null,
+            address: $('#cons-addsupplement input[name="address"]').val(),
+            content: $('#cons-addsupplement textarea[name="content"]').val(),
+        }
+        if (!judgeSupplements(data)) {
+            return;
+        }
+        console.log(node, data);
+        postData(window.location.pathname + '/update', {postType: 'save-contract-supplement', postData: { select: node.id, supplement: data }}, function (result) {
+            const refreshNode = contractTree.loadPostData(result.node);
+            contractTreeSpreadObj.refreshTree(contractSheet, refreshNode);
+            // contractTreeSpreadObj.setContractSupplements(result.supplements, node);
+            contractTreeSpreadObj.setContractListHtml('supplement', node, result.supplements);
+            contractTreeSpreadObj.setContract(contractSheet, false);
+            // const selection = contractSheet.getSelections();
+            // const sel = selection ? selection[0] : contractSheet.getSelections()[0];
+            // const row = sel ? sel.row : -1;
+            // contractTreeSpreadObj.setForeColor(contractSheet, row);
+            $('#cons-addsupplement').modal('hide');
+        })
+    });
+
+    function judgeSupplements(data) {
+        let flag = true;
+        if (!data.code) {
+            toastr.error('请输入编号');
+            return false;
+        }
+        if (!data.name) {
+            toastr.error('请输入名称');
+            return false;
+        }
+        console.log(data.price);
+        if (data.price === '') {
+            toastr.error('请输入金额');
+            return false;
+        }
+        // 金额只能输入数字
+        if (!/^\d+(\.\d+)?$/.test(data.price)) {
+            toastr.error('金额只能输入数字');
+            return false;
+        }
+        return flag;
+    }
+
+    $('body').on('click', '.open-supplement-files', function () {
+        const csid = $(this).attr('data-csid');
+        if (!csid) {
+            toastr.error('获取补充合同信息失败');
+            return;
+        }
+        const supplement = _.find(contractSupplements, { id: parseInt(csid) });
+        if (!supplement) {
+            toastr.error('获取补充合同信息失败');
+            return;
+        }
+        const node = SpreadJsObj.getSelectObject(contractSheet);
+        if (!node || !node.c_code) {
+            toastr.error('请选择一个合同节点');
+            return;
+        }
+        if (node.uid === user_id || supplement.uid === user_id || permission_att) {
+            $('#cons-supplement-file .upload-permission').show();
+        } else {
+            $('#cons-supplement-file .upload-permission').hide();
+        }
+        $('#cons-supplement-file').modal('show');
+        $('#cons-supplement-file input[name="csid"]').val(csid);
+        contractTreeSpreadObj.openContractSupplementFiles(supplement);
+    });
+
     $('#cons-close').on('show.bs.modal', function () {
         const node = SpreadJsObj.getSelectObject(contractSheet);
         if (!node || !node.c_code) {
@@ -2696,6 +3180,42 @@ $(document).ready(function() {
         return shenpi_html;
     }
 
+    function setSupplementShenpiHtml(pay, reload = false, contract = null) {
+        let shenpi_html = '';
+        if (shenpi_status) {
+            if (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>`;
+            } else if ((pay.status === auditConst.status.checking || pay.status === auditConst.status.checkNoPre) && pay.curAuditors && pay.curAuditors.findIndex(x => { return x.aid === user_id; }) >= 0) {
+                shenpi_html += `<a href="javascript: void(0);" node-cid="${pay.cid}" node-cpid="${pay.id}" data-operate="checked" data-title="审批通过" class="btn btn-success btn-sm mr-2 show-sp-list">审批通过</a>`;
+                shenpi_html += `<a href="javascript: void(0);" node-cid="${pay.cid}" node-cpid="${pay.id}" data-operate="checkNo" data-title="审批退回" class="btn btn-warning btn-sm show-sp-list">审批退回</a>`;
+            } else {
+                if (pay.status === auditConst.status.checked && pay.final_auditor_str) {
+                    shenpi_html += `<a href="javascript:void(0);" node-cid="${pay.cid}" node-cpid="${pay.id}" class="show-sp-list">${pay.final_auditor_str}</a>`;
+                } else if (pay.status === auditConst.status.checkNo && pay.curAuditors2 && pay.curAuditors2.length > 0) {
+                    if (pay.curAuditors2[0].audit_type === auditType.key.common) {
+                        shenpi_html += `<a href="javascript:void(0);" node-cid="${pay.cid}" node-cpid="${pay.id}" class="show-sp-list">${pay.curAuditors2[0].name}${pay.curAuditors2[0].role !== '' && pay.curAuditors2[0].role !== null ? '-' + pay.curAuditors2[0].role : ''}</a>`;
+                    } else {
+                        shenpi_html += `<a href="javascript:void(0);" node-cid="${pay.cid}" node-cpid="${pay.id}" class="show-sp-list">${transFormToChinese(pay.curAuditors2[0].audit_order) + '审'}</a>`;
+                    }
+                } else if (pay.curAuditors.length > 0) {
+                    if (pay.curAuditors[0].audit_type === auditType.key.common) {
+                        shenpi_html += `<a href="javascript:void(0);" node-cid="${pay.cid}" node-cpid="${pay.id}" class="show-sp-list">${pay.curAuditors[0].name}${pay.curAuditors[0].role !== '' && pay.curAuditors[0].role !== null ? '-' + pay.curAuditors[0].role : ''}</a>`;
+                    } else {
+                        shenpi_html += `<a href="javascript:void(0);" node-cid="${pay.cid}" node-cpid="${pay.id}" class="show-sp-list">${transFormToChinese(pay.curAuditors[0].audit_order) + '审'}</a>`;
+                    }
+                }
+                shenpi_html += ` ${auditConst.auditProgress[pay.status]}`;
+            }
+            shenpi_html += !reload ? '</td>' : '';
+        }
+        return shenpi_html;
+    }
+
     if (shenpi_status || pay_shenpi_status) {
         $('body').on('click', '.show-sub-sp', function () {
             console.log($(this).attr('node-cid'));

+ 159 - 0
app/service/contract_supplement.js

@@ -0,0 +1,159 @@
+'use strict';
+
+/**
+ * Created by EllisRan on 2020/3/3.
+ */
+
+const BaseService = require('../base/base_service');
+const contractConst = require('../const/contract');
+const auditConst = require('../const/audit').contract;
+const auditType = require('../const/audit').auditType;
+
+module.exports = app => {
+
+    class ContractSupplement extends BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'contract_supplement';
+            this.dataId = 'id';
+        }
+
+        async getSupplements(options, cid) {
+            const sql = 'SELECT * FROM ?? WHERE ' + this.ctx.helper._getOptionsSql(options) + ' AND `cid` = ? ORDER BY `create_time` DESC';
+            const sqlParams = [this.tableName, cid];
+            const list = await this.db.query(sql, sqlParams);
+            const shenpi_status = this.ctx.contract_tender ? this.ctx.subProject.page_show.openContractTenderShenpi : this.ctx.subProject.page_show.openContractSubProjectShenpi;
+            if (list.length > 0) {
+                const userList = await this.ctx.service.projectAccount.getAllDataByCondition({ where: { id: list.map(item => item.uid) } });
+                for (const l of list) {
+                    const userInfo = userList.find(item => item.id === l.uid);
+                    l.username = userInfo ? userInfo.name : '';
+                    l.files = await this.ctx.service.contractSupplementAtt.getAtt(l.id);
+                    if (shenpi_status) {
+                        await this.ctx.service.contractSpAudit.loadUser(l);
+                        await this.ctx.service.contractSpAudit.loadAuditViewData(l);
+                        await this.ctx.service.contractSpAudit.checkShenpi(l);
+                        if (l.status !== auditConst.status.checked || !l.final_auditor_str) {
+                            l.curAuditors2 = await this.ctx.service.contractSpAudit.getAuditorsByStatus(l.cid, l.id, l.status, l.times);
+                            if (l.status === auditConst.status.checked && !l.final_auditor_str) {
+                                const final_auditor_str = l.curAuditors2[0].audit_type === auditType.key.common
+                                    ? l.curAuditors2[0].name + (l.curAuditors2[0].role ? '-' + l.curAuditors2[0].role : '')
+                                    : this.ctx.helper.transFormToChinese(l.curAuditors2[0].audit_order) + '审';
+                                await this.defaultUpdate({ final_auditor_str, id: l.id });
+                                l.final_auditor_str = final_auditor_str;
+                            }
+                        }
+                    }
+                }
+            }
+            return list;
+        }
+
+        async add(options, cid, data) {
+            const node = await this.ctx.service.contract.getDataById(cid);
+            if (!node) {
+                throw '合同不存在';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                const insertData = {
+                    spid: options.spid || null,
+                    tid: options.tid || null,
+                    contract_type: options.contract_type,
+                    cid,
+                    uid: this.ctx.session.sessionUser.accountId,
+                    code: data.code,
+                    name: data.name,
+                    price: data.price || 0,
+                    party_a: data.party_a || 0,
+                    party_b: data.party_b || 0,
+                    sign_time: data.sign_time || null,
+                    address: data.address,
+                    content: data.content,
+                    create_time: new Date(),
+                    need_shenpi: options.spid ? this.ctx.subProject.page_show.openContractSubProjectShenpi : this.ctx.subProject.page_show.openContractTenderShenpi,
+                };
+                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();
+                throw err;
+            }
+            return { supplements: await this.getSupplements(options, cid), node: { update: node } };
+        }
+
+        async save(options, cid, data) {
+            if (!data.id) {
+                throw '参数有误';
+            }
+            const node = await this.ctx.service.contract.getDataById(cid);
+            if (!node) {
+                throw '合同不存在';
+            }
+            const csInfo = await this.getDataById(data.id);
+            if (!csInfo) {
+                throw '补充合同不存在';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.update(this.tableName, data);
+                await this.calcContract(transaction, node);
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+            return { supplements: await this.getSupplements(options, cid), node: { update: node } };
+        }
+
+        async del(options, cid, csid) {
+            if (!csid) {
+                throw '参数有误';
+            }
+            const node = await this.ctx.service.contract.getDataById(cid);
+            if (!node) {
+                throw '合同不存在';
+            }
+            const csInfo = await this.getDataById(csid);
+            if (!csInfo) {
+                throw '补充合同不存在';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.delete(this.tableName, { id: csid });
+                // 删除合同附件
+                const attList = await this.ctx.service.contractSupplementAtt.getAllDataByCondition({ where: { csid } });
+                await this.ctx.helper.delFiles(attList);
+                await transaction.delete(this.ctx.service.contractSupplementAtt.tableName, { csid });
+                await transaction.delete(this.ctx.service.contractSpAudit.tableName, { csid });
+                await this.calcContract(transaction, node);
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+            return { supplements: await this.getSupplements(options, cid), node: { update: node } };
+        }
+
+        async calcContract(transaction, node) {
+            const supplementsList = await transaction.query('SELECT `price` FROM ?? WHERE `cid` = ?', [this.tableName, node.id]);
+            let supplement_price = 0;
+            for (const l of supplementsList) {
+                supplement_price = this.ctx.helper.add(supplement_price, l.price);
+            }
+            node.supplement_price = supplement_price;
+            node.exist_supplement = supplementsList.length === 0 ? 0 : 1;
+            await transaction.update(this.ctx.service.contract.tableName, node);
+        }
+    }
+    return ContractSupplement;
+};

+ 61 - 0
app/service/contract_supplement_att.js

@@ -0,0 +1,61 @@
+'use strict';
+
+/**
+ * Created by EllisRan on 2020/3/3.
+ */
+
+const BaseService = require('../base/base_service');
+const contractConst = require('../const/contract');
+
+module.exports = app => {
+
+    class ContractSupplementAtt extends BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'contract_supplement_attachment';
+            this.dataId = 'id';
+        }
+
+        async getAtt(csid) {
+            const sql = 'SELECT a.*, b.name as username FROM ?? as a LEFT JOIN ?? as b ON a.`uid` = b.`id` WHERE a.`csid` = ? ORDER BY `upload_time` DESC';
+            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, csid];
+            const result = await this.db.query(sql, sqlParam);
+            return result.map(item => {
+                item.orginpath = this.ctx.app.config.fujianOssPath + item.filepath;
+                if (!this.ctx.helper.canPreview(item.fileext)) {
+                    item.filepath = `/sp/${this.ctx.subProject.id}/contract${item.tid ? '/tender/' + item.tid : ''}/detail/${contractConst.typeMap[item.contract_type]}/${item.cid}/supplement/${item.csid}/file/${item.id}/download`;
+                } else {
+                    item.filepath = this.ctx.app.config.fujianOssPath + item.filepath;
+                }
+                item.viewpath = this.ctx.helper.getPreviewPath(item.fileext, item.orginpath);
+                return item;
+            });
+        }
+
+        /**
+         * 存储上传的文件信息至数据库
+         * @param {Array} payload 载荷
+         * @return {Promise<void>} 数据库插入执行实例
+         */
+        async saveFileMsgToDb(payload) {
+            return await this.db.insert(this.tableName, payload);
+        }
+
+        /**
+         * 删除附件
+         * @param {Number} id - 附件id
+         * @return {void}
+         */
+        async delete(id) {
+            return await this.deleteById(id);
+        }
+    }
+    return ContractSupplementAtt;
+};

+ 26 - 4
app/view/contract/detail.ejs

@@ -79,6 +79,9 @@
                             <li class="nav-item">
                                 <a class="nav-link " data-toggle="tab" href="#htfile" role="tab">合同文件</a>
                             </li>
+<!--                            <li class="nav-item">-->
+<!--                                <a class="nav-link " data-toggle="tab" href="#htsupplement" role="tab">补充合同</a>-->
+<!--                            </li>-->
                             <li class="ml-auto">
                                 <!-- 结算合同所有tab可见 ,结算后见解锁合同,2者互斥-->
                                 <a href="#cons-unlock" data-toggle="modal" data-target="#cons-unlock" style="display: none;" class="btn btn-success btn-sm pull-right mr-2">解锁合同</a>
@@ -92,7 +95,8 @@
                                 <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) { %>
+                                <a href="javascript:void(0);" id="add_contract_supplement_btn" style="display: none;" class="btn btn-primary btn-sm pull-right mr-2">新增补充合同</a>
+                            <% if (shenpi_status) { %>
                                     <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>
@@ -224,15 +228,15 @@
                             <div class="sp-wrap" style="overflow: auto">
                                 <table class="table table-sm table-bordered" id="htpay-table" style="display: none;">
                                     <thead>
-                                    <tr class="text-center">
+                                    <tr class="text-center" id="pay-header">
                                         <% 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>
+                                            <th width="4%">序号</th><th width="5%" sort="pay_time">回款日期</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%" sort="create_time">创建时间</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="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>
+                                            <th width="4%">序号</th><th width="5%" sort="pay_time">支付日期</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%" sort="create_time">创建时间</th><th width="10%">备注</th><th width="4%">附件</th>
                                             <% if (pay_shenpi_status) { %>
                                                 <th width="15%">审批进度</th>
                                             <% } %>
@@ -258,6 +262,23 @@
                                 </table>
                             </div>
                         </div>
+                        <div class="tab-pane" id="htsupplement">
+                            <div class="sp-wrap" style="overflow: auto">
+                                <table class="table table-sm table-bordered" id="htsupplement-table" style="display: none;">
+                                    <thead>
+                                    <tr class="text-center" id="supplement-header">
+                                        <th width="5%" sort="code">编号</th><th width="10%">名称</th><th width="7%">金额</th><th width="10%">甲方</th><th width="10%">乙方</th><th width="5%">签订日期</th><th width="5%">签订地点</th><th width="5%">创建人</th><th width="9%" sort="create_time">创建时间</th><th width="15%">补充原因及内容</th><th width="4%">附件</th>
+                                        <% if (shenpi_status) { %>
+                                            <th width="15%">审批进度</th>
+                                        <% } %>
+                                        <th width="10%">操作</th>
+                                    </tr>
+                                    </thead>
+                                    <tbody>
+                                    </tbody>
+                                </table>
+                            </div>
+                        </div>
                     </div>
                 </div>
             </div>
@@ -300,5 +321,6 @@
     let types = JSON.parse(unescape('<%- escape(JSON.stringify(types)) %>'));
     types.push('');
     let contractPays = [];
+    let contractSupplements = [];
 </script>)
 </script>

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

@@ -249,6 +249,87 @@
         </div>
     </div>
 </div>
+<!--添加补充合同-->
+<div class="modal fade" id="cons-addsupplement" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">新增补充合同</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label>编号<b class="text-danger">*</b></label>
+                    <input class="form-control form-control-sm" name="code" placeholder="请输入编号" type="text">
+                </div>
+                <div class="form-group">
+                    <label>名称<b class="text-danger">*</b></label>
+                    <input class="form-control form-control-sm" name="name" placeholder="请输入名称" type="text">
+                </div>
+                <div class="form-group">
+                    <label>金额<b class="text-danger">*</b></label>
+                    <input class="form-control form-control-sm" name="price" placeholder="请输入金额" type="number">
+                </div>
+                <div class="form-group">
+                    <label>甲方</label>
+                    <input class="form-control form-control-sm" name="name" placeholder="" type="text">
+                </div>
+                <div class="form-group">
+                    <label>乙方</label>
+                    <input class="form-control form-control-sm" name="name" placeholder="" type="text">
+                </div>
+                <div class="form-group form-group-sm">
+                    <label>签订时间</label>
+                    <input class="datepicker-here form-control form-control-sm" name="sign_time" placeholder="点击选择时间" data-date-format="yyyy-MM-dd" data-language="zh" type="text" autocomplete="off">
+                </div>
+                <div class="form-group">
+                    <label>签订地点</label>
+                    <input class="form-control form-control-sm" name="address" placeholder="" type="text">
+                </div>
+                <div class="form-group">
+                    <label>补充原因及内容</label>
+                    <textarea class="form-control form-control-sm" name="content" rows="3"></textarea>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-sm btn-primary" id="add-contract-supplement">确定</button>
+                <input type="hidden" name="csid" value="">
+                <button type="button" class="btn btn-sm btn-primary" id="save-contract-supplement" style="display: none">确定</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--附件-->
+<div class="modal fade" id="cons-supplement-file" data-backdrop="static" style="z-index: 1049">
+    <input type="hidden" name="csid">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">附件</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group upload-permission">
+                    <label>单个文件大小限制:50MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
+                    <br>
+                    <input type="file" class="" multiple>
+                </div>
+                <div class="modal-height-500" style="overflow:auto;">
+                    <table class="table table-sm table-bordered text-center" style="word-break:break-all; table-layout: fixed">
+                        <thead>
+                        <tr><th width="5%">序号</th><th>名称</th><th width="8%">上传人</th><th width="20%">上传时间</th><th width="15%">操作</th></tr>
+                        </thead>
+                        <tbody>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-outline-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <!--<button type="button" class="btn btn-primary btn-sm" id="upload-file-btn">确定</button>-->
+            </div>
+        </div>
+    </div>
+</div>
 <!--结算合同-->
 <div class="modal fade" id="cons-close" data-backdrop="static">
     <div class="modal-dialog" role="document">

+ 1 - 0
config/web.js

@@ -1890,6 +1890,7 @@ const JsFiles = {
                     '/public/js/shares/cs_tools.js',
                     '/public/js/datepicker/datepicker.min.js',
                     '/public/js/datepicker/datepicker.zh.js',
+                    '/public/js/PinYinOrder.bundle.js',
                     '/public/js/zh_calc.js',
                     '/public/js/path_tree.js',
                     '/public/js/contract_detail.js',

+ 44 - 1
sql/update.sql

@@ -12,6 +12,7 @@
 ------------------------------------
 
 ALTER TABLE `zh_contract`
+ADD COLUMN `supplement_price` decimal(30, 8) NULL DEFAULT 0 COMMENT '补充合同金额' AFTER `total_price`,
 ADD COLUMN `need_shenpi` tinyint(1) NULL DEFAULT 0 COMMENT '是否需要审批' AFTER `name`,
 ADD COLUMN `status` tinyint(2) NULL DEFAULT 1 COMMENT '审批状态' AFTER `need_shenpi`,
 ADD COLUMN `times` tinyint(3) NULL DEFAULT 1 COMMENT '审批次数' AFTER `status`,
@@ -20,7 +21,8 @@ ADD COLUMN `final_auditor_str` varchar(50) NOT NULL DEFAULT '' COMMENT '终审
 ADD COLUMN `type` varchar(255) NULL DEFAULT '' COMMENT '合同类型(筛选的字段)' AFTER `final_auditor_str`,
 ADD COLUMN `remark1` varchar(1000) NULL DEFAULT '' COMMENT '备注1' AFTER `remark`,
 ADD COLUMN `tax` tinyint(3) NULL COMMENT '税率' AFTER `calc2`,
-ADD COLUMN `attribute_json` mediumtext NULL COMMENT '附加属性json' AFTER `tax`;
+ADD COLUMN `attribute_json` mediumtext NULL COMMENT '附加属性json' AFTER `tax`,
+ADD COLUMN `exist_supplement` tinyint(1) NULL DEFAULT 0 COMMENT '是否存在补充合同' AFTER `exist_pay`;
 
 ALTER TABLE `zh_contract_pay`
 ADD COLUMN `need_shenpi` tinyint(1) NULL COMMENT '是否需要审批' AFTER `fpcid`,
@@ -533,6 +535,47 @@ 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`;
 
+
+CREATE TABLE `zh_contract_supplement` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `spid` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '项目id',
+  `tid` int(11) DEFAULT NULL COMMENT '标段id',
+  `contract_type` tinyint(1) NOT NULL COMMENT '合同类型(1是支出,2是收入)',
+  `cid` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '合同详情id',
+  `uid` int(11) DEFAULT NULL COMMENT '创建人id',
+  `need_shenpi` tinyint(1) DEFAULT NULL COMMENT '是否需要审批',
+  `status` tinyint(2) DEFAULT '1' COMMENT '审批状态',
+  `times` tinyint(3) DEFAULT '1' COMMENT '审批次数',
+  `sp_group` int(11) DEFAULT '0' COMMENT '固定审批组id',
+  `final_auditor_str` varchar(50) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '终审人相关(cache)',
+  `code` varchar(255) COLLATE utf8_unicode_ci DEFAULT '' COMMENT '编号',
+  `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT '' COMMENT '名称',
+  `price` decimal(30,8) DEFAULT '0.00000000' COMMENT '金额',
+  `party_a` varchar(255) COLLATE utf8_unicode_ci DEFAULT '' COMMENT '甲方',
+  `party_b` varchar(255) COLLATE utf8_unicode_ci DEFAULT '' COMMENT '乙方',
+  `sign_time` datetime DEFAULT NULL COMMENT '签订时间',
+  `address` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '签订地点',
+  `content` varchar(1000) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '补充原因及内容',
+  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='合同补充表';
+
+CREATE TABLE `zh_contract_supplement_attachment` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `spid` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '项目id',
+  `tid` int(11) DEFAULT NULL COMMENT '标段id',
+  `contract_type` tinyint(1) NOT NULL COMMENT '合同类型(1是支出,2是收入)',
+  `cid` varchar(100) COLLATE utf8_unicode_ci NOT NULL COMMENT '合同详情id',
+  `csid` int(11) NOT NULL COMMENT '补充合同id',
+  `uid` int(11) NOT NULL COMMENT '上传者id',
+  `filename` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '文件名称',
+  `fileext` varchar(5) COLLATE utf8_unicode_ci NOT NULL COMMENT '文件后缀',
+  `filesize` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '文件大小',
+  `filepath` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '文件存储路径',
+  `upload_time` datetime NOT NULL COMMENT '上传时间',
+  PRIMARY KEY (`id`) USING BTREE,
+  KEY `idx_cid` (`csid`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='合同补充附件表';
 ------------------------------------
 -- 表数据
 ------------------------------------