'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; /* 以下变量在调用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) { let formula = pay.expr; 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; } } 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 value; } 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 value; } 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.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; } } /** * 计算本期、截止本期金额 * @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), 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), 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); await this.calculate(pays); } } module.exports = PayCalculate;