'use strict'; /** * * 单价调整计算: * 1. 台账修订上报,保存台账修订历史时,使用当前单价调整计算一次 * 2. 台账修订完成:计算台账、应用到未审完成期、应用到所有工程变更 * 3. 新增期:检查是否存在未应用的单价变更 * 4. 期审批完成:所有未应用的单价调整,记录应用到该期(记录revise_price.use_stage/use_stage_order) - 一条sql即可 * 5. 期重现审批:上一次应用的所有单价调整,回到未应用状态(revise_price.use_stage/use_stage_order均回到0) - 一条sql即可 * * @author Mai * @date * @version */ const Ledger = require('./ledger'); const audit = require('../const/audit'); class revisePriceCalc { constructor(ctx) { this.ctx = ctx; } findPrice(b_code, name, unit, unit_price) { const helper = this.ctx.helper; return this.price.find(x => { return b_code === x.b_code && name === x.name && unit === x.unit && helper.numEqual(unit_price, x.org_price); }); } /** * 新增一期计量,检查单价调整 * @param {Object} newStage - 新计量期 * @param {Object} preStage - 上一计量期 * @return { inseretPosData, insertBillsData } */ async newStagePriceChange(newStage, preStage, transaction) { // 获取未执行的单价变更,无单价变更不执行 const price = await this.ctx.service.revisePrice.getAllDataByCondition({ where: { tid: newStage.tid, valid: 1, use_stage: 0 } }); if (price.length === 0) return; // 无截止上期数据不执行 const preBillsData = await this.ctx.service.stageBillsFinal.getAllDataByCondition({ where: { sid: preStage.id } }); if (preBillsData.length === 0) return; // 加载树结构 const bills = await this.ctx.service.ledger.getData(newStage.tid); this.ctx.helper.assignRelaData(bills, [ { data: preBillsData, fields: ['id', 'contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'unit_price', 'positive_qc_qty', 'negative_qc_qty', 'positive_qc_tp', 'negative_qc_tp'], prefix: 'pre_', relaId: 'lid' }, ]); const billsTree = new Ledger.billsTree(this.ctx, { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1, calcFields: [] }); billsTree.loadDatas(bills); // 计算 const result = { ibData: [] }; const helper = this.ctx.helper; const decimal = this.ctx.tender.info.decimal; billsTree.calculateAll(node => { if (!node.pre_id) return; if (!node.pre_contract_qty && !node.pre_qc_qty) return; if (node.children && node.children.length > 0) return; const priceDiff = helper.sub(node.unit_price, node.pre_unit_price); if (!priceDiff) return; node.contract_pc_tp = helper.sub(helper.mul(node.pre_contract_qty, node.unit_price, decimal.tp), node.pre_contract_tp); node.qc_pc_tp = helper.sub(helper.mul(node.pre_qc_qty, node.unit_price, decimal.tp), node.pre_qc_tp); node.pc_tp = helper.add(node.contract_pc_tp, node.qc_pc_tp); node.positive_qc_pc_tp = helper.sub(helper.mul(node.pre_positive_qc_qty, node.unit_price, decimal.tp), node.pre_positive_qc_tp); node.negative_qc_pc_tp = helper.sub(helper.mul(node.pre_negative_qc_qty, node.unit_price, decimal.tp), node.pre_negative_qc_tp); result.ibData.push({ tid: newStage.tid, sid: newStage.id, sorder: newStage.order, lid: node.id, org_price: node.pre_unit_price, unit_price: node.unit_price, contract_pc_tp: node.contract_pc_tp, qc_pc_tp: node.qc_pc_tp, pc_tp: node.pc_tp, positive_qc_pc_tp: node.positive_qc_pc_tp, negative_qc_pc_tp: node.negative_qc_pc_tp, }); }); if (result.ibData.length > 0) await transaction.insert(this.ctx.service.stageBillsPc.tableName, result.ibData); let contract_pc_tp = 0, qc_pc_tp = 0, pc_tp = 0, positive_qc_pc_tp = 0, negative_qc_pc_tp = 0; for (const ibc of result.ibData) { contract_pc_tp = helper.add(contract_pc_tp, ibc.contract_pc_tp); qc_pc_tp = helper.add(qc_pc_tp, ibc.qc_pc_tp); pc_tp = helper.add(pc_tp, ibc.pc_tp); positive_qc_pc_tp = helper.add(positive_qc_pc_tp, ibc.positive_qc_pc_tp); negative_qc_pc_tp = helper.add(negative_qc_pc_tp, ibc.negative_qc_pc_tp); } await transaction.update(this.ctx.service.stage.tableName, { id: newStage.id, contract_pc_tp, qc_pc_tp, pc_tp, positive_qc_pc_tp, negative_qc_pc_tp, check_calc: true, cache_time_l: new Date() }); } /** * 期,重新审批,检查单价调整 * @param {Object} stage - 期 * @param {Number} auditOrder - 重新审批,最新审批人序号 * @param {Object} transaction - 事务 */ async stageCheckAgainPriceChange(stage, auditOrder, transaction) { // 获取未执行的单价变更,无单价变更不执行 const price = await this.ctx.service.revisePrice.getAllDataByCondition({ where: { tid: stage.tid, valid: 1, use_stage: 0 } }); if (price.length === 0) return; const curBillsData = await this.ctx.service.stageBills.getLastestStageData2(stage.tid, stage.id); const preBillsData = await this.ctx.service.stageBillsFinal.getAllDataByCondition({ where: { tid: stage.tid, sorder: stage.order - 1 } }); // 加载树结构 const bills = await this.ctx.service.ledger.getData(stage.tid); this.ctx.helper.assignRelaData(bills, [ { data: curBillsData, fields: ['id', 'contract_qty', 'qc_qty', 'positive_qc_qty', 'negative_qc_qty', 'postil', 'times', 'order'], prefix: 'cur_', relaId: 'lid' }, { data: preBillsData, fields: ['id', 'contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'unit_price', 'positive_qc_qty', 'negative_qc_qty', 'positive_qc_tp', 'negative_qc_tp'], prefix: 'pre_', relaId: 'lid' }, ]); const billsTree = new Ledger.billsTree(this.ctx, { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1, calcFields: [] }); billsTree.loadDatas(bills); const stageChange = await this.ctx.service.stageChange.getFinalStageData(stage.tid, stage.id); // 计算 insertBills billsPriceChange stageChange const result = { ibData: [], bpcData: [], scData: [] }; const helper = this.ctx.helper; const decimal = this.ctx.tender.info.decimal; const said = this.ctx.session.sessionUser.accountId; billsTree.calculateAll(node => { if (node.children && node.children.length > 0) return; const priceDiff = helper.sub(node.unit_price, node.pre_unit_price); if (!priceDiff) return; if (node.cur_id && (node.cur_contract_qty || node.cur_qc_qty)) { node.cur_contract_tp = helper.mul(node.cur_contract_qty, node.unit_price, decimal.tp); node.cur_qc_tp = helper.mul(node.cur_qc_qty, node.unit_price, decimal.tp); node.cur_positive_qc_tp = helper.mul(node.cur_positive_qc_qty, node.unit_price, decimal.tp); node.cur_negative_qc_tp = helper.mul(node.cur_negative_qc_qty, node.unit_price, decimal.tp); result.ibData.push({ tid: stage.tid, sid: stage.id, said, lid: node.id, times: stage.times, order: auditOrder, contract_qty: node.cur_contract_qty, contract_tp: node.cur_contract_tp, qc_qty: node.cur_qc_qty, qc_tp: node.cur_qc_tp, positive_qc_qty: node.cur_positive_qc_qty, positive_qc_tp: node.cur_positive_qc_tp, negative_qc_qty: node.cur_negative_qc_qty, negative_qc_tp: node.cur_negative_qc_tp, postil: node.postil, }); } if (node.pre_id && (node.pre_contract_qty || node.pre_qc_qty)) { node.contract_pc_tp = helper.sub(helper.mul(node.pre_contract_qty, node.unit_price, decimal.tp), node.pre_contract_tp); node.qc_pc_tp = helper.sub(helper.mul(node.pre_qc_qty, node.unit_price, decimal.tp), node.pre_qc_tp); node.pc_tp = helper.add(node.contract_pc_tp, node.qc_pc_tp); node.positive_qc_pc_tp = helper.sub(helper.mul(node.pre_positive_qc_qty, node.unit_price, decimal.tp), node.pre_positive_qc_tp); node.negative_qc_pc_tp = helper.sub(helper.mul(node.pre_negative_qc_qty, node.unit_price, decimal.tp), node.pre_negative_qc_tp); result.bpcData.push({ tid: stage.tid, sid: stage.id, sorder: stage.order, lid: node.id, org_price: node.pre_unit_price, unit_price: node.unit_price, contract_pc_tp: node.contract_pc_tp, qc_pc_tp: node.qc_pc_tp, pc_tp: node.pc_tp, positive_qc_pc_tp: node.positive_qc_pc_tp, negative_qc_pc_tp: node.negative_qc_pc_tp, }); } const scDetail = stageChange.filter(x => { return x.lid === node.id; }); for (const scd of scDetail) { result.scData.push({ id: scd.id, unit_price: node.unit_price }); } }); if (result.ibData.length > 0) await transaction.insert(this.ctx.service.stageBills.tableName, result.ibData); await transaction.delete(this.ctx.service.stageBillsPc.tableName, { sid: stage.id }); if (result.bpcData.length > 0) await transaction.insert(this.ctx.service.stageBillsPc.tableName, result.bpcData); if (result.scData.length > 0) await transaction.updateRows(this.ctx.service.stageChange.tableName, result.scData); let contract_pc_tp = 0, qc_pc_tp = 0, pc_tp = 0, positive_qc_pc_tp = 0, negative_qc_pc_tp = 0; for (const bpc of result.bpcData) { contract_pc_tp = helper.add(contract_pc_tp, bpc.contract_pc_tp); qc_pc_tp = helper.add(qc_pc_tp, bpc.qc_pc_tp); pc_tp = helper.add(pc_tp, bpc.pc_tp); positive_qc_pc_tp = helper.add(positive_qc_pc_tp, bpc.positive_qc_pc_tp); negative_qc_pc_tp = helper.add(negative_qc_pc_tp, bpc.negative_qc_pc_tp); } await transaction.update(this.ctx.service.stage.tableName, { id: stage.id, contract_pc_tp, qc_pc_tp, pc_tp, positive_qc_pc_tp, negative_qc_pc_tp, check_calc: true, cache_time_l: new Date() }); } /** * 重算工程变更(调整单价) * @param {Object} change - 工程变更 * @param {Object} transaction - 事务 (无则非事务提交) */ async calcChange(change, transaction) { const decimal = this.ctx.tender.info.decimal; const changeBills = await this.ctx.service.changeAuditList.getAllDataByCondition({ where: { cid: change.cid } }); const updateBills = []; let total_price = 0, positive_tp = 0, negative_tp = 0; for (const b of changeBills) { const p = this.findPrice(b.code, b.name, b.unit, b.unit_price); let bills_tp; if (p) { updateBills.push({ id: b.id, unit_price: p.new_price }); bills_tp = this.ctx.helper.mul(p.new_price, b.spamount, change.tp_decimal || decimal.tp); } else { bills_tp = this.ctx.helper.mul(b.unit_price, b.spamount, change.tp_decimal || decimal.tp); } total_price = this.ctx.helper.add(total_price, bills_tp); if (b.spamount >= 0) { positive_tp = this.ctx.helper.add(positive_tp, bills_tp); } else { negative_tp = this.ctx.helper.add(negative_tp, bills_tp); } } if (updateBills.length > 0) { await transaction.updateRows(this.ctx.service.changeAuditList.tableName, updateBills); await transaction.update(this.ctx.service.change.tableName, { total_price, positive_tp, negative_tp }, { where: { cid: change.cid } }); } } /** * 重算标段下所有工程变更(调整单价) * @param {Number} tid - 标段id * @param {Object} transaction - 事务 (无则非事务提交) */ async calcAllChanges(tid, transaction) { const change = await this.ctx.service.change.getAllDataByCondition({ where: { tid, valid: 1 } }); if (change.length === 0) return; for (const c of change) { await this.calcChange(c, transaction); } } async _calcStage(stage, bills, transaction) { // 无单价变更不执行 if (this.price.length === 0) return; const curBillsData = await this.ctx.service.stageBills.getLastestStageData2(stage.tid, stage.id); const preBillsData = await this.ctx.service.stageBillsFinal.getAllDataByCondition({ where: { tid: stage.tid, sorder: stage.order - 1 } }); // 加载树结构 this.ctx.helper.assignRelaData(bills, [ { data: curBillsData, fields: ['id', 'contract_qty', 'qc_qty', 'positive_qc_qty', 'negative_qc_qty', 'times', 'order', 'postil'], prefix: 'cur_', relaId: 'lid' }, { data: preBillsData, fields: ['id', 'contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'unit_price', 'positive_qc_qty', 'negative_qc_qty', 'positive_qc_tp', 'negative_qc_tp'], prefix: 'pre_', relaId: 'lid' }, ]); const billsTree = new Ledger.billsTree(this.ctx, { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1, calcFields: [] }); billsTree.loadDatas(bills); const stageChange = await this.ctx.service.stageChange.getFinalStageData(stage.tid, stage.id); // 计算 insertBills/updateBills billsPriceChange StageChange const result = { ibData: [], ubData: [], bpcData: [], scData: [] }; const helper = this.ctx.helper; const decimal = this.ctx.tender.info.decimal; const said = this.ctx.session.sessionUser.accountId; billsTree.calculateAll(node => { if (node.children && node.children.length > 0) return; const priceDiff = helper.sub(node.unit_price, node.pre_unit_price); if (!priceDiff) return; if (node.cur_id && (node.cur_contract_qty || node.cur_qc_qty)) { node.cur_contract_tp = helper.mul(node.cur_contract_qty, node.unit_price, decimal.tp); node.cur_qc_tp = helper.mul(node.cur_qc_qty, node.unit_price, decimal.tp); node.cur_positive_qc_tp = helper.mul(node.cur_positive_qc_qty, node.unit_price, decimal.tp); node.cur_negative_qc_tp = helper.mul(node.cur_negative_qc_qty, node.unit_price, decimal.tp); if (node.cur_times === stage.times && node.cur_order === 0) { result.ubData.push({ id: node.cur_id, contract_tp: node.cur_contract_tp, qc_tp: node.cur_qc_tp, positive_qc_tp: node.cur_positive_qc_tp, negative_qc_tp: node.cur_negative_qc_tp, }); } else { result.ibData.push({ tid: stage.tid, sid: stage.id, said, lid: node.id, times: stage.times, order: 0, contract_qty: node.cur_contract_qty, contract_tp: node.cur_contract_tp, qc_qty: node.cur_qc_qty, qc_tp: node.cur_qc_tp, positive_qc_qty: node.cur_positive_qc_qty, positive_qc_tp: node.cur_positive_qc_tp, negative_qc_qty: node.cur_negative_qc_qty, negative_qc_tp: node.cur_negative_qc_tp, postil: node.cur_postil, }); } } if (node.pre_id && (node.pre_contract_qty || node.pre_qc_qty)) { node.contract_pc_tp = helper.sub(helper.mul(node.pre_contract_qty, node.unit_price, decimal.tp), node.pre_contract_tp); node.qc_pc_tp = helper.sub(helper.mul(node.pre_qc_qty, node.unit_price, decimal.tp), node.pre_qc_tp); node.pc_tp = helper.add(node.contract_pc_tp, node.qc_pc_tp); node.positive_qc_pc_tp = helper.sub(helper.mul(node.pre_positive_qc_qty, node.unit_price, decimal.tp), node.pre_positive_qc_tp); node.negative_qc_pc_tp = helper.sub(helper.mul(node.pre_negative_qc_qty, node.unit_price, decimal.tp), node.pre_negative_qc_tp); result.bpcData.push({ tid: stage.tid, sid: stage.id, sorder: stage.order, lid: node.id, org_price: node.pre_unit_price, unit_price: node.unit_price, contract_pc_tp: node.contract_pc_tp, qc_pc_tp: node.qc_pc_tp, pc_tp: node.pc_tp, positive_qc_pc_tp: node.positive_qc_pc_tp, negative_qc_pc_tp: node.negative_qc_pc_tp, }); } const scDetail = stageChange.filter(x => { return x.lid === node.id; }); for (const scd of scDetail) { result.scData.push({ id: scd.id, unit_price: node.unit_price }); } }); if (result.ibData.length > 0) await transaction.insert(this.ctx.service.stageBills.tableName, result.ibData); if (result.ubData.length > 0) await transaction.updateRows(this.ctx.service.stageBills.tableName, result.ubData); await transaction.delete(this.ctx.service.stageBillsPc.tableName, { sid: stage.id }); if (result.bpcData.length > 0) await transaction.insert(this.ctx.service.stageBillsPc.tableName, result.bpcData); if (result.scData.length > 0) await transaction.updateRows(this.ctx.service.stageChange.tableName, result.scData); let contract_pc_tp = 0, qc_pc_tp = 0, pc_tp = 0, positive_qc_pc_tp = 0, negative_qc_pc_tp = 0; for (const bpc of result.bpcData) { contract_pc_tp = helper.add(contract_pc_tp, bpc.contract_pc_tp); qc_pc_tp = helper.add(qc_pc_tp, bpc.qc_pc_tp); pc_tp = helper.add(pc_tp, bpc.pc_tp); positive_qc_pc_tp = helper.add(positive_qc_pc_tp, bpc.positive_qc_pc_tp); negative_qc_pc_tp = helper.add(negative_qc_pc_tp, bpc.negative_qc_pc_tp); } await transaction.update(this.ctx.service.stage.tableName, { id: stage.id, contract_pc_tp, qc_pc_tp, pc_tp, positive_qc_pc_tp, negative_qc_pc_tp, check_calc: true, cache_time_l: new Date() }); } /** * 计算修订台账 * @param {Object}revise - 最新一次台账修订(此处不检查) * @param {Object} transaction - 事务 (无则非事务提交) */ async calcReviseLedger(revise, transaction) { const xmj = await this.ctx.helper.loadLedgerDataFromOss(revise.curHis.bills_file); xmj.forEach(x => { delete x.is_tp; delete x.gxby_status; delete x.gxby_limit; delete x.gxby_ratio; delete x.gxby_url; delete x.dagl_status; delete x.dagl_limit; delete x.dagl_ratio; delete x.dagl_url; }); const pos = await this.ctx.helper.loadLedgerDataFromOss(revise.curHis.pos_file); pos.forEach(p => { p.in_time = new Date(p.in_time); delete p.gxby_status; delete p.gxby_limit; delete p.gxby_ratio; delete p.gxby_url; delete p.dagl_status; delete p.dagl_limit; delete p.dagl_ratio; delete p.dagl_url; }); await transaction.delete(this.ctx.service.ledger.tableName, { tender_id: revise.tid }); if (xmj.length > 0) await transaction.insert(this.ctx.service.ledger.tableName, xmj); await transaction.delete(this.ctx.service.pos.tableName, { tid: revise.tid }); if (pos.length > 0) await transaction.insert(this.ctx.service.pos.tableName, pos); // 应用到未审完成期 const latestStage = await this.ctx.service.stage.getLastestStage(revise.tid, true); if (latestStage && latestStage.status !== audit.stage.status.checked) await this._calcStage(latestStage, xmj, transaction); } async calcRevise(revise, transaction) { if (revise.tid !== this.ctx.tender.id) throw '数据错误'; this.price = await this.ctx.service.revisePrice.getAllDataByCondition({ where: { rid: revise.id } }); await this.calcReviseLedger(revise, transaction); // 引用到所有工程变更 await this.calcAllChanges(revise.tid, transaction); } } module.exports = revisePriceCalc;