Przeglądaj źródła

合同结算金额功能

ellisran 20 godzin temu
rodzic
commit
fcffe57a59

+ 4 - 0
app/const/contract.js

@@ -28,6 +28,7 @@ const colSet = {
         { name: '合同类型', field: 'type', fixed: ['alias'], gd: true },
         { name: '甲方', field: 'party_a', fixed: ['alias'], gd: true },
         { name: '乙方', field: 'party_b', fixed: ['alias'], gd: true },
+        { name: '结算金额', field: 'settle_price', fixed: ['alias'], gd: true },
         { name: '累计应付', field: 'yf_price', fixed: ['alias'] },
         { name: '应付进度', field: 'stackedBar', fixed: ['alias'] },
         { name: '累计实付', field: 'sf_price', fixed: ['alias'] },
@@ -41,6 +42,7 @@ const colSet = {
         { name: '合同类型', field: 'type', fixed: ['alias'], gd: true },
         { name: '甲方', field: 'party_a', fixed: ['alias'], gd: true },
         { name: '乙方', field: 'party_b', fixed: ['alias'], gd: true },
+        { name: '结算金额', field: 'settle_price', fixed: ['alias'], gd: true },
         { name: '累计应回', field: 'yf_price', fixed: ['alias'] },
         { name: '应回进度', field: 'stackedBar', fixed: ['alias'] },
         { name: '累计实回', field: 'sf_price', fixed: ['alias'] },
@@ -57,6 +59,7 @@ const defaultColSet = {
         { field: 'type', show: 1, gd: true },
         { field: 'party_a', show: 0, gd: true },
         { field: 'party_b', show: 0, gd: true },
+        { field: 'settle_price', show: 0, gd: true },
         { field: 'yf_price', show: 1 },
         { field: 'stackedBar', show: 1 },
         { field: 'sf_price', show: 0 },
@@ -70,6 +73,7 @@ const defaultColSet = {
         { field: 'type', show: 1, gd: true },
         { field: 'party_a', show: 0, gd: true },
         { field: 'party_b', show: 0, gd: true },
+        { field: 'settle_price', show: 0, gd: true },
         { field: 'yf_price', show: 1 },
         { field: 'stackedBar', show: 1 },
         { field: 'sf_price', show: 0 },

+ 38 - 11
app/public/js/contract_detail.js

@@ -63,6 +63,9 @@ $(document).ready(function() {
             calc: function (data) {
                 return !permission_edit || (data && data.children && data.children.length > 0);
             },
+            settlePrice: function (data) {
+                return !(data && data.c_code && !data.settle_code && (data.uid === user_id || permission_edit_contract));
+            },
             type: function (data) {
                 return !(data && data.c_code && !data.settle_code && (data.uid === user_id || permission_edit_contract));
             }
@@ -86,8 +89,17 @@ $(document).ready(function() {
         };
     }
     const gdColField = ['type', 'party_a', 'party_b'];
+    const settlePriceEnabled = !!_.find(colSet, { field: 'settle_price', show: 1 });
+    const getContractPayLimit = function (node) {
+        const useSettlePrice = settlePriceEnabled && parseFloat(node.settle_price) !== 0;
+        return {
+            price: useSettlePrice ? node.settle_price : node.total_price,
+            name: useSettlePrice ? '结算金额' : '合同金额',
+        };
+    };
     const colMap = {
       yf_price: {title: '累计应付', colSpan: '1', rowSpan: '2', field: 'yf_price', hAlign: 2, width: 120, formatter: '@', readOnly: true},
+      settle_price: {title: '结算金额', colSpan: '1', rowSpan: '2', field: 'settle_price', hAlign: 2, width: 120, type: 'Number', readOnly: 'readOnly.settlePrice'},
       stackedBar: {title: '应付进度', colSpan: '1', rowSpan: '2', formatter: '@', readOnly: true, field: 'stackedBar', hAlign: 0, width: 200, cellType: 'stackedBar', stackedBarCover: true, bc_type: 'grid', getTip: getStackedBarTip, hintNum: true},
       sf_price: {title: '累计实付', colSpan: '1', rowSpan: '2', field: 'sf_price', hAlign: 2, width: 120, formatter: '@', readOnly: true},
       stackedBarSf: {title: '实付进度', colSpan: '1', rowSpan: '2', formatter: '@', readOnly: true, field: 'stackedBarSf', hAlign: 0, width: 200, cellType: 'stackedBar', stackedBarCover: true, bc_type: 'grid', getTip: getStackedBarTipSf, hintNum: true},
@@ -99,6 +111,13 @@ $(document).ready(function() {
     // 根据 colSet 组装最终显示的列配置
     colSet.forEach(col => {
         const colInfo = colMap && colMap[col.field];
+        if (col.field === 'settle_price') {
+            if (colInfo && col.show) {
+                const totalPriceIndex = contractSpreadSetting.cols.findIndex(c => c.field === 'total_price');
+                contractSpreadSetting.cols.splice(totalPriceIndex + 1, 0, Object.assign({}, colInfo, { title: col.name }));
+            }
+            return;
+        }
         if (colInfo && col.show) {
             contractSpreadSetting.cols.push(Object.assign({}, colInfo, { title: (col && col.alias) || col.name }));
         }
@@ -125,7 +144,7 @@ $(document).ready(function() {
         level: 'level',
         rootId: -1,
         keys: ['id', 'tid', 'spid', 'contract_type'],
-        calcFields: ['pay_price', 'total_price', 'sf_price', 'debit_price', 'yf_price', 'calc', 'calc2'],
+        calcFields: ['pay_price', 'total_price', 'settle_price', 'sf_price', 'debit_price', 'yf_price', 'calc', 'calc2'],
         autoExpand: 3,
         markExpandKey: 'contract-bills-expand' + window.location.pathname.split('/')[2] + contractConst.typeMap[contract_type],
         markExpandSubKey: window.location.pathname.split('/')[2] + contractConst.typeMap[contract_type],
@@ -2274,8 +2293,11 @@ $(document).ready(function() {
             return;
         }
         const newYfPrice = ZhCalc.add(node.yf_price || 0, addYfPrice);
-        if (newYfPrice > (node.total_price || 0)) {
-            toastr.warning(`累计应付金额不能大于合同金额(累计应付:${newYfPrice},合同金额:${node.total_price || 0})`);
+        const contractPayLimit = getContractPayLimit(node);
+        const payLimit = contractPayLimit.price;
+        const payLimitName = contractPayLimit.name;
+        if (newYfPrice > (payLimit || 0)) {
+            toastr.warning(`累计应付金额不能大于${payLimitName}(累计应付:${newYfPrice},${payLimitName}:${payLimit || 0})`);
             return;
         }
         postData(window.location.pathname + '/update', { postType: 'add-tender-pays', postData: { select: node.id, pays: pays } }, function (result) {
@@ -3012,8 +3034,9 @@ $(document).ready(function() {
             } else {
                 $('#cons-addpay .sf-tips').text('');
             }
-            if (parseFloat(yf_price) > ZhCalc.add(cpInfo.yf_price, ZhCalc.sub(node.total_price, node.yf_price))) {
-                $('#cons-addpay .yf-tips').text(' ≤ ' + ZhCalc.add(cpInfo.yf_price, ZhCalc.sub(node.total_price, node.yf_price)));
+            const payLimit = getContractPayLimit(node).price;
+            if (parseFloat(yf_price) > ZhCalc.add(cpInfo.yf_price, ZhCalc.sub(payLimit, node.yf_price))) {
+                $('#cons-addpay .yf-tips').text(' ≤ ' + ZhCalc.add(cpInfo.yf_price, ZhCalc.sub(payLimit, node.yf_price)));
             } else {
                 $('#cons-addpay .yf-tips').text('');
             }
@@ -3031,8 +3054,9 @@ $(document).ready(function() {
             } else {
                 $('#cons-addpay .sf-tips').text('');
             }
-            if (parseFloat(yf_price) > ZhCalc.sub(node.total_price, node.yf_price)) {
-                $('#cons-addpay .yf-tips').text(' ≤ ' + ZhCalc.sub(node.total_price, node.yf_price));
+            const payLimit = getContractPayLimit(node).price;
+            if (parseFloat(yf_price) > ZhCalc.sub(payLimit, node.yf_price)) {
+                $('#cons-addpay .yf-tips').text(' ≤ ' + ZhCalc.sub(payLimit, node.yf_price));
             } else {
                 $('#cons-addpay .yf-tips').text('');
             }
@@ -3146,6 +3170,9 @@ $(document).ready(function() {
 
     function judgePays(node, data, name, cpInfo = null) {
         let flag = true;
+        const contractPayLimit = getContractPayLimit(node);
+        const payLimit = contractPayLimit.price;
+        const payLimitName = contractPayLimit.name;
         if (!data.pay_time) {
             toastr.error('请输入'+ (name === '付' ? '支付' : '回款') +'日期');
             return false;
@@ -3176,8 +3203,8 @@ $(document).ready(function() {
             const newNodeDebitPrice = ZhCalc.add(ZhCalc.sub(node.debit_price, cpInfo.debit_price), parseFloat(data.debit_price));
             const newNodeYfPrice = ZhCalc.sub(newNodePayPrice, newNodeDebitPrice);
             const newNodeSfPrice = ZhCalc.add(ZhCalc.sub(node.sf_price, cpInfo.sf_price), parseFloat(data.sf_price));
-            if (newNodeYfPrice > node.total_price) {
-                toastr.error('累计应'+ name +'金额不能大于合同金额');
+            if (newNodeYfPrice > payLimit) {
+                toastr.error('累计应'+ name +'金额不能大于' + payLimitName);
                 return false;
             }
             if (newNodeSfPrice > newNodeYfPrice) {
@@ -3185,8 +3212,8 @@ $(document).ready(function() {
                 return false;
             }
         } else {
-            if (ZhCalc.add(parseFloat(data.yf_price), node.yf_price) > node.total_price) {
-                toastr.error('累计应'+ name +'金额不能大于合同金额');
+            if (ZhCalc.add(parseFloat(data.yf_price), node.yf_price) > payLimit) {
+                toastr.error('累计应'+ name +'金额不能大于' + payLimitName);
                 return false;
             }
             if (ZhCalc.add(parseFloat(data.sf_price), node.sf_price) > ZhCalc.add(parseFloat(data.yf_price), node.yf_price)) {

+ 10 - 0
app/service/contract.js

@@ -141,6 +141,16 @@ module.exports = app => {
                     if (!updateNode) {
                         throw '提交数据错误';
                     }
+                    if (Object.prototype.hasOwnProperty.call(row, 'settle_price')) {
+                        const settlePrice = Number(row.settle_price || 0);
+                        if (!Number.isFinite(settlePrice) || settlePrice < 0) {
+                            throw '结算金额只能输入大于等于0的数字';
+                        }
+                        if (settlePrice !== 0 && settlePrice < Number(updateNode.yf_price || 0)) {
+                            throw '结算金额不能小于累计应' + (options.contract_type === 1 ? '付' : '回') + '金额';
+                        }
+                        row.settle_price = settlePrice;
+                    }
                     updateDatas.push(row);
                 }
                 if (updateDatas.length > 0) await transaction.updateRows(this.tableName, updateDatas);

+ 31 - 0
app/service/contract_pay.js

@@ -25,6 +25,29 @@ module.exports = app => {
             this.dataId = 'id';
         }
 
+        async _isSettlePriceEnabled(options, node) {
+            const colSet = await this.ctx.service.contractColSet.getContractColSet(
+                options.spid || null,
+                options.tid || null,
+                node.contract_type
+            );
+            const settlePriceCol = colSet && colSet.info
+                ? colSet.info.find(col => col.field === 'settle_price')
+                : null;
+            return !!(settlePriceCol && Number(settlePriceCol.show) === 1);
+        }
+
+        async _checkPayLimit(options, node, yfPrice) {
+            const useSettlePrice = await this._isSettlePriceEnabled(options, node)
+                && Number(node.settle_price || 0) !== 0;
+            const limit = useSettlePrice ? Number(node.settle_price) : Number(node.total_price || 0);
+            if (Number(yfPrice) > limit) {
+                const limitName = useSettlePrice ? '结算金额' : '合同金额';
+                const actionName = node.contract_type === contractConst.type.expenses ? '付' : '回';
+                throw '累计应' + actionName + '金额不能大于' + limitName;
+            }
+        }
+
         async getPays(options, cid) {
             const sql = 'SELECT * FROM ?? WHERE ' + this.ctx.helper._getOptionsSql(options) + ' AND `cid` = ? ORDER BY `create_time` DESC';
             const sqlParams = [this.tableName, cid];
@@ -62,6 +85,7 @@ module.exports = app => {
             if (!node) {
                 throw '合同不存在';
             }
+            await this._checkPayLimit(options, node, this.ctx.helper.add(node.yf_price || 0, data.yf_price || 0));
             const transaction = await this.db.beginTransaction();
             try {
                 const insertData = {
@@ -117,6 +141,8 @@ module.exports = app => {
             if (selectedStages.some(stage => !stage)) {
                 throw '付款阶段不存在或未通过审批';
             }
+            const addYfPrice = selectedStages.reduce((total, stage) => this.ctx.helper.add(total, stage.yf_price || 0), 0);
+            await this._checkPayLimit(options, node, this.ctx.helper.add(node.yf_price || 0, addYfPrice));
 
             const existedPays = await this.getAllDataByCondition({ where: { cid, rela_stage: ids } });
             if (existedPays.length > 0) {
@@ -255,6 +281,11 @@ module.exports = app => {
             if (cpInfo.rela_stage) {
                 throw '关联标段期的合同支付不能编辑';
             }
+            const newYfPrice = this.ctx.helper.add(
+                this.ctx.helper.sub(node.yf_price || 0, cpInfo.yf_price || 0),
+                data.yf_price || 0
+            );
+            await this._checkPayLimit(options, node, newYfPrice);
             const transaction = await this.db.beginTransaction();
             try {
                 await transaction.update(this.tableName, data);

+ 2 - 1
sql/update.sql

@@ -20,7 +20,8 @@ ADD COLUMN `update_calc_time` timestamp NULL COMMENT '最后修改计算数据
 ADD COLUMN `update_expr_calc_time` timestamp NULL COMMENT '最后使用公式计算时间' AFTER `update_calc_time`;
 
 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`,
+ADD COLUMN `settle_price` decimal(30, 8) NOT NULL DEFAULT 0 COMMENT '结算金额(独立字段,非合同结算功能)' AFTER `total_price`;
 
 ALTER TABLE `zh_contract_pay`
 ADD COLUMN `rela_stage` varchar(255) NULL DEFAULT NULL COMMENT '关联的标段期id或者独立支付期id' AFTER `remark`;