|
|
@@ -155,6 +155,30 @@ const ImportXML = (() => {
|
|
|
'"': 'escape{quot}',
|
|
|
''': 'escape{apos}'
|
|
|
};
|
|
|
+ // 第x册定额名称包含字符与计算程序ID的匹配映射
|
|
|
+ const NameProgramMapping = {
|
|
|
+ '机械设备': 25,
|
|
|
+ '热力设备': 26,
|
|
|
+ '静置设备与工艺金属结构制作': 27,
|
|
|
+ '电气设备': 28,
|
|
|
+ '建筑智能化': 29,
|
|
|
+ '自动化控制仪表': 30,
|
|
|
+ '通风空调': 31,
|
|
|
+ '工业管道': 32,
|
|
|
+ '消防': 33,
|
|
|
+ '给排水、采暖、燃气': 34,
|
|
|
+ '刷油、防腐蚀、绝热': 35
|
|
|
+ };
|
|
|
+ // 获取定额取费专业,根据名称匹配有无固定映射,若没有则取单位工程取费专业(后台配置)
|
|
|
+ function getProgramID(name, mapping, projectEngineering) {
|
|
|
+ for (let mapName in mapping) {
|
|
|
+ const reg = new RegExp(`第.{1,2}册\\s*${mapName}(安装)?工程`);
|
|
|
+ if (name.match(reg)) {
|
|
|
+ return mapping[mapName];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return projectEngineering;
|
|
|
+ }
|
|
|
// 避免字符实体进行转义。原文本中含有xml字符实体,转换为其他字符。
|
|
|
function escapeXMLEntity(str) {
|
|
|
for (let [key, value] of Object.entries(XML_ENTITY)) {
|
|
|
@@ -382,7 +406,8 @@ const ImportXML = (() => {
|
|
|
let obj = {
|
|
|
rowCode: getValue(itemSrc, ['_行代号']),
|
|
|
name: getValue(itemSrc, ['_项目名称']),
|
|
|
- calcBase: getValue(itemSrc, ['_计算基础表达式'])
|
|
|
+ calcBase: getValue(itemSrc, ['_计算基础表达式']),
|
|
|
+ feeType: getValue(itemSrc, ['_费用类别'])
|
|
|
};
|
|
|
if (importFileKind === FileKind.tender) {
|
|
|
// obj.calcBase = getValue(itemSrc, ['_计算基础表达式']);
|
|
|
@@ -853,19 +878,16 @@ const ImportXML = (() => {
|
|
|
}
|
|
|
//根据费用类别、配比类比获取人材机类别(后端匹配不到标准人材机的时候用)
|
|
|
function getGljTypeData(feeType, ratioType, name) {
|
|
|
- let map = {
|
|
|
- '-': {type: 201, shortName: '材'},
|
|
|
- '1-': {type: 1, shortName: '人'},
|
|
|
- '2-': {type: 201, shortName: '材'},
|
|
|
- '2-1': {type: 202, shortName: '砼'},
|
|
|
- '2-2': {type: 205, shortName: '商砼'},
|
|
|
- '2-3': {type: 203, shortName: '浆'},
|
|
|
- '2-4': {type: 206, shortName: '商浆'},
|
|
|
- '2-5': {type: 204, shortName: '配比'},
|
|
|
- '3-': {type: 301, shortName: '机'},
|
|
|
- '4-': {type: 4, shortName: '主'},
|
|
|
+ // 因为这份文件不太标准,各家也不统一,没有一个靠谱的方法能准备获取人材机类型,所以有些地方需要特殊处理
|
|
|
+ // 有的人材机类型根据人材机名称特殊获取
|
|
|
+ const specialMap = {
|
|
|
+ '定额管理费': {type: 6, shortName: '管'}
|
|
|
};
|
|
|
- let nameMap = {
|
|
|
+ if (specialMap[name]) {
|
|
|
+ return specialMap[name];
|
|
|
+ }
|
|
|
+ // 一些需要特殊处理的动力材料
|
|
|
+ const powerNameMap = {
|
|
|
'柴油': {type: 305, shortName: '动'},
|
|
|
'柴油(机械用)': {type: 305, shortName: '动'},
|
|
|
'柴油(机械用)': {type: 305, shortName: '动'},
|
|
|
@@ -877,12 +899,26 @@ const ImportXML = (() => {
|
|
|
'电(机械用)': {type: 305, shortName: '动'},
|
|
|
'机上人工': {type: 303, shortName: '机人'},
|
|
|
};
|
|
|
- if (feeType === '3' && nameMap[name]) {
|
|
|
- return nameMap[name] || null;
|
|
|
+ // 人、机不需要匹配配比类别
|
|
|
+ if (feeType === '1') {
|
|
|
+ return {type: 1, shortName: '人'};
|
|
|
+ } else if (feeType === '3') {
|
|
|
+ // 特殊处理动力材料
|
|
|
+ return powerNameMap[name] || {type: 301, shortName: '机'};
|
|
|
}
|
|
|
+ const map = {
|
|
|
+ '-': {type: 201, shortName: '材'},
|
|
|
+ '2-': {type: 201, shortName: '材'},
|
|
|
+ '2-1': {type: 202, shortName: '砼'},
|
|
|
+ '2-2': {type: 205, shortName: '商砼'},
|
|
|
+ '2-3': {type: 203, shortName: '浆'},
|
|
|
+ '2-4': {type: 206, shortName: '商浆'},
|
|
|
+ '2-5': {type: 204, shortName: '配比'},
|
|
|
+ '4-': {type: 4, shortName: '主'},
|
|
|
+ };
|
|
|
return map[`${feeType}-${ratioType}`] || null;
|
|
|
}
|
|
|
- //主要材料类别-三材类别映射、、
|
|
|
+ //主要材料类别-三材类别映射
|
|
|
const MaterialMap = {
|
|
|
'100': 1, //钢材钢筋认为钢材,因为文件没细分
|
|
|
'200': 4, //水泥
|
|
|
@@ -1029,26 +1065,29 @@ const ImportXML = (() => {
|
|
|
);
|
|
|
return isCoe ? AdjustType.coe : AdjustType.info;
|
|
|
}
|
|
|
-
|
|
|
- let toMatches = [
|
|
|
- {reg: /分部分项/, flag: fixedFlag.SUB_ENGINERRING},
|
|
|
- {reg: /^措施项目/, flag: fixedFlag.MEASURE},
|
|
|
- {reg: /技术措施/, flag: fixedFlag.CONSTRUCTION_TECH},
|
|
|
- {reg: /组织措施/, flag: fixedFlag.CONSTRUCTION_ORGANIZATION},
|
|
|
- {reg: /组织措施/, flag: fixedFlag.CONSTRUCTION_ORGANIZATION},
|
|
|
- {reg: /安全文明/, flag: fixedFlag.SAFETY_CONSTRUCTION},
|
|
|
- {reg: /其他项目/, flag: fixedFlag.OTHER},
|
|
|
- {reg: /暂列金额/, flag: fixedFlag.PROVISIONAL},
|
|
|
- {reg: /暂估价/, flag: fixedFlag.ESTIMATE},
|
|
|
- {reg: /计日工/, flag: fixedFlag.DAYWORK},
|
|
|
- {reg: /总承包服务/, flag: fixedFlag.TURN_KEY_CONTRACT},
|
|
|
- {reg: /索赔(?:及|与|和)现场签证/, flag: fixedFlag.TURN_KEY_CONTRACT},
|
|
|
- {reg: /规费/, flag: fixedFlag.CHARGE},
|
|
|
- {reg: /税金/, flag: fixedFlag.TAX},
|
|
|
- {reg: /增值税/, flag: fixedFlag.ADDED_VALUE_TAX},
|
|
|
- {reg: /^附加税/, flag: fixedFlag.ADDITIONAL_TAX},
|
|
|
- {reg: /环境保护税/, flag: fixedFlag.ENVIRONMENTAL_PROTECTION_TAX},
|
|
|
- {reg: /(?:合\s*价)|(?:工程造价)/, flag: fixedFlag.ENGINEERINGCOST},
|
|
|
+
|
|
|
+ // 个软件公司确定了对应关系的其他项目、税金,使用费用类别进行匹配,其他按照名称关键字匹配
|
|
|
+ const matchRegs = [
|
|
|
+ {field: 'feeType', reg: /^1300$/, flag: fixedFlag.OTHER},
|
|
|
+ {field: 'feeType', reg: /^900$/, flag: fixedFlag.TAX},
|
|
|
+ {field: 'name', reg: /分部分项/, flag: fixedFlag.SUB_ENGINERRING},
|
|
|
+ {field: 'name', reg: /^措施项目/, flag: fixedFlag.MEASURE},
|
|
|
+ {field: 'name', reg: /技术措施/, flag: fixedFlag.CONSTRUCTION_TECH},
|
|
|
+ {field: 'name', reg: /组织措施/, flag: fixedFlag.CONSTRUCTION_ORGANIZATION},
|
|
|
+ {field: 'name', reg: /组织措施/, flag: fixedFlag.CONSTRUCTION_ORGANIZATION},
|
|
|
+ {field: 'name', reg: /安全文明/, flag: fixedFlag.SAFETY_CONSTRUCTION},
|
|
|
+ // {reg: /其他项目/, flag: fixedFlag.OTHER},
|
|
|
+ {field: 'name', reg: /暂列金额/, flag: fixedFlag.PROVISIONAL},
|
|
|
+ {field: 'name', reg: /暂估价/, flag: fixedFlag.ESTIMATE},
|
|
|
+ {field: 'name', reg: /计日工/, flag: fixedFlag.DAYWORK},
|
|
|
+ {field: 'name', reg: /总承包服务/, flag: fixedFlag.TURN_KEY_CONTRACT},
|
|
|
+ {field: 'name', reg: /索赔(?:及|与|和)现场签证/, flag: fixedFlag.TURN_KEY_CONTRACT},
|
|
|
+ {field: 'name', reg: /规费/, flag: fixedFlag.CHARGE},
|
|
|
+ // {reg: /税金/, flag: fixedFlag.TAX},
|
|
|
+ {field: 'name', reg: /增值税/, flag: fixedFlag.ADDED_VALUE_TAX},
|
|
|
+ {field: 'name', reg: /^附加税/, flag: fixedFlag.ADDITIONAL_TAX},
|
|
|
+ {field: 'name', reg: /环境保护税/, flag: fixedFlag.ENVIRONMENTAL_PROTECTION_TAX},
|
|
|
+ {field: 'name', reg: /(?:合\s*价)|(?:工程造价)/, flag: fixedFlag.ENGINEERINGCOST}
|
|
|
];
|
|
|
//处理单位工程费用汇总的清单,这一部分没有靠谱的规则,特殊处理并添加相关清单
|
|
|
function setupFeeSummary(feeSummary, needfulTemplate) {
|
|
|
@@ -1084,8 +1123,8 @@ const ImportXML = (() => {
|
|
|
} else {
|
|
|
let isMatched = false;
|
|
|
//匹配固定项
|
|
|
- for (let match of toMatches) {
|
|
|
- if (!match.reg.test(feeBills.name)) {
|
|
|
+ for (let match of matchRegs) {
|
|
|
+ if (!match.reg.test(feeBills[match.field])) {
|
|
|
continue;
|
|
|
}
|
|
|
let findBills = needfulTemplate.find(nData => getFlag(nData) === match.flag);
|
|
|
@@ -1352,7 +1391,7 @@ const ImportXML = (() => {
|
|
|
JSCS_JXF: '{技术措施项目定额施工机具使用费}',
|
|
|
JSCS_ZCF: '{技术措施项目主材费}',
|
|
|
JSCS_GR: '{技术措施项目人工工日}',
|
|
|
- JZMJ: '{建筑面积}',
|
|
|
+ //JZMJ: '{建筑面积}',
|
|
|
RCJJC: '{人材机价差}',
|
|
|
RGJC: '{人工价差}',
|
|
|
CLJC: '{材料价差}',
|
|
|
@@ -1374,10 +1413,33 @@ const ImportXML = (() => {
|
|
|
SJHJ: '{税金}',
|
|
|
SQGCZJ: '{税前工程造价}'
|
|
|
};
|
|
|
+ // 检查固定清单引用的基数造成自身循环,比如分部分项部分引用了FBFXHJ
|
|
|
+ // 有文件的单位工程费汇总中,含有技术措施项目费清单,含有基数,且无子项。
|
|
|
+ function isCalcBaseCycle(bills) {
|
|
|
+ const flag = getFlag(bills);
|
|
|
+ if (!flag) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 跟固定清单直接相关联的基数,若相关固定清单引用了其下的基数,则造成自身循环
|
|
|
+ const CycleMap = {
|
|
|
+ [fixedFlag.SUB_ENGINERRING]: ['FBFXHJ', 'RGF', 'CLF', 'JXF', 'ZCF', 'GR'],
|
|
|
+ [fixedFlag.MEASURE]: ['CSXMHJ'],
|
|
|
+ [fixedFlag.CONSTRUCTION_ORGANIZATION]: ['ZZCSF'],
|
|
|
+ [fixedFlag.CONSTRUCTION_TECH]: ['JSCSF', 'JSCS_RGF', 'JSCS_CLF', 'JSCS_JXF', 'JSCS_ZCF', 'JSCS_GR'],
|
|
|
+ [fixedFlag.CHARGE]: ['GF'],
|
|
|
+ [fixedFlag.TAX]: ['SJ', 'SJHJ']
|
|
|
+ };
|
|
|
+ const match = CycleMap[flag];
|
|
|
+ if (!match) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return match.some(item => bills.calcBase.match(new RegExp(`\\b${item}\\b`)));
|
|
|
+ }
|
|
|
//转换计算基数
|
|
|
//1.有子项数据,则清空基数
|
|
|
- //2.行代号引用转换为ID引用
|
|
|
- //3.对应字典代号转换,对应字典里找不到则设置成金额
|
|
|
+ //2.引用的基数造成自身循环,比如分部分项部分引用了FBFXHJ
|
|
|
+ //3.行代号引用转换为ID引用
|
|
|
+ //4.对应字典代号转换,对应字典里找不到则设置成金额
|
|
|
function transformCalcBase(billsData) {
|
|
|
//行代号 - ID映射
|
|
|
let rowCodeMap = {};
|
|
|
@@ -1400,6 +1462,12 @@ const ImportXML = (() => {
|
|
|
if (typeof bills.calcBase !== 'string') {
|
|
|
bills.calcBase = String(bills.calcBase);
|
|
|
}
|
|
|
+ // 引用的基数造成自循环,清空基数
|
|
|
+ const isCycle = isCalcBaseCycle(bills);
|
|
|
+ if (isCycle) {
|
|
|
+ bills.calcBase = '';
|
|
|
+ continue;
|
|
|
+ }
|
|
|
//提取基数
|
|
|
bills.calcBase = bills.calcBase.replace(/\s/g, '');
|
|
|
let bases = bills.calcBase.split(/[\+\-\*\/]/g);
|
|
|
@@ -1526,6 +1594,8 @@ const ImportXML = (() => {
|
|
|
}
|
|
|
//提取详细数据
|
|
|
let needfulTemplate = _.cloneDeep(templateMapping[curTender.property.templateLibID]);
|
|
|
+ // 重设模板树结构数据
|
|
|
+ BILLS_UTIL.resetTreeData(needfulTemplate, uuid.v1);
|
|
|
let postTenderData = await transformTender(curTender, IDPlaceholder, needfulTemplate);
|
|
|
postTenderData.tender.property.rootProjectID = postConstructData.ID;
|
|
|
postEngData.tenders.push(postTenderData);
|
|
|
@@ -1589,6 +1659,13 @@ const ImportXML = (() => {
|
|
|
// 项目人材机customCode、code-原始数据映射,有的导入数据中,有一部分关联自生成材料号CX,有的关联代码...
|
|
|
// 因此customCode、code都需要跟原始数据映射,通过customCode取不到数据的时候,通过orgCode获取
|
|
|
let projectGLJMap = {};
|
|
|
+ // 代码映射
|
|
|
+ let customCodeMap = {};
|
|
|
+ // 原始代码映射
|
|
|
+ let originCodeMap = {};
|
|
|
+ function getGLJByMap(code) {
|
|
|
+ return customCodeMap[code] || originCodeMap[code] || null;
|
|
|
+ }
|
|
|
//投标文件才需要导入定额等数据
|
|
|
if (importFileKind === FileKind.tender) {
|
|
|
tenderData.gljSummary.forEach(pGLJ => {
|
|
|
@@ -1598,14 +1675,17 @@ const ImportXML = (() => {
|
|
|
pGLJ.type = pGLJ.type || 201;
|
|
|
pGLJ.shortName = pGLJ.shortName || '材';
|
|
|
//gljCodeMap[pGLJ.code] = pGLJ;
|
|
|
- projectGLJMap[pGLJ.customCode] = pGLJ;
|
|
|
- projectGLJMap[pGLJ.code] = pGLJ;
|
|
|
+ //projectGLJMap[pGLJ.customCode] = pGLJ;
|
|
|
+ //projectGLJMap[pGLJ.code] = pGLJ;
|
|
|
+ customCodeMap[pGLJ.customCode] = pGLJ;
|
|
|
+ originCodeMap[pGLJ.code] = pGLJ;
|
|
|
});
|
|
|
//处理项目人材机数据
|
|
|
tenderData.gljSummary.forEach(pGLJ => {
|
|
|
//组成物数据
|
|
|
pGLJ.ratios.forEach(ratio => {
|
|
|
- let matchData = projectGLJMap[ratio.code];
|
|
|
+ //let matchData = projectGLJMap[ratio.code];
|
|
|
+ let matchData = getGLJByMap(ratio.code);
|
|
|
ratio.code = matchData.code;
|
|
|
ratio.projectGLJID = pGLJ.id; //后端查找标准数据后,方便更新组成物数据
|
|
|
ratio.id = IDPlaceholder.ratio++;
|
|
|
@@ -1648,7 +1728,8 @@ const ImportXML = (() => {
|
|
|
if (typeof data.seq === 'undefined') {
|
|
|
data.seq = data.code;
|
|
|
}
|
|
|
- const projectGLJ = projectGLJMap[data.code];
|
|
|
+ //const projectGLJ = projectGLJMap[data.code];
|
|
|
+ const projectGLJ = getGLJByMap(data.code);
|
|
|
if (projectGLJ) {
|
|
|
projectGLJ[relatedType] = 1;
|
|
|
data.is_related = 1;
|
|
|
@@ -1695,11 +1776,7 @@ const ImportXML = (() => {
|
|
|
billsHasRations.forEach(bills => {
|
|
|
//处理定额
|
|
|
bills.rations.forEach(ration => {
|
|
|
- // 定额的取费专业在定额库默认为空的话,取定额取费专业
|
|
|
- // 如果是量价、与定额同级的人材机,则取单位工程取费专业
|
|
|
- ration.programID = ration.type === RationType.ration
|
|
|
- ? tenderData.property.engineering
|
|
|
- : tenderData.property.projectEngineering;
|
|
|
+ ration.programID = getProgramID(ration.name, NameProgramMapping, tenderData.property.projectEngineering);
|
|
|
ration.ID = uuid.v1();
|
|
|
ration.projectID = tenderData.ID;
|
|
|
ration.billsItemID = bills.ID;
|
|
|
@@ -1710,21 +1787,10 @@ const ImportXML = (() => {
|
|
|
let tempV = ration.quantity / bills.quantity;
|
|
|
ration.contain = isFinite(tempV) ? scMathUtil.roundForObj(tempV, 6) : '0';
|
|
|
}
|
|
|
- //工程量表达式:工程量 * 单位前的量
|
|
|
- /*if (!ration.unit) {
|
|
|
- ration.quantityEXP = ration.quantity;
|
|
|
- } else {
|
|
|
- let unitNum = ration.unit.match(/^\d+/);
|
|
|
- //工程量小数位数
|
|
|
- let qDecimal = ration.quantity.match(/\.\d+/);
|
|
|
- ration.quantityEXP = unitNum
|
|
|
- ? scMathUtil.roundForObj(ration.quantity * unitNum[0], qDecimal ? qDecimal[0] - 1 : 0)
|
|
|
- : ration.quantity;
|
|
|
- }*/
|
|
|
- // 问题
|
|
|
//处理定额人材机,添加需要的数据
|
|
|
ration.rationGljs.forEach(rGLJ => {
|
|
|
- let matchGLJ = projectGLJMap[rGLJ.code];
|
|
|
+ //let matchGLJ = projectGLJMap[rGLJ.code];
|
|
|
+ let matchGLJ = getGLJByMap(rGLJ.code);
|
|
|
if (matchGLJ) {
|
|
|
rGLJ.projectGLJID = matchGLJ.id;
|
|
|
rGLJ.code = matchGLJ.code;
|