'use strict'; /** * * * @author Mai * @date * @version */ const math = require('mathjs'); const standard = require('../const/standard'); const moment = require('moment'); moment.locale('zh-cn'); class PosIndex { /** * 构造函数 * @param {id|String, masterId|String, order|String} setting */ constructor (setting) { // 无索引 this.datas = []; // 以key为索引 this.items = {}; // 以分类id为索引的有序 this.ledgerPos = {}; // pos设置 this.setting = setting; } /** * 加载部位明细数据 * @param datas */ loadDatas(datas) { this.datas = datas; this.items = {}; this.ledgerPos = {}; for (const data of this.datas) { const key = data[this.setting.id]; this.items[key] = data; const masterKey = data[this.setting.masterId]; if (!this.ledgerPos[masterKey]) { this.ledgerPos[masterKey] = []; } this.ledgerPos[masterKey].push(data); } for (const prop in this.ledgerPos) { this.resortLedgerPos(this.ledgerPos[prop]); } } getLedgerPos(mid) { return this.ledgerPos[mid]; } resortLedgerPos(ledgerPos) { const self = this; if (ledgerPos instanceof Array) { ledgerPos.sort(function (a, b) { return a[self.setting.order] - b[self.setting.order]; }) } } } // 通用方法 const rdaUtils = { orderCalc: function (ctx, data, fields) { const orderMatch = new RegExp('o[0-9]+', 'igm'); for (const c of data) { if (c.order_calc && c.order_calc !== '') { const matchs = c.order_calc.match(orderMatch); const calcMatch = []; for (const m of matchs) { const order = m.substring(1, m.length); const orderData = data.find(function (x) {return (x.order + '') === order}); if (orderData) { calcMatch.push({match: m, value: orderData}) } } for (const f of fields) { let expr = c.order_calc; for (const m of calcMatch) { expr = expr.replace(m.match, m.value[f] ? m.value[f] : 0); } try { c[f] = ctx.helper.round(math.eval(expr), 6); } catch(err) { } } } } } }; // 可提供的数据处理方法 const changeSort = { name: '变更令排序', hint: '默认的变更令排序,同时对变更令,变更清单进行排序\n' + '变更令排序勿需勾选任何预定义处理指标,但是在指标映射中,需要添加如下指标:' + '1. 如果只有变更令数据,则必须添加,"变更令(change)"下的"变更令号"' + '2. 如果有变更清单需要排序,则必须在1的基础上添加,变更令(change)"下的"变更令uuid"和"变更清单(chang_audit_list)"下的"所属变更令uuid"&"清单编号"', intro: '报表对于主从表的排序有要求,解决变更令数据中的变更令、变更清单排序问题', /** * * @param ctx - context常量 * @param data - 全部数据源{Array} * @param fieldsKey - 计算字段 * @param options - 计算设置 */ fun: function(ctx, data, fieldsKey, options) { const change = options && options.change ? data[options.change] : data.change; if (!change) return; // 变更令排序 change.sort(function (a, b) { return a.code.localeCompare(b.code); }); const changeBills = options && options.changeBills ? data[options.changeBills] : data.change_audit_list; if (changeBills) { changeBills.sort(function (a, b) { const aCIndex = data.change.findIndex(function (c) { return c.cid === a.cid; }); const bCIndex = data.change.findIndex(function (c) { return c.cid === b.cid; }); return aCIndex === bCIndex ? ctx.helper.compareCode(a.code, b.code) : aCIndex - bCIndex; }); } }, }; const gatherGcl = { name: '汇总工程量清单', hint: '请使用mem_stage_bills下指标,注意事项:\n' + '1. 以下字段,不管报表是否实际使用,均应添加至指标映射,且在此处应勾选(不要求顺序):\n' + ' 清单编号(b_code), 名称(name), 单位(unit), 单价(unit_price), 树结构-是否子项(is_leaf)\n' + '2. 汇总后,以下字段,均会失效, 请勿使用:\n' + ' 台账ID(id), 树结构-ID(ledger_id), 树结构父项-ID(ledger_pid),\n' + ' 树结构-层级(level), 树结构-同层排序(order), 树结构-完整路径(full_path),\n' + ' 图册号(drawing_code), 备注(memo), 节点类型(node_type), 总额计量(is_tp)\n' + '3. 如需汇总"未计入清单章节项",请勾选"章节编号(chapter)"字段\n', intro: '根据三级清单树结构,汇总平面工程量清单,目前仅支持mem_stage_bills表', _gatherCalcField: function (ctx, gcl, data) { for (const prop in data) { if (((prop === 'total_price' || prop.indexOf('tp') > 0) || (prop === 'quantity' || prop.indexOf('qty') > 0)) && data[prop]) { gcl[prop] = ctx.helper.add(gcl[prop], data[prop]); } } }, _gatherTpField: function (ctx, gcl, data) { for (const prop in data) { if ((prop === 'total_price' || prop.indexOf('tp') > 0) && data[prop]) { gcl[prop] = ctx.helper.add(gcl[prop], data[prop]); } } }, _loadGatherInfo: function (gcl, data) { for (const prop in data) { if (/^t_[0-9]+_(id|name)$/.test(prop)) { gcl[prop] = data[prop]; } } }, _loadStageSumInfo: function (gcl, data) { for (const prop in data) { if (/^s_[0-9]+_order$/.test(prop)) { gcl[prop] = data[prop]; } } }, _gatherFields: function(gcl, data, fields) { for (const f of fields) { if (data[f]) { gcl[f] = ctx.helper.add(gcl[f], data[f]); } } }, fun: function (ctx, data, fieldsKey, options) { const tableName = options ? options.table : 'mem_stage_bills'; const gatherData = data[tableName]; if (!gatherData || gatherData.length === 0) return; const fields = ctx.helper._.map(fieldsKey, 'field'); const needFields = ['b_code', 'name', 'unit', 'unit_price', 'is_leaf']; for (const nf of needFields) { if (fields.indexOf(nf) === -1) return; } const gatherOther = fields.indexOf('chapter') >= 0; const gclBills = [], other = {name: '未计入清单章节项', chapter: '10000'}; if (tableName.indexOf('mem_gather_') >= 0) this._loadGatherInfo(other, gatherData[0]); if (tableName.indexOf('mem_stage_sum_') >= 0) this._loadStageSumInfo(other, gatherData[0]); for (const b of gatherData) { const child = ctx.helper._.find(gatherData, {ledger_pid: b.ledger_id}); if (child) continue; if (b.b_code && b.b_code !== '') { let gcl = gclBills.find(function (g) { return g.b_code === b.b_code && g.name === b.name && g.unit === b.unit && ctx.helper.checkZero(ctx.helper.sub(g.unit_price, b.unit_price)); }); if (!gcl) { gcl = { b_code: b.b_code, name: b.name, unit: b.unit, unit_price: b.unit_price, qc_bgl_code: [], chapter: b.chapter, deal_bills_qty: b.deal_bills_qty, deal_bills_tp: b.deal_bills_tp, }; if (tableName.indexOf('mem_gather_') >= 0) this._loadGatherInfo(gcl, b); if (tableName.indexOf('mem_stage_sum_') >= 0) this._loadStageSumInfo(gcl, b); gclBills.push(gcl); } this._gatherCalcField(ctx, gcl, b); if (b.qc_bgl_code && b.qc_bgl_code !== '') { gcl.qc_bgl_code = gcl.qc_bgl_code.concat(b.qc_bgl_code.split(';')); } } else if (gatherOther) { this._gatherTpField(ctx, other, b); } } if (gatherOther) gclBills.push(other); for (const g of gclBills) { if (g.qc_bgl_code) g.qc_bgl_code = g.qc_bgl_code.join(';'); g.final_ratio = ctx.helper.mul(ctx.helper.div(g.end_gather_tp, g.final_ratio), 100); } data[tableName] = gclBills; } }; const sortGcl = { name: '工程量清单排序', hint: '只对一张表,进行工程量清单排序,排序哪张表,根据勾选的清单编号字段决定:\n' + 'e.g.1 要对mem_stage_bills排序,需要勾选mem_stage_bills下的"清单编号(b_code)"字段\n' + 'e.g.2 要对mem_stage_im_zl排序,需要勾选mem_stage_im_zl下的"中间计量总量信息_编号(code)"字段\n' + '特别的,如有"未计入清单章节项": \n' + ' 1. 默认"未计入清单章节项"排列在最后\n' + ' 2. 如须"未计入清单章节项"排在100章之后,请在清单编号字段后,依次勾选"章节编号(chapter)", "名称(name)"\n', intro: '根据选择列,对数据进行工程量清单排序,兼容1000章后清单,以及403-1-a类的非纯数字编号排序', fun: function (ctx, data, fieldsKey, options) { if (fieldsKey.length !== 1 && fieldsKey.length !== 3) return; const code = fieldsKey[0].field; const chapter = fieldsKey.length > 1 ? fieldsKey[1].field : ''; const name = fieldsKey.length > 2 ? fieldsKey[2].field : ''; const sortData = data[fieldsKey[0].table]; if (!sortData) return; sortData.sort(function (a, b) { if (chapter !== '') { if (a[name] === '未计入清单章节项') { return b[chapter] === '100' ? 1 : -1; } else if (b[name] === '未计入清单章节项') { return a[chapter] === '100' ? -1 : 1 } else { return ctx.helper.compareCode(a[code], b[code]); } } else { return ctx.helper.compareCode(a[code], b[code]); } }); } }; const gatherChapter = { name: '汇总章级数据', hint: '请使用mem_stage_bills/mem_stage_bills_compare/ledger,仅对一张表进行汇总,并生成数据:\n'+ '1. 因为是汇总章级数据,必须在离散数据中添加"章节代码"&"章节名称"\n' + '2. 需勾选"清单编号(b_code)", "树结构-是否子项(is_leaf)"字段,可以对任何含有这些字段的表汇总\n' + '注意事项:\n' + '1. 算法对数据表没有要求,保证有上述字段,且按顺序勾选即可, 仅汇总金额\n' + '2. 算法计算后,原数据表中非数字类型的字段全部失效(除清单编号、名称外),请勿在指标映射中添加\n' + '示例:\n' + 'e.g.1 要对mem_stage_bills汇总,须勾选mem_stage_bills下的"清单编号(b_code)", "树结构-是否子项((is_leaf)"字段\n' + 'e.g.2 要对mem_stage_bills_compare汇总,须勾选mem_stage_bills_compare下的"清单编号(b_code)", "树结构-是否子项((is_leaf)"字段\n' + '结果:\n' + '汇总结果可参照 清单汇总--章节合计,但是不过滤1000-1300章数据', intro: '用于得到类似“清单汇总--章级合计”的数据,可通过options自定义数据', defaultSetting: { count: 9, unChapter: { name: '未计入清单章节合计', order: 1, }, gclSum: { name: '清单小计', order: 2, }, unGcl: { name: '非清单项费用', order: 3, }, sum: { name: '合计', order: 4, }, filter: [{node_type: standard.nodeType.find(function (x) {return x.text === '计日工'}).value}, {field: 'name', part: '计日工'}], }, customSetting1: { count: 7, gclSum: { name: '第100章至700章清单合计', order: 1, }, custom: [ {name: '已包含在清单合计中的材料、工程设备、专业工程暂估价', order: 2, visible: false}, {name: '清单合计减去材料、工程设备、专业工程暂估价(即8-9=10)', order_calc: 'o1-o2', order: 3}, {name: '计日工合计', node_type: '计日工', order: 4}, {name: '暂列金额(不含计日工总额)(即10×暂列金额比列)', order: 5, match: [{node_type: '暂列金额'}, {field: 'name', part: '暂列金额'}, {field: 'name', all: '暂定金额'}]}, {name: '投标报价、台账价(8+11+12)=13', order_calc: 'o1+o4+o5', order: 6}, ], rela: [ { table: 'deal_bills', key: 'code', fields: {source: 'total_price', target: 'ex_value1'}, }, { table: 'mem_change_bills', key: 'code', fields: {source: '', target: 'ex_value2'}, } ] }, _getCalcChapter: function (chapter, options) { const gclChapter = [], otherChapter = [], customChapter = []; let serialNo = 1; for (const c of chapter) { const cc = { code: c.code, name: c.name, cType: 1 }; cc.serialNo = serialNo++; cc.filter = '^[\\D]*' + c.code.substr(0, c.code.length - 2) + '[0-9]{2}-'; //cc.visible = true; gclChapter.push(cc); if (options.activeFields) { cc.active = cc.serialNo > options.count; } else { if (serialNo > options.count) break; } } if (options.unChapter) { gclChapter.push({ name: options.unChapter.name, cType: 21, serialNo: serialNo + options.unChapter.order, order: options.unChapter.order, visible: options.unChapter.visible, }); } if (options.gclSum) { otherChapter.push({ name: options.gclSum.name, cType: 11, serialNo: serialNo + options.gclSum.order, order: options.gclSum.order, visible: options.gclSum.visible, }); } if (options.unGcl) { otherChapter.push({ name: options.unGcl.name, cType: 31, serialNo: serialNo + options.unGcl.order, order: options.unGcl.order, visible: options.unGcl.visible, }); } if (options.sum) { otherChapter.push({ name: options.sum.name , cType: 41, serialNo: serialNo + options.sum.order, order: options.sum.order, visible: options.sum.visible, }); } if (options.custom && options.custom instanceof Array) { for (const c of options.custom) { const cc = { name: c.name, serialNo: serialNo + c.order, order_calc: c.order_calc, cType: 5, order: c.order, visible: c.visible, }; if (c.match) { cc.match = JSON.parse(JSON.stringify(c.match)); for (const m of cc.match) { if (m.node_type && m.node_type !== '') { const nodeType = standard.nodeType.find(function (x) {return x.text === m.node_type}); m.node_type = nodeType.value; } } } customChapter.push(cc); } } return [gclChapter, otherChapter, customChapter]; }, _completeGatherInfo: function (chapters, source) { for (const f in source) { if (/^t_[0-9]*_(id|name)$/.test(f)) { for (const chapter of chapters) { chapter[f] = source[f]; } } } }, _completeStageSumInfo: function (chapters, source) { for (const f in source) { if (/^s_[0-9]*_order$/.test(f)) { for (const chapter of chapters) { chapter[f] = source[f]; } } } }, _getGclChapter: function (chapter, data, field) { for (const c of chapter) { if (c.filter) { const reg = new RegExp(c.filter); if (reg.test(data[field])) { return c; } } else { return c; } } }, _checkFilter: function (fullPath, filter) { for (const f of filter) { if (fullPath.indexOf(f + '-') === 0 || fullPath === f) return true; } return false; }, _gatherRela: function (ctx, data, rela, gclChapter, otherChapter) { if (!rela) return; const gatherRelaFields = function (chapter, source, field) { const fields = field instanceof Array ? field : [field]; for (const f of fields) { chapter[f.target] = ctx.helper.add(chapter[f.target], source[f.source]); } }; const relaBills = rela instanceof Array ? rela : [rela]; for (const rb of relaBills) { if (!rb.table) continue; const relaData = data[rb.table]; if (!relaData) continue; for (const rd of relaData) { for (const c of otherChapter) { if (c.cType === 41) { gatherRelaFields(c, rd, rb.fields); } else if (c.cType === 31 && (!rd[rb.key] || rd[rb.key] === '')) { gatherRelaFields(c, rd, rb.fields); } else if (c.cType === 11 && (rd[rb.key])) { gatherRelaFields(c, rd, rb.fields); } } if (rd[rb.key]) { const c = this._getGclChapter(gclChapter, rd, rb.key); if (c) gatherRelaFields(c, rd, rb.fields); } } } }, _orderCalc: function (ctx, chapter, fields) { const orderMatch = new RegExp('o[0-9]+', 'igm'); for (const c of chapter) { if (c.order_calc && c.order_calc !== '') { const matchs = c.order_calc.match(orderMatch); const calcMatch = []; for (const m of matchs) { const order = m.substring(1, m.length); const orderChapter = chapter.find(function (x) {return x.order == order}); if (orderChapter) { calcMatch.push({match: m, value: orderChapter}) } } for (const f of fields) { let expr = c.order_calc; for (const m of calcMatch) { expr = expr.replace(m.match, m.value[f] ? m.value[f] : 0); } try { c[f] = ctx.helper.round(math.eval(expr), 6); } catch(err) { } } } } }, _checkMatch: function (match, d) { for (const m of match) { if (m.node_type) { if (m.node_type === d.node_type) return true; } else if (m.field) { const value = d[m.field]; if (m.part && value) { if (value.indexOf(m.part) >= 0) return true; } else if (m.all && value) { if (m.all === value) return true; } } } return false; }, fun: function (ctx, data, fieldsKey, options) { if (!data.tender_info || !data.tender_info.chapter) return; if (!fieldsKey && fieldsKey.length < 0) return; const calcFields = []; const gatherData = function (chapter, data) { if (!chapter) return; for (const f in data) { if (!data[f]) continue; if ((f.indexOf('tp') >= 0 || f === 'total_price')) { chapter[f] = ctx.helper.add(chapter[f], data[f]); if (calcFields.indexOf(f) === -1) calcFields.push(f); } } }; const fields = ctx.helper._.map(fieldsKey, 'field'); const needFields = ['b_code', 'is_leaf']; for (const nf of needFields) { if (fields.indexOf(nf) === -1) return; } const sourceData = data[fieldsKey[0].table]; if (!sourceData) return; const [gclChapter, otherChapter, customChapter] = this._getCalcChapter(data.tender_info.chapter, options ? options : this.defaultSetting); if (fieldsKey[0].table.indexOf('mem_gather') >= 0) { this._completeGatherInfo(gclChapter, sourceData[0]); this._completeGatherInfo(otherChapter, sourceData[0]); this._completeGatherInfo(customChapter, sourceData[0]); } if (fieldsKey[0].table.indexOf('mem_stage_sum_') >= 0) { this._completeStageSumInfo(gclChapter, sourceData[0]); this._completeStageSumInfo(otherChapter, sourceData[0]); this._completeStageSumInfo(customChapter, sourceData[0]); } const filter = [], defaultFilter = []; for (const d of sourceData) { if (this._checkMatch(this.defaultSetting.filter, d)) defaultFilter.push(d.full_path); for (const c of customChapter) { if (c.match && this._checkMatch(c.match, d)) { gatherData(c, d); filter.push(d.full_path); } } if (!d.is_leaf || this._checkFilter(d.full_path, filter)) continue; for (const c of otherChapter) { if (c.cType === 41) { gatherData(c, d); } else if (c.cType === 31 && (!d.b_code || d.b_code === '')) { gatherData(c, d); } else if (c.cType === 11 && (d.b_code)) { gatherData(c, d); } } if (d.b_code) { const c = this._checkFilter(d.full_path, defaultFilter) ? gclChapter.find(x => {return x.cType === 21}) : this._getGclChapter(gclChapter, d, 'b_code'); if (c) { gatherData(c, d); } } } this._gatherRela(ctx, data, options.rela, gclChapter, otherChapter); const chapter = gclChapter.concat(otherChapter).concat(customChapter); this._orderCalc(ctx, chapter, calcFields); chapter.sort(function (a, b) {return a.serialNo - b.serialNo}); data[fieldsKey[0].table] = chapter.filter(function (x) { if (x.active) { for (const f of options.activeFields) { if (!ctx.helper.checkZero(x[f])) return true; } return false; } else { return (x.visible !== undefined && x.visible !== null) ? x.visible : true; } }); }, }; const join = { name: "连接两张数据表", hint: "用于处理类似于关联签约清单的情况,会改变主表的数据", intro: '用于处理类似于关联签约清单的情况,会根据关联表(sub)改变主表(main)的数据', defaultSetting: { main: 'mem_stage_bills', sub: 'deal_bills', keyFields: [ {main: 'b_code', sub: 'code', type: 'string'}, {main: 'name', sub: 'name',type: 'string'}, {main: 'unit', sub: 'unit',type: 'string'}, {main: 'unit_price', sub: 'unit_price',type: 'number'}, ], importFields: [ {main: 'ex_value1', sub: 'quantity', type: 'sum'}, {main: 'ex_value2', sub: 'total_price', type: 'sum'} ], joinType: 'outer', //'outer', 'main', 'sub', 'inner', }, fun: function (ctx, data, fields, options) { if (!options || !options.main || !options.sub || !options.keyFields || options.keyFields.length === 0) return; const main = data[options.main]; const sub = data[options.sub]; if (!main || !sub) return; const _ = ctx.helper._, result = _.cloneDeep(main); for (const r of result) { r._join_tag = 'main'; for (const i of options.importFields) { r[i.main] = null; } } for (const s of sub) { let r = result.find(function (x) { for (const k of options.keyFields) { switch (k.type) { case 'string': if (x[k.main] !== s[k.sub] && (!_.isNil(x[k.main]) || !_.isNil(s[k.sub]))) return false; break; case 'number': if (!ctx.helper.checkZero(ctx.helper.sub(x[k.main] - s[k.sub]))) return false; break; } } return true; }); if (r && r._join_tag === 'main') { r._join_tag = 'both'; } if (!r) { r = {_join_tag: 'sub'}; for (const k of options.keyFields) { r[k.main] = s[k.sub]; } result.push(r); } for (const i of options.importFields) { //r[i.main] = s[i.sub]; if (i.type === 'sum') { r[i.main] = ctx.helper.add(r[i.main], s[i.sub]); } else { r[i.main] = s[i.sub]; } } } switch (options.joinType) { case 'main': data[options.main] = _.filter(result, function (r) {return ['main', 'both'].indexOf(r._join_tag) >= 0}); break; case 'sub': data[options.main] = _.filter(result, function (r) {return ['sub', 'both'].indexOf(r._join_tag) >= 0}); break; case 'inner': data[options.main] = _.filter(result, function (r) {return r._join_tag === 'both'}); break; case 'outer': data[options.main] = result; break; } } }; const getChapterCode = { name: '获取章级编号', intro: '根据清单编号,计算章级编号,并写入指定字段,适用于未提供chapter字段的数据,例如签约清单', hint: '', defaultSetting: { table: 'mem_stage_bills', b_code: 'b_code', chapter: 'chapter', }, fun: function (ctx, data, fields, options) { if (!options || !options.table || !options.b_code || !options.chapter) return; const cData = data[options.table]; if (!cData) return; for (const d of cData) { d[options.chapter] = ctx.helper.getChapterCode(d[options.b_code]); } } }; const filter = { name: '数据过滤', intro: '根据设置过滤数据,会直接修改数据表', defaultSetting: { "table": "pay", "condition": [ {"field": "minus", "type": "bool", "value": "false", }, {"field": "ptype", "type": "num", "operate": "=", "value": 1, "rela": "and"}, {"field": "name", "type": "str", "operate": "=", "value": "扣回", "rela": "or"} ], "f_type": "or'", }, _typeFun: { bool: '_checkBoolean', str: '_checkString', num: '_checkNumber', }, _checkBoolean: function (ctx, value, condition) { switch (condition.value) { case 'true': return value ? true : false; case 'false': return value ? false : true; default: return true; } }, _checkNumber: function (ctx, value, condition) { //if (ctx.helper._.isNil(value)) value = 0; switch (condition.operate) { case 'non-zero': return !ctx.helper.checkZero(value); case '=': return !ctx.helper._.isNil(value) ? value === condition.value : false; case '>': return !ctx.helper._.isNil(value) ? value > condition.value : false; case '<': return !ctx.helper._.isNil(value) ? value < condition.value : false; case '>=': return !ctx.helper._.isNil(value) ? value >= condition.value : false; case '<=': return !ctx.helper._.isNil(value) ? value <= condition.value : false; default: return true; } }, _checkString: function (ctx, value, condition) { switch (condition.operate) { case '=': return value === condition.value; case 'part': return !ctx.helper._.isNil(value) ? value.indexOf(condition.value) >= 0 : false; case 'enum': return (!ctx.helper._.isNil(value) && condition.value instanceof Array) ? condition.value.indexOf(value) >= 0 : false; default: return true; } }, fun: function (ctx, data, fields, options) { if (!options || !options.table || !options.condition) return; const fData = data[options.table], self = this; if (!fData) return; for (const c of options.condition) { c.fun = this._typeFun[c.type]; } data[options.table] = fData.filter(function (d) { if (options.condition.length > 0) { let con = options.condition[0]; let result = self[con.fun](ctx, d[con.field], con); for (let i = 1, iLen = options.condition.length; i < iLen; i++) { con = options.condition[i]; result = (con.rela && con.rela === 'or') ? (result || self[con.fun](ctx, d[con.field], con)) : (result && self[con.fun](ctx, d[con.field], con)); } return result; } else { return true; } // const result; // for (const c of options.condition) { // if (!self[c.fun](ctx, d[c.field], c)) return false; // } // return true; }); } }; const gatherStagePay = { name: '汇总合同支付数据', intro: '根据付扣款项等,分类汇总合同支付的数据', defaultSetting: { table: 'mem_stage_pay', custom: [ {name: '本期完成计量', ptype: 4, order: 1, visible: false}, {name: '业主违约罚金', match: '业主违约罚金', order: 2}, {name: '迟付款利息', match: '迟付款利息', order: 3}, {flow: true, minus: 0, rid: ['业主违约罚金', '迟付款利息'], order: 4}, {name: '其他付款', minus: 0, rid: ['业主违约罚金', '迟付款利息'], order: 4}, {name: '合计', order: 5, order_calc: 'o1+o2+o3+o4', }, {name: '动员预付款', order: 6}, {name: '扣动员预付款', match: ['扣动员预付款', '扣回动员预付款', '扣开工预付款', '扣回开工预付款'], order: 7}, {name: '扣材料预付款', match: '扣材料预付款', order: 8}, {name: '承包人违约罚金', match: '承包人违约罚金', order: 9}, {name: '保留金', match: '保留金', order: 10}, {name: '税金', match: '税金', order: 11}, {name: '质量保证金', match: '质量保证金', order: 12}, {name: '其他扣款', minus: 1, rid: ['扣动员预付款', '扣回动员预付款', '扣开工预付款', '扣回开工预付款', '扣材料预付款', '承包人违约罚金', '保留金', '税金', '质量保证金'], order: 13}, {name: '扣款合计', minus: 1, rid: [], order: 14}, {name: '支付', ptype: 2, order: 15}, ] }, _filterFields: function (ctx, d, source) { let filterData = source; if (d.ptype) { filterData = filterData.filter(function (x) { return x.ptype === d.ptype; }); } if (d.match) { filterData = filterData.filter(function (x) { if (x.name) { if (d.match instanceof Array) { for (const m of d.match) { if (x.name.indexOf(m) >= 0) return true; } } else { return x.name.indexOf(d.match) >= 0; } } else { return false; } }); } if (!ctx.helper._.isNil(d.minus)) { filterData = filterData.filter(function (x) { return d.minus === 1 ? x.minus : !x.minus; }); } if (d.rid) { filterData = filterData.filter(function (x) { if (x.name) { for (const r of d.rid) { if (x.name.indexOf(r) >= 0) return false; } } return true; }); } return filterData; }, _gatherFields: function (ctx, d, source, calcFields) { const filterData = this._filterFields(ctx, d, source); for (const fd of filterData) { for (const prop in fd) { if (prop.indexOf('tp') >= 0) { d[prop] = ctx.helper.add(d[prop], fd[prop]); if (calcFields.indexOf(prop) === -1) calcFields.push(prop); } } } }, _completeGatherInfo(d, source) { for (const f in source) { if (/^t_[0-9]*_(id|name)$/.test(f)) { d[f] = source[f]; } } }, _completeStageSumInfo(d, source) { for (const f in source) { if (/^t_[0-9]*_order$/.test(f)) { d[f] = source[f]; } } }, fun: function (ctx, data, fields, options) { if (!options || !options.table || !options.custom) return; const gatherData = data[options.table]; const result = [], calcFields = []; for (const c of options.custom) { const cData = JSON.parse(JSON.stringify(c)); cData.subOrder = 0; if (cData.flow) { const fData = this._filterFields(ctx, cData, gatherData); for (const [i, f] of fData.entries()) { f.order = cData.order; f.subOrder = i; result.push(f); } } else { if (!cData.order_calc && cData.empty !== 1) { this._gatherFields(ctx, cData, gatherData, calcFields); } if (options.table.indexOf('mem_gather') >= 0) this._completeGatherInfo(cData, gatherData[0]); if (options.table.indexOf('mem_stage_sum_') >= 0) this._completeStageSumInfo(cData, gatherData[0]); result.push(cData); } } rdaUtils.orderCalc(ctx, result, calcFields); data[options.table] = result.filter(function (x) { return x.visible === undefined || x.visible; }); data[options.table].sort(function (a, b) { return a.order === b.order ? a.subOrder - b.subOrder : a.order - b.order; }); }, }; const union = { name: '合并数据', intro: '类似sql的union,可合并任意数据,合并结果写到"预留扩展数据-合并(mem_union_data)"中', defaultSetting: { union: [ { table: 'mem_stage_bills', fields: [ {target: 'str1', source: 'chapter'}, {target: 'str2', source: 'name'}, {target: 'tp1', source: 'total_price'}, {target: 'tp2', source: 'gather_tp'}, {target: 'tp3', source: 'pre_gather_tp'}, {target: 'tp4', source: 'end_gather_tp'}, {target: 'str3', data: '章级合计哟'}, ] }, { table: 'mem_stage_pay', fields: [ {target: 'str2', source: 'name'}, {target: 'tp2', source: 'tp'}, {target: 'tp3', source: 'pre_tp'}, {target: 'tp4', source: 'end_tp'}, {target: 'str3', data: '合同支付哟'}, ] } ] }, fun: function(ctx, data, fields, options) { if (!options || !options.union) return; const result = []; for (const u of options.union) { const unionData = data[u.table]; for (const d of unionData) { const nd = {}; for (const f of u.fields) { if (f.data) { nd[f.target] = f.data; } else if (f.source) { nd[f.target] = d[f.source]; } } result.push(nd); } } data.mem_union_data = result; } }; const addSumChapter = { name: '添加章级合计', intro: '在排序好的工程量清单列表中,根据清单所属章级,添加章级标题行、合计行', defaultSetting: { table: 'mem_stage_bills', code: 'b_code', chapter: 'chapter', stdChapter: { title: '%s章', sum: '%s章合计'}, otherChapter: { title: '其他章', sum: '其他章合计'}, sum: { name: '总合计' }, fields: ['ex_value1', 'ex_value2', 'end_gather_tp', 'pre_gather_tp', 'gather_tp'], }, fun: function (ctx, data, fields, options) { if (!options || !options.table || !options.code || !options.chapter || !options.stdChapter) return; const gclData = data[options.table]; if (!gclData || !data.tender_info) return; const chapters = ctx.helper._.uniq(ctx.helper._.map(gclData, options.chapter)); const finalSum = options.sum ? {name: options.sum.name, chapterNum: 100000, chapterPart: 1} : null; for (const chapter of chapters) { const chapterInfo = data.tender_info.chapter.find(function (x) { return x.code === chapter}); const chapterOptions = chapterInfo ? options.stdChapter : options.otherChapter; // sum const chapterGcl = gclData.filter(function (x) { return x.chapter === chapter; }); const sum = { chapter: chapter, chapterPart: 3, chapterNum: parseInt(chapter) }; for (const cg of chapterGcl) { cg.chapterPart = 2; cg.chapterNum = parseInt(cg[options.chapter]); for (const f of options.fields) { sum[f] = ctx.helper.add(sum[f], cg[f]); if (finalSum) { finalSum[f] = ctx.helper.add(finalSum[f], cg[f]); } } } if (chapterOptions.sum !== undefined) { sum.name = chapterOptions.sum.replace('%s', chapter); gclData.push(sum); } // title if (chapterOptions.title !== undefined) { const title = { chapter: chapter, chapterPart: 1, chapterNum: parseInt(chapter) }; if (chapterInfo) { title[options.code] = options.stdChapter.title.replace('%s', chapter); title.name = chapterInfo.name; } else { title.name = chapterOptions.title; } gclData.push(title); } } if (finalSum) { gclData.push(finalSum); } gclData.sort(function (a, b) { if (a.chapter !== b.chapter) { return a.chapterNum - b.chapterNum; } else if (a.chapterPart !== b.chapterPart) { return a.chapterPart - b.chapterPart; } else { return ctx.helper.compareCode(a[options.code], b[options.code]); } }); } }; const auditSelect = { name: '审批人选择', hint: '需搭配用户交互--审批人选择一起使用', defaultSetting: { table: ['mem_stage_bills_compare', 'mem_stage_pos_compare', 'mem_stage_pay'], }, _stageBillsCompare(data, order) { const fields = []; for (const [i, o] of order.entries()) { const sPrefix = 'r' + o + '_'; const tPrefix = 'as' + i + '_'; fields.push({source: sPrefix + 'contract_qty', target: tPrefix + 'contract_qty'}); fields.push({source: sPrefix + 'contract_tp', target: tPrefix + 'contract_tp'}); fields.push({source: sPrefix + 'qc_qty', target: tPrefix + 'qc_qty'}); fields.push({source: sPrefix + 'qc_tp', target: tPrefix + 'qc_tp'}); fields.push({source: sPrefix + 'gather_qty', target: tPrefix + 'gather_qty'}); fields.push({source: sPrefix + 'gather_tp', target: tPrefix + 'gather_tp'}); } for (const d of data) { for (const f of fields) { d[f.target] = d[f.source]; } } }, _stagePosCompare(data, order) { const fields = []; for (const [i, o] of order.entries()) { const sPrefix = 'r' + o + '_'; const tPrefix = 'as' + i + '_'; fields.push({source: sPrefix + 'contract_qty', target: tPrefix + 'contract_qty'}); fields.push({source: sPrefix + 'qc_qty', target: tPrefix + 'qc_qty'}); fields.push({source: sPrefix + 'gather_qty', target: tPrefix + 'gather_qty'}); } for (const d of data) { for (const f of fields) { d[f.target] = d[f.source]; } } }, _stagePay(data, order) { const fields = []; for (const [i, o] of order.entries()) { const sPrefix = 'r' + o + '_'; const tPrefix = 'as' + i + '_'; fields.push({source: sPrefix + 'tp', target: tPrefix + 'tp'}); } for (const d of data) { for (const f of fields) { d[f.target] = d[f.source]; } } }, fun: function (ctx, data, fieldsKey, options, csRela) { if (!ctx.tender || !ctx.stage) return; if (!csRela.tplDefine) return; const asDefine = csRela.tplDefine.audit_select; if (!asDefine || !asDefine.enable || !asDefine.setting || asDefine.setting === '') return; const asCustom = csRela.cDefine ? csRela.cDefine.audit_select : null; const order = []; if (asCustom) { for (const asc of asCustom) { order.push(asc.order); } } for (const t of options.table) { switch (t) { case 'mem_stage_bills_compare': this._stageBillsCompare(data[t], order); break; case 'mem_stage_pos_compare': this._stagePosCompare(data[t], order); break; case 'mem_stage_pay': this._stagePay(data[t], order); break; } } } }; const datetimeFormat = { name: '日期格式化', hint: '参见帮助', defaultSetting: { tables: [ {name: 'mem_stage_bonus', fields: [ {field: 'real_time', formatter: 'yyyy-MM-DD'}, {field: 'create_time', formatter: 'yyyy-MM-DD'} ]}, ] }, fun: function (ctx, data, fieldsKey, options, csRela) { if (!options.tables) return; const tables = options.tables instanceof Array ? tables : [options.tables]; if (tables.length === 0) return; for (const table of tables) { const formatData = data[table.name]; for (const fd of formatData) { for (const f of table.fields) { fd[f.field] = moment(fd[f.field]).format(f.formatter); } } } } }; const gatherSelectConverse = { name: '交叉汇总数据', hint: '需搭配 用户交互--汇总标段选择 一起使用', defaultSetting: { table: ['mem_gather_stage_bills'], }, _commonConverse: function (helper, data, count) { const result = []; const reg = new RegExp('^t_[0-9]+_'); for (let i = 0; i < count; i++) { const curReg = new RegExp('^t_' + i + '_'); for (const [i, d] of data.entries()) { const nd = { cross_index: i + 1}; for (const prop in d) { if (reg.test(prop)) { if (curReg.test(prop)) { nd[prop.replace(curReg, 't_')] = d[prop]; } } else { nd[prop] = d[prop]; } } result.push(nd); } } // for (const d of data) { // for (let i = 0; i < count; i++) { // const curReg = new RegExp('^t_' + i + '_'); // const nd = {}; // for (const prop in d) { // if (reg.test(prop)) { // if (curReg.test(prop)) { // nd[prop.replace(curReg, 't_')] = d[prop]; // } // } else { // nd[prop] = d[prop]; // } // } // result.push(nd); // } // } return result; }, fun: function (ctx, data, fieldsKey, options, csRela) { if (!csRela.tplDefine) return; const gsDefine = csRela.tplDefine.gather_select; if (!gsDefine || !gsDefine.enable || !gsDefine.setting || gsDefine.setting === '') return; const gsCustom = csRela.cDefine ? csRela.cDefine.gather_select : null; const count = gsDefine.setting.special ? gsCustom.tenders.length - gsDefine.setting.special.length : gsCustom.tenders.length; for (const t of options.table) { switch (t) { case 'mem_gather_stage_bills': case 'mem_gather_stage_pay': case 'mem_gather_deal_bills': case 'mem_union_data': data[t] = this._commonConverse(ctx.helper, data[t], count); break; } } } }; const sortPos = { name: '计量单元排序', hint: '', defaultSetting: { bills: 'mem_stage_bills', pos: 'mem_stage_pos', }, fun: function (ctx, data, fieldsKey, options, csRela) { if (!options || !options.bills || !options.pos) return; const bills = data[options.bills]; const pos = data[options.pos]; if (!bills || bills.length === 0 || !pos || pos.length === 0) return; const billsIndex = {}; const findBillsIndex = function (billsId) { if (!billsIndex[billsId]) { billsIndex[billsId] = ctx.helper._.findIndex(bills, function (x) { return x.id === billsId; }); } return billsIndex[billsId]; }; pos.sort(function (x, y) { const xIndex = findBillsIndex(x.lid) * 1000 + x.porder; const yIndex = findBillsIndex(y.lid) * 1000 + y.porder; return xIndex - yIndex; }); } }; const unionPos = { name: '拼接计量单元', hint: '', defaultSetting: { bills: 'mem_stage_bills', pos: 'mem_stage_pos', }, fun: function (ctx, data, fieldsKey, options, csRela) { if (!options || !options.bills || !options.pos) return; const bills = data[options.bills]; const pos = data[options.pos]; if (!bills || bills.length === 0 || !pos || pos.length === 0) return; const posIndex = new PosIndex({ id: 'id', masterId: 'lid', order: 'porder', }); posIndex.loadDatas(pos); const unionData = []; for (const b of bills) { unionData.push(b); const posRange = posIndex.getLedgerPos(b.id); b.posCount = posRange ? posRange.length : 0; if (!posRange || posRange.length === 0) continue; for (const p of posRange) { unionData.push(p); } } data[options.bills] = unionData; } }; const splitXmjCode = { name: '拆分项目节编号', hint: '', defaultSetting: { table: 'mem_stage_bills', type: '11', // '18', '18-0-1', '18-1', '18-1-1' code: 'code', targetField: ['xiang', 'mu', 'jie', 'ximu'], }, // 11标准 _split11Std: function (data, options, helper) { for (const d of data) { if (!d[options.code]) continue; const splitCodes = d[options.code].split('-'); if (d.level === 2) { const field = options.targetField[0]; if (field && splitCodes.length > 1) { d[field] = helper.transFormToChinese(splitCodes[1]); } } else if (d.level === 3) { const field = options.targetField[1]; if (field && splitCodes.length > 2) { d[field] = splitCodes[2]; } } else if (d.level === 4) { const field = options.targetField[2]; if (field && splitCodes.length > 3) { d[field] = splitCodes[3]; } } else if (d.level > 4) { const field = options.targetField[3]; if (field && splitCodes.length > 4) { d[field] = splitCodes.reduce(function (s, v, i) { return i >= 4 ? (s ? s + '-' + v : v + '') : ''}); } } } }, _getParentByLevel: function (data, node, level) { if (node.full_path) { const pid = node.full_path.split('-')[level-1]; return data.find(function (x) { return x.level === level && x.ledger_id == pid; }); } else { return null; } }, // 18标准 _split18Std: function (data, options, helper) { const types = options.type.split('-'); const isSplitSub = types[1] && types[1] === '1' ? true : false; const isKeepGD = types[2] && types[2] === '1' ? true : false; for (const d of data) { if (!d[options.code]) continue; let target = ''; if (d.level === 2) { target = options.targetField[0] ? options.targetField[0]: ''; } else if (d.level === 3) { target = options.targetField[1] ? options.targetField[1]: ''; } else if (d.level === 4) { target = options.targetField[2] ? options.targetField[2]: ''; } else if (d.level > 4) { target = options.targetField[3] ? options.targetField[3]: ''; } if (!target) continue; if (helper.check18MainCode(d[options.code])) { const parent = helper._.find(data, {ledger_id: d.ledger_pid}); const splitParent = !parent || parent.level < 5 ? parent : this._getParentByLevel(data, d, 4); d[target] = !splitParent ? d[options.code] : d[options.code].replace(splitParent[options.code], ''); } else if (helper.check18SubCode(d[options.code]) && isSplitSub) { const parent = helper._.find(data, {ledger_id: d.ledger_pid}); const splitParent = !parent || parent.level < 5 ? parent : this._getParentByLevel(data, d, 4); d[target] = !splitParent || helper.check18MainCode(splitParent.code) ? d[options.code] : d[options.code].replace(splitParent[options.code], ''); } else { d[target] = d[options.code]; } if (!isKeepGD) d[target] = d[target].replace(/^G[D]?/, ''); } }, // 根据层次填入 _splitDefault: function (data, options) { for (const d of data) { if (!d[options.code]) continue; if (d.level === 2) { if (options.targetField[0]) d[options.targetField[0]] = d[options.code]; } else if (d.level === 3) { if (options.targetField[1]) d[options.targetField[1]] = d[options.code]; } else if (d.level === 4) { if (options.targetField[2]) d[options.targetField[2]] = d[options.code]; } else if (d.level > 4) { if (options.targetField[3]) d[options.targetField[3]] = d[options.code]; } } }, fun: function (ctx, data, fieldsKey, options, csRela) { if (!options || !options.table || !options.code || !options.targetField) return; if (!data[options.table]) return; if (options.type === '11') { this._split11Std(data[options.table], options, ctx.helper); } else if (/^18(-[0,1]){0,2}$/.test(options.type)) { this._split18Std(data[options.table], options, ctx.helper); } else { this._splitDefault(data[options.table], options); } } }; const stageSelectConverse = { name: '交叉多期汇总数据', hint: '需搭配 用户交互--单标段多期汇总选择 一起使用', defaultSetting: { table: ['mem_stage_sum_bills'], }, _commonConverse: function (helper, data, stages) { const result = []; const reg = new RegExp('^s_[0-9]+_'); for (const s of stages) { const curReg = new RegExp('^s_' + s + '_'); for (const [i, d] of data.entries()) { const nd = { cross_index: i + 1}; for (const prop in d) { if (reg.test(prop)) { if (curReg.test(prop)) { nd[prop.replace(curReg, 's_')] = d[prop]; } } else { nd[prop] = d[prop]; } } result.push(nd); } } return result; }, fun: function (ctx, data, fieldsKey, options, csRela) { if (!csRela.tplDefine) return; const gsDefine = csRela.tplDefine.stage_select; if (!gsDefine || !gsDefine.enable || !gsDefine.setting || gsDefine.setting === '') return; const gsCustom = csRela.cDefine ? csRela.cDefine.stage_select : null; if (gsCustom) { console.log(gsCustom[0]); console.log(data.mem_stage_sum_bills[0]); for (const t of options.table) { switch (t) { case 'mem_stage_sum_bills': case 'mem_stage_sum_pay': case 'mem_union_data': data[t] = this._commonConverse(ctx.helper, data[t], gsCustom.stages); break; } } } else { console.log('gsCustom is null!'); } } }; const analysisObj = { changeSort, gatherGcl, sortGcl, gatherChapter, join, getChapterCode, filter, union, gatherStagePay, addSumChapter, auditSelect, datetimeFormat, gatherSelectConverse, stageSelectConverse, sortPos, unionPos, splitXmjCode, }; const analysisDefine = (function (obj) { const result = []; for (const o in obj) { const analysis = obj[o]; if (analysis.name && analysis.fun) { result.push({ name: analysis.name, key: o, hint: analysis.hint, intro: analysis.intro, url: '/help?m=report&s=analysis&d=' + o, }) } } return result; })(analysisObj); module.exports = { analysisObj, analysisDefine };