material_bills.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. 'use strict';
  2. /**
  3. * 期计量 数据模型
  4. *
  5. * @author Mai
  6. * @date 2018/8/13
  7. * @version
  8. */
  9. const auditConst = require('../const/audit').material;
  10. const materialConst = require('../const/material');
  11. const MaterialCalculator = require('../lib/material_calc');
  12. module.exports = app => {
  13. class MaterialBills extends app.BaseService {
  14. /**
  15. * 构造函数
  16. *
  17. * @param {Object} ctx - egg全局变量
  18. * @return {void}
  19. */
  20. constructor(ctx) {
  21. super(ctx);
  22. this.tableName = 'material_bills';
  23. }
  24. /**
  25. * 添加工料
  26. * @return {void}
  27. */
  28. async add() {
  29. if (!this.ctx.tender || !this.ctx.material) {
  30. throw '数据错误';
  31. }
  32. const order = await this._getMaxOrder(this.ctx.tender.id);
  33. const transaction = await this.db.beginTransaction();
  34. try {
  35. const newBills = {
  36. tid: this.ctx.tender.id,
  37. mid: this.ctx.material.id,
  38. order: order + 1,
  39. in_time: new Date(),
  40. };
  41. // 新增工料
  42. const result = await transaction.insert(this.tableName, newBills);
  43. if (result.affectedRows !== 1) {
  44. throw '新增工料数据失败';
  45. }
  46. const insertArray = [];
  47. const material_month = this.ctx.material.months ? this.ctx.material.months.split(',') : [];
  48. for (const ym of material_month) {
  49. const one_month = {
  50. tid: this.ctx.tender.id,
  51. mid: this.ctx.material.id,
  52. mb_id: result.insertId,
  53. msg_tp: null,
  54. yearmonth: ym,
  55. };
  56. insertArray.push(one_month);
  57. }
  58. if (insertArray.length !== 0) await transaction.insert(this.ctx.service.materialMonth.tableName, insertArray);
  59. await transaction.commit();
  60. return await this.getDataById(result.insertId);
  61. } catch (error) {
  62. console.log(error);
  63. await transaction.rollback();
  64. throw error;
  65. }
  66. }
  67. async _getMaxOrder(tenderId) {
  68. const sql = 'SELECT Max(??) As value FROM ?? Where tid = ' + tenderId;
  69. const sqlParam = ['order', this.tableName];
  70. const queryResult = await this.db.queryOne(sql, sqlParam);
  71. return queryResult.value ? queryResult.value : 0;
  72. }
  73. /**
  74. * 删除工料
  75. * @param {int} id 工料id
  76. * @return {void}
  77. */
  78. async del(id) {
  79. if (!this.ctx.tender || !this.ctx.material) {
  80. throw '数据错误';
  81. }
  82. // 判断t_type是否为费用,且存在quantity,m_spread值
  83. const transaction = await this.db.beginTransaction();
  84. try {
  85. const mbInfo = await this.getDataById(id);
  86. await transaction.delete(this.tableName, { id });
  87. let m_tp = this.ctx.material.m_tp;
  88. if (mbInfo.t_type === materialConst.t_type[1].value && mbInfo.quantity !== null && mbInfo.m_spread !== null) {
  89. // 金额发生变化,则重新计算本期金额
  90. m_tp = await this.calcMaterialMTp(transaction);
  91. }
  92. const material_month = this.ctx.material.months ? this.ctx.material.months.split(',') : [];
  93. if (material_month.length > 0) {
  94. await transaction.delete(this.ctx.service.materialMonth.tableName, { mb_id: id });
  95. }
  96. await transaction.commit();
  97. return m_tp;
  98. } catch (err) {
  99. await transaction.rollback();
  100. throw err;
  101. }
  102. }
  103. /**
  104. * 交换两个工料的顺序
  105. * @param {Number} id1 - 工料1的id
  106. * @param {Number} id2 - 工料2的id
  107. * @returns {Promise<void>}
  108. */
  109. async changeOrder(id1, id2) {
  110. if (!this.ctx.tender || !this.ctx.material) {
  111. throw '数据错误';
  112. }
  113. const bill1 = await this.getDataByCondition({ tid: this.ctx.tender.id, id: id1 });
  114. const bill2 = await this.getDataByCondition({ tid: this.ctx.tender.id, id: id2 });
  115. if (!bill1 || !bill2) {
  116. throw '数据错误';
  117. }
  118. const transaction = await this.db.beginTransaction();
  119. try {
  120. const order = bill1.order;
  121. bill1.order = bill2.order;
  122. bill2.order = order;
  123. await transaction.update(this.tableName, { id: bill1.id, order: bill1.order });
  124. await transaction.update(this.tableName, { id: bill2.id, order: bill2.order });
  125. await transaction.commit();
  126. return true;
  127. } catch (err) {
  128. await transaction.rollback();
  129. throw err;
  130. }
  131. }
  132. /**
  133. * 修改工料信息
  134. * @param {Object} data 工料内容
  135. * @return {void}
  136. */
  137. async save(data) {
  138. if (!this.ctx.tender || !this.ctx.material) {
  139. throw '数据错误';
  140. }
  141. delete data.in_time;
  142. // delete data.m_tp;
  143. // 判断是否可修改
  144. // 判断t_type是否为费用
  145. const transaction = await this.db.beginTransaction();
  146. try {
  147. await transaction.update(this.tableName, data);
  148. const m_tp = await this.calcMaterialMTp(transaction);
  149. await transaction.commit();
  150. return m_tp;
  151. } catch (err) {
  152. await transaction.rollback();
  153. throw err;
  154. }
  155. }
  156. async saveOrigin(data) {
  157. if (!this.ctx.tender || !this.ctx.material) {
  158. throw '数据错误';
  159. }
  160. return await this.db.update(this.tableName, { id: data.mb_id, origin: data.value });
  161. }
  162. async saveOrigins(datas) {
  163. if (!this.ctx.tender || !this.ctx.material) {
  164. throw '数据错误';
  165. }
  166. const updateData = [];
  167. for (const data of datas) {
  168. updateData.push({
  169. id: data.mb_id,
  170. origin: data.origin,
  171. });
  172. }
  173. if (updateData.length > 0) await this.db.updateRows(this.tableName, updateData);
  174. return true;
  175. }
  176. /**
  177. * 修改工料信息
  178. * @param {Object} data 工料内容
  179. * @return {void}
  180. */
  181. async saveDatas(datas) {
  182. if (!this.ctx.tender || !this.ctx.material) {
  183. throw '数据错误';
  184. }
  185. // 判断是否可修改
  186. // 判断t_type是否为费用
  187. const transaction = await this.db.beginTransaction();
  188. try {
  189. for (const data of datas) {
  190. delete data.in_time;
  191. // delete data.m_tp;
  192. // console.log(data);
  193. await transaction.update(this.tableName, data);
  194. }
  195. // console.log(datas);
  196. // await transaction.update(this.tableName, datas);
  197. const m_tp = await this.calcMaterialMTp(transaction);
  198. await transaction.commit();
  199. return m_tp;
  200. } catch (err) {
  201. await transaction.rollback();
  202. throw err;
  203. }
  204. }
  205. /**
  206. * 更新新一期的quantity和截止上期金额并返回本期总金额
  207. * @param transaction
  208. * @param tid
  209. * @param mid
  210. * @returns {Promise<number>}
  211. */
  212. async updateNewMaterial(transaction, tid, mid, ctx, stage_id, decimal) {
  213. const materialBillsData = await this.getAllDataByCondition({ where: { tid } });
  214. let m_tp = 0;
  215. let m_tax_tp = 0;
  216. const materialCalculator = new MaterialCalculator(ctx, stage_id, ctx.tender.info);
  217. for (const mb of materialBillsData) {
  218. const [one_tp, one_tax_tp] = await this.calcQuantityByMB(transaction, mid, mb, materialCalculator, decimal);
  219. m_tp = this.ctx.helper.add(m_tp, one_tp);
  220. m_tax_tp = this.ctx.helper.add(m_tax_tp, one_tax_tp);
  221. }
  222. return [m_tp, m_tax_tp];
  223. }
  224. /**
  225. * 修改quantity,m_spread值和返回单条调差金额(新增一期)
  226. * @param transaction
  227. * @param mid
  228. * @param mb
  229. * @returns {Promise<*>}
  230. */
  231. async calcQuantityByMB(transaction, mid, mb, materialCalculator, decimal) {
  232. const [newmsg_spread, newm_spread] = await this.getSpread(mb, null, decimal.up);
  233. if (mb.t_type === materialConst.t_type[0].value) {
  234. const sql = 'SELECT SUM(`gather_qty`*`quantity`) as quantity FROM ' + this.ctx.service.materialList.tableName + ' WHERE `mid`=? AND `mb_id`=? AND `is_join`=1';
  235. const sqlParam = [mid, mb.id];
  236. const mb_quantity = await transaction.queryOne(sql, sqlParam);
  237. console.log(mb_quantity);
  238. // 取历史期记录获取截止上期调差金额,并清空本期单价和时间,来源地,重新计算价差和有效价差
  239. const newQuantity = this.ctx.helper.round(mb_quantity.quantity, decimal.qty);
  240. const newTp = this.ctx.helper.round(this.ctx.helper.mul(newQuantity, newm_spread), decimal.tp);
  241. const updateData = {
  242. id: mb.id,
  243. quantity: newQuantity,
  244. msg_tp: null,
  245. msg_times: null,
  246. msg_spread: newmsg_spread,
  247. m_spread: newm_spread,
  248. origin: null,
  249. m_tp: newTp,
  250. pre_tp: mb.m_tp !== null ? this.ctx.helper.add(mb.pre_tp, mb.m_tp) : mb.pre_tp,
  251. m_tax_tp: this.ctx.helper.round(this.ctx.helper.mul(newTp, (1 + this.ctx.helper.div(mb.m_tax, 100))), decimal.tp),
  252. tax_pre_tp: mb.m_tax_tp !== null ? this.ctx.helper.add(mb.tax_pre_tp, mb.m_tax_tp) : mb.tax_pre_tp,
  253. };
  254. await transaction.update(this.tableName, updateData);
  255. const m_tp = mb.is_summary === 1 ? await this.ctx.helper.round(this.ctx.helper.mul(mb_quantity.quantity, newm_spread), decimal.tp) : 0;
  256. const m_tax_tp = this.ctx.helper.round(this.ctx.helper.mul(m_tp, (1 + this.ctx.helper.div(mb.m_tax, 100))), decimal.tp);
  257. return [m_tp, m_tax_tp];
  258. } else if (mb.t_type === materialConst.t_type[1].value) {
  259. const quantity = await materialCalculator.calculateExpr(mb.expr);
  260. const newTp = quantity !== 0 && quantity !== null ? this.ctx.helper.round(this.ctx.helper.mul(this.ctx.helper.round(quantity, decimal.qty), newm_spread), decimal.tp) : null;
  261. const updateData = {
  262. id: mb.id,
  263. quantity: quantity !== 0 && quantity !== null ? this.ctx.helper.round(quantity, decimal.qty) : null,
  264. msg_tp: null,
  265. msg_times: null,
  266. msg_spread: newmsg_spread,
  267. m_spread: newm_spread,
  268. origin: null,
  269. m_tp: newTp,
  270. pre_tp: mb.m_tp !== null ? this.ctx.helper.add(mb.pre_tp, mb.m_tp) : mb.pre_tp,
  271. m_tax_tp: this.ctx.helper.round(this.ctx.helper.mul(newTp, (1 + this.ctx.helper.div(mb.m_tax, 100))), decimal.tp),
  272. tax_pre_tp: mb.m_tax_tp !== null ? this.ctx.helper.add(mb.tax_pre_tp, mb.m_tax_tp) : mb.tax_pre_tp,
  273. };
  274. await transaction.update(this.tableName, updateData);
  275. const m_tp = mb.is_summary === 1 ? await this.ctx.helper.round(this.ctx.helper.mul(quantity, newm_spread), decimal.tp) : 0;
  276. const m_tax_tp = this.ctx.helper.round(this.ctx.helper.mul(m_tp, (1 + this.ctx.helper.div(mb.m_tax, 100))), decimal.tp);
  277. return [m_tp, m_tax_tp];
  278. }
  279. }
  280. /**
  281. * 清空本期信息价后更新价差和有效价差
  282. * @param data
  283. * @returns {Promise<void>}
  284. */
  285. async getSpread(data, msg_tp, newDecimalUp = this.ctx.material.decimal.up) {
  286. data.msg_tp = msg_tp;
  287. const msg_spread = this.ctx.helper.round(this.ctx.helper.sub(data.msg_tp, data.basic_price), newDecimalUp);
  288. const cor = msg_spread >= 0 ? this.ctx.helper.mul(data.basic_price, this.ctx.helper.div(data.m_up_risk, 100)) : this.ctx.helper.mul(data.basic_price, this.ctx.helper.div(data.m_down_risk, 100));
  289. const m_spread = Math.abs(msg_spread) > Math.abs(cor) ? (msg_spread > 0 ? this.ctx.helper.round(this.ctx.helper.sub(msg_spread, cor), newDecimalUp) : this.ctx.helper.round(this.ctx.helper.add(msg_spread, cor), newDecimalUp)) : 0;
  290. return [msg_spread, m_spread];
  291. }
  292. /**
  293. * 修改 expr和quantity值,返回本期金额和单条数据
  294. * @param data
  295. * @returns {Promise<void>}
  296. */
  297. async updateFYQuantity(data) {
  298. if (!this.ctx.tender || !this.ctx.material) {
  299. throw '数据错误';
  300. }
  301. const transaction = await this.db.beginTransaction();
  302. try {
  303. const mbInfo = await this.getDataById(data.id);
  304. data.m_tp = this.ctx.helper.round(this.ctx.helper.mul(data.quantity, mbInfo.m_spread), this.ctx.material.decimal.tp);
  305. data.m_tax_tp = this.ctx.helper.round(this.ctx.helper.mul(data.m_tp, (1 + this.ctx.helper.div(mbInfo.m_tax, 100))), this.ctx.material.decimal.tp);
  306. await transaction.update(this.tableName, data);
  307. let m_tp = this.ctx.material.m_tp;
  308. if (mbInfo.quantity !== data.quantity) {
  309. m_tp = await this.calcMaterialMTp(transaction);
  310. }
  311. await transaction.commit();
  312. const returnData = {
  313. m_tp,
  314. info: await this.getDataById(data.id),
  315. }
  316. return returnData;
  317. } catch (err) {
  318. await transaction.rollback();
  319. throw err;
  320. }
  321. }
  322. // 更改计算总金额并返回值
  323. async calcMaterialMTp(transaction) {
  324. // 金额发生变化,则重新计算本期金额
  325. const sql = 'SELECT SUM(`m_tp`) as total_price, SUM(IF(`m_tax_tp` is null, `m_tp`, `m_tax_tp`)) as tax_total_price FROM ' + this.tableName + ' WHERE `tid` = ? AND `is_summary` = 1';
  326. const sqlParam = [this.ctx.tender.id];
  327. const tp = await transaction.queryOne(sql, sqlParam);
  328. const updateData2 = {
  329. id: this.ctx.material.id,
  330. m_tp: tp.total_price,
  331. m_tax_tp: tp.tax_total_price,
  332. };
  333. console.log(tp);
  334. // if (this.ctx.material.material_tax) {
  335. // updateData2.m_tax_tp = tp.tax_total_price;
  336. // }
  337. await transaction.update(this.ctx.service.material.tableName, updateData2);
  338. return tp.total_price;
  339. }
  340. // 小数位变化更新单价和金额
  341. async resetDecimal(transaction, newDecimalUp, newDecimalTp) {
  342. const mbList = await transaction.select(this.tableName, { where: { tid: this.ctx.tender.id }, orders: [['order', 'asc']] });
  343. const updateList = [];
  344. const material_month = this.ctx.material.months ? this.ctx.material.months.split(',') : [];
  345. const updateMonthList = [];
  346. for (const mb of mbList) {
  347. const updateData = {
  348. id: mb.id,
  349. };
  350. if (newDecimalUp !== this.ctx.material.decimal.up) {
  351. let newmsg_tp = this.ctx.helper.round(mb.msg_tp, newDecimalUp);
  352. mb.msg_tp = newmsg_tp;
  353. // 判断是否有月信息价,如果有则msg_tp值由月信息价的平均单价获得,并更新月信息价单价
  354. if (material_month.length > 0) {
  355. const monthList = await transaction.select(this.ctx.service.materialMonth.tableName, { where: { mb_id: mb.id, mid: this.ctx.material.id } });
  356. if (monthList.length !== 0) {
  357. for (const m of monthList) {
  358. // 更新月信息单价小数位
  359. const newMonthMsgTP = this.ctx.helper.round(m.msg_tp, newDecimalUp);
  360. if (m.msg_tp && newMonthMsgTP !== m.msg_tp) {
  361. m.msg_tp = newMonthMsgTP;
  362. updateMonthList.push({ id: m.id, msg_tp: m.msg_tp });
  363. }
  364. }
  365. const mb_msg_tp_sum = this._.sumBy(monthList, 'msg_tp');
  366. const month_num = material_month.length - this.ctx.helper.arrayCount(this._.map(monthList, 'msg_tp'), [null, '', 0]);
  367. newmsg_tp = month_num !== 0 ? this.ctx.helper.round(this.ctx.helper.div(mb_msg_tp_sum, month_num), newDecimalUp) : null;
  368. mb.msg_tp = newmsg_tp;
  369. }
  370. }
  371. const newbasic_price = this.ctx.helper.round(mb.basic_price, newDecimalUp);
  372. mb.basic_price = newbasic_price;
  373. const [newmsg_spread, newm_spread] = await this.getSpread(mb, mb.msg_tp, newDecimalUp);
  374. mb.m_spread = newm_spread;
  375. updateData.basic_price = newbasic_price;
  376. updateData.msg_tp = newmsg_tp;
  377. updateData.msg_spread = newmsg_spread;
  378. updateData.m_spread = newm_spread;
  379. }
  380. const newTp = this.ctx.helper.round(this.ctx.helper.mul(mb.quantity, mb.m_spread), newDecimalTp);
  381. updateData.m_tp = newTp;
  382. updateData.m_tax_tp = this.ctx.helper.round(this.ctx.helper.mul(newTp, (1 + this.ctx.helper.div(mb.m_tax, 100))), newDecimalTp);
  383. updateList.push(updateData);
  384. }
  385. if (updateMonthList.length > 0) await transaction.updateRows(this.ctx.service.materialMonth.tableName, updateMonthList);
  386. if (updateList.length > 0) await transaction.updateRows(this.tableName, updateList);
  387. }
  388. }
  389. return MaterialBills;
  390. };