order.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776
  1. 'use strict';
  2. const _ = require('lodash');
  3. const assert = require('assert');
  4. const { ObjectId } = require('mongoose').Types;
  5. const { CrudService } = require('naf-framework-mongoose/lib/service');
  6. const { BusinessError, ErrorCode } = require('naf-core').Error;
  7. class OrderService extends CrudService {
  8. constructor(ctx) {
  9. super(ctx, 'order');
  10. this.model = this.ctx.model.Order;
  11. this.util = this.ctx.service.util.util;
  12. }
  13. /**
  14. * 新添订单
  15. * @param {Object} data 订单数据
  16. */
  17. async create(data) {
  18. const res = await this.model.create(data);
  19. // 复制进split中
  20. if (!res) throw new BusinessError(ErrorCode.SERVICE_FAULT, '订单创建失败');
  21. await this.copyToSplit(res._id);
  22. try {
  23. this.record(res._id, { method: 'create' });
  24. } catch (error) {
  25. this.logger.error(`订单id:${res.id}记录创建失败:${error.toString()}`);
  26. }
  27. return res;
  28. }
  29. /**
  30. * 修改订单
  31. * @param {Object} { id, ...data } 要修改的订单数据
  32. */
  33. async update({ id, ...data }) {
  34. const res = await this.model.findById(id);
  35. const { goods: newGoods, ...info } = data;
  36. let { goods: oldGoods, split } = res;
  37. if (oldGoods) oldGoods = JSON.parse(JSON.stringify(oldGoods));
  38. if (split) split = JSON.parse(JSON.stringify(split));
  39. // 找到删除项
  40. // oldGoods中有,但是newGoods中却没有的数据,就是要删除的;且需要检验被删除的数据有没有被拆分
  41. oldGoods = oldGoods.map(og => {
  42. const need_delete = newGoods.find(f => ObjectId(f._id).equals(og._id));
  43. if (!need_delete) {
  44. const sobj = split.find(f => ObjectId(f.pid).equals(og._id));
  45. if (sobj) {
  46. // 查找其有没有被拆分
  47. const r = split.find(f => ObjectId(f.pid).equals(sobj._id));
  48. if (r) {
  49. throw new BusinessError(
  50. ErrorCode.DATA_INVALID,
  51. '无法删除已被拆分过的货物'
  52. );
  53. }
  54. const s_index = split.findIndex(f =>
  55. ObjectId(f.pid).equals(og._id)
  56. );
  57. split.splice(s_index, 1);
  58. }
  59. return undefined;
  60. }
  61. return og;
  62. });
  63. oldGoods = _.compact(oldGoods);
  64. // 判断是否有修改项
  65. const updateRes = await this.goodsUpdate(oldGoods, newGoods, split);
  66. if (updateRes) {
  67. const { oldGoods: ogs, split: splist } = updateRes;
  68. if (ogs) oldGoods = ogs;
  69. if (splist) split = splist;
  70. }
  71. // 判断有没有新添的数据
  72. const addGoods = newGoods.filter(f => !f._id);
  73. if (addGoods.length > 0) {
  74. // 有新增货物项
  75. // TODO,复制到split中和oldGoods中
  76. for (const ng of addGoods) {
  77. oldGoods.push(ng);
  78. }
  79. }
  80. res.goods = oldGoods;
  81. res.split = split;
  82. await this.model.update({ _id: ObjectId(id) }, info);
  83. await res.save();
  84. this.copyToSplit(id);
  85. try {
  86. this.record(res._id, { method: 'update' });
  87. } catch (error) {
  88. this.logger.error(`订单id:${res.id}记录创建失败:${error.toString()}`);
  89. }
  90. return;
  91. }
  92. /**
  93. * 检查并处理有修改的货物:拆分/发车就不允许修改了
  94. * @param {Array} oldGoods 查出修改前的货物列表
  95. * @param {Array} newGoods 修改后的货物列表
  96. * @param {Array} split 原拆分货物列表,用来检查是否可以修改
  97. */
  98. async goodsUpdate(oldGoods, newGoods, split) {
  99. oldGoods = oldGoods.map(og => {
  100. const is_split = split.find(
  101. f =>
  102. ObjectId(f.pid).equals(og._id) && f.type === '1' && f.status === '0'
  103. );
  104. if (is_split) return og;
  105. // 没有拆分,可以直接更换
  106. const ng = newGoods.find(f => ObjectId(f._id).equals(og._id));
  107. if (ng) return { ...ng, update: true };
  108. return og;
  109. });
  110. // 修改拆分货物列表
  111. const toUpdateSplit = oldGoods.filter(f => f.update);
  112. split = split.map(i => {
  113. const res = toUpdateSplit.find(f => ObjectId(f._id).equals(i.pid));
  114. if (res) {
  115. const obj = this.goodsCopy(res);
  116. return obj;
  117. }
  118. return i;
  119. });
  120. // 将oldGoods的update摘出去
  121. oldGoods = oldGoods.map(i => _.omit(i, [ 'update' ]));
  122. return { oldGoods, split };
  123. }
  124. /**
  125. * 将货物复制成拆分货物的数据并返回
  126. * @param {Object} data 货物列表的每项
  127. * @return split
  128. */
  129. goodsCopy(data) {
  130. const split = _.pick(data, [
  131. 'name',
  132. 'number',
  133. 'weight',
  134. 'volume',
  135. 'transport_type',
  136. 'remark',
  137. ]);
  138. split.pid = data._id;
  139. return split;
  140. }
  141. /**
  142. * 根据订单id,复制货物 到 拆分货物,只是复制,其他操作在各自的方法中,这里只是复制
  143. * @param {String} id 订单id
  144. */
  145. async copyToSplit(id) {
  146. const order = await this.model.findById(id);
  147. if (!order) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到订单');
  148. const { goods, split } = order;
  149. for (const g of goods) {
  150. const has = split.find(f => ObjectId(f.pid).equals(g._id));
  151. if (!has) {
  152. order.split.push(this.goodsCopy(g));
  153. }
  154. }
  155. await order.save();
  156. }
  157. /**
  158. * 发货,添加记录及修改状态
  159. * @param {Array} goods 发货的货物列表
  160. * @param {String} no 运输单号
  161. * @param {String} time 发货日期
  162. */
  163. async sendGoods(goods, no, time) {
  164. for (const g of goods) {
  165. const { split_id, name, number, weight, volume } = g;
  166. const order = await this.model.findOne({
  167. 'split._id': ObjectId(split_id),
  168. });
  169. if (!order) {
  170. throw new BusinessError(
  171. ErrorCode.DATA_NOT_EXIST,
  172. `未找到该${name}所在的订单信息`
  173. );
  174. }
  175. const obj = { split_id, name, number, weight, volume, no, time };
  176. // 添加该货物的发货记录
  177. order.send_time.push(obj);
  178. // 修改该货物的状态
  179. const good = order.split.id(split_id);
  180. good.status = '1';
  181. console.log(good);
  182. order.goods_status = await this.checkGoodsStatus(order.split);
  183. await this.model.update({ _id: order._id }, order);
  184. // 更新记录
  185. let message = `${name}已发货(数量:${number};重量:${weight}吨;体积:${volume}m³)`;
  186. const all_send = order.split.every(e => e.status === '0');
  187. if (all_send) message = `${message}; 所有货物已发出`;
  188. try {
  189. this.record(order._id, { method: 'send', message });
  190. } catch (error) {
  191. this.logger.error(`订单id:${order.id}记录创建失败:${error.toString()}`);
  192. }
  193. }
  194. }
  195. /**
  196. * 签收,添加记录及修改状态
  197. * @param {Array} goods 签收的货物列表
  198. * @param {String} no 运输单号
  199. * @param {String} time 签收时间
  200. */
  201. async arriveGoods(goods, no, time) {
  202. for (const g of goods) {
  203. const { split_id, name, number, weight, volume } = g;
  204. const order = await this.model.findOne({
  205. 'split._id': ObjectId(split_id),
  206. });
  207. if (!order) {
  208. throw new BusinessError(
  209. ErrorCode.DATA_NOT_EXIST,
  210. `未找到该${name}所在的订单信息`
  211. );
  212. }
  213. const obj = { split_id, name, number, weight, volume, no };
  214. if (time) obj.time = time;
  215. // 添加收货记录
  216. order.arrive_time.push(obj);
  217. // 修改该货物的状态
  218. const good = order.split.id(split_id);
  219. good.status = '-1';
  220. order.goods_status = await this.checkGoodsStatus(order.split);
  221. await this.model.update({ _id: order._id }, order);
  222. // 更新记录
  223. let message = `${name}已签收(数量:${number};重量:${weight}吨;体积:${volume}m³)`;
  224. const all_arrive = order.split.every(e => e.status === '-1');
  225. if (all_arrive) message = `${message}; 所有货物已签收`;
  226. try {
  227. this.record(order._id, { method: 'arrive', message });
  228. } catch (error) {
  229. this.logger.error(`订单id:${order.id}记录创建失败:${error.toString()}`);
  230. }
  231. }
  232. }
  233. /**
  234. * 检查拆分货物列表,更新goods_status
  235. * @param {Array} splitList 拆分货物列表
  236. */
  237. async checkGoodsStatus(splitList) {
  238. // 未发车
  239. const res = splitList.every(e => e.status === '0');
  240. if (res) return '未发货';
  241. // 检查是否全发车的货物
  242. const all_send = splitList.every(e => e.status === '1');
  243. if (all_send) return '所有货物已发出';
  244. // 检查是否全到达了
  245. const all_arrive = splitList.every(e => e.status === '-1');
  246. if (all_arrive) return '所有货物全部到达';
  247. // 检查是否有发货的
  248. const is_send = splitList.some(e => e.status === '1');
  249. // 检查是否有到达的
  250. const is_arrive = splitList.some(e => e.status === '-1');
  251. const word = [];
  252. if (is_send) word.push('部分货物已发出');
  253. if (is_arrive) word.push('部分货物已到达');
  254. if (word.length > 0) return word.join(';');
  255. return '状态错误';
  256. }
  257. /**
  258. * 修改订单负责人
  259. * @param {Object} data 订单数据
  260. */
  261. async principalChange(data) {
  262. const { principal, _id } = data;
  263. const res = await this.model.update({ _id: ObjectId(_id) }, { principal });
  264. try {
  265. // 跨库查询该用户姓名
  266. const user = await this.ctx.service.httpUtil.cget(
  267. `/user/${principal}`,
  268. 'userAuth',
  269. { _tenant: 'zhwl' }
  270. );
  271. const message = `变更订单负责人 ${user.name}`;
  272. this.record(_id, { method: 'principal', message });
  273. } catch (error) {
  274. this.logger.error(`订单id:${res.id}记录创建失败:${error.toString()}`);
  275. }
  276. return res;
  277. }
  278. /**
  279. * 订单操作记录
  280. * @param {String} id 订单id
  281. * @param {Object} {method:方法, message:自定义文字} 参数
  282. */
  283. async record(id, { method, message }) {
  284. const order = await this.model.findById(id);
  285. if (!order) {
  286. throw new BusinessError(
  287. ErrorCode.DATA_NOT_EXIST,
  288. `未找到订单,传入id:${id}`
  289. );
  290. }
  291. const { authorization } = this.ctx.request.header;
  292. let user = decodeURI(authorization);
  293. if (!user) {
  294. throw new BusinessError(ErrorCode.USER_NOT_EXIST, '未找到操作人');
  295. }
  296. user = JSON.parse(user);
  297. const { id: userid, name: username } = user;
  298. const record = { opera: username, operaid: userid };
  299. // 创建记录
  300. if (method === 'create') record.message = `${username}创建订单`;
  301. else if (method === 'update') record.message = `${username}修改订单`;
  302. else if (method === 'in') record.message = `${username}修改收入`;
  303. else if (method === 'out') record.message = `${username}修改支出`;
  304. else if (method === 'split') record.message = `${username}拆分货物`;
  305. else if (method === 'js') record.message = `${username}结算订单`;
  306. else if (
  307. method === 'send' ||
  308. method === 'arrive' ||
  309. method === 'principal' ||
  310. method === 'outJs'
  311. ) {
  312. record.message = `${message}`;
  313. }
  314. order.record.push(record);
  315. await order.save();
  316. }
  317. /**
  318. * 客户结算查询
  319. * @param {Object} query 查询条件
  320. */
  321. async clientCalculate(query) {
  322. query = this.util.turnDateRangeQuery(this.util.turnFilter(query));
  323. let list = await this.model
  324. .$where('this.split.every(i=>i.status=== "-1")')
  325. .find({ ...query, is_js: false })
  326. .populate([
  327. {
  328. path: 'client',
  329. model: 'Client',
  330. },
  331. {
  332. path: 'item',
  333. model: 'Item',
  334. },
  335. {
  336. path: 'route',
  337. model: 'Route',
  338. },
  339. {
  340. path: 'treaty',
  341. model: 'Treaty',
  342. },
  343. {
  344. path: 'goods',
  345. populate: [
  346. {
  347. path: 'mode',
  348. model: 'Mode',
  349. },
  350. ],
  351. },
  352. ]);
  353. if (list.length > 0) list = JSON.parse(JSON.stringify(list));
  354. // 组织数据
  355. list = this.toResetOrder(list);
  356. return list;
  357. }
  358. /**
  359. * 整合订单信息
  360. * @param {Array} list 订单列表
  361. */
  362. toResetOrder(list) {
  363. list = list.map(i => {
  364. i = this.orderReset(i);
  365. i = this.orderIn(i);
  366. return i;
  367. });
  368. return list;
  369. }
  370. /**
  371. * 将客户相关信息转换
  372. * @param {Object} order 订单信息
  373. */
  374. orderReset(order) {
  375. order = JSON.parse(JSON.stringify(order));
  376. const { client, item, route, treaty } = order;
  377. if (client && _.isObject(client)) {
  378. order.client_id = _.get(client, '_id');
  379. order.client = _.get(client, 'name');
  380. }
  381. if (item && _.isObject(item)) {
  382. order.item_id = _.get(item, '_id');
  383. order.item = _.get(item, 'name');
  384. }
  385. if (route && _.isObject(route)) {
  386. order.route_id = _.get(route, '_id');
  387. order.route = _.get(route, 'name');
  388. }
  389. if (treaty && _.isObject(treaty)) {
  390. order.treaty_id = _.get(treaty, '_id');
  391. order.treaty = _.get(treaty, 'number');
  392. }
  393. return order;
  394. }
  395. /**
  396. * 计算收入总价
  397. * @param {Object} order 订单信息
  398. */
  399. orderIn(order) {
  400. const { goods, in_bill } = order;
  401. const gi = goods.reduce((p, n) => p + (n.sh_ss || 0), 0);
  402. const bi = in_bill.reduce((p, n) => p + (n.sh_ss || 0), 0);
  403. const sh_ss = gi + bi;
  404. order.sh_ss = sh_ss;
  405. return order;
  406. }
  407. /**
  408. * 选择指定的订单,导出收入excel
  409. * @param {Object} query 查询条件
  410. * @property Array ids 订单id集合
  411. */
  412. async clientExport(query) {
  413. const { ids } = query;
  414. console.log(ids);
  415. let list = await this.model
  416. .find({ _id: ids.map(i => ObjectId(i)) })
  417. .populate([
  418. {
  419. path: 'client',
  420. model: 'Client',
  421. },
  422. {
  423. path: 'item',
  424. model: 'Item',
  425. },
  426. {
  427. path: 'route',
  428. model: 'Route',
  429. },
  430. {
  431. path: 'treaty',
  432. model: 'Treaty',
  433. },
  434. {
  435. path: 'goods',
  436. populate: [
  437. {
  438. path: 'mode',
  439. model: 'Mode',
  440. },
  441. ],
  442. },
  443. ]);
  444. if (list.length > 0) list = JSON.parse(JSON.stringify(list));
  445. // 处理信息
  446. const arr = [];
  447. // 获取头
  448. const { header, otherList } = this.getHeader(list);
  449. // 已经被占用的行数
  450. let ouse = 1;
  451. for (const order of list) {
  452. // 计算这个订单的开始行和结束行位置
  453. const os = ouse + 1; // 订单开始行数
  454. const { goods } = order;
  455. // 整理共同部分数据
  456. const edata = this.getPublicExcelData(order, os);
  457. // 再处理额外项的头数据
  458. const odata = this.getOtherInExcelData(order, os, otherList);
  459. arr.push({ ...edata, ...odata });
  460. // 处理完后,更新被占用行数
  461. ouse = ouse + goods.length;
  462. }
  463. // 处理头
  464. let pkeys = Object.keys(header);
  465. pkeys = pkeys.map(i => {
  466. const reg = /[^a-zA-Z]/i;
  467. const res = i.replace(reg, '');
  468. return res;
  469. });
  470. let lastData = arr.map(i => ({ content: i }));// this.toResetExcelData(pkeys, arr);
  471. lastData.push({ content: header });
  472. const alignment = { vertical: 'middle', horizontal: 'center' };
  473. lastData = lastData.map(i => ({ ...i, alignment }));
  474. const res = await this.ctx.service.util.excel.toExcel({ data: lastData });
  475. return res;
  476. }
  477. /**
  478. * 合并处理数据
  479. * @param {Array} keys 列字母数组
  480. * @param {Array} data excel数据(不含头)
  481. */
  482. toResetExcelData(keys, data) {
  483. const reg = /[^a-zA-Z]/i;
  484. const arr = [];
  485. const clear = [];
  486. for (const key of keys) {
  487. // 找出每列的内容
  488. const col = data.map(i => {
  489. const lks = Object.keys(i);
  490. const r = lks.find(f => {
  491. const rr = f.replace(reg, '');
  492. return rr === key;
  493. });
  494. if (r) return { key: r, value: _.get(i, r) };
  495. });
  496. // 同一列满足以下条件可以合并:
  497. // 1,值相同;2数字连贯;3指定范围内
  498. // 先查范围
  499. const head = _.head(col);
  500. if (!head) continue;
  501. const letter = head.key.replace(reg, '');
  502. const r3 = this.mergeRange(letter);
  503. if (!r3) continue;
  504. const l = col.length;
  505. const ul = _.uniqBy(col, 'value').length;
  506. if (ul === l) continue;
  507. // 可以合并,需要重新拼个Object,{scell,ecell,content}
  508. // scell 是上面head的key, ecell是last获取key,content,随意拿出一个就行
  509. const obj = {};
  510. obj.scell = _.get(head, 'key');
  511. obj.ecell = _.get(_.last(col), 'key');
  512. obj.content = _.get(head, 'value');
  513. clear.push(_.get(head, 'key'), _.get(_.last(col), 'key'));
  514. arr.push(obj);
  515. }
  516. // 将scell和ecell都干掉
  517. data = data.map(i => {
  518. i = _.omitBy(i, (value, key) => {
  519. return clear.includes(key);
  520. });
  521. return { content: i };
  522. });
  523. data = [ ...data, ...arr ];
  524. return data;
  525. }
  526. /**
  527. * 查询该列是否可以合并
  528. * @param {String} letter 字母
  529. */
  530. mergeRange(letter) {
  531. const arr = [ 'U', 'Z', 'AA', 'AB', 'AC' ];
  532. return !arr.includes(letter);
  533. }
  534. /**
  535. * 整理成excel数据(先整理固定部分,即不包含额外收入部分)
  536. * @param {Object} order 订单信息
  537. * @param {Number} os 该订单开始行数
  538. */
  539. getPublicExcelData(order, os) {
  540. // 如果不需要合并,那一个订单也就是1个object,如果需要合并,那就是多个object,
  541. // 即使是多个object也没问题,因为控制的是每个单元格,所以出问题一定是单元格没弄明白
  542. // 除了货物,方式外,其他的固定项都需要判断是否需要合并单元格,所以先处理货物,方式
  543. const arr = [];
  544. const content = {};
  545. const { goods } = order;
  546. // 货物,方式部分
  547. for (let i = 0; i < goods.length; i++) {
  548. const good = goods[i];
  549. const {
  550. mode,
  551. name,
  552. costname = '运费',
  553. sq_ys,
  554. sq_ss,
  555. sh_ys,
  556. sh_ss,
  557. } = good;
  558. const { name: modeName, price, is_lf, send_type, computed_type } = mode;
  559. content[`S${i + os}`] = modeName;
  560. content[`T${i + os}`] = price;
  561. content[`U${i + os}`] = name;
  562. content[`V${i + os}`] = costname;
  563. content[`W${i + os}`] = is_lf ? '是' : '否';
  564. content[`X${i + os}`] = send_type;
  565. content[`Y${i + os}`] = computed_type;
  566. content[`Z${i + os}`] = sq_ys;
  567. content[`AA${i + os}`] = sq_ss;
  568. content[`AB${i + os}`] = sh_ys;
  569. content[`AC${i + os}`] = sh_ss;
  570. // 订单
  571. content[`A${i + os}`] = _.get(order, 'order_no');
  572. content[`AD${i + os}`] = _.get(order, 'remark');
  573. // 客户部分一定合并,不在这里处理
  574. content[`B${i + os}`] = _.get(order.client, 'name');
  575. content[`C${i + os}`] = _.get(order.client, 'address');
  576. content[`D${i + os}`] = _.get(order.client, 'legal');
  577. content[`E${i + os}`] = _.get(order.client, 'mobile');
  578. content[`F${i + os}`] = _.get(order.client, 'taxes_no');
  579. content[`G${i + os}`] = _.get(order.client, 'account_bank');
  580. content[`H${i + os}`] = _.get(order.client, 'account');
  581. // 合同部分
  582. content[`I${i + os}`] = _.get(order.treaty, 'number');
  583. content[`J${i + os}`] = _.get(order.treaty, 'jf');
  584. content[`K${i + os}`] = _.get(order.treaty, 'yf');
  585. content[`L${i + os}`] = _.get(order.treaty, 'period');
  586. content[`M${i + os}`] = _.get(order.treaty, 'settle_up');
  587. // 项目
  588. content[`N${i + os}`] = _.get(order.item, 'name');
  589. content[`O${i + os}`] = _.get(order.item, 'taxes');
  590. // 线路
  591. content[`P${i + os}`] = _.get(order.route, 'name');
  592. content[`Q${i + os}`] = _.get(order.route, 's_p');
  593. content[`R${i + os}`] = _.get(order.route, 'e_p');
  594. }
  595. arr.push(content);
  596. return content;
  597. }
  598. /**
  599. * 填充额外收入项
  600. * @param {Object} order 订单信息
  601. * @param {Number} os 该订单开始行数
  602. * @param {Array} inList 额外收入项
  603. */
  604. getOtherInExcelData(order, os, inList) {
  605. const obj = {};
  606. const { in_bill } = order;
  607. for (const oin of inList) {
  608. const { key, item, value } = oin;
  609. const r = in_bill.find(f => f.item === item);
  610. // console.log(key, item, value, r);
  611. if (r) {
  612. obj[`${key}${os}`] = _.get(r, value);
  613. }
  614. }
  615. return obj;
  616. }
  617. /**
  618. * 获取导出的订单头
  619. * @param {Array} list 选择的订单
  620. */
  621. getHeader(list) {
  622. const obj = {};
  623. // 同一客户下:B-H一定是合并的
  624. // 同一订单下: A,AD1一定是合并的
  625. // 订单部分
  626. obj.A1 = '订单编号';
  627. // 客户部分
  628. obj.B1 = '客户名称';
  629. obj.C1 = '地址';
  630. obj.D1 = '法人';
  631. obj.E1 = '联系电话';
  632. obj.F1 = '税号';
  633. obj.G1 = '开户行';
  634. obj.H1 = '银行账号';
  635. // 合同部分
  636. obj.I1 = '合同编号';
  637. obj.J1 = '甲方';
  638. obj.K1 = '乙方';
  639. obj.L1 = '合同周期';
  640. obj.M1 = '结算方式';
  641. // 项目部分
  642. obj.N1 = '项目名称';
  643. obj.O1 = '税率';
  644. // 线路部分
  645. obj.P1 = '线路名称';
  646. obj.Q1 = '起始地';
  647. obj.R1 = '目的地';
  648. // 方式部分
  649. obj.S1 = '方式名称';
  650. obj.T1 = '价格';
  651. // 货物部分
  652. obj.U1 = '货物名称';
  653. obj.V1 = '费用名称'; // 固定全是运费
  654. // 方式部分
  655. obj.W1 = '量份收费'; // 需要换成 是/否
  656. obj.X1 = '发货方式';
  657. obj.Y1 = '计费方式';
  658. // 货物部分
  659. obj.Z1 = '税前应收';
  660. obj.AA1 = '税前实收';
  661. obj.AB1 = '税后应收';
  662. obj.AC1 = '税后实收';
  663. // 订单部分
  664. obj.AD1 = '订单备注';
  665. // 处理额外收入头部
  666. const in_item = _.uniqBy(list.map(i => i.in_bill).flat(), 'item');
  667. const arr = [];
  668. let i = 31;
  669. for (const inInfo of in_item) {
  670. const { item } = inInfo;
  671. const fl = this.ctx.service.util.excel.numberToLetter(_.floor(i / 26));
  672. // const sl = this.ctx.service.util.excel.numberToLetter(i % 26);
  673. const sqyl = this.ctx.service.util.excel.numberToLetter(i % 26);
  674. obj[`${fl}${sqyl}1`] = `${item}税前应收`;
  675. arr.push({ key: `${fl}${sqyl}`, item, value: 'sq_ys' });
  676. i++;
  677. const sqsl = this.ctx.service.util.excel.numberToLetter(i % 26);
  678. obj[`${fl}${sqsl}1`] = `${item}税前实收`;
  679. arr.push({ key: `${fl}${sqsl}`, item, value: 'sq_ss' });
  680. i++;
  681. const shyl = this.ctx.service.util.excel.numberToLetter(i % 26);
  682. obj[`${fl}${shyl}1`] = `${item}税后应收`;
  683. arr.push({ key: `${fl}${shyl}`, item, value: 'sh_ys' });
  684. i++;
  685. const shsl = this.ctx.service.util.excel.numberToLetter(i % 26);
  686. obj[`${fl}${shsl}1`] = `${item}税后实收`;
  687. arr.push({ key: `${fl}${shsl}`, item, value: 'sh_ss' });
  688. i++;
  689. }
  690. return { header: obj, otherList: arr };
  691. }
  692. /**
  693. * 订单结算
  694. * @param {Object} {ids} 要结算的订单
  695. */
  696. async js({ ids, client, owner, ...info }) {
  697. assert(ids, '缺少订单信息');
  698. assert(client, '缺少客户信息');
  699. assert(owner, '缺少创建人信息');
  700. const params = { ids, client, owner };
  701. const bill = await this.ctx.model.Bill.create({ params, client, owner, ...info });
  702. if (!bill) { throw new BusinessError(ErrorCode.DATABASE_FAULT, '结算单创建失败'); }
  703. const res = await this.model.updateMany(
  704. { _id: ids.map(i => ObjectId(i)) },
  705. { is_js: true }
  706. );
  707. try {
  708. for (const id of ids) {
  709. this.record(id, { method: 'js' });
  710. }
  711. } catch (error) {
  712. this.logger.error(`订单id:${res.id}记录创建失败:${error.toString()}`);
  713. }
  714. }
  715. /**
  716. * 支出结算
  717. * @param {Object} {ids,client,car_no} ids:订单id列表,client:客户id,car_no:车牌号(第三方)/车辆id(自运)
  718. */
  719. async outJs({ ids, client, car_no }) {
  720. const orderList = await this.model.find({ _id: ids.map(i => ObjectId(i)) });
  721. for (const order of orderList) {
  722. const { out_bill, _id } = order;
  723. let target;
  724. let res;
  725. if (client) {
  726. res = out_bill.filter(f => f.client === client);
  727. const cInfo = await this.ctx.model.Client.findById(client);
  728. if (!cInfo) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到指定供应商');
  729. target = _.get(cInfo, 'name');
  730. } else {
  731. res = out_bill.filter(f => f.car_no === car_no);
  732. const reg = new RegExp('[\\u4E00-\\u9FFF]+', 'g');
  733. if (reg.test(car_no)) {
  734. target = car_no;
  735. } else {
  736. const cInfo = await this.ctx.model.Car.findById(car_no);
  737. if (!cInfo) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到指定车辆');
  738. target = cInfo.car_no;
  739. }
  740. }
  741. for (const bill of res) {
  742. bill.is_js = true;
  743. }
  744. order.save();
  745. try {
  746. this.record(_id, { method: 'outJs', message: `结算 ${target} 支出` });
  747. } catch (error) {
  748. this.logger.error(`订单id:${res.id}记录创建失败:${error.toString()}`);
  749. }
  750. }
  751. }
  752. }
  753. module.exports = OrderService;