'use strict'; /** * * 清单汇总对比(需使用 decimal.min.js, zh_calc.js, path_tree.js, lodash.js) * * @author Mai * @date 2020/10/28 * @version */ const gclCompareModel = (function () { const leafXmjs = [], mergeChar = ';'; let gclList, gclChapter, otherChapter; let ledgerSetting, gsTree; function gatherfields(obj, src, fields, prefix = '') { if (obj && src) { for (const f of fields) { obj[prefix + f] = ZhCalc.add(obj[prefix + f], src[f]); } } } /** * 新建 清单汇总节点 * @param node - 最底层 工程量清单节点 * @returns {obj} */ function newGclNode(node) { const gcl = { b_code: node.b_code, name: node.name, unit: node.unit, unit_price: node.unit_price, leafXmjs: [], }; gclList.push(gcl); return gcl; } /** * 获取清单汇总节点 * * @param node - 最底层清单节点 * @returns {*} */ function getGclNode(node) { const gcl = gclList.find(function (g) { return g.b_code === node.b_code && (g.name || node.name ? g.name === node.name : true) && (g.unit || node.unit ? g.unit === node.unit : true) && checkZero(ZhCalc.sub(g.unit_price, node.unit_price)); }); if (gcl) { return gcl } else { return newGclNode(node); } } /** * 检查 text 是否是Peg * e.g. K123+000(true) Kab+123(false) K123.234+234(false) K12+324.234(true) * * @param text * @returns {*} * @constructor */ function CheckPeg(text) { const pegReg = /[a-zA-Z]?[kK][0-9]+[++][0-9]{3}([.][0-9]+)?/; return pegReg.test(text); } /** * 获取 桩号节点 * @param node - 检索起始节点 * @returns {*} */ function getPegNode (node) { if (node) { if (CheckPeg(node.name)) { return node; } else { const parent = gsTree.getParent(node); return parent ? getPegNode(parent) : null; } } } /** * 获取节点的第N层父节点 * * @param node - 节点(检索起点) * @param level - 第N层 * @returns {*} */ function getNodeByLevel(node, level) { let cur = node; while (cur && cur.level > level) { cur = gsTree.getParent(cur); } return cur; } /** * 获取 单位工程 * * @param xmj - 计量单元(最底层项目节) * @returns {string} */ function getDwgc(peg, xmj) { if (peg) { return peg.name; } else { const node = getNodeByLevel(xmj, 2); return node ? node.name : ''; } } /** * 获取 分部工程 * * @param peg - 桩号节点 * @param xmj - 计量单元(最底层项目节) * @returns {string} */ function getFbgc(peg, xmj) { if (peg && peg.id !== xmj.id) { const node = getNodeByLevel(xmj, peg.level + 1); return node ? node.name : ''; } else { const node = getNodeByLevel(xmj, 3); return node ? node.name : ''; } } /** * 获取 分项工程 * * @param peg - 桩号节点 * @param xmj - 计量单元(最底层项目节) * @returns {string} */ function getFxgc(peg, xmj) { if (!peg) { const node = getNodeByLevel(xmj, 4); return node ? node.name : ''; } else if (peg.id === xmj.id) { if (xmj.level > 4) { let value = ''; for (let level = 4; level < xmj.level; level++) { const node = getNodeByLevel(xmj, level); value = value === '' ? node.name : value + mergeChar + node.name; } return value; } else { return ''; } } else { if (peg.level + 2 < xmj.level) { let value = ''; for (let level = peg.level + 2; level < xmj.level; level++) { const node = getNodeByLevel(xmj, level); value = value === '' ? node.name : value + mergeChar + node.name; } return value; } else { return ''; } } } /** * 新建 最底层项目节 缓存数据 * @param leafXmj * @returns {{id, code: *|string[], jldy, fbgc: string, fxgc: string, dwgc: string, bwmx: string, drawing_code: string}} */ function newCacheLeafXmj(leafXmj) { const peg = getPegNode(leafXmj); const cacheLX = { id: leafXmj.id, code: leafXmj.code, jldy: leafXmj.name, fbgc: getFbgc(peg, leafXmj), fxgc: getFxgc(peg, leafXmj), dwgc: getDwgc(peg, leafXmj), drawing_code: leafXmj.drawing_code, }; leafXmjs.push(cacheLX); return cacheLX; } /** * 获取缓存的最底层项目节数据 * * @param leafXmj - 最底层项目节 * @returns {*} */ function getCacheLeafXmj(leafXmj) { const cacheLX = leafXmjs.find(function (lx) { return lx.id === leafXmj.id; }); if (!cacheLX) { return newCacheLeafXmj(leafXmj); } else { return cacheLX; } } /** * 汇总节点 * @param node - 最底层 工程量清单 节点 * @param leafXmj - 所属 最底层 项目节 */ function loadGatherGclNode(node, leafXmj, gsPos) { const gcl = getGclNode(node); gatherfields(gcl, node, ledgerSetting.billsFields, ledgerSetting.prefix); const cacheLeafXmj = getCacheLeafXmj(leafXmj); const posRange = gsPos.getLedgerPos(node.id); const detail = posRange && posRange.length > 0 ? posRange : [node]; for (const d of detail) { const dx = _.assign({}, cacheLeafXmj); gatherfields(dx, d, ledgerSetting.posFields, ledgerSetting.prefix); dx.gcl_id = node.id; if (d.name !== node.name) { dx.bwmx = d.name; dx.mx_id = d.id; } if (d.drawing_code) { dx.drawing_code = d.drawing_code; } gcl.leafXmjs.push(dx); } } /** * (递归)汇总树节点 * @param nodes - 汇总节点列表 * @param leafXmj - 汇总节点所属的底层项目节 */ function recursiveGatherGclData(nodes, leafXmj, gsPos) { for (const node of nodes) { if (node.b_code) { if (node.children.length > 0) { recursiveGatherGclData(node.children, leafXmj, gsPos); } else { loadGatherGclNode(node, leafXmj, gsPos); } } else if (node.children.length > 0) { recursiveGatherGclData(node.children, node, gsPos); } } } function _getCalcChapter(chapter) { const gclChapter = [], otherChapter = []; let serialNo = 1; for (const c of chapter) { const cc = { code: c.code, name: c.name, cType: 1 }; cc.serialNo = serialNo++; cc.filter = '^[^0-9]*' + c.code.substr(0, c.code.length - 2) + '[0-9]{2}-'; gclChapter.push(cc); } gclChapter.push({ name: '未计入章节清单合计', cType: 21, serialNo: serialNo++, }); otherChapter.push({ name: '清单小计(A)', cType: 11, serialNo: serialNo++ }); otherChapter.push({ name: '非清单项费用(B)', cType: 31, serialNo: serialNo++ }); otherChapter.push({ name: '合计(C=A+B)', cType: 41, serialNo: serialNo }); return [gclChapter, otherChapter]; } function _gatherChapterFields(chapter, data, fields) { for (const f of fields) { chapter[f] = ZhCalc.add(chapter[f], data[f]); } } function _getGclChapter(chapter, data) { for (const c of chapter) { if (c.filter) { const reg = new RegExp(c.filter); if (reg.test(data.b_code)) { return c; } } else { return c; } } } function _gatherChapter() { for (const d of gsTree.datas) { if (d.children && d.children.length > 0) continue; for (const c of otherChapter) { if (c.cType === 41) { gatherfields(c, d, ledgerSetting.chapterFields, ledgerSetting.prefix); } else if (c.cType === 31 && (!d.b_code || d.b_code === '')) { gatherfields(c, d, ledgerSetting.chapterFields, ledgerSetting.prefix); } else if (c.cType === 11 && (d.b_code)) { gatherfields(c, d, ledgerSetting.chapterFields, ledgerSetting.prefix); } } if (d.b_code) { const c = _getGclChapter(gclChapter, d); gatherfields(c, d, ledgerSetting.chapterFields, ledgerSetting.prefix); } } } function init (gclData, chapter) { gclList = gclData; [gclChapter, otherChapter] = _getCalcChapter(chapter); } /** * * @param bills - 项目节+清单数据 * @param pos - 计量单元数据 * @param setting 配置 * e.g. * { * tree: { * id: 'ledger_id', * pid: 'ledger_pid', * order: 'order', * level: 'level', * rootId: -1, * keys: ['id', 'tender_id', 'ledger_id'], * stageId: 'id', * }, * pos: { id: 'id', ledgerId: 'lid', }, * billsFields: ['quantity', 'total_price', 'deal_qty', 'deal_tp'], * posFields: ['quantity'], * chapterFields: [new_total_price], * prefix: 'org' // 'new' * } */ function gatherLedgerData(bills, pos, setting) { ledgerSetting = setting; try { //if (leafXmjs.length > 0) leafXmjs.length = 0; gsTree = createNewPathTree('ledger', setting.tree); gsTree.loadDatas(bills); const gsPos = new PosData(setting.pos); gsPos.loadDatas(pos); recursiveGatherGclData(gsTree.children, null, gsPos); _gatherChapter(); } catch(err) { console.log(err); } ledgerSetting = null; } /** * * @param data {Array} - 签约清单数据 * @returns */ function gatherDealBills(data) { if (data instanceof Array && data.length > 0) { for (const node of data) { node.b_code = node.code; const gcl = getGclNode(node); if (!d.quantity || !d.unit_price) continue; gcl.deal_bills_qty = node.quantity || 0; gcl.deal_bills_tp = node.total_price || 0; for (const c of otherChapter) { if (c.cType === 41 || c.cType === 11) { c.deal_bills_tp = ZhCalc.add(c.deal_bills_tp, d.total_price); } } const c = _getGclChapter(gclChapter, d); c.deal_bills_tp = ZhCalc.add(c.deal_bills_tp, d.total_price); } } } /** * 检查汇总完的工程量清单中,同编号 & 不同名称/单位/单价 的清单并标记 */ function checkDiffer() { for (const gcl of gclList) { gcl.differ = false; } for (const [i, gcl] of gclList.entries()) { if (i === gclList.length - 1) continue; const next = gclList[i+1]; if (gcl.b_code === next.b_code) { if (gcl.name !== next.name || gcl.unit !== next.unit || !checkZero(gcl.unit_price - next.unit_price)) { gcl.differ = true; next.differ = true; } } } } function chapterData () { return gclChapter.concat(otherChapter); } return { init, gatherLedgerData, gatherDealBills, checkDiffer, chapterData }; })();