'use strict'; /** * * * @author Mai * @date * @version */ const math = require('mathjs'); math.config({ number: 'BigNumber', }); const PayConst = require('../const/deal_pay.js'); const payType = PayConst.payType; const deadlineType = PayConst.deadlineType; class PayCalculate { constructor (ctx, stage, tenderInfo) { this.ctx = ctx; this.stage = stage; this.percentReg = /((\d+)|((\d+)(\.\d+)))%/g; this.tenderInfo = tenderInfo; this.decimal = tenderInfo.decimal.pay ? tenderInfo.decimal.payTp : tenderInfo.decimal.tp; this.orderReg = /f\d+/ig; /* 以下变量在调用calculate方法后获得 this.add; this.pre; this.cur; this.yf; */ } /** * 获取 计算基数 * @returns {Promise} */ async getCalcBase () { if (this.bases) { return; } const bases = await this.ctx.service.stage.getStagePayCalcBase(this.stage, this.tenderInfo); this.bases = bases.sort(function (a, b) { return a.sort - b.sort; // if (a && b) { // return b.code.indexOf(a.code) >= 0 ? 1 : 0; // } else { // return 0; // } }); for (const b of this.bases) { b.reg = new RegExp(b.code, 'igm'); } } _calculateTpExpr(pay, pays) { let formula = pay.expr; const orderParam = pay.expr.match(this.orderReg); if (orderParam) { for (const op of orderParam) { const order = parseInt(op.substring(1, op.length)); const orderPay = pays.find(x => { return x.order === order }); formula = formula.replace(op, orderPay && orderPay.tp || 0); } } for (const b of this.bases) { if ((b.code === 'bqwc' || b.code === 'bqht') && (!pay.pre_used && pay.sprice)) { switch (b.code) { case 'bqwc': formula = formula.replace(b.reg, this.ctx.helper.sub(this.add.gather_tp, pay.sprice)); break; case 'bqht': formula = formula.replace(b.reg, this.ctx.helper.sub(this.add.contract_tp, pay.sprice)); break; default: throw '未知参数'; } } else { formula = formula.replace(b.reg, b.value); } } const percent = formula.match(this.percentReg); if (percent) { for (const p of percent) { const v = math.eval(p.replace('%', '/100')); formula = formula.replace(p, v); } } try { // 使用mathjs计算 math.eval('17259401.95*0.9') = 15533461.754999999,正确应为 15533461.755 // const value = math.eval(formula); // 使用逆波兰法四则运算,可防止出现误差,但是只支持四则运算,不支持科学运算 // const value = this.ctx.helper.calcExprStrRpn(formula); // 使用mathjs的大数运算,可支持所有 const value = parseFloat(math.eval(formula)); return Number.isFinite(value) ? value : 0; } catch(err) { return 0; } } _calculateExpr(expr) { let formula = expr; for (const b of this.bases) { formula = formula.replace(b.reg, b.value ? b.value : 0); } const percent = formula.match(this.percentReg); if (percent) { for (const p of percent) { const v = math.eval(p.replace('%', '/100')); formula = formula.replace(p, v); } } try { // 使用mathjs计算 math.eval('17259401.95*0.9') = 15533461.754999999,正确应为 15533461.755 // const value = math.eval(formula); // 使用逆波兰法四则运算,可防止出现误差,但是只支持四则运算,不支持科学运算 // const value = this.ctx.helper.calcExprStrRpn(formula); // 使用mathjs的大数运算,可支持所有 const value = parseFloat(math.eval(formula)); return Number.isFinite(value) ? value : 0; } catch(err) { return 0; } } /** * 计算起扣金额、付(扣)款限额 * * @param {Array} pays - (标段)合同支付数据 */ async calculateStartRangePrice (pays) { //const order = this.stage.curOrder; for (const p of pays) { // 非本期,本次添加的合同支付项,不允许计算,其中默认添加的合同支付项,归属于第一期原报 // if (p.csorder === this.stage.order || (p.csorder === 0 || this.stage.order === 1)) { // if (p.csaorder === order) { // } // } // 上一期已计量的合同支付项,不予计算起扣金额、扣款限额 if (!p.pre_used) { if (!p.sprice && p.sexpr && p.sexpr !== '') { p.sprice = this.ctx.helper.round(this._calculateExpr(p.sexpr), this.decimal); } else if (p.sprice && !p.sexpr) { p.sprice = this.ctx.helper.round(p.sprice, this.decimal); } if (!p.rprice && p.rexpr && p.rexpr !== '') { p.rprice = this.ctx.helper.round(this._calculateExpr(p.rexpr), this.decimal); } else if (p.rprice && !p.rexpr) { p.rprice = this.ctx.helper.round(p.rprice, this.decimal); } } } } /** * 累计 计量等数据 */ async _getAddCalcRela() { if (this.cur && this.add) return; this.pre = this.stage.order > 1 ? await this.ctx.service.stageBillsFinal.getSumTotalPrice(this.stage.tid, this.stage.order - 1) : {}; this.cur = await this.ctx.service.stageBills.getSumTotalPrice(this.stage); this.add = {}; if (this.pre) { this.add.contract_tp = this.ctx.helper.add(this.pre.contract_tp, this.cur.contract_tp); this.add.qc_tp = this.ctx.helper.add(this.pre.qc_tp, this.cur.qc_tp); } else { this.add.contract_tp = this.cur.contract_tp; this.add.qc_tp = this.cur.qc_tp; } this.add.gather_tp = this.ctx.helper.add(this.add.contract_tp, this.add.qc_tp); } /** * 检查是否到达 计提期限 * @param pay */ _checkDeadline(pay) { if (pay.dl_type === deadlineType.tp.value) { const deadlineTp = this.add[pay.dl_tp_type + '_tp']; if (deadlineTp > pay.dl_tp) { return true; } } else if (pay.dl_type === deadlineType.count.value) { return this.stage.order >= pay.dl_count; } else { return false; } } getLeafOrder(data, pays, parentOrder) { // if (!data) return []; // if (!data.expr) return [`f${data.order}`]; // const orderParam = data.expr.match(this.orderReg); // if (!orderParam || orderParam.length === 0) return [`f${data.order}`]; // // const result = [...orderParam]; // for (const op of orderParam) { // const order = op.substring(1, op.length); // if (parseInt(order) === data.order || op === parentOrder) { // result.push(op); // } else { // result.push(...this.getLeafOrder(pays[parseInt(order) -1], pays, `f${data.order}`)); // } // } // return this.ctx.helper._.uniq(result); if (!data || !data.expr) return []; const orderParam = data.expr.match(this.orderReg); if (!orderParam || orderParam.length === 0) return []; const result = [...orderParam]; for (const op of orderParam) { const order = op.substring(1, op.length); if (parseInt(order) === data.order || op === parentOrder) { result.push(op); } else { const sub = this.getLeafOrder(pays[parseInt(order) -1], pays, `f${data.order}`); if (sub.length > 0) { result.push(...sub); } else { result.push(op); } } } return this.ctx.helper._.uniq(result); } sortPaysByCalc(pays) { for (const pay of pays) { pay.calcLeaf = this.getLeafOrder(pay, pays); } pays.sort((x, y) => {return x.calcLeaf.length - y.calcLeaf.length; }); } sortPaysByOrder(pays) { pays.sort((x, y) => { return x.order - y.order; }); } /** * 计算本期、截止本期金额 * @param {Array} pays - (标段&期)合同支付数据 */ async calculate(pays) { this.yf = pays.find(function (p) { return p.ptype === payType.yf; }); this.sf = pays.find(function (p) { return p.ptype === payType.sf; }); if (!this.yf) return false; await this._getAddCalcRela(); this.yf.tp = 0; for (const p of pays) { if (p.ptype === payType.normal || p.ptype === payType.wc) { if (!p.pause && (!p.sprice || this.add.gather_tp >= p.sprice)) { if (p.expr && p.expr !== '') { const value = this.ctx.helper.round(this._calculateTpExpr(p, pays), this.decimal); if (p.rprice) { if (this._checkDeadline(p)) { p.tp = this.ctx.helper.sub(p.rprice, p.pre_tp); } else { p.tp = Math.min(this.ctx.helper.sub(p.rprice, p.pre_tp), value); } } else { p.tp = value; } } else if (p.tp && !this.ctx.helper.checkZero(p.tp)) { if (p.rprice) { if (this._checkDeadline(p)) { p.tp = this.ctx.helper.sub(p.rprice, p.pre_tp); } else { p.tp = Math.min(this.ctx.helper.sub(p.rprice, p.pre_tp), this.ctx.helper.round(p.tp, this.decimal)); } } else { p.tp = this.ctx.helper.round(p.tp, this.decimal); } } else { if (p.rprice) { if (this._checkDeadline(p)) { p.tp = this.ctx.helper.sub(p.rprice, p.pre_tp); } } } } else { p.tp = 0; } // 累加 至 应付 if (p.is_yf) { if (p.minus) { this.yf.tp = this.ctx.helper.sub(this.yf.tp, p.tp); } else { this.yf.tp = this.ctx.helper.add(this.yf.tp, p.tp); } } } p.end_tp = this.ctx.helper.round(this.ctx.helper.add(p.tp, p.pre_tp), this.decimal); } this.yf.end_tp = this.ctx.helper.add(this.yf.tp, this.yf.pre_tp); const bqyf = this.bases.find(function (x) {return x.code === 'bqyf'}); if (bqyf) { bqyf.value = this.yf.tp; } if (this.sf.expr === null || this.sf.expr === '') { if (this.sf.rprice) { this.sf.tp = Math.min(this.ctx.helper.sub(this.sf.rprice, this.sf.pre_tp), this.yf.tp); } else { this.sf.tp = this.yf.tp; } } else { const value = this.ctx.helper.round(this._calculateTpExpr(this.sf, pays), this.decimal); if (this.sf.rprice) { this.sf.tp = Math.min(this.ctx.helper.sub(this.sf.rprice, this.sf.pre_tp), value); } else { this.sf.tp = this.ctx.helper.round(value, this.decimal); } } this.sf.end_tp = this.ctx.helper.add(this.sf.tp, this.sf.pre_tp); } async calculateAll(pays) { await this.getCalcBase(); await this._getAddCalcRela(); await this.calculateStartRangePrice(pays); this.sortPaysByCalc(pays); await this.calculate(pays); this.sortPaysByOrder(pays); } } module.exports = PayCalculate;