bills_controller.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  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 raiton_facade = require("../facade/ration_facade");
  12. let stdBillsModel = mongoose.model('std_bills_lib_bills');
  13. let stdBillJobsModel = mongoose.model('std_bills_lib_jobContent');
  14. let stdBillCharacterModel = mongoose.model('std_bills_lib_itemCharacter');
  15. const { fixedFlag } = require('../../../public/common_constants');
  16. let LZString = require('lz-string');
  17. const uuidV1 = require('uuid/v1');
  18. const billType ={
  19. DXFY:1,//大项费用
  20. FB:2,//分部
  21. FX:3,//分项
  22. BILL:4,//清单
  23. BX:5//补项
  24. };
  25. //上传的09表、广联达表
  26. const uploadType = {lj: 'lj', gld: 'gld'};
  27. // 上传控件
  28. const multiparty = require("multiparty");
  29. const fs = require("fs");
  30. // excel解析
  31. const excel = require("node-xlsx");
  32. //统一回调函数
  33. var callback = function(req, res, err, message, data){
  34. res.json({error: err, message: message, data: data});
  35. };
  36. module.exports = {
  37. getData: function(req, res){
  38. var data = JSON.parse(req.body.data);
  39. billsData.getData(data.projectId, function(err, message, billsList){
  40. if (err === 0) {
  41. callback(req, res, err, message, billsList);
  42. } else {
  43. callback(req, res, err, message, null);
  44. }
  45. });
  46. },
  47. getItemTemplate: function(req, res){
  48. //var data = JSON.parse(req.body.data);
  49. billsData.getItemTemplate(function(err, message, billsItem){
  50. if (billsItem) {
  51. callback(req, res, err, message, billsItem);
  52. } else {
  53. callback(req, res, err, message, null);
  54. }
  55. });
  56. },
  57. allocIDs: function(req, res){
  58. billsData.allocIDs(function(err, message, data){
  59. if (err) {
  60. callback(req, res, err, message, data);
  61. } else {
  62. callback(req, res, err, message, null);
  63. }
  64. });
  65. },
  66. //zhong 2017-9-1
  67. updateCharacterContent: function (req, res) {
  68. let data = JSON.parse(req.body.data);
  69. let findSet = data.findSet,
  70. updateObj = data.updateObj,
  71. txtObj = data.txtObj;
  72. billsData.updateCharacterContent(findSet, updateObj, txtObj, function (err, message) {
  73. callback(req, res, err, message, null);
  74. });
  75. },
  76. updateBills: async function (req, res) {
  77. try{
  78. let data = JSON.parse(req.body.data);
  79. await billsData.updateBills(data.updateDatas);
  80. callback(req, res, 0, 'success', null);
  81. }
  82. catch (err){
  83. callback(req, res, 1, err, null);
  84. }
  85. },
  86. updateBill: async function(request, response) {
  87. const data = JSON.parse(request.body.data);
  88. const findSet = data.findSet;
  89. const updateData = data.updateData;
  90. let settingData = {};
  91. // 筛选出要保存在项目属性的设置
  92. for (const index in updateData) {
  93. if (updateData[index].field === 'addRule') {
  94. settingData = updateData[index].value;
  95. delete updateData[index];
  96. }
  97. }
  98. /* // 更新项目属性
  99. const propertyUpdateData = {
  100. property: 'addRule',
  101. data: settingData
  102. };*/
  103. //const projectResult = await ProjectsData.updateProjectProperty(findSet.projectID, propertyUpdateData);
  104. const result = await billsData.updateBill(findSet, updateData);
  105. const message = !result ? '修改失败' : '修改成功';
  106. const err = !result ? 1 : 0;
  107. callback(request, response, err, message, null);
  108. },
  109. singleDelete:async function(req, res){
  110. let result={
  111. error:0
  112. }
  113. try {
  114. let data = req.body.data;
  115. data = JSON.parse(data);
  116. let tasks = generateSingleDeleteTasks(data);
  117. let resultData= await billsData.model.bulkWrite(tasks);
  118. //删除工程量明细
  119. await quantity_detail.deleteByQuery({projectID: data.projectID, billID: data.ID}) ;
  120. result.data=resultData;
  121. }catch (err){
  122. logger.err(err);
  123. result.error=1;
  124. result.message = err.message;
  125. }
  126. res.json(result);
  127. },
  128. multiDelete:async function(req, res){
  129. let result={
  130. error:0
  131. };
  132. try {
  133. let data = req.body.data;
  134. data = JSON.parse(data);
  135. result.data=await doBillsOrRationsDelete(data);
  136. }catch (err){
  137. logger.err(err);
  138. result.error=1;
  139. result.message = err.message;
  140. }
  141. res.json(result);
  142. },
  143. getSectionInfo:async function(req, res){
  144. let result={
  145. error:0
  146. }
  147. try {
  148. let data = req.body.data;
  149. data = JSON.parse(data);
  150. let sectionInfo= await bill_facade.getSectionInfo(data);
  151. result.data=sectionInfo;
  152. }catch (err){
  153. logger.err(err);
  154. result.error=1;
  155. result.message = err.message;
  156. }
  157. res.json(result);
  158. },
  159. reorganizeFBFX:async function(req,res){
  160. let result={
  161. error:0
  162. }
  163. try {
  164. let data = req.body.data;
  165. data = JSON.parse(data);
  166. let reorganizeResult= await bill_facade.reorganizeFBFX(data);
  167. result.data=reorganizeResult;
  168. }catch (err){
  169. logger.err(err);
  170. result.error=1;
  171. result.message = err.message;
  172. }
  173. res.json(result);
  174. },
  175. pasteBlock:async function(req,res){
  176. let result={
  177. error:0
  178. };
  179. try {
  180. let data = req.body.data;
  181. data = JSON.parse(data);
  182. let pasteResult = await bill_facade.pasteBlock(data,req.session.sessionCompilation);
  183. result.data = pasteResult;
  184. }catch (err){
  185. logger.err(err);
  186. result.error=1;
  187. result.message = err.message;
  188. }
  189. res.json(result);
  190. },
  191. //下载导入清单示例
  192. downloadExample: function(request, response) {
  193. try {
  194. //导入类型(09表、广联达)对应的示例文件名
  195. const uploadTypeMap = { lj: '分部分项工程项目清单计价表.xlsx', gld: '算量示例-清单汇总表.xlsx' };
  196. const type = request.query.type;
  197. const fileName = uploadTypeMap[type];
  198. const filePath = `./public/static/${fileName}`;
  199. const stats = fs.statSync(filePath);
  200. // 下载相关header
  201. response.set({
  202. 'Content-Type': 'application/octet-stream',
  203. 'Content-Disposition': `attachment; filename=${encodeURI(fileName)}`,
  204. 'Content-Length': stats.size
  205. });
  206. fs.createReadStream(filePath).pipe(response);
  207. } catch (error) {
  208. response.end(error);
  209. }
  210. },
  211. //导入清单
  212. import: async function(req, res){
  213. let responseData = {
  214. err: 0,
  215. msg: '',
  216. data: []
  217. };
  218. const form = new multiparty.Form();
  219. form.parse(req, async function(err, fields, files) {
  220. try{
  221. const projectID = fields.projectID !== undefined && fields.projectID.length > 0 ?
  222. parseInt(fields.projectID[0]) : 0;
  223. if (projectID <= 0) {
  224. throw '参数错误';
  225. }
  226. //导入清单数据
  227. let compressData = fields.compressData !== undefined && fields.compressData.length > 0 ?
  228. fields.compressData[0] : null;
  229. if(compressData === null){
  230. throw 'excel没有对应数据'
  231. }
  232. let importData = JSON.parse(LZString.decompressFromUTF16(compressData));
  233. //匹配的清单库
  234. const billsLibId = fields.billsLibId !== undefined && fields.billsLibId.length > 0 && fields.billsLibId[0]? parseInt(fields.billsLibId[0]) : null;
  235. let stdBills = [], stdJobs = [], stdCharacters = [];
  236. if(billsLibId){
  237. stdBills = await stdBillsModel.find({billsLibId: billsLibId, deleted: false}, '-_id code jobs items engineering billsLibId ruleText');
  238. stdJobs = await stdBillJobsModel.find({billsLibId: billsLibId, deleted: false});
  239. stdCharacters = await stdBillCharacterModel.find({billsLibId: billsLibId, deleted: false});
  240. }
  241. let stdData = {stdBills: stdBills, stdJobs: stdJobs, stdCharacters: stdCharacters};
  242. //导入表
  243. let importDateA = +new Date();
  244. for(let position in importData){
  245. if(importData[position].length > 0){
  246. let updateFrontData = await importSheet(position, importData[position], req.session.sessionUser.id, projectID, stdData);
  247. if(updateFrontData){
  248. responseData.data.push(updateFrontData);
  249. }
  250. }
  251. }
  252. let importDateB = +new Date();
  253. console.log(`导入时间: ${importDateB - importDateA}=========================================================================`);
  254. res.json(responseData);
  255. }
  256. catch (error){
  257. responseData.err = 1;
  258. console.log(error);
  259. responseData.msg = typeof error === 'object' ? '上传失败' : error;
  260. res.json(responseData);
  261. }
  262. });
  263. },
  264. insertBills: async function (req, res) {
  265. let data = JSON.parse(req.body.data);
  266. try {
  267. await bill_facade.insertBills(data.postData);
  268. callback(req, res, 0, 'success', null);
  269. } catch (err) {
  270. callback(req, res, 1, err, null);
  271. }
  272. }
  273. };
  274. async function importSheet(position, excelBills, userID, projectID, stdData){
  275. //导入位置的有固定行
  276. let flag = getImportFlag(position);
  277. if(!flag){
  278. throw 'excel数据错误';
  279. }
  280. let fixedBill = await billsData.model.findOne({projectID: projectID, 'flags.flag': flag, deleteInfo: null});
  281. let insertFixedBill = null;
  282. //导入xx措施项目,若不存在此固定清单,则先插入相关固定清单
  283. if(!fixedBill){
  284. //分部分项工程(不可删除)应存在
  285. if(flag === fixedFlag.SUB_ENGINERRING){
  286. throw '项目不存在分部分项工程'
  287. }
  288. //措施项目是否存在
  289. let csxm = await billsData.model.findOne({projectID: projectID, 'flags.flag': fixedFlag.MEASURE, deleteInfo: null});
  290. if(!csxm){
  291. throw '项目不存在措施项目'
  292. }
  293. //插入清单固定行(施工技术措施项目、施工组织措施项目可删除)
  294. insertFixedBill = {projectID: projectID, name: flag === fixedFlag.CONSTRUCTION_TECH ? '施工技术措施项目' : '施工组织措施项目', code: '1',
  295. ID: uuidV1(), NextSiblingID: -1, ParentID: csxm.ID, flags: [{fieldName: 'fixed', flag: flag}], type: billType.BILL};
  296. //更新前节点
  297. let preDatas = await billsData.model.find({projectID: projectID, ParentID: csxm.ID, deleteInfo: null});
  298. for(let preData of preDatas){
  299. if(preData.NextSiblingID == -1){
  300. await billsData.model.update({ID: preData.ID}, {$set: {NextSiblingID: insertFixedBill.ID}});
  301. break;
  302. }
  303. }
  304. await billsData.model.create(insertFixedBill);
  305. fixedBill = insertFixedBill;
  306. }
  307. //将excel清单数据转换成完整清单数据(设置ParentID、匹配标准清单库)
  308. parseToCompleteBills(excelBills, fixedBill, stdData);
  309. //删除相关数据
  310. let deleteDatas = await billsData.deepDeleteBill([fixedBill], userID);
  311. //新增清单数据
  312. await billsData.importBills(excelBills);
  313. //返回数据以更新前端
  314. if(insertFixedBill){
  315. excelBills.push(insertFixedBill);
  316. }
  317. return {fixedBill: fixedBill, insert: {bill: excelBills, ration: []}, remove: {bill: deleteDatas.bill, ration: deleteDatas.ration}};
  318. }
  319. function getImportFlag(position){
  320. const fixedItem = {'fbfx': fixedFlag.SUB_ENGINERRING, 'jscsxm': fixedFlag.CONSTRUCTION_TECH, 'zzcsxm': fixedFlag.CONSTRUCTION_ORGANIZATION};
  321. return fixedItem[position] ? fixedItem[position] : null;
  322. }
  323. function isDef(data){
  324. return typeof data !== 'undefined' && data !== null && data !== '';
  325. }
  326. //将前端解析生成的清单节点数据完善(ParentID、匹配标准清单)
  327. function parseToCompleteBills(excelBills, fixedBills, stdData){
  328. //设置清单ParentID
  329. let rootID = fixedBills ? fixedBills.ID: -1;
  330. for(let bills of excelBills){
  331. if(bills.nodeType === 'root'){
  332. rootID = bills.ID;
  333. bills.ParentID = fixedBills.ID;
  334. }
  335. else {
  336. bills.ParentID = rootID;
  337. }
  338. delete bills.nodeType;
  339. matchStdBill(bills, stdData);
  340. }
  341. //excel数据与标准库数据匹配,根据清单前九位编码匹配,匹配成功则获取标准清单对应的工程专业、特征及内容
  342. function matchStdBill(excelBill, stdData){
  343. let isMatch = false;
  344. let regExp = /^\d{12}$/g;
  345. if(excelBill.code.length >8){
  346. let nineCode = excelBill.code.substr(0, 9);
  347. for(let stdBill of stdData.stdBills){
  348. //set programID
  349. if(nineCode == stdBill.code){
  350. isMatch = true;
  351. excelBill.programID = stdBill.engineering ? stdBill.engineering : null;
  352. excelBill.billsLibId = stdBill.billsLibId ? stdBill.billsLibId : null;
  353. excelBill.ruleText = stdBill.ruleText ? stdBill.ruleText : '';
  354. //set jobContent and itemCharacter
  355. let tempJob = [], tempCharacter = [];
  356. for(let billJob of stdBill.jobs){
  357. for(let stdJob of stdData.stdJobs) {
  358. if (billJob.id == stdJob.id) {
  359. tempJob.push({isChecked: false, serialNo: billJob.serialNo, content: stdJob.content});
  360. }
  361. }
  362. }
  363. for(let billCharacter of stdBill.items){
  364. for(let stdCharacter of stdData.stdCharacters){
  365. if(billCharacter.id == stdCharacter.id){
  366. let eigenvalue = [];
  367. for(let eValue of stdCharacter.itemValue){
  368. eigenvalue.push({isSelected: false, value: eValue.value});
  369. }
  370. tempCharacter.push({isChecked: false, serialNo: billCharacter.serialNo, character: stdCharacter.content, eigenvalue: eigenvalue});
  371. }
  372. }
  373. }
  374. excelBill.jobContent = tempJob;
  375. excelBill.itemCharacter = tempCharacter;
  376. }
  377. }
  378. }
  379. if(!isMatch && excelBill.type === billType.FX){//分项不为空,同时在标准清单中不匹配,则识别为补项
  380. excelBill.type = billType.BX;
  381. }
  382. }
  383. }
  384. async function doBillsOrRationsDelete(data) {
  385. let billTask = [];
  386. let deleteBillIDs = [];
  387. let rationTask=[];
  388. let deleteRationIDs=[];
  389. let qd_query=null;
  390. let sub_query=null;
  391. if(data['bills']){
  392. billTask = generateUpdateTasks(data['bills'],data.projectID,data.user_id);
  393. for(let b_key in data['bills']){
  394. if(data['bills'][b_key]===true){
  395. deleteBillIDs.push(b_key+'');
  396. }
  397. }
  398. if(deleteBillIDs.length>0){
  399. qd_query={projectID: data.projectID, billID: {"$in": deleteBillIDs}};
  400. }
  401. }
  402. if(data['ration']){
  403. rationTask = generateUpdateTasks(data['ration'],data.projectID,data.user_id);
  404. for(let r_key in data['ration']){
  405. if(data['ration'][r_key]===true){
  406. deleteRationIDs.push(r_key+'');
  407. }
  408. }
  409. if(deleteRationIDs.length>0){
  410. if(qd_query==null){//说明没删除清单
  411. qd_query={projectID: data.projectID, rationID: {"$in": deleteRationIDs}};
  412. }else {
  413. qd_query={
  414. "$or":[
  415. {projectID: data.projectID, billID: {"$in": deleteBillIDs}},
  416. {projectID: data.projectID, rationID: {"$in": deleteRationIDs}}
  417. ]
  418. }
  419. }
  420. sub_query={projectID: data.projectID, rationID: {"$in": deleteRationIDs}};
  421. }
  422. }
  423. //先删除工程量明细
  424. if(qd_query!=null){
  425. await quantity_detail.deleteByQuery(qd_query) ;
  426. }
  427. if(sub_query!=null){
  428. await raiton_facade.deleteSubListByQuery(sub_query);
  429. }
  430. if(rationTask.length>0){
  431. await ration_model.model.bulkWrite(rationTask);//删除定额
  432. }
  433. if(billTask.length>0){
  434. await billsData.model.bulkWrite(billTask);//删除清单
  435. }
  436. return 'success';
  437. }
  438. function generateSingleDeleteTasks(data) {
  439. let updateData = data.updateData;
  440. updateData[data.ID]=true;
  441. let tasks = generateUpdateTasks(updateData,data.projectID,data.user_id);
  442. return tasks;
  443. }
  444. function generateUpdateTasks(data,projectID,user_id) {
  445. let tasks=[];
  446. let updateData = data;
  447. let deleteInfo={deleted: true, deleteDateTime: new Date(), deleteBy: user_id};
  448. for(let key in updateData){
  449. let task={
  450. updateOne:{
  451. filter:{
  452. ID:key,
  453. projectID:projectID
  454. }
  455. }
  456. };
  457. if(updateData[key]===true){
  458. //原先是假删除,现在改成真删除
  459. task = {
  460. deleteOne:{
  461. filter:{
  462. ID:key,
  463. projectID:projectID
  464. }
  465. }
  466. }
  467. /* task.updateOne.update={
  468. deleteInfo:deleteInfo
  469. };*/
  470. }else {
  471. task.updateOne.update=updateData[key];
  472. }
  473. tasks.push(task);
  474. }
  475. return tasks;
  476. }