Przeglądaj źródła

合同关联标段功能

ellisran 9 godzin temu
rodzic
commit
4961c5e0f5

+ 11 - 0
app/controller/contract_controller.js

@@ -644,6 +644,10 @@ module.exports = app => {
                         responseData.data.tenders = tenderList;
                         responseData.data.tenders = tenderList;
                         break;
                         break;
                     case 'get-tender-pays':
                     case 'get-tender-pays':
+                        responseData.data.tender = await ctx.service.tender.getDataById(data.postData.tid);
+                        if (!responseData.data.tender) {
+                            throw '关联标段不存在';
+                        }
                         // 判断项目开没开启独立支付合同
                         // 判断项目开没开启独立支付合同
                         if (ctx.subProject.page_show.phasePay) {
                         if (ctx.subProject.page_show.phasePay) {
                             // 直接读phasePay表数据并展示
                             // 直接读phasePay表数据并展示
@@ -653,6 +657,13 @@ module.exports = app => {
                             responseData.data.stages = await ctx.service.stagePay.getDatasForContract(data.postData.tid);
                             responseData.data.stages = await ctx.service.stagePay.getDatasForContract(data.postData.tid);
                         }
                         }
                         break;
                         break;
+                    case 'add-tender-pays':
+                        responseData.data = await ctx.service.contractPay.addTenderPays(
+                            options,
+                            data.postData.select,
+                            data.postData.pays
+                        );
+                        break;
                     default:
                     default:
                         throw '未知操作';
                         throw '未知操作';
                 }
                 }

+ 26 - 13
app/public/js/contract_detail.js

@@ -613,8 +613,13 @@ $(document).ready(function() {
             newPays.forEach((pay, idx) => {
             newPays.forEach((pay, idx) => {
                 const shenpi_html = setPayShenpiHtml(pay, false, node);
                 const shenpi_html = setPayShenpiHtml(pay, false, node);
                 const operationHtml = !pay.fpcid ? `<a href="javascript:void(0);" class="text-primary pay-edit" data-id="${pay.id}" ${!pay.showEdit ? `style="display:none"` : ''}>编辑</a> <a href="javascript:void(0);" class="text-danger pay-del" data-id="${pay.id}" ${!pay.showDel ? `style="display:none"` : ''}>删除</a>` : '';
                 const operationHtml = !pay.fpcid ? `<a href="javascript:void(0);" class="text-primary pay-edit" data-id="${pay.id}" ${!pay.showEdit ? `style="display:none"` : ''}>编辑</a> <a href="javascript:void(0);" class="text-danger pay-del" data-id="${pay.id}" ${!pay.showDel ? `style="display:none"` : ''}>删除</a>` : '';
+                const sourceIconHtml = pay.fpcid
+                    ? '<a href="javascript:void(0);" title="关联资金支付" style="position: absolute;right: 2px;top:50%;transform: translate(-50%, -50%);"><i class="fa fa-cny"></i></a>'
+                    : pay.rela_stage
+                        ? '<a href="javascript:void(0);" title="关联标段期支付" style="position: absolute;right: 2px;top:50%;transform: translate(-50%, -50%);"><i class="fa fa-calendar-check-o"></i></a>'
+                        : '';
                 paysHtml += `<tr class="text-center" data-cpid="${pay.id}">
                 paysHtml += `<tr class="text-center" data-cpid="${pay.id}">
-                                        <td style="position: relative">${idx + 1}${ pay.fpcid ? '<a href="javascript:void(0);" style="position: absolute;right: 2px;top:50%;transform: translate(-50%, -50%);"><i class="fa fa-cny"></i></a>' : ''}</td>
+                                        <td style="position: relative">${idx + 1}${sourceIconHtml}</td>
                                         <td>${moment(pay.pay_time).format('YYYY-MM-DD')}</td>
                                         <td>${moment(pay.pay_time).format('YYYY-MM-DD')}</td>
                                         <td>${pay.used}</td>
                                         <td>${pay.used}</td>
                                         <td>${pay.pay_price}</td>
                                         <td>${pay.pay_price}</td>
@@ -2221,12 +2226,14 @@ $(document).ready(function() {
             return;
             return;
         }
         }
         $('#stages-pay').html('');
         $('#stages-pay').html('');
+        $('#tender-contract-pay-name').text('');
         postData(window.location.pathname + '/update', { postType: 'get-tender-pays', postData: { tid: node.rela_tender } }, function (result) {
         postData(window.location.pathname + '/update', { postType: 'get-tender-pays', postData: { tid: node.rela_tender } }, function (result) {
+            $('#tender-contract-pay-name').text(result.tender ? result.tender.name : '');
             let html = '';
             let html = '';
             for (const stage of result.stages) {
             for (const stage of result.stages) {
-                const hadStage = _.find(contractPays, { rela_stage: stage.id });
+                const hadStage = _.find(contractPays, pay => pay.rela_stage + '' === stage.id + '');
                 html += `<tr>
                 html += `<tr>
-                    <td><input type="checkbox" name="pays[]" value="${stage.id}" ${hadStage ? 'checked disabled' : ''} /></td>
+                    <td><input type="checkbox" name="pays[]" value="${stage.id}" data-yf-price="${stage.yf_price || 0}" ${hadStage ? 'checked disabled' : ''} /></td>
                     <td>${stage.order}</td>
                     <td>${stage.order}</td>
                     <td>${stage.pay_time}</td>
                     <td>${stage.pay_time}</td>
                     <td>${stage.pay_price}</td>
                     <td>${stage.pay_price}</td>
@@ -2251,22 +2258,28 @@ $(document).ready(function() {
             return;
             return;
         }
         }
         const pays = [];
         const pays = [];
+        let addYfPrice = 0;
         $('#stages-pay input[name="pays[]"]:checked:not(:disabled)').each(function () {
         $('#stages-pay input[name="pays[]"]:checked:not(:disabled)').each(function () {
-            pays.push(parseInt($(this).val()));
+            pays.push($(this).val());
+            addYfPrice = ZhCalc.add(addYfPrice, parseFloat($(this).attr('data-yf-price')) || 0);
         });
         });
         if (pays.length === 0) {
         if (pays.length === 0) {
             toastr.warning('请选择至少一个付款阶段');
             toastr.warning('请选择至少一个付款阶段');
             return;
             return;
         }
         }
-        console.log(pays);
-        // postData(window.location.pathname + '/update', { postType: 'add-tender-pays', postData: { select: node.id, pays: pays } }, function (result) {
-        //     const refreshNode = contractTree.loadPostData(result.node);
-        //     contractTreeSpreadObj.refreshTree(contractSheet, refreshNode);
-        //     contractTreeSpreadObj.setContractListHtml('pay', node, result.pays);
-        //     contractTreeSpreadObj.checkCloseStatus(node);
-        //     contractTreeSpreadObj.setContract(contractSheet, false);
-        //     $('#cons-tender-addpay').modal('hide');
-        // });
+        const newYfPrice = ZhCalc.add(node.yf_price || 0, addYfPrice);
+        if (newYfPrice > (node.total_price || 0)) {
+            toastr.warning(`累计应付金额不能大于合同金额(累计应付:${newYfPrice},合同金额:${node.total_price || 0})`);
+            return;
+        }
+        postData(window.location.pathname + '/update', { postType: 'add-tender-pays', postData: { select: node.id, pays: pays } }, function (result) {
+            const refreshNode = contractTree.loadPostData(result.node);
+            contractTreeSpreadObj.refreshTree(contractSheet, refreshNode);
+            contractTreeSpreadObj.setContractListHtml('pay', node, result.pays);
+            contractTreeSpreadObj.checkCloseStatus(node);
+            contractTreeSpreadObj.setContract(contractSheet, false);
+            $('#cons-tender-addpay').modal('hide');
+        });
     });
     });
 
 
     $('body').on('click', '#htpay .pay-edit', function () {
     $('body').on('click', '#htpay .pay-edit', function () {

+ 147 - 0
app/service/contract_pay.js

@@ -93,6 +93,153 @@ module.exports = app => {
             return { pays: await this.getPays(options, cid), node: { update: node } };
             return { pays: await this.getPays(options, cid), node: { update: node } };
         }
         }
 
 
+        async addTenderPays(options, cid, stageIds) {
+            const node = await this.ctx.service.contract.getDataById(cid);
+            if (!node) {
+                throw '合同不存在';
+            }
+            if (!node.rela_tender) {
+                throw '该合同没有关联标段';
+            }
+            if (!Array.isArray(stageIds) || stageIds.length === 0) {
+                throw '请选择至少一个付款阶段';
+            }
+
+            const ids = this.ctx.helper._.uniq(stageIds.map(id => String(id).trim()).filter(id => id));
+            if (ids.length !== stageIds.length) {
+                throw '付款阶段参数有误';
+            }
+
+            const stages = this.ctx.subProject.page_show.phasePay
+                ? await this.ctx.service.phasePay.getDatasForContract(node.rela_tender)
+                : await this.ctx.service.stagePay.getDatasForContract(node.rela_tender);
+            const selectedStages = ids.map(id => stages.find(stage => stage.id + '' === id));
+            if (selectedStages.some(stage => !stage)) {
+                throw '付款阶段不存在或未通过审批';
+            }
+
+            const existedPays = await this.getAllDataByCondition({ where: { cid, rela_stage: ids } });
+            if (existedPays.length > 0) {
+                throw '选择的付款阶段已关联该合同';
+            }
+
+            const isPhasePay = !!this.ctx.subProject.page_show.phasePay;
+            const sourcePays = isPhasePay
+                ? await this.ctx.service.phasePay.getAllDataByCondition({ where: { id: ids } })
+                : await this.ctx.service.stage.getAllDataByCondition({ where: { id: ids } });
+            const transaction = await this.db.beginTransaction();
+            try {
+                for (const stage of selectedStages) {
+                    const sourcePay = sourcePays.find(item => item.id + '' === stage.id + '');
+                    if (!sourcePay) {
+                        throw '付款阶段源数据不存在';
+                    }
+                    const insertData = {
+                        spid: options.spid || null,
+                        tid: options.tid || null,
+                        contract_type: options.contract_type,
+                        cid,
+                        uid: isPhasePay ? sourcePay.create_user_id : sourcePay.user_id,
+                        pay_time: stage.pay_time,
+                        used: stage.used || '合同',
+                        pay_price: stage.pay_price,
+                        debit_price: stage.debit_price,
+                        yf_price: stage.yf_price,
+                        sf_price: stage.sf_price,
+                        pay_type: stage.pay_type,
+                        remark: '',
+                        rela_stage: stage.id,
+                        create_time: new Date(),
+                        status: auditConst.status.checked,
+                        times: 1,
+                        final_auditor_str: sourcePay.final_auditor_str || '',
+                        need_shenpi: options.spid
+                            ? this.ctx.subProject.page_show.openContractPaySubProjectShenpi
+                            : this.ctx.subProject.page_show.openContractPayTenderShenpi,
+                    };
+                    const result = await transaction.insert(this.tableName, insertData);
+                    insertData.id = result.insertId;
+                    await this.copyTenderPayAudits(transaction, insertData, sourcePay, isPhasePay);
+                }
+                await this.calcContract(transaction, node);
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+            return { pays: await this.getPays(options, cid), node: { update: node } };
+        }
+
+        async copyTenderPayAudits(transaction, contractPay, sourcePay, isPhasePay) {
+            const sourceAudits = isPhasePay
+                ? await this.ctx.service.phasePayAudit.getAuditors(sourcePay.id, sourcePay.audit_times)
+                : await this.ctx.service.stageAudit.getAuditors(sourcePay.id, sourcePay.times);
+            const audits = [];
+            if (isPhasePay) {
+                let preAuditTime = sourcePay.create_time;
+                for (const audit of sourceAudits) {
+                    audits.push({
+                        spid: contractPay.spid,
+                        tid: contractPay.tid,
+                        cid: contractPay.cid,
+                        cpid: contractPay.id,
+                        csid: null,
+                        aid: audit.audit_id,
+                        order: audit.active_order,
+                        times: 1,
+                        status: auditConst.status.checked,
+                        begin_time: preAuditTime,
+                        end_time: audit.audit_time,
+                        opinion: audit.opinion || '',
+                        audit_type: audit.audit_type,
+                        audit_order: audit.audit_order,
+                    });
+                    if (audit.audit_time) {
+                        preAuditTime = audit.audit_time;
+                    }
+                }
+            } else {
+                const firstAudit = sourceAudits.length > 0 ? sourceAudits[0] : null;
+                audits.push({
+                    spid: contractPay.spid,
+                    tid: contractPay.tid,
+                    cid: contractPay.cid,
+                    cpid: contractPay.id,
+                    csid: null,
+                    aid: sourcePay.user_id,
+                    order: 0,
+                    times: 1,
+                    status: auditConst.status.checked,
+                    begin_time: sourcePay.create_time,
+                    end_time: firstAudit && firstAudit.begin_time ? firstAudit.begin_time : sourcePay.create_time,
+                    opinion: '',
+                    audit_type: auditType.key.common,
+                    audit_order: 0,
+                });
+                for (const audit of sourceAudits) {
+                    audits.push({
+                        spid: contractPay.spid,
+                        tid: contractPay.tid,
+                        cid: contractPay.cid,
+                        cpid: contractPay.id,
+                        csid: null,
+                        aid: audit.aid,
+                        order: audit.order,
+                        times: 1,
+                        status: auditConst.status.checked,
+                        begin_time: audit.begin_time,
+                        end_time: audit.end_time,
+                        opinion: audit.opinion || '',
+                        audit_type: audit.audit_type,
+                        audit_order: audit.audit_order,
+                    });
+                }
+            }
+            if (audits.length > 0) {
+                await transaction.insert(this.ctx.service.contractSpAudit.tableName, audits);
+            }
+        }
+
         async save(options, cid, data) {
         async save(options, cid, data) {
             if (!data.id) {
             if (!data.id) {
                 throw '参数有误';
                 throw '参数有误';

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

@@ -513,6 +513,10 @@
                 <h5 class="modal-title">添加标段<% if (ctx.contract_type === contractConst.type.expenses) { %>支付<% } else if (ctx.contract_type === contractConst.type.income) { %>回款<% } %>记录</h5>
                 <h5 class="modal-title">添加标段<% if (ctx.contract_type === contractConst.type.expenses) { %>支付<% } else if (ctx.contract_type === contractConst.type.income) { %>回款<% } %>记录</h5>
             </div>
             </div>
             <div class="modal-body">
             <div class="modal-body">
+                <div class="mb-2 text-left">
+                    <span class="text-muted">关联标段名称:</span>
+                    <span id="tender-contract-pay-name"></span>
+                </div>
                 <div class="modal-height-500" style="overflow:auto;">
                 <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">
                     <table class="table table-sm table-bordered text-center" style="word-break:break-all; table-layout: fixed">
                         <thead>
                         <thead>

+ 1 - 1
sql/update.sql

@@ -23,7 +23,7 @@ ALTER TABLE `zh_contract`
 ADD COLUMN `rela_tender` int NULL DEFAULT NULL COMMENT '关联标段id' AFTER `exist_supplement`;
 ADD COLUMN `rela_tender` int NULL DEFAULT NULL COMMENT '关联标段id' AFTER `exist_supplement`;
 
 
 ALTER TABLE `zh_contract_pay`
 ALTER TABLE `zh_contract_pay`
-ADD COLUMN `rela_stage` int NULL DEFAULT NULL COMMENT '关联的标段期id' AFTER `remark`;
+ADD COLUMN `rela_stage` varchar(255) NULL DEFAULT NULL COMMENT '关联的标段期id或者独立支付期id' AFTER `remark`;
 
 
 ------------------------------------
 ------------------------------------
 -- 表数据
 -- 表数据