| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 | /** * Created by Tony on 2017/6/21. * Modified by CSL, 2017-08-01 引入多套计算程序、费率同步、人工系数同步、改进基数计算、费字段映射等。 * added by CSL, 2017-09-01 增加公式解析对象analyzer,用于解析用户修改公式、自定义表达式。 */let executeObj = {    treeNode: null,    template: null,    calcBase: null,    at: function(ID) {        let me = executeObj,            rst = 0;        rst = me.template.compiledCalcItems[ID].unitFee;        rst = parseFloat(rst);        return rst;    },    base: function(calcBaseName) {        let me = executeObj, rst = 0,            //base = getRationCalcBase(calcBaseName);            base = me.calcBase[calcBaseName];        if (base != null) {            let price = 0, aprice = 0, mprice = 0, tmpSum = 0, mdSum = 0;            function isSubset(sub, arr){                // if(!(sub instanceof Array) || !(arr instanceof Array)) return false;                // if(sub.length > arr.length) return false;                for(var i = 0, len = sub.length; i < len; i++){                    if(arr.indexOf(sub[i]) == -1) return false;                }                return true;            }            // 机上人工费:多一层            if (isSubset(base.gljTypes, [gljType.MACHINE_LABOUR])) {                for (let glj of me.treeNode.data.gljList) {                       if (glj.type == gljType.GENERAL_MACHINE) {                            // 获取机械组成物                           let mds = projectObj.project.composition.getCompositionByCode(glj.code);                           if (!mds) mds = [];                           for (let md of mds){                               if (base.gljTypes.indexOf(md.glj_type) >= 0) {                                   price = md["base_price"];                                   if (!price) price = 0;                                   mdSum = mdSum + (md["consumption"] * price).toDecimal(me.digit);                                   mdSum = (mdSum).toDecimal(me.digitDefault);                               }                           };                           tmpSum = tmpSum + (glj["quantity"] * mdSum).toDecimal(me.digitDefault);                           tmpSum = (tmpSum).toDecimal(me.digitDefault);                       }                };            }else{                for (let glj of me.treeNode.data.gljList) {                    if (base.gljTypes.indexOf(glj.type) >= 0) {                        if (base.calcType == baseCalc){ price = glj["basePrice"];}                        else if (base.calcType == adjustCalc){price = glj["adjustPrice"];}                        else if (base.calcType == budgetCalc){price = glj["marketPrice"];}                        else if (base.calcType == diffCalc){                            aprice = glj["adjustPrice"];                            if (!aprice) aprice = 0;                            mprice = glj["marketPrice"];                            if (!mprice) mprice = 0;                            price = mprice - aprice;                        };                        if (!price) price = 0;                        tmpSum = tmpSum + (glj["quantity"] * price).toDecimal(me.digitDefault);                        tmpSum = (tmpSum).toDecimal(me.digitDefault);                    };                };            };            rst = (tmpSum).toDecimal(me.digitDefault);        };        return rst;    },    HJ: function () {        let me = this;        return me.treeNode.data.baseTotalPrice;    }};let analyzer = {    calcTemplate: null,    success: true,    standard: function(expr){        let str = expr;        str = str.replace(/\s/g, "");               // 去空格、去中文空格        str = str.replace(/(/g, "(");              // 中文括号"("换成英文括号"("        str = str.replace(/)/g, ")");              // 中文括号")"换成英文括号")"        str = str.replace(/f/g, "F");               // f换成F        return str;    },    analyzeCalcBase: function(expr){        // 前提:必须无空格、无特殊符号        function getCalcBase(expr){            let base = '',                iPos1 = -1, iPos2 = -1;            for (let i = 0; i < expr.length; i++) {                if (expr[i] === '['){                    iPos1 = i;                }                else if (iPos1 != -1 && expr[i]===']'){                    iPos2 = i;                };                if (iPos1 != -1 && iPos2 != -1){                    base = expr.slice(iPos1, iPos2 + 1);                    break;                }            };            return base;        };        function calcBaseToIDExpr(base){            /*// for test. 公路模式,基数到ID            let id = -1;            if (base == '[人工费]'){                id = 111;            }            else if (base == '[材料费]'){                id = 222;            }            else if (base == '[机械费]'){                id = 333;            }            else id = "错误";            return "@('" + id + "')";*/            let baseValue = base.slice(1, -1);            return "base('" + baseValue + "')";        };        while (expr.indexOf('[') > 0) {            let base = getCalcBase(expr);            let id = calcBaseToIDExpr(base);            let baseValue = base.slice(1, -1);   // []会给下面的正则带来干扰,这里去掉            var pattBase =new RegExp(baseValue, "g");            expr = expr.replace(pattBase, id);            expr = expr.replace(/\[base\('/g, "base('");      // [@('       [base('            expr = expr.replace(/'\)\]/g, "')");        // ')]        };        return expr;    },    analyzeLineRef: function(expr){        let me = this;        function isOperator(char){            var operator = "+-*/()";            return operator.indexOf(char) > -1;        };        function lineNumToID(lineNum){            if (lineNum > me.calcTemplate.calcItems.length){                me.success = false;                return '越界';            }            else{                let id = me.calcTemplate.calcItems[lineNum - 1].ID;                return id;            }        };        // 前提:必须无空格、无特殊符号、标准大写F        function getSection(expr){            let section = '',                iPos1 = -1, iPos2 = -1;            for (let i = 0; i < expr.length; i++) {                if (expr[i] === 'F'){                    iPos1 = i;                }                else if (iPos1 != -1 && isOperator(expr[i])){                    iPos2 = i;                }                else if (iPos1 != -1 && i == expr.length - 1){                    iPos2 = i + 1;                };                if (iPos1 != -1 && iPos2 != -1){                    section = expr.slice(iPos1, iPos2);                    break;                }            };            return section;        };        function sectionToIDExpr(section){            if (section){                let lineNum = section.slice(1);                if (isNaN(lineNum)){                    me.success = false;                    return '错误';      // 这里的返回提示不能加上section,因为会无限循环                }                else                    return "@('" + lineNumToID(lineNum) + "')";            }            else return '';        };        while (expr.indexOf('F') > 0) {            let sec = getSection(expr);            let id = sectionToIDExpr(sec);            var pattSec =new RegExp(sec, "g");            expr = expr.replace(pattSec, id);        };        return expr;    },    analyzeUserExpr: function(calcTemplate, calcItem){        let me = this;        me.calcTemplate = calcTemplate;        let expr = calcItem.dispExpr;        // 标准化:处理特殊字符、中文符号、大小写        expr = me.standard(expr);        calcItem.dispExpr = expr;        // 先换掉计算基数        expr = me.analyzeCalcBase(expr);        // 再换掉行引用        expr = me.analyzeLineRef(expr);        calcItem.expression = expr;        return me.success;    }};class Calculation {    // 先编译公用的基础数据    compilePublics(feeRates, labourCoes, feeTypes, calcBases){        let me = this;        me.compiledFeeRates = {};        me.compiledLabourCoes = {};        me.compiledTemplates = {};        me.compiledFeeTypes = {};        me.compiledFeeTypeNames = [];        me.compiledCalcBases = {};        me.saveForReports = [];        me.digit = 2;        me.digitDefault = 6;        let private_compile_feeRateFile = function() {            if (feeRates) {                for (let rate of feeRates) {                    me.compiledFeeRates["feeRate_" + rate.ID] = rate;                }            }        };        let private_compile_labourCoeFile = function() {            if (labourCoes) {                for (let coe of labourCoes) {                    me.compiledLabourCoes["LabourCoe_" + coe.ID] = coe;                }            }        };        let private_compile_feeType = function() {            if (feeTypes) {                for (let ft of feeTypes) {                    me.compiledFeeTypes[ft.type] = ft.name;                    me.compiledFeeTypes[ft.name] = ft.type;    // 中文预编译,可靠性有待验证                    me.compiledFeeTypeNames.push(ft.name);                }            }        };        let private_compile_calcBase = function() {            if (calcBases) {                for (let cb of calcBases) {                    me.compiledCalcBases[cb.dispName] = cb;         // 中文预编译,可靠性有待验证                }            }        };        private_compile_feeRateFile();        private_compile_labourCoeFile();        private_compile_feeType();        private_compile_calcBase();    };    compileTemplate(template){        let me = this;        me.compiledTemplates[template.ID] = template;        template.hasCompiled = false;        template.errs = [];        let private_extract_ID = function(str, idx){            let rst = '', lBracket = 0, rBracket = 0, firstIdx = idx, lastIdx = 0;            for (let i = idx; i < str.length; i++) {                if (str[i] === '(') {                    lBracket++;                    if (lBracket == 1) firstIdx = i + 1;                }                if (str[i] === ')') {                    rBracket++;                    if (lBracket == rBracket) {                        lastIdx = i - 1;                        if (lastIdx > firstIdx) {                            if (str[firstIdx] === "'") firstIdx++;                            if (str[lastIdx] !== "'") lastIdx++;                            if (lastIdx > firstIdx) {                                rst = str.slice(firstIdx, lastIdx);                            }                        }                        break;                    }                }            }            return rst;        };        let private_parse_ref = function(item, itemIdx){            let idx = item.expression.indexOf('@(', 0);            while (idx >= 0) {                let ID = private_extract_ID(item.expression, idx);                if (ID.length > 0) {                    let subItem = template.compiledCalcItems[ID];                    if (subItem) {                        if (subItem.ID !== item.ID) {                            private_parse_ref(subItem, template.compiledCalcItems[ID + "_idx"]);                        } else {                            template.errs.push("There exists the self refer ID: " + ID);                        }                    } else {                        template.errs.push("There exists the invalid ID by which could not find the item: " + ID);                        console.log('invalid ID: ' + ID);                    }                }                idx = item.expression.indexOf('@(', idx + ID.length + 3);            }            if (template.compiledSeq.indexOf(itemIdx) < 0) {                template.compiledSeq.push(itemIdx);            }        };        let private_setup_seq = function(item, itemIdx){            if (template.compiledSeq.indexOf(itemIdx) < 0) {                private_parse_ref(item, itemIdx);            }        };        let private_compile_items = function() {            for (let idx of template.compiledSeq) {                let item = template.calcItems[idx];                item.dispExprUser = item.dispExpr;    // 用于界面显示。disExpr是公式模板,不允许修改:人工系数占位符被修改后变成数值,第二次无法正确替换。                if (item.expression == 'HJ')                    item.compiledExpr = '$CE.HJ()'                else{                    item.compiledExpr = item.expression.split('@(').join('$CE.at(');                    item.compiledExpr = item.compiledExpr.split('base(').join('$CE.base(');                };                if (item.labourCoeID){                    let lc = me.compiledLabourCoes["LabourCoe_" + item.labourCoeID].coe;                    item.dispExprUser = item.dispExpr.replace(/L/gi, lc.toString());                    item.compiledExpr = item.compiledExpr.replace(/L/gi, lc.toString());                };                if (item.feeRateID) {                    let orgFeeRate = item.feeRate;                    let cmf = me.compiledFeeRates["feeRate_" + item.feeRateID];                    item.feeRate = cmf?cmf.rate:100;                    if (!orgFeeRate || (orgFeeRate && orgFeeRate != item.feeRate)){                        me.saveForReports.push({templatesID: template.ID, calcItem: item});                    }                };                // 字段名映射                item.displayFieldName = me.compiledFeeTypes[item.fieldName];            }        };        if (template && template.calcItems && template.calcItems.length > 0) {            template.compiledSeq = [];            template.compiledCalcItems = {};            for (let i = 0; i < template.calcItems.length; i++) {                let item = template.calcItems[i];                template.compiledCalcItems[item.ID] = item;                template.compiledCalcItems[item.ID + "_idx"] = i;            }            for (let i = 0; i < template.calcItems.length; i++) {                private_setup_seq(template.calcItems[i], i);            }            if (template.errs.length == 0) {                private_compile_items();                template.hasCompiled = true;            } else {                console.log('errors: ' + template.errs.toString());            }        };    };    initFees(treeNode){        if (!treeNode.data.fees) {            treeNode.data.fees = [];            treeNode.data.feesIndex = {};            treeNode.changed = true;        };    };    checkFee(treeNode, feeObj){        if (feeObj.fieldName == '') return;        if (!treeNode.data.feesIndex[feeObj.fieldName]){            let fee = {                'fieldName': feeObj.fieldName,                'unitFee': feeObj.unitFee,                'totalFee': feeObj.totalFee,                'tenderUnitFee': 0,                'tenderTotalFee': 0            };            treeNode.data.fees.push(fee);            treeNode.data.feesIndex[feeObj.fieldName] = fee;            treeNode.changed = true;        }        else{            if (treeNode.data.feesIndex[feeObj.fieldName].unitFee != feeObj.unitFee){                treeNode.data.feesIndex[feeObj.fieldName].unitFee = feeObj.unitFee;                treeNode.changed = true;            };            if (treeNode.data.feesIndex[feeObj.fieldName].totalFee != feeObj.totalFee){                treeNode.data.feesIndex[feeObj.fieldName].totalFee = feeObj.totalFee;                treeNode.changed = true;            };        };    };    calculate(treeNode){        let me = this;        let project = projectObj.project;        // 汇总定额或子清单的费用类别        if (treeNode.data.gatherType != undefined){            if (treeNode.sourceType != project.Bills.getSourceType()) return;            me.initFees(treeNode);            let objsArr = (treeNode.data.gatherType === CP_GatherType.rations) ? project.Ration.getRationsByNode(treeNode) : treeNode.children;            let rst = [];            for (let ft of feeType) {                let ftObj = {};                ftObj.fieldName = ft.type;                ftObj.name = ft.name;                let uf = 0, tf = 0, tuf = 0, ttf = 0;                for (let item of objsArr) {                    let data = (treeNode.data.gatherType === CP_GatherType.rations) ? item : item.data;                    if (data.feesIndex && data.feesIndex[ft.type]) {                        uf = (uf + parseFloat(data.feesIndex[ft.type].unitFee)).toDecimal(me.digitDefault);                        tf = (tf + parseFloat(data.feesIndex[ft.type].totalFee)).toDecimal(me.digitDefault);                        tuf = (tuf + parseFloat(data.feesIndex[ft.type].tenderUnitFee)).toDecimal(me.digitDefault);                        ttf = (ttf + parseFloat(data.feesIndex[ft.type].tenderTotalFee)).toDecimal(me.digitDefault);                    };                };                ftObj.unitFee = uf.toDecimal(me.digit);                ftObj.totalFee = tf.toDecimal(me.digit);                ftObj.tenderUnitFee = tuf.toDecimal(me.digit);                ftObj.tenderTotalFee = ttf.toDecimal(me.digit);                me.checkFee(treeNode, ftObj);                rst.push(ftObj);            };            treeNode.data.calcTemplate = {"calcItems": rst};        }        else{            // 叶子清单的缺省计算程序需要提供总金额作为计算基数(不需要工料机),然后每条按比例(费率)计算,不需要工料机明细。            if (treeNode.data.baseTotalPrice != undefined){                if (treeNode.sourceType != project.Bills.getSourceType()) return;                delete treeNode.data.gljList;                if (treeNode.data.programID == undefined){                    treeNode.data.programID = defaultBillTemplate.ID;                };            }            else {                if (treeNode.sourceType === project.Ration.getSourceType()) {                    treeNode.data.gljList = project.ration_glj.getGljArrByRation(treeNode.data.ID);                }                else if (treeNode.sourceType === project.Bills.getSourceType()) {                    let rations = project.Ration.getBillsSortRation(treeNode.source.getID());                    treeNode.data.gljList = project.ration_glj.getGatherGljArrByRations(rations);                };                if (treeNode.data.programID == undefined){                    treeNode.data.programID = 1;                };            };            let template = me.compiledTemplates[treeNode.data.programID];            treeNode.data.calcTemplate = template;            if (treeNode && template.hasCompiled) {                let $CE = executeObj;                $CE.treeNode = treeNode;                $CE.template = template;                $CE.calcBase = me.compiledCalcBases;                me.initFees(treeNode);                for (let idx of template.compiledSeq) {                    let calcItem = template.calcItems[idx];                    let feeRate = calcItem.feeRate;                    if (!feeRate) feeRate = 100;    // 100%                    calcItem.unitFee = (eval(calcItem.compiledExpr) * feeRate * 0.01).toDecimal(me.digit);   // 如果eval()对清单树有影响,就换成小麦的Expression对象再试                    let quantity = treeNode.data.quantity;                    if (!quantity) quantity = 0;                    calcItem.totalFee = (calcItem.unitFee * quantity).toDecimal(me.digit);                    me.checkFee(treeNode, calcItem);                };            }        };    }};/*export default analyzer;*/
 |