ledger_controller.js 23 KB

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