Pārlūkot izejas kodu

feat: 重庆18增加累进算法

vian 4 gadi atpakaļ
vecāks
revīzija
6bde1d1f90

+ 1 - 0
config/gulpConfig.js

@@ -91,6 +91,7 @@ module.exports = {
         'public/web/socket/connection.js',
         'public/web/uuid.js',
         'public/web/sheet/sheet_common.js',
+        'public/calculate_util.js',
         'web/building_saas/main/js/models/calc_program.js',
         'web/building_saas/main/js/models/calc_base.js',
         'web/building_saas/main/js/views/calc_program_manage.js',

+ 32 - 0
modules/all_models/std_progressive_lib.js

@@ -0,0 +1,32 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2019/1/25
+ * @version
+ */
+
+/*
+* 累进区间库
+* 设置清单基数中金额区间与费率的对应关系,清单基数累进算法使用
+* */
+
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+const oprSchema = require('../all_schemas/opr_schema');
+
+const progressiveInterval = new Schema({
+    ID: String,
+    name: String,
+    creator: String,
+    createDate: Date,
+    recentOpr: [oprSchema],
+    data: {
+        type: [Schema.Types.Mixed],
+        default: []
+    },
+}, {versionKey: false});
+
+mongoose.model('std_progressive_lib', progressiveInterval, 'std_progressive_lib');

+ 8 - 0
modules/pm/facade/pm_facade.js

@@ -42,6 +42,7 @@ module.exports={
     changeFile:changeFile,
     getBasicInfo: getBasicInfo,
     getProjectFeature: getProjectFeature,
+    getProgressiveInterval: getProgressiveInterval,
     getProjectByGranularity: getProjectByGranularity,
     importProject: importProject,
     getProjectPlaceholder: getProjectPlaceholder,
@@ -92,6 +93,7 @@ let compilationModel = mongoose.model('compilation');
 let engineeringModel = mongoose.model('engineering_lib');
 let basicInfoModel = mongoose.model('std_basic_info_lib');
 let projectFeatureModel = mongoose.model('std_project_feature_lib');
+let progressiveModel = mongoose.model('std_progressive_lib');
 let stdRationItemModel = mongoose.model('std_ration_lib_ration_items');
 let stdGljItemModel = mongoose.model('std_glj_lib_gljList');
 let evaluateListModel = mongoose.model("evaluate_list");
@@ -1582,6 +1584,12 @@ async function getFullPath(projectID) {
     }
 }
 
+//获取累进区间数据
+async function getProgressiveInterval(libID) {
+    let lib = await progressiveModel.findOne({ID: libID});
+    return lib ? lib.data : [];
+}
+
 // 获取项目链上,自身、父项、爷爷项...的ID
 async function getUpChainIDs(projectID) {
     const rst = [];

+ 4 - 0
modules/pm/models/project_model.js

@@ -160,6 +160,10 @@ ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId
                     data.updateData.property.decimal = defaultDecimal;
                     //清单工程量精度
                     data.updateData.property.billsQuantityDecimal = billsQuantityDecimal;
+                    // 累进区间
+                    if (data.updateData.property.progressiveLibID) {
+                        data.updateData.property.progressiveInterval = await pmFacade.getProgressiveInterval(data.updateData.property.progressiveLibID);
+                    }
                     //呈现选项
                     data.updateData.property.displaySetting = displaySetting;
 

+ 164 - 0
public/calculate_util.js

@@ -0,0 +1,164 @@
+'use strict';
+
+((factory) => {
+    if (typeof module !== 'undefined') {
+        const scMathUtil = require('./scMathUtil').getUtil();
+        const commonConstants = require('./common_constants');
+        module.exports = factory(scMathUtil, commonConstants);
+    } else {
+        window.calculateUtil = factory(scMathUtil, commonConstants);
+    }
+})((scMathUtil, commonConstants) => {
+    function standar(exp) {
+        //去空格
+        exp = exp.replace(/\s/g, '');
+        //( to (
+        exp = exp.replace(/(/g, '(');
+        //)to )
+        exp = exp.replace(/)/g, ')');
+        //,to ,
+        exp = exp.replace(/,/g, ',');
+        //f to F
+        exp = exp.replace(new RegExp('f', 'g'), 'F');
+        return exp;
+    }
+
+    /**
+     * 获取累进办法计算的金额
+     * @param {Number} baseFee - 基准金额
+     * @param {String} name - 使用累进计算的基数名称(需要与累进库中的名称匹配)
+     * @param {Array} progressiveData - 项目的累进数据(property.progressiveInterval)
+     * @param {Number | String} taxType - 项目的计税方式
+     * @param {Number} decimal - 精度
+     * @param {Object} deficiency - 不足处理映射 @example: {'路线工程监理费': 20000 } // 不足2万按2万
+     * @param {Object} beyond - 超出处理映射 @example: {'路线工程监理费': 500000 } // 超过50万按50万
+     * @return {Number}
+     */
+    function getProgressiveFee(baseFee, name, progressiveData, taxType, decimal, deficiency, beyond) {
+        if (!progressiveData) {
+            throw '该项目不存在累进区间数据';
+        }
+        //根据基数名称匹配累进库数据,标准化以免(())等不同导致不匹配
+        const matchProgressiveData = progressiveData.find(item => standar(item.name) === standar(name));
+        if (!matchProgressiveData) {
+            throw `计算基数{${name}}不存在累进区间数据`;
+        }
+        // 将原始数据转换成方便处理的数据:[{feeRate: xx, min: 0, max: 200, minOpr: '(', maxOpr: ']'}]
+        const progression = matchProgressiveData.progression.map(item => {
+            let feeRateField = +taxType === commonConstants.TaxType.NORMAL
+                ? 'generalRate'
+                : +taxType === commonConstants.TaxType.SIMPLE
+                    ? 'simpleRate'
+                    : '';
+            if (!feeRateField) {
+                throw `计算基数{${name}}累进区间费率数据错误`;
+            }
+            // item.interval内容: eg (0,200]、[300,500) [1000,+)....
+            const interval = standar(item.interval);
+            // ( => 大于 [ => 大于等于 ) => 小于 ] => 小于等于
+            const minReg = /([\(\[])(\d+)/;
+            const minMatch = minReg.exec(interval);
+            if (!minMatch || !minMatch[1] || !minMatch[2]) {
+                throw `计算基数{${name}}累进区间数据错误`;
+            }
+            const minOpr = minMatch[1];
+            // 后台数据单位为万元,这里转为为元
+            const min = parseFloat(minMatch[2]) * 10000;
+            const maxReg = /[\,,]([\d\+]+)([\)\]])/;
+            const maxMatch = maxReg.exec(interval);
+            if (!maxMatch || !maxMatch[1] || !maxMatch[2]) {
+                throw `计算基数{${name}}累进区间数据错误`;
+            }
+            const max = maxMatch[1] === '+' ? 'infinity' : parseFloat(maxMatch[1]) * 10000;
+            const maxOpr = maxMatch[2];
+            return {
+                feeRate: item[feeRateField],
+                min,
+                minOpr,
+                max,
+                maxOpr
+            }
+        });
+        progression.sort((a, b) => a.min - b.min);
+        // 基数所在区间
+        const withinData = progression.find(item => {
+            const oprMiddle = item.max === 'infinity' ? '+' : '';
+            const oprLink = item.minOpr + oprMiddle + item.maxOpr;
+            switch (oprLink) {
+                case '()':
+                    return baseFee > item.min && baseFee < item.max;
+                case '(]':
+                    return baseFee > item.min && baseFee <= item.max;
+                case '[)':
+                    return baseFee >= item.min && baseFee < item.max;
+                case '[]':
+                    return baseFee >= item.min && baseFee <= item.max;
+                case '(+)':
+                case '(+]':
+                    return baseFee > item.min;
+                case '[+)':
+                case '[+]':
+                    return baseFee >= item.min;
+                default:
+                    return false;
+            }
+        });
+        if (!withinData) {
+            return 0;
+        }
+        // 累进计算
+        let fee = 0;
+        //累进之前的区间
+        for (let i = 0; i < progression.indexOf(withinData); i++) {
+            const perData = progression[i];
+            fee += (perData.max - perData.min) * perData.feeRate * 0.01;
+        }
+        //累进所在区间
+        fee = scMathUtil.roundForObj(fee + (baseFee - withinData.min) * withinData.feeRate * 0.01, decimal);
+        // 不足、超出处理
+        const deficiencyFee = deficiency && deficiency[name] || 0;
+        const beyondFee = beyond && beyond[name] || 0;
+        return deficiencyFee && fee > 0 && fee < deficiencyFee
+            ? deficiencyFee
+            : beyondFee && fee > beyondFee
+                ? beyondFee
+                : fee;
+    }
+
+    /**
+     * 该基数包含的累进基数
+     * @param {String} calcBase - 计算基数
+     * @param {Array} progression - 累进基数名称数组
+     * @return {Array}
+     */
+    function getProgressive(calcBase, progression) {
+        if (typeof calcBase !== 'string' || !progression) {
+            return [];
+        }
+        const reg = /{[^}]+}/g;
+        const matched = calcBase.match(reg);
+        if (!matched) {
+            return [];
+        }
+        return matched
+            .filter(mStr => progression.some(pStr => `{${pStr}}` === mStr))
+            .map(mStr => mStr.replace(/[{}]/g, ''));
+    }
+
+    /**
+     * 该基数是否含有累进基数
+     * @param {String} calcBase - 计算基数
+     * @param {Array} progression - 累进基数名称数组
+     * @return {Boolean}
+     */
+    function isProgressive(calcBase, progression) {
+        const progressiveBase = getProgressive(calcBase, progression);
+        return !!progressiveBase.length;
+    }
+
+    return {
+        getProgressiveFee,
+        isProgressive,
+        getProgressive,
+    };
+});

+ 1 - 0
web/building_saas/main/html/main.html

@@ -2727,6 +2727,7 @@
   <script type="text/javascript" src="/web/building_saas/main/js/models/ration_template.js"></script>
   <!--<script type="text/javascript" src="/web/building_saas/main/js/models/volume_price.js"></script>-->
   <script type="text/javascript" src="/web/building_saas/main/js/models/labour_coe.js"></script>
+  <script type="text/javascript" src="/public/calculate_util.js"></script>
   <script type="text/javascript" src="/web/building_saas/main/js/models/calc_program.js"></script>
   <script type="text/javascript" src="/web/building_saas/main/js/models/calc_base.js"></script>
   <script type="text/javascript" src="/web/building_saas/main/js/models/installation_fee.js"></script>

+ 42 - 7
web/building_saas/main/js/models/calc_base.js

@@ -554,6 +554,41 @@ let baseFigureTemplate = {
         }
         return projectObj.project.calcProgram.getBeforeTaxTotalFee(excludeNodes, tender);
     },
+    // 市政道路工程安全文明施工费: 按“税前工程造价”为基数,以累进办法计算。
+    SZDLGCAQWMSGF(tender) {
+        const baseFee = this['SQGCZJ'](tender);
+        return calculateUtil.getProgressiveFee(baseFee, '市政道路工程安全文明施工费', projectObj.project.property.progressiveInterval, projectObj.project.property.taxType, decimalObj.bills.totalPrice);
+    },
+    // 市政桥梁工程安全文明施工费: 按“税前工程造价”为基数,以累进办法计算。
+    SZQLGCAQWMSGF(tender) {
+        const baseFee = this['SQGCZJ'](tender);
+        return calculateUtil.getProgressiveFee(baseFee, '市政桥梁工程安全文明施工费', projectObj.project.property.progressiveInterval, projectObj.project.property.taxType, decimalObj.bills.totalPrice);
+    },
+    // 市政隧道工程安全文明施工费: 按“税前工程造价”为基数,以累进办法计算。
+    SZSDGCAQWMSGF(tender) {
+        const baseFee = this['SQGCZJ'](tender);
+        return calculateUtil.getProgressiveFee(baseFee, '市政隧道工程安全文明施工费', projectObj.project.property.progressiveInterval, projectObj.project.property.taxType, decimalObj.bills.totalPrice);
+    },
+    // 城轨盾构工程安全文明施工费: 按“税前工程造价”为基数,以累进办法计算。
+    CGDGGCAQWMSGF(tender) {
+        const baseFee = this['SQGCZJ'](tender);
+        return calculateUtil.getProgressiveFee(baseFee, '城轨盾构工程安全文明施工费', projectObj.project.property.progressiveInterval, projectObj.project.property.taxType, decimalObj.bills.totalPrice);
+    },
+    // 城轨高架桥工程安全文明施工费: 按“税前工程造价”为基数,以累进办法计算。
+    CGGJQGCAQWMSGF(tender) {
+        const baseFee = this['SQGCZJ'](tender);
+        return calculateUtil.getProgressiveFee(baseFee, '城轨高架桥工程安全文明施工费', projectObj.project.property.progressiveInterval, projectObj.project.property.taxType, decimalObj.bills.totalPrice);
+    },
+    // 城轨地下工程安全文明施工费: 按“税前工程造价”为基数,以累进办法计算。
+    CGDXGCAQWMSGF(tender) {
+        const baseFee = this['SQGCZJ'](tender);
+        return calculateUtil.getProgressiveFee(baseFee, '城轨地下工程安全文明施工费', projectObj.project.property.progressiveInterval, projectObj.project.property.taxType, decimalObj.bills.totalPrice);
+    },
+    // 城轨轨道工程安全文明施工费: 按“税前工程造价”为基数,以累进办法计算。
+    CGGDGCAQWMSGF(tender) {
+        const baseFee = this['SQGCZJ'](tender);
+        return calculateUtil.getProgressiveFee(baseFee, '城轨轨道工程安全文明施工费', projectObj.project.property.progressiveInterval, projectObj.project.property.taxType, decimalObj.bills.totalPrice);
+    },
     'RCJJC': function (tender) {//人材机价差
         return (this['RGJC'](tender) + this['CLJC'](tender) + this['JXJC'](tender)).toDecimal(decimalObj.bills.totalPrice);
     },
@@ -997,15 +1032,15 @@ let baseFigureTemplate = {
         return rst;
     }
 };
-
+const progreesiveBases = ['SZDLGCAQWMSGF', 'SZQLGCAQWMSGF', 'SZSDGCAQWMSGF', 'CGDGGCAQWMSGF', 'CGGJQGCAQWMSGF', 'CGDXGCAQWMSGF', 'CGGDGCAQWMSGF'];
 let figureClassTemplate = {
-    'CONSTRUCTION_ORGANIZATION': {flag: fixedFlag.CONSTRUCTION_ORGANIZATION, filter: ['CSXMF', 'ZZCSXMF', 'ZZCSXMDEJJZJGCF', 'ZZCSXMDEJJRGF', 'ZZCSXMDEJJCLF', 'ZZCSXMDEJJJXF', 'QTXMF', 'GF', 'SJ', 'SQGCZJ', 'AQWMSGZXF']},
+    'CONSTRUCTION_ORGANIZATION': {flag: fixedFlag.CONSTRUCTION_ORGANIZATION, filter: ['CSXMF', 'ZZCSXMF', 'ZZCSXMDEJJZJGCF', 'ZZCSXMDEJJRGF', 'ZZCSXMDEJJCLF', 'ZZCSXMDEJJJXF', 'QTXMF', 'GF', 'SJ', 'SQGCZJ', 'AQWMSGZXF', ...progreesiveBases]},
     'SAFETY_CONSTRUCTION': {flag: fixedFlag.SAFETY_CONSTRUCTION, filter: ['CSXMF', 'ZZCSXMF', 'ZZCSXMDEJJZJGCF', 'ZZCSXMDEJJRGF', 'ZZCSXMDEJJCLF', 'ZZCSXMDEJJJXF', 'QTXMF', 'GF', 'SJ', 'AQWMSGZXF']},
-    'OTHER': {flag: fixedFlag.OTHER, filter: ['QTXMF', 'SQGCZJ', 'AQWMSGZXF']},
-    'CHARGE': {flag: fixedFlag.CHARGE, filter: ['GF', 'SQGCZJ', 'AQWMSGZXF']},
-    'TAX': {flag: fixedFlag.TAX, filter: ['SJ', 'SQGCZJ']},
-    'ENGINEERINGCOST': {flag: fixedFlag.ENGINEERINGCOST, filter: ['SQGCZJ']},
-    'OTHERS': {flag: fixedFlag.ENGINEERINGCOST, filter: ['SQGCZJ', 'AQWMSGZXF']},
+    'OTHER': {flag: fixedFlag.OTHER, filter: ['QTXMF', 'SQGCZJ', 'AQWMSGZXF', ...progreesiveBases]},
+    'CHARGE': {flag: fixedFlag.CHARGE, filter: ['GF', 'SQGCZJ', 'AQWMSGZXF', ...progreesiveBases]},
+    'TAX': {flag: fixedFlag.TAX, filter: ['SJ', 'SQGCZJ', ...progreesiveBases]},
+    'ENGINEERINGCOST': {flag: fixedFlag.ENGINEERINGCOST, filter: ['SQGCZJ', ...progreesiveBases]},
+    'OTHERS': {flag: fixedFlag.ENGINEERINGCOST, filter: ['SQGCZJ', 'AQWMSGZXF', ...progreesiveBases]},
 };
 
 //基数的值不是通过清单节点获得的,则该基数的fixedBill为空,如价差、甲供、分包; class:分类,用于基数选择界面分类显示

+ 6 - 2
web/building_saas/pm/js/pm_newMain.js

@@ -3871,9 +3871,12 @@ function AddTender() {
         // 一个项目里面,这两个文件必须得有,而界面又没有像费率、单价文件那样给出可选项。所以这里给出提示。
         if (!libs.artificial_lib)  throw '编办没有绑定人工系数标准文件';
 
-        let featureLibID = "";
+        let featureLibID = "",
+            progressiveLibID = "";
         if(libs.feature_lib && libs.feature_lib.length > 0) featureLibID = libs.feature_lib[0].id;
-
+        if(libs.progressive_lib && libs.progressive_lib.length > 0) {
+            progressiveLibID = libs.progressive_lib[0].id;
+        }
 
         let calcProgramName = $('#tender-calcProgram').children("option:selected").text();
         $.bootstrapLoading.start();
@@ -3922,6 +3925,7 @@ function AddTender() {
             templateLibID:templateLibID,
             colLibID:colLibID,
             featureLibID:featureLibID,
+            progressiveLibID:progressiveLibID,
             region: region,
             projectFeature: featureData ? featureData : []
         };

+ 21 - 0
web/over_write/js/chongqing_2018.js

@@ -312,6 +312,27 @@ if(typeof baseFigureMap !== 'undefined'){
         '税前工程造价': {base: 'SQGCZJ', class: 'SQGCZJ',
             cycleCalcRef: [fixedFlag.SUB_ENGINERRING, fixedFlag.OTHER, fixedFlag.CHARGE],   //循环计算相关固定行,由于计算排除了本身,不用判断措施项目
             multiRef: [fixedFlag.SUB_ENGINERRING, fixedFlag.MEASURE, fixedFlag.OTHER, fixedFlag.CHARGE]},//相关固定行
+        '市政道路工程安全文明施工费': {base: 'SZDLGCAQWMSGF', class: 'CSXM',
+            cycleCalcRef: [fixedFlag.SUB_ENGINERRING, fixedFlag.OTHER, fixedFlag.CHARGE],   // 以税前工程造价作为累进基数,因此配置沿用{税前工程造价}
+            multiRef: [fixedFlag.SUB_ENGINERRING, fixedFlag.MEASURE, fixedFlag.OTHER, fixedFlag.CHARGE]},
+        '市政桥梁工程安全文明施工费': {base: 'SZQLGCAQWMSGF', class: 'CSXM',
+            cycleCalcRef: [fixedFlag.SUB_ENGINERRING, fixedFlag.OTHER, fixedFlag.CHARGE],   // 以税前工程造价作为累进基数,因此配置沿用{税前工程造价}
+            multiRef: [fixedFlag.SUB_ENGINERRING, fixedFlag.MEASURE, fixedFlag.OTHER, fixedFlag.CHARGE]},
+        '市政隧道工程安全文明施工费': {base: 'SZSDGCAQWMSGF', class: 'CSXM',
+            cycleCalcRef: [fixedFlag.SUB_ENGINERRING, fixedFlag.OTHER, fixedFlag.CHARGE],   // 以税前工程造价作为累进基数,因此配置沿用{税前工程造价}
+            multiRef: [fixedFlag.SUB_ENGINERRING, fixedFlag.MEASURE, fixedFlag.OTHER, fixedFlag.CHARGE]},
+        '城轨盾构工程安全文明施工费': {base: 'CGDGGCAQWMSGF', class: 'CSXM',
+            cycleCalcRef: [fixedFlag.SUB_ENGINERRING, fixedFlag.OTHER, fixedFlag.CHARGE],   // 以税前工程造价作为累进基数,因此配置沿用{税前工程造价}
+            multiRef: [fixedFlag.SUB_ENGINERRING, fixedFlag.MEASURE, fixedFlag.OTHER, fixedFlag.CHARGE]},
+        '城轨高架桥工程安全文明施工费': {base: 'CGGJQGCAQWMSGF', class: 'CSXM',
+            cycleCalcRef: [fixedFlag.SUB_ENGINERRING, fixedFlag.OTHER, fixedFlag.CHARGE],   // 以税前工程造价作为累进基数,因此配置沿用{税前工程造价}
+            multiRef: [fixedFlag.SUB_ENGINERRING, fixedFlag.MEASURE, fixedFlag.OTHER, fixedFlag.CHARGE]},
+        '城轨地下工程安全文明施工费': {base: 'CGDXGCAQWMSGF', class: 'CSXM',
+            cycleCalcRef: [fixedFlag.SUB_ENGINERRING, fixedFlag.OTHER, fixedFlag.CHARGE],   // 以税前工程造价作为累进基数,因此配置沿用{税前工程造价}
+            multiRef: [fixedFlag.SUB_ENGINERRING, fixedFlag.MEASURE, fixedFlag.OTHER, fixedFlag.CHARGE]},
+        '城轨轨道工程安全文明施工费': {base: 'CGGDGCAQWMSGF', class: 'CSXM',
+            cycleCalcRef: [fixedFlag.SUB_ENGINERRING, fixedFlag.OTHER, fixedFlag.CHARGE],   // 以税前工程造价作为累进基数,因此配置沿用{税前工程造价}
+            multiRef: [fixedFlag.SUB_ENGINERRING, fixedFlag.MEASURE, fixedFlag.OTHER, fixedFlag.CHARGE]},
     };
 }
 if(typeof baseFigureTemplate !== 'undefined'){