order.js 24 KB

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