ledger_controller.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. 'use strict';
  2. /**
  3. * 台账相关控制器
  4. *
  5. * @author CaiAoLin
  6. * @date 2017/11/30
  7. * @version
  8. */
  9. const stdDataAddType = {
  10. self: 1,
  11. withParent: 2,
  12. };
  13. const auditConst = require('../const/audit').flow;
  14. const tenderMenu = require('../../config/menu').tenderMenu;
  15. const measureType = require('../const/tender').measureType;
  16. const spreadConst = require('../const/spread');
  17. const fs = require('fs');
  18. const LzString = require('lz-string');
  19. module.exports = app => {
  20. class LedgerController extends app.BaseController {
  21. /**
  22. * 构造函数
  23. *
  24. * @param {Object} ctx - egg全局变量
  25. * @return {void}
  26. */
  27. constructor(ctx) {
  28. super(ctx);
  29. ctx.showProject = true;
  30. ctx.showTitle = true;
  31. ctx.showTender = true;
  32. }
  33. /**
  34. * 检查标段是否只读(审核中,审核完成)
  35. * @param {Object} tenderData
  36. * @return {boolean}
  37. * @private
  38. */
  39. _ledgerReadOnly() {
  40. const tender = this.ctx.tender.data;
  41. return tender.ledger_status === auditConst.status.checking || tender.ledger_status === auditConst.status.checked;
  42. }
  43. /**
  44. * 获取SpreadSetting
  45. * @private
  46. */
  47. _getSpreadSetting() {
  48. const _ = this.app._;
  49. function removeFieldCols(setting, cols) {
  50. _.remove(setting.cols, function(c) {
  51. return cols.indexOf(c.field) > -1;
  52. });
  53. }
  54. const ledger = JSON.parse(JSON.stringify(spreadConst.ledgerSpread));
  55. const pos = JSON.parse(JSON.stringify(spreadConst.ledgerPosSpread));
  56. const tender = this.ctx.tender;
  57. if (this._ledgerReadOnly(tender.data)) {
  58. ledger.readOnly = true;
  59. pos.readOnly = true;
  60. }
  61. if (tender.data.measure_type === measureType.tz.value) {
  62. removeFieldCols(ledger, spreadConst.filterCols.tzWithoutCols);
  63. }
  64. if (!tender.info.display.ledger.dgnQty) {
  65. removeFieldCols(ledger, spreadConst.filterCols.dgnCols);
  66. }
  67. return [ledger, pos];
  68. }
  69. /**
  70. * 台账分解页面 (Get)
  71. *
  72. * @param {Object} ctx - egg全局变量
  73. * @return {void}
  74. */
  75. async explode(ctx) {
  76. try {
  77. const tender = ctx.tender;
  78. const [ledgerSpread, posSpread] = this._getSpreadSetting();
  79. const curAuditor = await ctx.service.ledgerAudit.getCurAuditor(tender.id, tender.data.ledger_times);
  80. const times = tender.data.ledger_status === auditConst.status.checkNo ? tender.data.ledger_times - 1 : tender.data.ledger_times;
  81. const auditors = await ctx.service.ledgerAudit.getAuditors(tender.id, times);
  82. const content = auditors.length > 0 ? await ctx.service.ledgerAuditContent.getAllDataByCondition({
  83. where: { tender_id: tender.id, times, audit_id: auditors[0].audit_id },
  84. }) : null;
  85. const ledgerData = await ctx.service.ledger.getDataByTenderId(tender.id, -1);
  86. const user = await ctx.service.projectAccount.getAccountInfoById(ctx.tender.data.user_id);
  87. const auditHistory = [];
  88. if (ctx.tender.data.ledger_times > 1) {
  89. for (let i = 1; i < ctx.tender.data.ledger_times; i++) {
  90. auditHistory.push(await ctx.service.ledgerAudit.getAuditors(ctx.tender.id, i));
  91. }
  92. }
  93. const renderData = {
  94. tender: tender.data,
  95. tenderInfo: tender.info,
  96. auditConst,
  97. auditors,
  98. curAuditor,
  99. user,
  100. auditHistory,
  101. content,
  102. ledger: JSON.stringify(ledgerData),
  103. ledgerSpreadSetting: JSON.stringify(ledgerSpread),
  104. posSpreadSetting: JSON.stringify(posSpread),
  105. tenderMenu,
  106. preUrl: '/tender/' + tender.id,
  107. measureType,
  108. };
  109. await this.layout('ledger/explode.ejs', renderData, 'ledger/explode_modal.ejs');
  110. } catch (err) {
  111. this.log(err);
  112. await this.layout('/dashboard');
  113. }
  114. }
  115. /**
  116. * 获取子节点 (Ajax)
  117. * @param ctx
  118. * @return {Promise<void>}
  119. */
  120. async getChildren(ctx) {
  121. const responseData = {
  122. err: 0,
  123. msg: '',
  124. data: [],
  125. };
  126. try {
  127. const data = JSON.parse(ctx.request.body.data);
  128. const id = data.ledger_id;
  129. if (isNaN(id) || id <= 0) {
  130. throw '参数错误';
  131. }
  132. responseData.data = await ctx.service.ledger.getChildrenByParentId(ctx.tender.id, id);
  133. } catch (err) {
  134. responseData.err = 1;
  135. responseData.msg = err;
  136. }
  137. ctx.body = responseData;
  138. }
  139. /**
  140. * 树结构基本操作(增、删、上下移、升降级)(Ajax)
  141. * @param {Object} ctx - egg全局变量
  142. * @return {Promise<void>}
  143. */
  144. async baseOperation(ctx) {
  145. const responseData = {
  146. err: 0,
  147. msg: '',
  148. data: [],
  149. };
  150. try {
  151. if (!ctx.tender.data || ctx.tender.data.user_id !== ctx.session.sessionUser.accountId || this._ledgerReadOnly()) {
  152. throw '标段数据错误';
  153. }
  154. const data = JSON.parse(ctx.request.body.data);
  155. switch (data.postType) {
  156. case 'add':
  157. responseData.data = await ctx.service.ledger.addNode(ctx.tender.id, data.id);
  158. break;
  159. case 'delete':
  160. responseData.data = await ctx.service.ledger.deleteNode(ctx.tender.id, data.id);
  161. break;
  162. case 'up-move':
  163. responseData.data = await ctx.service.ledger.upMoveNode(ctx.tender.id, data.id);
  164. break;
  165. case 'down-move':
  166. responseData.data = await ctx.service.ledger.downMoveNode(ctx.tender.id, data.id);
  167. break;
  168. case 'up-level':
  169. responseData.data = await ctx.service.ledger.upLevelNode(ctx.tender.id, data.id);
  170. break;
  171. case 'down-level':
  172. responseData.data = await ctx.service.ledger.downLevelNode(ctx.tender.id, data.id);
  173. break;
  174. default:
  175. throw '未知操作';
  176. }
  177. } catch (err) {
  178. responseData.err = 1;
  179. responseData.msg = err;
  180. this.log(err);
  181. }
  182. ctx.body = responseData;
  183. }
  184. /**
  185. * 提交更新数据 (Ajax)
  186. * @param ctx
  187. * @return {Promise<void>}
  188. */
  189. async updateInfo(ctx) {
  190. const responseData = {
  191. err: 0,
  192. msg: '',
  193. data: [],
  194. };
  195. try {
  196. if (!ctx.tender.data || ctx.tender.data.user_id !== ctx.session.sessionUser.accountId || this._ledgerReadOnly()) {
  197. throw '标段数据错误';
  198. }
  199. const data = JSON.parse(ctx.request.body.data);
  200. if (data instanceof Array) {
  201. responseData.data = await ctx.service.ledger.updateInfos(ctx.tender.id, data);
  202. } else {
  203. responseData.data = await ctx.service.ledger.updateInfo(ctx.tender.id, data);
  204. }
  205. } catch (err) {
  206. responseData.err = 1;
  207. responseData.msg = err;
  208. }
  209. ctx.body = responseData;
  210. }
  211. async update(ctx) {
  212. const responseData = {
  213. err: 0,
  214. msg: '',
  215. data: [],
  216. };
  217. try {
  218. const tenderId = parseInt(ctx.params.id);
  219. if (!tenderId) {
  220. throw '当前未打开标段';
  221. }
  222. const tenderData = await ctx.service.tender.getDataById(tenderId);
  223. if (!tenderData || tenderData.user_id !== ctx.session.sessionUser.accountId || this._ledgerReadOnly(tenderData)) {
  224. throw '标段数据错误';
  225. }
  226. const data = JSON.parse(ctx.request.body.data);
  227. responseData.data = await ctx.service.ledger.updateCalc(ctx.tender.id, data);
  228. } catch (err) {
  229. this.log(err);
  230. responseData.err = 1;
  231. responseData.msg = err;
  232. }
  233. ctx.body = responseData;
  234. }
  235. /**
  236. * 复制粘贴整块 (Ajax)
  237. *
  238. * @param ctx
  239. * @return {Promise<void>}
  240. */
  241. async pasteBlock(ctx) {
  242. const responseData = {
  243. err: 0,
  244. msg: '',
  245. data: [],
  246. };
  247. try {
  248. if (!ctx.tender.data || ctx.tender.data.user_id !== ctx.session.sessionUser.accountId || this._ledgerReadOnly()) {
  249. throw '标段数据错误';
  250. }
  251. const data = JSON.parse(ctx.request.body.data);
  252. if ((isNaN(data.id) || data.id <= 0) || (!data.block || data.block.length <= 0)) {
  253. throw '参数错误';
  254. }
  255. responseData.data = await ctx.service.ledger.pasteBlock(ctx.tender.id, data.id, data.block);
  256. } catch (err) {
  257. responseData.err = 1;
  258. responseData.msg = err;
  259. }
  260. ctx.body = responseData;
  261. }
  262. /**
  263. * 从标准项目表添加数据 (Ajax)
  264. * @param ctx
  265. * @return {Promise<void>}
  266. */
  267. async addFromStandardLib(ctx) {
  268. const responseData = {
  269. err: 0,
  270. msg: '',
  271. data: [],
  272. };
  273. try {
  274. if (!ctx.tender.data || ctx.tender.data.user_id !== ctx.session.sessionUser.accountId || this._ledgerReadOnly()) {
  275. throw '标段数据错误';
  276. }
  277. const data = JSON.parse(ctx.request.body.data);
  278. if ((isNaN(data.id) || data.id <= 0) || !data.stdType || !data.stdNode) {
  279. throw '参数错误';
  280. }
  281. // todo 校验项目是否使用该库的权限
  282. let stdLib;
  283. switch (data.stdType) {
  284. case 'chapter':
  285. stdLib = ctx.service.stdChapter;
  286. break;
  287. case 'bills':
  288. stdLib = ctx.service.stdBills;
  289. break;
  290. default:
  291. throw '未知标准库';
  292. }
  293. const stdData = await stdLib.getDataByDataId(data.stdLibId, data.stdNode);
  294. const addType = stdDataAddType.withParent;
  295. switch (addType) {
  296. case stdDataAddType.self:
  297. responseData.data = await ctx.service.ledger.addStdNode(ctx.tender.id, data.id, stdData);
  298. break;
  299. case stdDataAddType.withParent:
  300. responseData.data = await ctx.service.ledger.addStdNodeWithParent(ctx.tender.id, stdData, stdLib);
  301. break;
  302. default:
  303. throw '未知添加方法';
  304. }
  305. } catch (err) {
  306. responseData.err = 1;
  307. responseData.msg = err;
  308. }
  309. ctx.body = responseData;
  310. }
  311. /**
  312. * 批量插入数据 (Ajax)
  313. *
  314. * data = {id, batchData, batchType}
  315. * data.batchType = 'batchInsertChild'/'batchInsertNext'
  316. * data.batchData = [{name, children}] -- 项目节列表
  317. * data.batchData.children = [{code, name, unit, unit_price, quantity}] -- 工程量清单列表
  318. *
  319. * @param ctx
  320. * @return {Promise<void>}
  321. */
  322. async batchInsert(ctx) {
  323. const responseData = {
  324. err: 0,
  325. msg: '',
  326. data: [],
  327. };
  328. try {
  329. if (!ctx.tender.data || ctx.tender.data.user_id !== ctx.session.sessionUser.accountId || this._ledgerReadOnly()) {
  330. throw '标段数据错误';
  331. }
  332. const data = JSON.parse(ctx.request.body.data);
  333. if ((isNaN(data.id) || data.id <= 0) || !data.batchType) {
  334. throw '参数错误';
  335. }
  336. switch (data.batchType) {
  337. case 'child':
  338. responseData.data = await ctx.service.ledger.batchInsertChild(ctx.tender.id, data.id, data.batchData);
  339. break;
  340. case 'next':
  341. responseData.data = await ctx.service.ledger.batchInsertNext(ctx.tender.id, data.id, data.batchData);
  342. break;
  343. default:
  344. throw '参数错误';
  345. }
  346. } catch (err) {
  347. this.log(err);
  348. responseData.err = 1;
  349. responseData.msg = err;
  350. }
  351. ctx.body = responseData;
  352. }
  353. /**
  354. * 查询
  355. *
  356. * @param ctx
  357. * @return {Promise<void>}
  358. */
  359. async search(ctx) {
  360. const responseData = {
  361. err: 0,
  362. msg: '',
  363. data: [],
  364. };
  365. try {
  366. const tenderId = ctx.params.id;
  367. if (!tenderId) {
  368. throw '当前未打开标段';
  369. }
  370. const data = JSON.parse(ctx.request.body.data);
  371. if (!data.keyword || data.keyword === '') {
  372. throw '参数错误';
  373. }
  374. responseData.data = await ctx.service.ledger.search(tenderId, {
  375. value: ctx.app.mysql.escape('%' + data.keyword + '%'),
  376. operate: 'Like',
  377. fields: ['code', 'b_code', 'name'],
  378. });
  379. } catch (err) {
  380. this.log(err);
  381. responseData.err = 1;
  382. responseData.msg = err;
  383. }
  384. ctx.body = responseData;
  385. }
  386. /**
  387. * 定位
  388. * @param ctx
  389. * @return {Promise<void>}
  390. */
  391. async locate(ctx) {
  392. const responseData = {
  393. err: 0,
  394. msg: '',
  395. data: [],
  396. };
  397. try {
  398. const tenderId = ctx.params.id;
  399. if (!tenderId) {
  400. throw '当前未打开标段';
  401. }
  402. const data = JSON.parse(ctx.request.body.data);
  403. if ((isNaN(data.id) || data.id <= 0)) {
  404. throw '参数错误';
  405. }
  406. responseData.data = await ctx.service.ledger.locateNode(tenderId, data.id);
  407. } catch (err) {
  408. this.log(err);
  409. responseData.err = 1;
  410. responseData.msg = err;
  411. }
  412. ctx.body = responseData;
  413. }
  414. /**
  415. * 获取全部子节点
  416. *
  417. * @param ctx
  418. * @return {Promise<void>}
  419. */
  420. async posterity(ctx) {
  421. const responseData = {
  422. err: 0,
  423. msg: '',
  424. data: [],
  425. };
  426. try {
  427. const tenderId = ctx.params.id;
  428. if (!tenderId) {
  429. throw '当前未打开标段';
  430. }
  431. const data = JSON.parse(ctx.request.body.data);
  432. if ((isNaN(data.id) || data.id <= 0)) {
  433. throw '参数错误';
  434. }
  435. const expandData = await ctx.service.ledger.getPosterityByParentId(tenderId, data.id);
  436. responseData.data = { expand: expandData };
  437. } catch (err) {
  438. this.log(err);
  439. responseData.err = 1;
  440. responseData.msg = err;
  441. }
  442. ctx.body = responseData;
  443. }
  444. /**
  445. * 获取部位明细数据(Ajax)
  446. *
  447. * @param ctx
  448. * @return {Promise<void>}
  449. */
  450. async pos(ctx) {
  451. try {
  452. await this.checkMeasureType(measureType.tz.value);
  453. const condition = JSON.parse(ctx.request.body.data) || {};
  454. condition.tid = ctx.tender.id;
  455. const posData = await ctx.service.pos.getPosData(condition);
  456. ctx.body = { err: 0, msg: '', data: posData };
  457. } catch (err) {
  458. this.log(err);
  459. ctx.body = { err: 1, msg: err.toString(), data: [] };
  460. }
  461. }
  462. /**
  463. * 更新部位明细数据
  464. *
  465. * @param ctx
  466. * @return {Promise<void>}
  467. */
  468. async posUpdate(ctx) {
  469. try {
  470. await this.checkMeasureType(measureType.tz.value);
  471. const data = JSON.parse(ctx.request.body.data);
  472. const responseData = await ctx.service.pos.savePosData(data, ctx.tender.id);
  473. ctx.body = { err: 0, msg: '', data: responseData };
  474. } catch (err) {
  475. this.log(err);
  476. ctx.body = { err: 1, msg: err.toString(), data: null };
  477. }
  478. }
  479. /**
  480. * 上传 清单Excel 并导入
  481. * @param ctx
  482. * @return {Promise<void>}
  483. */
  484. async uploadExcel(ctx) {
  485. try {
  486. const compressData = ctx.request.body.data;
  487. const data = JSON.parse(LzString.decompressFromUTF16(compressData));
  488. const responseData = { err: 0, msg: '', data: {}, };
  489. await ctx.service.ledger.importExcel(data);
  490. responseData.data.bills = await ctx.service.ledger.getDataByTenderId(ctx.tender.id, -1);
  491. responseData.data.pos = await ctx.service.pos.getPosData(null);
  492. ctx.body = responseData;
  493. } catch (err) {
  494. this.log(err);
  495. ctx.body = { err: 1, msg: err.toString(), data: null };
  496. }
  497. }
  498. /**
  499. * 下载(清单Excel模板 or 导出项目台账Excel)
  500. * @param ctx
  501. * @return {Promise<void>}
  502. */
  503. async download(ctx) {
  504. const file = ctx.params.file;
  505. if (file) {
  506. try {
  507. let fileName;
  508. if (file === 'template') {
  509. fileName = this.app.baseDir + '/app/public/files/template/ledger/导入分项清单Excel格式.xls';
  510. } else if (file === 'ledger') {
  511. const create_time = Date.parse(new Date()) / 1000;
  512. fileName = this.app.baseDir + '/app/public/files/downloads/ledger/' + ctx.tender.id + '-' + create_time + '.xlsx';
  513. // todo 导出台账清单Excel
  514. }
  515. ctx.body = await fs.readFileSync(fileName);
  516. } catch (err) {
  517. this.log(err);
  518. this.setMessage(err.toString(), this.messageType.ERROR);
  519. }
  520. }
  521. }
  522. /**
  523. * 台账变更页面 (Get)
  524. *
  525. * @param {object} ctx - egg全局变量
  526. * @return {void}
  527. */
  528. async change(ctx) {
  529. try {
  530. const renderData = {
  531. tender: ctx.tender.data,
  532. tenderMenu: this.menu.tenderMenu,
  533. preUrl: '/tender/' + ctx.tender.id,
  534. };
  535. await this.layout('ledger/change.ejs', renderData, 'ledger/change_modal.ejs');
  536. } catch (err) {
  537. this.log(err);
  538. ctx.redirect(ctx.request.header.referer);
  539. }
  540. }
  541. /**
  542. * 计量台账页面 (Get)
  543. *
  544. * @param {object} ctx - egg全局变量
  545. * @return {void}
  546. */
  547. async index(ctx) {
  548. const renderData = {};
  549. await this.layout('ledger/index.ejs', renderData);
  550. }
  551. }
  552. return LedgerController;
  553. };