bills_controller.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. /**
  2. * Created by jimiz on 2017/4/7.
  3. */
  4. let mongoose = require('mongoose');
  5. var billsData = require('../models/bills');
  6. let ration_model = require('../models/ration');
  7. let ProjectsData = require('../../pm/models/project_model').project;
  8. let logger = require("../../../logs/log_helper").logger;
  9. let quantity_detail = require("../facade/quantity_detail_facade");
  10. let bill_facade = require("../facade/bill_facade");
  11. let ration_glj = mongoose.model('ration_glj');
  12. let ration_coe = mongoose.model('ration_coe');
  13. let rationInstallationModel = mongoose.model('ration_installation');
  14. let stdBillsModel = mongoose.model('std_bills_lib_bills');
  15. let stdBillJobsModel = mongoose.model('std_bills_lib_jobContent');
  16. let stdBillCharacterModel = mongoose.model('std_bills_lib_itemCharacter');
  17. import fixedFlag from '../../common/const/bills_fixed';
  18. const uuidV1 = require('uuid/v1');
  19. const billType ={
  20. DXFY:1,//大项费用
  21. FB:2,//分部
  22. FX:3,//分项
  23. BILL:4,//清单
  24. BX:5//补项
  25. };
  26. //上传的09表、广联达表
  27. const uploadType = {lj: 'lj', gld: 'gld'};
  28. // 上传控件
  29. const multiparty = require("multiparty");
  30. const fs = require("fs");
  31. // excel解析
  32. const excel = require("node-xlsx");
  33. //统一回调函数
  34. var callback = function(req, res, err, message, data){
  35. res.json({error: err, message: message, data: data});
  36. };
  37. module.exports = {
  38. getData: function(req, res){
  39. var data = JSON.parse(req.body.data);
  40. billsData.getData(data.projectId, function(err, message, billsList){
  41. if (err === 0) {
  42. callback(req, res, err, message, billsList);
  43. } else {
  44. callback(req, res, err, message, null);
  45. }
  46. });
  47. },
  48. getItemTemplate: function(req, res){
  49. //var data = JSON.parse(req.body.data);
  50. billsData.getItemTemplate(function(err, message, billsItem){
  51. if (billsItem) {
  52. callback(req, res, err, message, billsItem);
  53. } else {
  54. callback(req, res, err, message, null);
  55. }
  56. });
  57. },
  58. allocIDs: function(req, res){
  59. billsData.allocIDs(function(err, message, data){
  60. if (err) {
  61. callback(req, res, err, message, data);
  62. } else {
  63. callback(req, res, err, message, null);
  64. }
  65. });
  66. },
  67. //zhong 2017-9-1
  68. updateCharacterContent: function (req, res) {
  69. let data = JSON.parse(req.body.data);
  70. let findSet = data.findSet,
  71. updateObj = data.updateObj,
  72. txtObj = data.txtObj;
  73. billsData.updateCharacterContent(findSet, updateObj, txtObj, function (err, message) {
  74. callback(req, res, err, message, null);
  75. });
  76. },
  77. updateBill: async function(request, response) {
  78. const data = JSON.parse(request.body.data);
  79. const findSet = data.findSet;
  80. const updateData = data.updateData;
  81. let settingData = {};
  82. // 筛选出要保存在项目属性的设置
  83. for (const index in updateData) {
  84. if (updateData[index].field === 'addRule') {
  85. settingData = updateData[index].value;
  86. delete updateData[index];
  87. }
  88. }
  89. /* // 更新项目属性
  90. const propertyUpdateData = {
  91. property: 'addRule',
  92. data: settingData
  93. };*/
  94. //const projectResult = await ProjectsData.updateProjectProperty(findSet.projectID, propertyUpdateData);
  95. const result = await billsData.updateBill(findSet, updateData);
  96. const message = !result ? '修改失败' : '修改成功';
  97. const err = !result ? 1 : 0;
  98. callback(request, response, err, message, null);
  99. },
  100. singleDelete:async function(req, res){
  101. let result={
  102. error:0
  103. }
  104. try {
  105. let data = req.body.data;
  106. data = JSON.parse(data);
  107. let tasks = generateSingleDeleteTasks(data);
  108. let resultData= await billsData.model.bulkWrite(tasks);
  109. //删除工程量明细
  110. await quantity_detail.deleteByQuery({projectID: data.projectID, billID: data.ID}) ;
  111. result.data=resultData;
  112. }catch (err){
  113. logger.err(err);
  114. result.error=1;
  115. result.message = err.message;
  116. }
  117. res.json(result);
  118. },
  119. multiDelete:async function(req, res){
  120. let result={
  121. error:0
  122. };
  123. try {
  124. let data = req.body.data;
  125. data = JSON.parse(data);
  126. result.data=await doBillsOrRationsDelete(data);
  127. }catch (err){
  128. logger.err(err);
  129. result.error=1;
  130. result.message = err.message;
  131. }
  132. res.json(result);
  133. },
  134. getSectionInfo:async function(req, res){
  135. let result={
  136. error:0
  137. }
  138. try {
  139. let data = req.body.data;
  140. data = JSON.parse(data);
  141. let sectionInfo= await bill_facade.getSectionInfo(data);
  142. result.data=sectionInfo;
  143. }catch (err){
  144. logger.err(err);
  145. result.error=1;
  146. result.message = err.message;
  147. }
  148. res.json(result);
  149. },
  150. reorganizeFBFX:async function(req,res){
  151. let result={
  152. error:0
  153. }
  154. try {
  155. let data = req.body.data;
  156. data = JSON.parse(data);
  157. let reorganizeResult= await bill_facade.reorganizeFBFX(data);
  158. result.data=reorganizeResult;
  159. }catch (err){
  160. logger.err(err);
  161. result.error=1;
  162. result.message = err.message;
  163. }
  164. res.json(result);
  165. },
  166. pasteBlock:async function(req,res){
  167. let result={
  168. error:0
  169. };
  170. try {
  171. let data = req.body.data;
  172. data = JSON.parse(data);
  173. let pasteResult = await bill_facade.pasteBlock(data);
  174. result.data = pasteResult;
  175. }catch (err){
  176. logger.err(err);
  177. result.error=1;
  178. result.message = err.message;
  179. }
  180. res.json(result);
  181. },
  182. //下载导入清单示例
  183. downloadExample: async function(request, response) {
  184. try {
  185. const filePath = './public/static/uploadExample.xlsx';
  186. const stats = fs.statSync(filePath);
  187. // 下载相关header
  188. response.set({
  189. 'Content-Type': 'application/octet-stream',
  190. 'Content-Disposition': 'attachment; filename=uploadExample.xlsx',
  191. 'Content-Length': stats.size
  192. });
  193. fs.createReadStream(filePath).pipe(response);
  194. } catch (error) {
  195. response.end(error);
  196. }
  197. },
  198. //导入清单
  199. upload: async function(req, res){
  200. let responseData = {
  201. err: 0,
  202. msg: '',
  203. data: []
  204. };
  205. const allowHeader = ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];
  206. const uploadOption = {
  207. uploadDir: './public'
  208. };
  209. const form = new multiparty.Form(uploadOption);
  210. let uploadFullName;
  211. let parseSheetDateA = +new Date();
  212. form.parse(req, async function(err, fields, files) {
  213. try{
  214. const projectID = fields.projectID !== undefined && fields.projectID.length > 0 ?
  215. parseInt(fields.projectID[0]) : 0;
  216. if (projectID <= 0) {
  217. throw '参数错误';
  218. }
  219. const file = files.file !== undefined ? files.file[0] : null;
  220. if (err || file === null) {
  221. throw '上传失败';
  222. }
  223. // 判断类型
  224. if (file.headers['content-type'] === undefined || allowHeader.indexOf(file.headers['content-type']) < 0) {
  225. throw '不支持该类型';
  226. }
  227. //导入表类型(09表lj、广联达gld)
  228. const fileType = fields.fileType !== undefined && fields.fileType.length > 0 ? fields.fileType[0] : uploadType.lj;
  229. // 重命名文件名
  230. uploadFullName = uploadOption.uploadDir + '/' + file.originalFilename;
  231. fs.renameSync(file.path, uploadFullName);
  232. const sheets = excel.parse(uploadFullName);
  233. if (sheets[0] === undefined || sheets[0].data === undefined) {
  234. throw 'excel没有对应数据';
  235. }
  236. //出现错误的表表名,前端提示用
  237. let invalidSheets = [];
  238. let validSheets = {fbfx: [], jscsxm: [], zzcsxm: []};
  239. //获得选择的导入的表及导入位置
  240. const uploadWorkBook = fields.uploadWorkBook !== undefined && fields.uploadWorkBook.length > 0 ? JSON.parse(fields.uploadWorkBook[0]) : [];
  241. //识别非法和合法表,sheetInfo存储前端勾选的表的位置索引以及选择的导入位置
  242. for(let sheetInfo of uploadWorkBook){
  243. let sheet = sheets[sheetInfo.sheetIdx];
  244. if(sheet.data === undefined){
  245. invalidSheets.push(sheet.name);
  246. continue;
  247. }
  248. //获取表的列设置确定导入的格式是否合法(09、广联达)
  249. let colMapping = getColMapping(sheet.data);
  250. if(!isValidSheet(colMapping, fileType)){
  251. invalidSheets.push(sheet.name);
  252. continue;
  253. }
  254. //合法的表
  255. sheet.colMapping = colMapping;
  256. //将合法的表按导入位置分类当做一个表来处理
  257. if(validSheets[sheetInfo.position] !== undefined){
  258. validSheets[sheetInfo.position].push(sheet)
  259. }
  260. }
  261. let parseSheetB = +new Date();
  262. console.log(`解析表时间: ${parseSheetB - parseSheetDateA}===================================================================`);
  263. //合并同类表并提取表的有效数据
  264. let validSheetDataA = +new Date();
  265. let toImportSheets = [];
  266. for(let uploadPosition in validSheets){
  267. let validExcelData = [];
  268. for(let uSheet of validSheets[uploadPosition]){
  269. validExcelData = validExcelData.concat(getValidImportData(uSheet.colMapping, uSheet.data))
  270. }
  271. if(validSheets[uploadPosition].length > 0){
  272. toImportSheets.push({position: uploadPosition, colMapping: validSheets[uploadPosition][0].colMapping, validExcelData: validExcelData});
  273. }
  274. }
  275. let validSheetDataB = +new Date();
  276. console.log(`获取表格有效数据时间: ${validSheetDataB - validSheetDataA}==========================================================`);
  277. //匹配的清单库
  278. let stdDateA = +new Date();
  279. const billsLibId = fields.billsLibId !== undefined && fields.billsLibId.length > 0 && fields.billsLibId[0]? parseInt(fields.billsLibId[0]) : null;
  280. let stdBills = [], stdJobs = [], stdCharacters = [];
  281. if(billsLibId){
  282. stdBills = await stdBillsModel.find({billsLibId: billsLibId, deleted: false}, '-_id code jobs items engineering billsLibId');
  283. stdJobs = await stdBillJobsModel.find({billsLibId: billsLibId, deleted: false});
  284. stdCharacters = await stdBillCharacterModel.find({billsLibId: billsLibId, deleted: false});
  285. }
  286. let stdData = {stdBills: stdBills, stdJobs: stdJobs, stdCharacters: stdCharacters};
  287. let stdDateB = +new Date();
  288. console.log(`获取标准清单库数据时间: ${stdDateB - stdDateA}`);
  289. //导入表
  290. let importDateA = +new Date();
  291. for(let importData of toImportSheets){
  292. let updateFrontData = await importSheet(importData, req.session.sessionUser.id, projectID, stdData);
  293. if(updateFrontData){
  294. responseData.data.push(updateFrontData);
  295. }
  296. }
  297. let importDateB = +new Date();
  298. console.log(`导入时间: ${importDateB - importDateA}=========================================================================`);
  299. if(responseData.data.length === 0){
  300. throw 'excel无有效数据';
  301. }
  302. if(invalidSheets.length > 0){
  303. let msg = invalidSheets.join('、');
  304. responseData.msg = `${msg},导入失败`;
  305. }
  306. //删除暂存文件
  307. fs.unlink(uploadFullName);
  308. res.json(responseData);
  309. }
  310. catch (error){
  311. if(fs.existsSync(uploadFullName)){
  312. fs.unlink(uploadFullName);
  313. }
  314. responseData.err = 1;
  315. console.log(error);
  316. responseData.msg = typeof error === 'object' ? '上传失败' : error;
  317. res.json(responseData);
  318. }
  319. });
  320. }
  321. };
  322. //
  323. async function importSheet(importData, userID, projectID, stdData){
  324. //导入位置的有固定行
  325. let flag = getImportFlag(importData.position);
  326. if(!flag){
  327. throw 'excel数据错误';
  328. }
  329. let fixedBill = await billsData.model.findOne({projectID: projectID, 'flags.flag': flag, deleteInfo: null});
  330. let insertFixedBill = null;
  331. //导入xx措施项目,若不存在此固定清单,则先插入相关固定清单
  332. if(!fixedBill){
  333. //分部分项工程(不可删除)应存在
  334. if(flag === fixedFlag.SUB_ENGINERRING){
  335. throw '项目不存在分部分项工程'
  336. }
  337. //措施项目是否存在
  338. let csxm = await billsData.model.findOne({projectID: projectID, 'flags.flag': fixedFlag.MEASURE, deleteInfo: null});
  339. if(!csxm){
  340. throw '项目不存在措施项目'
  341. }
  342. //插入清单固定行(施工技术措施项目、施工组织措施项目可删除)
  343. insertFixedBill = {projectID: projectID, name: flag === fixedFlag.CONSTRUCTION_TECH ? '施工技术措施项目' : '施工组织措施项目', code: '1',
  344. ID: uuidV1(), NextSiblingID: -1, ParentID: csxm.ID, flags: [{fieldName: 'fixed', flag: flag}], type: billType.BILL};
  345. //更新前节点
  346. let preDatas = await billsData.model.find({projectID: projectID, ParentID: csxm.ID, deleteInfo: null});
  347. for(let preData of preDatas){
  348. if(preData.NextSiblingID == -1){
  349. await billsData.model.update({ID: preData.ID}, {$set: {NextSiblingID: insertFixedBill.ID}});
  350. break;
  351. }
  352. }
  353. await billsData.model.create(insertFixedBill);
  354. fixedBill = insertFixedBill;
  355. }
  356. //将excel数据转换成清单树结构数据
  357. let insertDatas = parseToBillData(importData.validExcelData, importData.colMapping, fixedBill, projectID, stdData);
  358. /*if(insertDatas.length === 0){
  359. throw 'excel无有效数据';
  360. }*/
  361. if(insertDatas.length === 0){
  362. return null;
  363. }
  364. //删除相关数据
  365. let deleteDatas = await billsData.deepDeleteBill([fixedBill], userID);
  366. //新增清单数据
  367. await billsData.importBills(insertDatas);
  368. //返回数据以更新前端
  369. if(insertFixedBill){
  370. insertDatas.push(insertFixedBill);
  371. }
  372. return {fixedBill: fixedBill, insert: {bill: insertDatas, ration: []}, remove: {bill: deleteDatas.bill, ration: deleteDatas.ration}};
  373. }
  374. //是否是有效的表头列格式,只要含有各表需要的列就行,不严格控制多少列
  375. function isValidSheet(colMapping, fileType){
  376. function hasField(field, all){
  377. for(let i of all){
  378. if(field === i){
  379. return true;
  380. }
  381. }
  382. return false;
  383. }
  384. let needFields;
  385. if(fileType === uploadType.lj){
  386. //09表:序号、项目编码、项目名称、项目特征、计量单位、工程量、金额
  387. needFields = ['serialNo', 'code', 'name', 'money'];
  388. }
  389. else {
  390. //广联达表:序号、项目编码、项目名称、项目特征、计量单位、工程量、工程量明细、费用明细
  391. needFields = ['serialNo', 'code', 'name', 'itemCharacterText', 'unit', 'quantity', 'quantityDetail', 'feeDetail'];
  392. }
  393. let hasFieldCount = 0;
  394. for(let attr in colMapping){
  395. if(hasField(attr, needFields)){
  396. hasFieldCount++;
  397. }
  398. }
  399. return hasFieldCount === needFields.length;
  400. }
  401. //提取excel表头列对应数据
  402. function getColMapping(sheetData){
  403. //获取表头
  404. function getHeadRow(sheetData){
  405. for(let rData of sheetData) {
  406. //寻找含有序号的行,认作表头行
  407. for(let cData of rData){
  408. if (cData && cData.toString().replace(/\s/g, '') === '序号') {
  409. headRow = rData;
  410. return rData;
  411. }
  412. }
  413. }
  414. return [];
  415. }
  416. //获取需要的表头列与列号对应关系
  417. let colMapping = {};
  418. let headRow = getHeadRow(sheetData);
  419. for(let c = 0; c < headRow.length; c++){
  420. if(headRow[c]){
  421. headRow[c] = headRow[c].toString().replace(/\s/g, '');
  422. //重复的,只取第一个
  423. if(headRow[c] === '序号' && colMapping.serialNo === undefined){
  424. colMapping.serialNo = c;
  425. }
  426. else if((headRow[c] === '编码' || headRow[c] === '项目编码') && colMapping.code === undefined){
  427. colMapping.code = c;
  428. }
  429. else if((headRow[c] === '名称' || headRow[c] === '项目名称') && colMapping.name === undefined){
  430. colMapping.name = c;
  431. }
  432. else if((headRow[c] === '特征' || headRow[c] === '项目特征') && colMapping.itemCharacterText === undefined){
  433. colMapping.itemCharacterText = c;
  434. }
  435. else if((headRow[c] === '单位' || headRow[c] === '计量单位') && colMapping.unit === undefined){
  436. colMapping.unit = c;
  437. }
  438. else if((headRow[c] === '工程量' || headRow[c] === '项目工程量') && colMapping.quantity === undefined){
  439. colMapping.quantity = c;
  440. }
  441. else if(headRow[c].includes('金额') && colMapping.money === undefined){
  442. colMapping.money = c;
  443. }
  444. else if(headRow[c] === '工程量明细' && colMapping.quantityDetail === undefined){
  445. colMapping.quantityDetail = c;
  446. }
  447. else if(headRow[c] === '费用明细' && colMapping.feeDetail === undefined){
  448. colMapping.feeDetail = c;
  449. }
  450. }
  451. }
  452. return colMapping;
  453. }
  454. function rowExistData(rowData){
  455. for(let cData of rowData){
  456. if(cData !== undefined && cData !== ''){
  457. return true;
  458. }
  459. }
  460. return false;
  461. }
  462. //提取excel表数据中的有效数据(去表头表尾,提取其中的excel数据)(根据fixedBill获取栏头占行数)
  463. function getValidImportData(colMapping, sheetData){
  464. let withingD = false;
  465. let validData = [];
  466. function isHead(rData){
  467. return rData[colMapping.serialNo] && rData[colMapping.serialNo].toString().replace(/\s/g, '') === '序号';
  468. }
  469. function isTail(rData){
  470. for(let cData of rData){
  471. if(cData){
  472. let trimCData = cData.toString().replace(/\s/g, '');
  473. if(trimCData === '本页小计' || trimCData === '本页小计'){
  474. return true;
  475. }
  476. }
  477. }
  478. return false;
  479. }
  480. for(let r = 0; r < sheetData.length; r++){
  481. let rData = sheetData[r];
  482. if(isHead(rData)){
  483. withingD = true;
  484. /* if(fixedBill.name !== '施工组织措施项目'){
  485. r++;
  486. }*/
  487. continue;
  488. }
  489. else if(isTail(rData)){
  490. withingD = false;
  491. }
  492. if(withingD && rowExistData(rData)){
  493. validData.push(rData);
  494. }
  495. /*if(rData[0]){
  496. //首列去空格
  497. rData[0] = rData[0].toString().replace(/\s/g, '');
  498. //表头
  499. if(rData[0] === '序号'){
  500. withingD = true;
  501. if(fixedBill.name !== '施工组织措施项目'){
  502. r++;
  503. }
  504. continue;
  505. }
  506. //表尾
  507. else if(rData[0] === '本页小计' || rData[0] === '合计'){
  508. withingD = false;
  509. }
  510. }
  511. if(withingD && rowExistData(rData)){
  512. validData.push(rData);
  513. }*/
  514. }
  515. return validData;
  516. }
  517. function getImportFlag(position){
  518. const fixedItem = {'fbfx': fixedFlag.SUB_ENGINERRING, 'jscsxm': fixedFlag.CONSTRUCTION_TECH, 'zzcsxm': fixedFlag.CONSTRUCTION_ORGANIZATION};
  519. return fixedItem[position] ? fixedItem[position] : null;
  520. }
  521. function isDef(data){
  522. return typeof data !== 'undefined' && data !== null && data !== '';
  523. }
  524. //excel数据转换成清单数据
  525. function parseToBillData(validData, colMapping, fixedBill, projectID, stdData){
  526. let rst = [];
  527. let billIdx = {};
  528. let preRootID = -1,
  529. preLeafID = -1,
  530. preID = -1;
  531. //去除转义字符
  532. function removeESC(data){
  533. return isDef(data) ? data.toString().replace(/[\r,\n,\s,\t]/g, '') : data;
  534. }
  535. //父节点:1.无序号 2有编码
  536. function isRoot(rData){
  537. //序号和编码去除转义字符(有的表格单元格看起来是没数据,实际含有\r,\n等数据)
  538. let serialNo = removeESC(rData[colMapping.serialNo]);
  539. let code = removeESC(rData[colMapping.code]);
  540. return !isDef(serialNo) && isDef(code);
  541. }
  542. //子节点:有序号
  543. function isLeaf(rData){
  544. let serialNo = removeESC(rData[colMapping.serialNo]);
  545. return isDef(serialNo);
  546. }
  547. //续数据:1. 前数据有效 2.无序号 3.无编码 4.有名称或特征
  548. function isExtend(preData, rData){
  549. let serialNo = removeESC(rData[colMapping.serialNo]);
  550. let code = removeESC(rData[colMapping.code]);
  551. let name = removeESC(rData[colMapping.name]);
  552. let itemCharacterText = removeESC(rData[colMapping.itemCharacterText]);
  553. return isDef(preData) && (isRoot(preData) || isLeaf(preData)) && !isDef(serialNo) && !isDef(code) && (isDef(name) || isDef(itemCharacterText));
  554. }
  555. function getBillType(rData, flag){
  556. if(flag === fixedFlag.CONSTRUCTION_TECH || flag === fixedFlag.CONSTRUCTION_ORGANIZATION){
  557. return billType.BILL;
  558. }
  559. else if(flag === fixedFlag.SUB_ENGINERRING){
  560. return isLeaf(rData) ? billType.FX : billType.FB;
  561. }
  562. return null;
  563. }
  564. //excel数据与标准库数据匹配,根据清单前九位编码匹配,匹配成功则获取标准清单对应的工程专业、特征及内容
  565. function matchStdBill(excelBill, stdData){
  566. let isMatch = false;
  567. let regExp = /^\d{12}$/g;
  568. if(regExp.test(excelBill.code)){
  569. let nineCode = excelBill.code.substr(0, 9);
  570. for(let stdBill of stdData.stdBills){
  571. //set programID
  572. if(nineCode == stdBill.code){
  573. isMatch = true;
  574. excelBill.programID = stdBill.engineering ? stdBill.engineering : null;
  575. excelBill.billsLibId = stdBill.billsLibId ? stdBill.billsLibId : null;
  576. //set jobContent and itemCharacter
  577. let tempJob = [], tempCharacter = [];
  578. for(let billJob of stdBill.jobs){
  579. for(let stdJob of stdData.stdJobs) {
  580. if (billJob.id == stdJob.id) {
  581. tempJob.push({isChecked: false, serialNo: billJob.serialNo, content: stdJob.content});
  582. }
  583. }
  584. }
  585. for(let billCharacter of stdBill.items){
  586. for(let stdCharacter of stdData.stdCharacters){
  587. if(billCharacter.id == stdCharacter.id){
  588. let eigenvalue = [];
  589. for(let eValue of stdCharacter.itemValue){
  590. eigenvalue.push({isSelected: false, value: eValue.value});
  591. }
  592. tempCharacter.push({isChecked: false, serialNo: billCharacter.serialNo, character: stdCharacter.content, eigenvalue: eigenvalue});
  593. }
  594. }
  595. }
  596. excelBill.jobContent = tempJob;
  597. excelBill.itemCharacter = tempCharacter;
  598. }
  599. }
  600. }
  601. if(!isMatch && excelBill.type === billType.FX){//分项不为空,同时在标准清单中不匹配,则识别为补项
  602. excelBill.type = billType.BX;
  603. }
  604. }
  605. for(let r = 0; r < validData.length; r++){
  606. /* //序号和编码去除转义字符(有的表格单元格看起来是没数据,实际含有\r,\n等数据)
  607. let serialNo = validData[r][colMapping.serialNo];
  608. let code = validData[r][colMapping.code];
  609. if(isDef(serialNo)){
  610. serialNo = removeESC(serialNo);
  611. }
  612. if(isDef(code)){
  613. code = removeESC(code);
  614. }*/
  615. let preData = validData[r-1],
  616. rData = validData[r];
  617. if(fixedBill.flags[0].flag == fixedFlag.CONSTRUCTION_TECH && rData[colMapping.name] === '施工技术措施项目'
  618. || fixedBill.flags[0].flag == fixedFlag.CONSTRUCTION_ORGANIZATION && rData[colMapping.name] === '施工组织措施项目'){
  619. continue;
  620. }
  621. //过滤无效数据
  622. if(!isRoot(rData) && !isLeaf(rData) && !isExtend(preData, rData)){
  623. continue;
  624. }
  625. if(isExtend(preData, rData)){
  626. let preBill = billIdx[preID];
  627. if(preBill){
  628. //合并续数据
  629. preBill.code += rData[colMapping.code] ? rData[colMapping.code] : '';
  630. preBill.name += rData[colMapping.name] ? rData[colMapping.name] : '';
  631. preBill.itemCharacterText += rData[colMapping.itemCharacterText] ? rData[colMapping.itemCharacterText] : '';
  632. preBill.unit += rData[colMapping.unit] ? rData[colMapping.unit] : '';
  633. preBill.quantity += rData[colMapping.quantity] ? rData[colMapping.quantity] : '';
  634. }
  635. }
  636. else {
  637. let newID = uuidV1();
  638. let pID = -1;
  639. let preBill = null;
  640. if(isRoot(rData)){
  641. pID = fixedBill.ID;
  642. preBill = billIdx[preRootID];
  643. }
  644. else if(isLeaf(rData)){
  645. pID = preRootID !== -1 ? preRootID : fixedBill.ID;
  646. preBill = billIdx[preLeafID];
  647. }
  648. //set bill data
  649. billIdx[newID] = {
  650. ID: newID, ParentID: pID, NextSiblingID: -1,
  651. code: rData[colMapping.code] ? removeESC(rData[colMapping.code]) : '',
  652. name: rData[colMapping.name] ? removeESC(rData[colMapping.name]) : '',
  653. itemCharacterText: rData[colMapping.itemCharacterText] ? rData[colMapping.itemCharacterText] : '',
  654. itemCharacter: [],
  655. jobContentText: '',
  656. jobContent: [],
  657. programID: null,
  658. unit: rData[colMapping.unit] ? rData[colMapping.unit] : '',
  659. quantity: rData[colMapping.quantity] ? rData[colMapping.quantity] : '',
  660. //安全文明
  661. flags: fixedBill.flags[0].flag === fixedFlag.CONSTRUCTION_ORGANIZATION && (rData[colMapping.name] === '安全文明施工专项费用' || rData[colMapping.name] === '安全文明施工费') ?
  662. [{fieldName: 'fixed', flag: fixedFlag.SAFETY_CONSTRUCTION}] : [],
  663. fees: [],
  664. projectID: projectID,
  665. type: getBillType(rData, fixedBill.flags[0].flag)};
  666. //match stdBill and reset programID、jobContent、itemCharacter
  667. matchStdBill(billIdx[newID], stdData);
  668. //update preBill NextSibling
  669. if(preBill){
  670. preBill.NextSiblingID = newID;
  671. }
  672. //set new preID
  673. preID = newID;
  674. preRootID = isRoot(rData) ? newID : preRootID;
  675. preLeafID = isLeaf(rData) ? newID : preLeafID;
  676. }
  677. }
  678. for(let i in billIdx){
  679. rst.push(billIdx[i]);
  680. }
  681. return rst;
  682. }
  683. async function doBillsOrRationsDelete(data) {
  684. let billTask = [];
  685. let deleteBillIDs = [];
  686. let rationTask=[];
  687. let deleteRationIDs=[];
  688. let qd_query=null;
  689. let sub_query=null;
  690. if(data['bills']){
  691. billTask = generateUpdateTasks(data['bills'],data.projectID,data.user_id);
  692. for(let b_key in data['bills']){
  693. if(data['bills'][b_key]===true){
  694. deleteBillIDs.push(b_key+'');
  695. }
  696. }
  697. if(deleteBillIDs.length>0){
  698. qd_query={projectID: data.projectID, billID: {"$in": deleteBillIDs}};
  699. }
  700. }
  701. if(data['ration']){
  702. rationTask = generateUpdateTasks(data['ration'],data.projectID,data.user_id);
  703. for(let r_key in data['ration']){
  704. if(data['ration'][r_key]===true){
  705. deleteRationIDs.push(r_key+'');
  706. }
  707. }
  708. if(deleteRationIDs.length>0){
  709. if(qd_query==null){//说明没删除清单
  710. qd_query={projectID: data.projectID, rationID: {"$in": deleteRationIDs}};
  711. }else {
  712. qd_query={
  713. "$or":[
  714. {projectID: data.projectID, billID: {"$in": deleteBillIDs}},
  715. {projectID: data.projectID, rationID: {"$in": deleteRationIDs}}
  716. ]
  717. }
  718. }
  719. sub_query={projectID: data.projectID, rationID: {"$in": deleteRationIDs}};
  720. }
  721. }
  722. //先删除工程量明细
  723. if(qd_query!=null){
  724. await quantity_detail.deleteByQuery(qd_query) ;
  725. }
  726. if(sub_query!=null){
  727. await ration_coe.deleteMany(sub_query);//删除附注条件
  728. await ration_glj.deleteMany(sub_query);//删除定额工料机
  729. await rationInstallationModel.deleteMany(sub_query);//删除安装增加费
  730. }
  731. if(rationTask.length>0){
  732. await ration_model.model.bulkWrite(rationTask);//删除定额
  733. }
  734. if(billTask.length>0){
  735. await billsData.model.bulkWrite(billTask);//删除清单
  736. }
  737. return 'success';
  738. }
  739. function generateSingleDeleteTasks(data) {
  740. let updateData = data.updateData;
  741. updateData[data.ID]=true;
  742. let tasks = generateUpdateTasks(updateData,data.projectID,data.user_id);
  743. return tasks;
  744. }
  745. function generateUpdateTasks(data,projectID,user_id) {
  746. let tasks=[];
  747. let updateData = data;
  748. let deleteInfo={deleted: true, deleteDateTime: new Date(), deleteBy: user_id};
  749. for(let key in updateData){
  750. let task={
  751. updateOne:{
  752. filter:{
  753. ID:key,
  754. projectID:projectID
  755. }
  756. }
  757. };
  758. if(updateData[key]===true){
  759. task.updateOne.update={
  760. deleteInfo:deleteInfo
  761. };
  762. }else {
  763. task.updateOne.update=updateData[key];
  764. }
  765. tasks.push(task);
  766. }
  767. return tasks;
  768. }