ledger_controller.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  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, 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. const renderData = {
  108. tender: tender.data,
  109. tenderInfo: tender.info,
  110. auditConst,
  111. auditors,
  112. curAuditor,
  113. user,
  114. auditHistory,
  115. content,
  116. ledger: JSON.stringify(ledgerData),
  117. ledgerSpreadSetting: JSON.stringify(ledgerSpread),
  118. posSpreadSetting: JSON.stringify(posSpread),
  119. tenderMenu,
  120. preUrl: '/tender/' + tender.id,
  121. measureType,
  122. jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.ledger.explode),
  123. };
  124. if ((tender.data.ledger_status === auditConst.status.uncheck || tender.data.ledger_status === auditConst.status.checkNo) && tender.data.user_id === ctx.session.sessionUser.accountId) {
  125. renderData.accountGroup = accountGroup;
  126. // 获取所有项目参与者
  127. const accountList = await ctx.service.projectAccount.getAllDataByCondition({
  128. where: { project_id: ctx.session.sessionProject.id, enable: 1 },
  129. columns: ['id', 'name', 'company', 'role', 'enable', 'is_admin', 'account_group'],
  130. });
  131. renderData.accountList = accountList;
  132. renderData.auditorList = await ctx.service.ledgerAudit.getAuditors(tender.id, tender.data.ledger_times);
  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. * 从签约清单添加节点
  347. * @param ctx
  348. * @returns {Promise<void>}
  349. */
  350. async addFromDealBills(ctx) {
  351. try {
  352. if (ctx.tender.ledgerReadOnly) {
  353. throw '数据错误';
  354. }
  355. const data = JSON.parse(ctx.request.body.data);
  356. if ((isNaN(data.id) || data.id <= 0) || !data.type || !data.dealBills) {
  357. throw '参数错误';
  358. }
  359. let result;
  360. if (data.type === 'child') {
  361. result = await ctx.service.ledger.addChild(ctx.tender.id, data.id, data.dealBills);
  362. } else if (data.type === 'next') {
  363. result = await ctx.service.ledger.addNode(ctx.tender.id, data.id, data.dealBills);
  364. } else {
  365. throw '参数错误';
  366. }
  367. ctx.body = {err: 0, msg: '', data: result};
  368. } catch (err) {
  369. this.log(err);
  370. if (err.stack) {
  371. ctx.body = {err: 1, msg: '未知错误', data: null};
  372. } else {
  373. ctx.body = {err: 1, msg: err, data: null};
  374. }
  375. }
  376. }
  377. /**
  378. * 批量插入数据 (Ajax)
  379. *
  380. * data = {id, batchData, batchType}
  381. * data.batchType = 'batchInsertChild'/'batchInsertNext'
  382. * data.batchData = [{name, children}] -- 项目节列表
  383. * data.batchData.children = [{code, name, unit, unit_price, quantity}] -- 工程量清单列表
  384. *
  385. * @param ctx
  386. * @return {Promise<void>}
  387. */
  388. async batchInsert(ctx) {
  389. const responseData = {
  390. err: 0,
  391. msg: '',
  392. data: [],
  393. };
  394. try {
  395. if (!ctx.tender.data || ctx.tender.data.user_id !== ctx.session.sessionUser.accountId || this._ledgerReadOnly()) {
  396. throw '标段数据错误';
  397. }
  398. const data = JSON.parse(ctx.request.body.data);
  399. if ((isNaN(data.id) || data.id <= 0) || !data.batchType) {
  400. throw '参数错误';
  401. }
  402. switch (data.batchType) {
  403. case 'child':
  404. responseData.data = await ctx.service.ledger.batchInsertChild(ctx.tender.id, data.id, data.batchData);
  405. break;
  406. case 'next':
  407. responseData.data = await ctx.service.ledger.batchInsertNext(ctx.tender.id, data.id, data.batchData);
  408. break;
  409. default:
  410. throw '参数错误';
  411. }
  412. } catch (err) {
  413. this.log(err);
  414. responseData.err = 1;
  415. responseData.msg = err;
  416. }
  417. ctx.body = responseData;
  418. }
  419. /**
  420. * 查询
  421. *
  422. * @param ctx
  423. * @return {Promise<void>}
  424. */
  425. async search(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 (!data.keyword || data.keyword === '') {
  438. throw '参数错误';
  439. }
  440. responseData.data = await ctx.service.ledger.search(tenderId, {
  441. value: ctx.app.mysql.escape('%' + data.keyword + '%'),
  442. operate: 'Like',
  443. fields: ['code', 'b_code', 'name'],
  444. });
  445. } catch (err) {
  446. this.log(err);
  447. responseData.err = 1;
  448. responseData.msg = err;
  449. }
  450. ctx.body = responseData;
  451. }
  452. /**
  453. * 定位
  454. * @param ctx
  455. * @return {Promise<void>}
  456. */
  457. async locate(ctx) {
  458. const responseData = {
  459. err: 0,
  460. msg: '',
  461. data: [],
  462. };
  463. try {
  464. const tenderId = ctx.params.id;
  465. if (!tenderId) {
  466. throw '当前未打开标段';
  467. }
  468. const data = JSON.parse(ctx.request.body.data);
  469. if ((isNaN(data.id) || data.id <= 0)) {
  470. throw '参数错误';
  471. }
  472. responseData.data = await ctx.service.ledger.locateNode(tenderId, data.id);
  473. } catch (err) {
  474. this.log(err);
  475. responseData.err = 1;
  476. responseData.msg = err;
  477. }
  478. ctx.body = responseData;
  479. }
  480. /**
  481. * 获取全部子节点
  482. *
  483. * @param ctx
  484. * @return {Promise<void>}
  485. */
  486. async posterity(ctx) {
  487. const responseData = {
  488. err: 0,
  489. msg: '',
  490. data: [],
  491. };
  492. try {
  493. const tenderId = ctx.params.id;
  494. if (!tenderId) {
  495. throw '当前未打开标段';
  496. }
  497. const data = JSON.parse(ctx.request.body.data);
  498. if ((isNaN(data.id) || data.id <= 0)) {
  499. throw '参数错误';
  500. }
  501. const expandData = await ctx.service.ledger.getPosterityByParentId(tenderId, data.id);
  502. responseData.data = { expand: expandData };
  503. } catch (err) {
  504. this.log(err);
  505. responseData.err = 1;
  506. responseData.msg = err;
  507. }
  508. ctx.body = responseData;
  509. }
  510. /**
  511. * 获取部位明细数据(Ajax)
  512. *
  513. * @param ctx
  514. * @return {Promise<void>}
  515. */
  516. async pos(ctx) {
  517. try {
  518. await this.checkMeasureType(measureType.tz.value);
  519. const condition = JSON.parse(ctx.request.body.data) || {};
  520. condition.tid = ctx.tender.id;
  521. const posData = await ctx.service.pos.getPosData(condition);
  522. ctx.body = { err: 0, msg: '', data: posData };
  523. } catch (err) {
  524. this.log(err);
  525. ctx.body = { err: 1, msg: err.toString(), data: [] };
  526. }
  527. }
  528. /**
  529. * 更新 部位明细数据
  530. *
  531. * @param ctx
  532. * @return {Promise<void>}
  533. */
  534. async posUpdate(ctx) {
  535. try {
  536. await this.checkMeasureType(measureType.tz.value);
  537. const data = JSON.parse(ctx.request.body.data);
  538. const responseData = await ctx.service.pos.savePosData(data, ctx.tender.id);
  539. ctx.body = { err: 0, msg: '', data: responseData };
  540. } catch (err) {
  541. this.log(err);
  542. ctx.body = { err: 1, msg: err.toString(), data: null };
  543. }
  544. }
  545. /**
  546. * 复制粘贴 部位明细
  547. * @param ctx
  548. * @returns {Promise<void>}
  549. */
  550. async posPaste(ctx) {
  551. try {
  552. await this.checkMeasureType(measureType.tz.value);
  553. const data = JSON.parse(ctx.request.body.data);
  554. const result = await ctx.service.pos.pastePosData(data, ctx.tender.id);
  555. ctx.body = { err: 0, msg: '', data: result };
  556. } catch(err) {
  557. this.log(err);
  558. ctx.body = { err: 1, msg: err.toString(), data: null };
  559. }
  560. }
  561. /**
  562. * 上传 清单Excel 并导入
  563. * @param ctx
  564. * @return {Promise<void>}
  565. */
  566. async uploadExcel(ctx) {
  567. try {
  568. const compressData = ctx.request.body.data;
  569. const data = JSON.parse(LzString.decompressFromUTF16(compressData));
  570. const responseData = { err: 0, msg: '', data: {}, };
  571. await ctx.service.ledger.importExcel(data);
  572. responseData.data.bills = await ctx.service.ledger.getDataByTenderId(ctx.tender.id, -1);
  573. responseData.data.pos = await ctx.service.pos.getPosData(null);
  574. ctx.body = responseData;
  575. } catch (err) {
  576. this.log(err);
  577. ctx.body = { err: 1, msg: err.toString(), data: null };
  578. }
  579. }
  580. /**
  581. * 下载(清单Excel模板 or 导出项目台账Excel)
  582. * @param ctx
  583. * @return {Promise<void>}
  584. */
  585. async download(ctx) {
  586. const file = ctx.params.file;
  587. if (file) {
  588. try {
  589. let fileName;
  590. if (file === 'template') {
  591. fileName = this.app.baseDir + '/app/public/files/template/ledger/导入分项清单Excel格式.xls';
  592. } else if (file === 'ledger') {
  593. const create_time = Date.parse(new Date()) / 1000;
  594. fileName = this.app.baseDir + '/app/public/files/downloads/ledger/' + ctx.tender.id + '-' + create_time + '.xlsx';
  595. // todo 导出台账清单Excel
  596. }
  597. ctx.body = await fs.readFileSync(fileName);
  598. } catch (err) {
  599. this.log(err);
  600. this.setMessage(err.toString(), this.messageType.ERROR);
  601. }
  602. }
  603. }
  604. /**
  605. * 台账变更页面 (Get)
  606. *
  607. * @param {object} ctx - egg全局变量
  608. * @return {void}
  609. */
  610. async change(ctx) {
  611. try {
  612. const renderData = {
  613. tender: ctx.tender.data,
  614. tenderMenu: this.menu.tenderMenu,
  615. preUrl: '/tender/' + ctx.tender.id,
  616. };
  617. await this.layout('ledger/change.ejs', renderData, 'ledger/change_modal.ejs');
  618. } catch (err) {
  619. this.log(err);
  620. ctx.redirect(ctx.request.header.referer);
  621. }
  622. }
  623. /**
  624. * 计量台账页面 (Get)
  625. *
  626. * @param {object} ctx - egg全局变量
  627. * @return {void}
  628. */
  629. async index(ctx) {
  630. const renderData = {};
  631. await this.layout('ledger/index.ejs', renderData);
  632. }
  633. }
  634. return LedgerController;
  635. };