|
@@ -1,108 +1,331 @@
|
|
|
/**
|
|
|
* Created by CSL on 2017-07-19.
|
|
|
+ * 计算程序。所有定额、清单、父清单的计算都从此入。
|
|
|
* dispExpr: F8*(L-1); expression: "@('8') * (L-1)";
|
|
|
* 说明:F后跟行号,L替换人工系数值,@后跟ID。用到L的规则必须有labourCoeID属性(反过来不要求),
|
|
|
* 用到费率的规则必须有feeRateID属性,当有该属性时,会自动显示费率值。
|
|
|
*/
|
|
|
|
|
|
-let defaultBillTemplate = [
|
|
|
- {
|
|
|
- ID: 1,
|
|
|
- serialNo: '一',
|
|
|
- code: "A",
|
|
|
- name: "定额直接费",
|
|
|
- dispExpr: "A1+A2+A3",
|
|
|
- statement: "人工费+材料费+机械费",
|
|
|
- feeRate: null,
|
|
|
- memo: ''
|
|
|
- },
|
|
|
- {
|
|
|
- ID: 2,
|
|
|
- serialNo: '1',
|
|
|
- code: "A1",
|
|
|
- name: "人工费",
|
|
|
- dispExpr: "HJ",
|
|
|
- statement: "合计",
|
|
|
- feeRate: 50,
|
|
|
- fieldName: 'labour',
|
|
|
- memo: ''
|
|
|
- },
|
|
|
- {
|
|
|
- ID: 3,
|
|
|
- serialNo: '2',
|
|
|
- code: "A2",
|
|
|
- name: "材料费",
|
|
|
- dispExpr: "HJ",
|
|
|
- statement: "合计",
|
|
|
- feeRate: 30,
|
|
|
- fieldName: 'material',
|
|
|
- memo: ''
|
|
|
+let defaultBillTemplate = {
|
|
|
+ ID: 15,
|
|
|
+ name: "清单缺省",
|
|
|
+ calcItems: [
|
|
|
+ {
|
|
|
+ ID: 1,
|
|
|
+ code: "1",
|
|
|
+ name: "定额直接费",
|
|
|
+ dispExpr: "F2+F3+F4",
|
|
|
+ expression: "@('2')+@('3')+@('4')",
|
|
|
+ statement: "人工费+材料费+机械费",
|
|
|
+ feeRate: null,
|
|
|
+ memo: ''
|
|
|
+ },
|
|
|
+ {
|
|
|
+ ID: 2,
|
|
|
+ code: "1.1",
|
|
|
+ name: "人工费",
|
|
|
+ dispExpr: "HJ",
|
|
|
+ expression: "HJ",
|
|
|
+ statement: "合计",
|
|
|
+ feeRate: 50,
|
|
|
+ fieldName: 'labour',
|
|
|
+ memo: ''
|
|
|
+ },
|
|
|
+ {
|
|
|
+ ID: 3,
|
|
|
+ code: "1.2",
|
|
|
+ name: "材料费",
|
|
|
+ dispExpr: "HJ",
|
|
|
+ expression: "HJ",
|
|
|
+ statement: "合计",
|
|
|
+ feeRate: 30,
|
|
|
+ fieldName: 'material',
|
|
|
+ memo: ''
|
|
|
+ },
|
|
|
+ {
|
|
|
+ ID: 4,
|
|
|
+ code: "1.3",
|
|
|
+ name: "机械费",
|
|
|
+ dispExpr: "HJ",
|
|
|
+ expression: "HJ",
|
|
|
+ statement: "合计",
|
|
|
+ feeRate: 20,
|
|
|
+ fieldName: 'machine',
|
|
|
+ memo: ''
|
|
|
+ },
|
|
|
+ {
|
|
|
+ ID: 5,
|
|
|
+ code: "2",
|
|
|
+ name: "企业管理费",
|
|
|
+ dispExpr: "F1",
|
|
|
+ expression: "@('1')",
|
|
|
+ statement: "定额直接费",
|
|
|
+ feeRate: null,
|
|
|
+ fieldName: 'manage',
|
|
|
+ memo: ''
|
|
|
+ },
|
|
|
+ {
|
|
|
+ ID: 6,
|
|
|
+ code: "3",
|
|
|
+ name: "利润",
|
|
|
+ dispExpr: "F1",
|
|
|
+ expression: "@('1')",
|
|
|
+ statement: "定额直接费",
|
|
|
+ feeRate: null,
|
|
|
+ fieldName: 'profit',
|
|
|
+ memo: ''
|
|
|
+ },
|
|
|
+ {
|
|
|
+ ID: 7,
|
|
|
+ code: "4",
|
|
|
+ name: "风险费用",
|
|
|
+ dispExpr: "F1",
|
|
|
+ expression: "@('1')",
|
|
|
+ statement: "定额直接费",
|
|
|
+ feeRate: null,
|
|
|
+ fieldName: 'risk',
|
|
|
+ memo: ''
|
|
|
+ },
|
|
|
+ {
|
|
|
+ ID: 8,
|
|
|
+ code: "5",
|
|
|
+ name: "综合单价",
|
|
|
+ dispExpr: "F1+F5+F6+F7",
|
|
|
+ expression: "@('1')+@('5')+@('6')+@('7')",
|
|
|
+ statement: "定额直接费+企业管理费+利润+风险费用",
|
|
|
+ feeRate: null,
|
|
|
+ fieldName: 'common',
|
|
|
+ memo: ''
|
|
|
+ }
|
|
|
+ ]
|
|
|
+};
|
|
|
+
|
|
|
+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;
|
|
|
},
|
|
|
- {
|
|
|
- ID: 4,
|
|
|
- serialNo: '3',
|
|
|
- code: "A3",
|
|
|
- name: "机械费",
|
|
|
- dispExpr: "HJ",
|
|
|
- statement: "合计",
|
|
|
- feeRate: 20,
|
|
|
- fieldName: 'machine',
|
|
|
- memo: ''
|
|
|
+
|
|
|
+ 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;
|
|
|
},
|
|
|
- {
|
|
|
- ID: 5,
|
|
|
- serialNo: '二',
|
|
|
- code: "A4",
|
|
|
- name: "管理费",
|
|
|
- dispExpr: "A",
|
|
|
- statement: "定额直接费",
|
|
|
- feeRate: null,
|
|
|
- fieldName: 'manage',
|
|
|
- memo: ''
|
|
|
+
|
|
|
+ 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;
|
|
|
},
|
|
|
- {
|
|
|
- ID: 6,
|
|
|
- serialNo: '三',
|
|
|
- code: "B",
|
|
|
- name: "利润",
|
|
|
- dispExpr: "A",
|
|
|
- statement: "定额直接费",
|
|
|
- feeRate: null,
|
|
|
- fieldName: 'profit',
|
|
|
- memo: ''
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+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;
|
|
|
},
|
|
|
- {
|
|
|
- ID: 7,
|
|
|
- serialNo: '四',
|
|
|
- code: "C",
|
|
|
- name: "风险费用",
|
|
|
- dispExpr: "",
|
|
|
- statement: "",
|
|
|
- feeRate: null,
|
|
|
- fieldName: 'risk',
|
|
|
- memo: ''
|
|
|
+ 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;
|
|
|
},
|
|
|
- {
|
|
|
- ID: 8,
|
|
|
- serialNo: '',
|
|
|
- code: "",
|
|
|
- name: "综合单价",
|
|
|
- dispExpr: "A+B",
|
|
|
- statement: "定额直接费+利润",
|
|
|
- feeRate: null,
|
|
|
- fieldName: 'common',
|
|
|
- memo: ''
|
|
|
+ HJ: function () {
|
|
|
+ let me = this;
|
|
|
+ return me.treeNode.calcBaseValue;
|
|
|
}
|
|
|
-];
|
|
|
+};
|
|
|
|
|
|
class CalcProgram {
|
|
|
constructor(project){
|
|
|
- this.project = project;
|
|
|
- this.datas = [];
|
|
|
- this.digit = 2;
|
|
|
- this.digitDefault = 6;
|
|
|
- this.calc = new Calculation();
|
|
|
- project.registerModule(ModuleNames.calc_program, this);
|
|
|
+ let me = this;
|
|
|
+ me.project = project;
|
|
|
+ me.datas = [];
|
|
|
+ project.registerModule(ModuleNames.calc_program, me);
|
|
|
};
|
|
|
|
|
|
getSourceType () {
|
|
@@ -121,119 +344,229 @@ class CalcProgram {
|
|
|
|
|
|
// 经测试,全部编译一次耗时0.003~0.004秒。耗时基本忽略不计。
|
|
|
compileAllTemps(){
|
|
|
- let calcFeeRates = this.project.FeeRate.datas.rates;
|
|
|
- let calcLabourCoes = this.project.labourCoe.datas.coes;
|
|
|
- let calcTemplates = this.project.calcProgram.datas.templates;
|
|
|
-
|
|
|
- this.calc.compilePublics(calcFeeRates, calcLabourCoes, feeType, rationCalcBase);
|
|
|
- for (let ct of calcTemplates){
|
|
|
- this.calc.compileTemplate(ct);
|
|
|
+ let me = this;
|
|
|
+ me.digit = 2;
|
|
|
+ me.digitDefault = 6;
|
|
|
+
|
|
|
+ me.compiledFeeRates = {};
|
|
|
+ me.compiledLabourCoes = {};
|
|
|
+ me.compiledTemplates = {};
|
|
|
+ me.compiledFeeTypes = {};
|
|
|
+ me.compiledFeeTypeNames = [];
|
|
|
+ me.compiledCalcBases = {};
|
|
|
+ me.saveForReports = [];
|
|
|
+
|
|
|
+ me.feeRates = this.project.FeeRate.datas.rates;
|
|
|
+ me.labourCoes = this.project.labourCoe.datas.coes;
|
|
|
+ me.feeTypes = feeType;
|
|
|
+ me.calcBases = rationCalcBase;
|
|
|
+ me.templates = this.project.calcProgram.datas.templates;
|
|
|
+
|
|
|
+ me.templates.push(defaultBillTemplate);
|
|
|
+ // 先编译公用的基础数据
|
|
|
+ me.compilePublics();
|
|
|
+ for (let t of me.templates){
|
|
|
+ me.compileTemplate(t);
|
|
|
};
|
|
|
|
|
|
// 存储费率临时数据,报表用。
|
|
|
- if (this.calc.saveForReports.length > 0){
|
|
|
+ if (me.saveForReports.length > 0){
|
|
|
let saveDatas = {};
|
|
|
saveDatas.projectID = projectInfoObj.projectInfo.ID;
|
|
|
- saveDatas.calcItems = this.calc.saveForReports;
|
|
|
+ saveDatas.calcItems = me.saveForReports;
|
|
|
CommonAjax.post('/calcProgram/saveCalcItems', saveDatas, function (data) {
|
|
|
- this.calc.saveForReports = [];
|
|
|
+ me.saveForReports = [];
|
|
|
});
|
|
|
};
|
|
|
};
|
|
|
|
|
|
- calculate(treeNode){
|
|
|
+ compilePublics(){
|
|
|
let me = this;
|
|
|
- if (treeNode.sourceType === this.project.Ration.getSourceType()) {
|
|
|
- treeNode.data.gljList = this.project.ration_glj.getGljArrByRation(treeNode.data.ID);
|
|
|
+ for (let rate of me.feeRates) {
|
|
|
+ me.compiledFeeRates[rate.ID] = rate;
|
|
|
}
|
|
|
- else if (treeNode.sourceType === this.project.Bills.getSourceType()) {
|
|
|
- let rations = this.project.Ration.getBillsSortRation(treeNode.source.getID());
|
|
|
- treeNode.data.gljList = this.project.ration_glj.getGatherGljArrByRations(rations);
|
|
|
- };
|
|
|
|
|
|
- this.calc.calculate(treeNode);
|
|
|
+ for (let coe of me.labourCoes) {
|
|
|
+ me.compiledLabourCoes[coe.ID] = coe;
|
|
|
+ }
|
|
|
|
|
|
- // 存储、刷新本结点、所有父结点
|
|
|
- if (treeNode.changed) {
|
|
|
- me.saveAndCalcParents(treeNode);
|
|
|
- delete treeNode.changed;
|
|
|
- };
|
|
|
+ for (let ft of me.feeTypes) {
|
|
|
+ me.compiledFeeTypes[ft.type] = ft.name;
|
|
|
+ me.compiledFeeTypes[ft.name] = ft.type; // 中文预编译,可靠性有待验证
|
|
|
+ me.compiledFeeTypeNames.push(ft.name);
|
|
|
+ }
|
|
|
|
|
|
+ for (let cb of me.calcBases) {
|
|
|
+ me.compiledCalcBases[cb.dispName] = cb; // 中文预编译,可靠性有待验证
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
- saveAndCalcParents(treeNode) {
|
|
|
- if (treeNode.parent) {
|
|
|
- projectObj.converseCalculateBills(treeNode.parent);
|
|
|
+ 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[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[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];
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
- let data = {ID: treeNode.data.ID, projectID: projectObj.project.ID(), fees: treeNode.data.fees};
|
|
|
- let newDta = {'updateType': 'ut_update', 'updateData': data};
|
|
|
- let newDataArr = [];
|
|
|
- newDataArr.push(newDta);
|
|
|
- projectObj.project.pushNow('', treeNode.sourceType, newDataArr);
|
|
|
- projectObj.mainController.refreshTreeNode([treeNode]);
|
|
|
- };
|
|
|
+ if (template && template.calcItems && template.calcItems.length > 0) {
|
|
|
+ template.compiledSeq = [];
|
|
|
+ template.compiledCalcItems = {};
|
|
|
|
|
|
- initFees(treeNode){
|
|
|
- if (!treeNode.data.fees) {
|
|
|
- treeNode.data.fees = [];
|
|
|
- treeNode.data.feesIndex = {};
|
|
|
- treeNode.changed = true;
|
|
|
+ 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());
|
|
|
+ }
|
|
|
};
|
|
|
};
|
|
|
|
|
|
- checkFee(treeNode, ftObj){
|
|
|
- if (!treeNode.data.feesIndex[ftObj.fieldName]){
|
|
|
- let fee = {
|
|
|
- 'fieldName': ftObj.fieldName,
|
|
|
- 'unitFee': ftObj.unitFee,
|
|
|
- 'totalFee': ftObj.totalFee,
|
|
|
- 'tenderUnitFee': 0,
|
|
|
- 'tenderTotalFee': 0
|
|
|
- };
|
|
|
- treeNode.data.fees.push(fee);
|
|
|
- treeNode.data.feesIndex[ftObj.fieldName] = fee;
|
|
|
- treeNode.changed = true;
|
|
|
- }
|
|
|
- else{
|
|
|
- if (treeNode.data.feesIndex[ftObj.fieldName].unitFee != ftObj.unitFee){
|
|
|
- treeNode.data.feesIndex[ftObj.fieldName].unitFee = ftObj.unitFee;
|
|
|
- treeNode.changed = true;
|
|
|
- };
|
|
|
+ // 内部调用,外部不能直接使用
|
|
|
+ InnerCalc(treeNode){
|
|
|
+ let me = this;
|
|
|
+ let project = me.project;
|
|
|
|
|
|
- if (treeNode.data.feesIndex[ftObj.fieldName].totalFee != ftObj.totalFee){
|
|
|
- treeNode.data.feesIndex[ftObj.fieldName].totalFee = ftObj.totalFee;
|
|
|
+ function initFees(treeNode){
|
|
|
+ if (!treeNode.data.fees) {
|
|
|
+ treeNode.data.fees = [];
|
|
|
+ treeNode.data.feesIndex = {};
|
|
|
treeNode.changed = true;
|
|
|
};
|
|
|
};
|
|
|
- };
|
|
|
|
|
|
- gatherFeeTypes(treeNode, gatherType){
|
|
|
- let me = this;
|
|
|
- let rst = [];
|
|
|
+ function checkFee(treeNode, feeObj){
|
|
|
+ if (feeObj.fieldName == '') return;
|
|
|
|
|
|
- if (treeNode.sourceType === this.project.Bills.getSourceType()) {
|
|
|
- me.initFees(treeNode);
|
|
|
+ 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;
|
|
|
+ };
|
|
|
|
|
|
- let objsArr = [];
|
|
|
- if (gatherType == CP_GatherType.rations){
|
|
|
- objsArr = this.project.Ration.getRationsByNode(treeNode);
|
|
|
- }else if (gatherType == CP_GatherType.bills){
|
|
|
- objsArr = treeNode.children;
|
|
|
+ if (treeNode.data.feesIndex[feeObj.fieldName].totalFee != feeObj.totalFee){
|
|
|
+ treeNode.data.feesIndex[feeObj.fieldName].totalFee = feeObj.totalFee;
|
|
|
+ treeNode.changed = true;
|
|
|
+ };
|
|
|
};
|
|
|
+ };
|
|
|
+
|
|
|
+ // 汇总定额或子清单的费用类别
|
|
|
+ if (treeNode.calcType == treeNodeCalcType.ctGatherRations || treeNode.calcType == treeNodeCalcType.ctGatherBills){
|
|
|
+ initFees(treeNode);
|
|
|
|
|
|
+ let objsArr = (treeNode.calcType == treeNodeCalcType.ctGatherRations) ? 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 = {};
|
|
|
- if (gatherType == CP_GatherType.rations){
|
|
|
- data = item;
|
|
|
- }else if (gatherType == CP_GatherType.bills){
|
|
|
- data = item.data;
|
|
|
- };
|
|
|
+ let data = (treeNode.calcType == treeNodeCalcType.ctGatherRations) ? 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);
|
|
@@ -246,100 +579,136 @@ class CalcProgram {
|
|
|
ftObj.tenderUnitFee = tuf.toDecimal(me.digit);
|
|
|
ftObj.tenderTotalFee = ttf.toDecimal(me.digit);
|
|
|
|
|
|
- me.checkFee(treeNode, ftObj);
|
|
|
+ checkFee(treeNode, ftObj);
|
|
|
|
|
|
rst.push(ftObj);
|
|
|
};
|
|
|
+ treeNode.data.calcTemplate = {"calcItems": rst};
|
|
|
+ }
|
|
|
+ else{
|
|
|
+ // 叶子清单的缺省计算程序需要提供总金额作为计算基数(不需要工料机),然后每条按比例(费率)计算,不需要工料机明细。
|
|
|
+ if (treeNode.calcType == treeNodeCalcType.ctCalcBaseValue){
|
|
|
+ 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.changed) {
|
|
|
- me.saveAndCalcParents(treeNode);
|
|
|
- delete treeNode.changed;
|
|
|
+ if (treeNode.data.programID == undefined){
|
|
|
+ treeNode.data.programID = 1;
|
|
|
+ };
|
|
|
};
|
|
|
- };
|
|
|
|
|
|
- return rst;
|
|
|
- };
|
|
|
+ let template = me.compiledTemplates[treeNode.data.programID];
|
|
|
+ treeNode.data.calcTemplate = template;
|
|
|
|
|
|
- calcDefaultBillTemp(treeNode, totalPrice){
|
|
|
- let me = this;
|
|
|
- let rst = [];
|
|
|
- if (treeNode.sourceType != this.project.Bills.getSourceType()){return rst};
|
|
|
- me.initFees(treeNode);
|
|
|
- for (let item of defaultBillTemplate) {
|
|
|
- let num = totalPrice;
|
|
|
- item.dispExprUser = item.dispExpr;
|
|
|
- item.displayFieldName = me.calc.compiledFeeTypes[item.fieldName];
|
|
|
-
|
|
|
- if (item.feeRate)
|
|
|
- item.unitFee = (totalPrice * item.feeRate * 0.01).toDecimal(me.digit)
|
|
|
- else
|
|
|
- item.unitFee = 0;
|
|
|
-
|
|
|
- let quantity = treeNode.data.quantity;
|
|
|
- if (!quantity) quantity = 0;
|
|
|
- item.totalFee = (item.unitFee * quantity).toDecimal(me.digit);
|
|
|
- item.tenderUnitFee = 0;
|
|
|
- item.tenderTotalFee = 0;
|
|
|
-
|
|
|
- me.checkFee(treeNode, item);
|
|
|
- };
|
|
|
+ if (treeNode && template.hasCompiled) {
|
|
|
+ let $CE = executeObj;
|
|
|
+ $CE.treeNode = treeNode;
|
|
|
+ $CE.template = template;
|
|
|
+ $CE.calcBase = me.compiledCalcBases;
|
|
|
|
|
|
- if (treeNode.changed) {
|
|
|
- me.saveAndCalcParents(treeNode);
|
|
|
- delete treeNode.changed;
|
|
|
- };
|
|
|
+ initFees(treeNode);
|
|
|
|
|
|
- rst = defaultBillTemplate;
|
|
|
- return rst;
|
|
|
- };
|
|
|
+ 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对象再试
|
|
|
|
|
|
- getCalcDatas(treeNode){
|
|
|
+ let quantity = treeNode.data.quantity;
|
|
|
+ if (!quantity) quantity = 0;
|
|
|
+ calcItem.totalFee = (calcItem.unitFee * quantity).toDecimal(me.digit);
|
|
|
+
|
|
|
+ checkFee(treeNode, calcItem);
|
|
|
+ };
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算本节点(默认同时递归计算所有父节点,可选)
|
|
|
+ calculate(treeNode, calcParents = true){
|
|
|
let me = this;
|
|
|
- let rst = [];
|
|
|
+
|
|
|
let isRation = treeNode.sourceType === me.project.Ration.getSourceType();
|
|
|
let isBill = treeNode.sourceType === me.project.Bills.getSourceType();
|
|
|
let isLeafBill = isBill && treeNode.source.children && treeNode.source.children.length === 0;
|
|
|
let isBillPriceCalc = me.project.projSetting.billsCalcMode === billsPrice;
|
|
|
|
|
|
- if (isRation) { // 清单单价计算模式下的叶子清单:取自己的计算程序ID,找到自己的计算程序计算。
|
|
|
- me.calculate(treeNode);
|
|
|
- rst = treeNode.data.calcTemplate.calcItems;
|
|
|
- }
|
|
|
+ if (isRation)
|
|
|
+ treeNode.calcType = treeNodeCalcType.ctRationCalcProgram
|
|
|
else if (isLeafBill) {
|
|
|
- let ct = '';
|
|
|
if (treeNode.children && treeNode.children.length > 0){
|
|
|
if (treeNode.children[0].sourceType == me.project.Ration.getSourceType()){
|
|
|
- ct = childrenType.ration;
|
|
|
+ if (isBillPriceCalc) // 清单单价计算模式下的叶子清单:取自己的计算程序ID,找到自己的计算程序计算
|
|
|
+ treeNode.calcType = treeNodeCalcType.ctBillCalcProgram;
|
|
|
+ else // 前三种计算模式下的叶子清单:汇总定额的计算程序的费用类别
|
|
|
+ treeNode.calcType = treeNodeCalcType.ctGatherRations;
|
|
|
}
|
|
|
else if (treeNode.children[0].sourceType == me.project.VolumePrice.getSourceType()){
|
|
|
- ct = childrenType.volumePrice;
|
|
|
+ let value = 20000;
|
|
|
+ // if (treeNode.data.feesIndex && treeNode.data.feesIndex.common && treeNode.data.feesIndex.common.unitFee != 0)
|
|
|
+ // value = treeNode.data.feesIndex.common.unitFee;
|
|
|
+ treeNode.calcType = treeNodeCalcType.ctCalcBaseValue;
|
|
|
+ treeNode.calcBaseValue = value;
|
|
|
};
|
|
|
}
|
|
|
- else{
|
|
|
- ct = childrenType.formula;
|
|
|
+ else{ // 公式计算
|
|
|
+ let value = 20000;
|
|
|
+ // if (treeNode.data.feesIndex && treeNode.data.feesIndex.common && treeNode.data.feesIndex.common.unitFee != 0)
|
|
|
+ // value = treeNode.data.feesIndex.common.unitFee;
|
|
|
+ treeNode.calcType = treeNodeCalcType.ctCalcBaseValue;
|
|
|
+ treeNode.calcBaseValue = value;
|
|
|
};
|
|
|
+ }
|
|
|
+ else if (isBill) // 父清单:汇总子清单的费用类别
|
|
|
+ treeNode.calcType = treeNodeCalcType.ctGatherBills;
|
|
|
+
|
|
|
+ me.InnerCalc(treeNode);
|
|
|
|
|
|
- if (ct == childrenType.ration){
|
|
|
- if (isBillPriceCalc){ // 清单单价计算模式下的叶子清单:取自己的计算程序ID,找到自己的计算程序计算。
|
|
|
- me.calculate(treeNode);
|
|
|
- rst = treeNode.data.calcTemplate.calcItems;
|
|
|
- }else{ // 前三种计算模式下的叶子清单:汇总定额的计算程序的费用类别
|
|
|
- rst = me.gatherFeeTypes(treeNode, CP_GatherType.rations);
|
|
|
+ // 计算所有父结点
|
|
|
+ if (treeNode.changed && calcParents && treeNode.parent) {
|
|
|
+ me.calculate(treeNode.parent);
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+ // 存储、刷新本节点(默认存储刷新所有父节点,可选)
|
|
|
+ saveNode(treeNode, saveParents = true) {
|
|
|
+ if (!treeNode.changed) return;
|
|
|
+ let me = this;
|
|
|
+ let newDataArr = [], nodesArr = [];
|
|
|
+
|
|
|
+ me.project.beginUpdate('');
|
|
|
+ let curNode = treeNode;
|
|
|
+ while (curNode) {
|
|
|
+ if (curNode.changed){
|
|
|
+ let data = {
|
|
|
+ ID: curNode.data.ID,
|
|
|
+ projectID: me.project.ID(),
|
|
|
+ quantity: curNode.data.quantity,
|
|
|
+ fees: curNode.data.fees
|
|
|
};
|
|
|
- }
|
|
|
- else if (ct == childrenType.volumePrice){
|
|
|
- let totalPrice = 10000;
|
|
|
- rst = me.calcDefaultBillTemp(treeNode, totalPrice);
|
|
|
- }
|
|
|
- else if (ct == childrenType.formula){
|
|
|
- let totalPrice = 20000;
|
|
|
- rst = me.calcDefaultBillTemp(treeNode, totalPrice);
|
|
|
+ let newDta = {'updateType': 'ut_update', 'updateData': data};
|
|
|
+ newDataArr.push(newDta);
|
|
|
+ nodesArr.push(curNode);
|
|
|
+ me.project.push(curNode.sourceType, newDataArr);
|
|
|
};
|
|
|
- }
|
|
|
- else if (isBill){ // 父清单:汇总子清单的费用类别
|
|
|
- rst = me.gatherFeeTypes(treeNode, CP_GatherType.bills);
|
|
|
+ if (saveParents) curNode = curNode.parent
|
|
|
+ else break;
|
|
|
};
|
|
|
+ me.project.endUpdate();
|
|
|
|
|
|
- return rst;
|
|
|
- }
|
|
|
+ for (let node of nodesArr){delete node.changed;};
|
|
|
+ projectObj.mainController.refreshTreeNode(nodesArr);
|
|
|
+ };
|
|
|
}
|