bill.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. 'use strict';
  2. const { CrudService } = require('naf-framework-mongoose-free/lib/service');
  3. const { BusinessError, ErrorCode } = require('naf-core').Error;
  4. const _ = require('lodash');
  5. const assert = require('assert');
  6. const Transaction = require('mongoose-transactions');
  7. // 对账
  8. class BillService extends CrudService {
  9. constructor(ctx) {
  10. super(ctx, 'bill');
  11. this.orderDetailModel = this.ctx.model.Trade.OrderDetail;
  12. this.afterSaleModel = this.ctx.model.Trade.AfterSale;
  13. this.tran = new Transaction();
  14. }
  15. /**
  16. * 对账标记
  17. * @param {Object} body 参数体
  18. * @param body.order 对账的订单内容
  19. // ** _id:orderDetail的id
  20. // ** spec_id: 商品规格id
  21. * @param body.afterSale 对账的售后id集合
  22. */
  23. async outBill({ order = [], afterSale = [] }) {
  24. try {
  25. for (const o of order) {
  26. this.tran.update('OrderDetail', o, { out_bill: '0' });
  27. }
  28. for (const as of afterSale) {
  29. this.tran.update('AfterSale', as, { out_bill: '0' });
  30. }
  31. this.tran.run();
  32. } catch (error) {
  33. this.tran.rollback();
  34. console.error(error);
  35. throw new BusinessError(ErrorCode.SERVICE_FAULT, '对账失败');
  36. }
  37. }
  38. /**
  39. * 生成店铺对账单
  40. ** 根据店铺id,开始时间和结束时间,形成对账单
  41. * @param {Object} query 查询条件
  42. */
  43. async getBill(query) {
  44. const { shop, start, end } = query;
  45. assert(start, '缺少开始时间');
  46. assert(end, '缺少结束时间');
  47. const orderList = await this.makeBillOrderPart(query);
  48. const afterSaleList = await this.makeBillAfterSalePart(query);
  49. const ot = orderList.reduce((p, n) => this.ctx.plus(p, n.total), 0);
  50. const oa = afterSaleList.reduce((p, n) => this.ctx.plus(p, n.money), 0);
  51. const total = this.ctx.minus(ot, oa);
  52. return { total, orderList, afterSaleList };
  53. }
  54. /**
  55. * 对账单售后部分
  56. * @param {Object} query 查询条件
  57. */
  58. async makeBillAfterSalePart(query) {
  59. const { shop, start, end } = query;
  60. const pipline = [];
  61. // 商店过滤&时间过滤
  62. const q = { $match: { $and: [{ apply_time: { $gte: start } }, { apply_time: { $lte: end } }] } }; // 退款,退货的完成状态, status:{$in:['-1','-2']}
  63. if (shop) q.$match.shop = shop;
  64. pipline.push(q);
  65. // 组织数据
  66. // 单号,商品,规格,退款金额,退款时间
  67. // 单号是要用orderDetail关联获取;商品&规格&退款时间&退款金额有;
  68. pipline.push({ $addFields: { orderDetail_id: { $toObjectId: '$order_detail' } } });
  69. pipline.push({
  70. $lookup: {
  71. from: 'orderDetail',
  72. localField: 'orderDetail_id',
  73. foreignField: '_id',
  74. pipeline: [{ $project: { no: 1 } }],
  75. as: 'orderDetailInfo',
  76. },
  77. });
  78. // 组织数据
  79. pipline.push({ $unwind: '$orderDetailInfo' });
  80. pipline.push({
  81. $project: {
  82. no: '$ordderDetailInfo.no',
  83. goods: '$goods.goods.name',
  84. spec: '$goods.name',
  85. end_time: 1,
  86. money: { $toString: '$money' },
  87. },
  88. });
  89. const afterSaleList = await this.afterSaleModel.aggregate(pipline);
  90. return afterSaleList;
  91. }
  92. /**
  93. * 对账单订单部分
  94. * @param {Object} query 查询条件
  95. */
  96. async makeBillOrderPart(query) {
  97. const { shop, start, end } = query;
  98. const pipline = [];
  99. // 商店过滤&时间过滤
  100. const q = { $match: { $and: [{ pay_time: { $gte: start } }, { pay_time: { $lte: end } }] } };
  101. if (shop) q.$match.shop = shop;
  102. pipline.push(q);
  103. // #region 整理数据
  104. // 整理最外层的orderDetail,没用的裁掉
  105. const $project = {
  106. type: 1,
  107. total_detail: 1,
  108. no: 1,
  109. pay_time: 1,
  110. goods: {
  111. _id: 1,
  112. sell_money: 1,
  113. buy_num: 1,
  114. freight: 1,
  115. group_config: { money: 1 },
  116. name: 1,
  117. goods: { name: 1 },
  118. },
  119. };
  120. pipline.push({ $project });
  121. // 按规格平铺开
  122. pipline.push({ $unwind: '$goods' });
  123. // #endregion
  124. const orderList = await this.orderDetailModel.aggregate(pipline);
  125. const dictData = await this.ctx.model.Dev.DictData.find({ code: 'order_type' });
  126. const list = [];
  127. for (const order of orderList) {
  128. const o = _.cloneDeep(order);
  129. const type = dictData.find(f => f.value === o.type);
  130. const moneyDetail = this.moneyDetail(order);
  131. const obj = {
  132. _id: _.get(o, '_id'),
  133. no: _.get(o, 'no'),
  134. type: _.get(type, 'label'),
  135. pay_time: _.get(o, 'pay_time'),
  136. goods_id: _.get(o, 'goods.goods._id'),
  137. goods: _.get(o, 'goods.goods.name'),
  138. spec: _.get(o, 'goods.name'),
  139. spec_id: _.get(o, 'goods._id'),
  140. buy_num: _.get(o, 'goods.buy_num'),
  141. price: o.type === '1' ? _.get(o, 'goods.group_config.money') : _.get(o, 'goods.sell_money'),
  142. discount: _.get(moneyDetail, 'dt'),
  143. total: o.type === '1' ? _.get(moneyDetail, 'ggrp') : _.get(moneyDetail, 'grp'),
  144. };
  145. list.push(obj);
  146. }
  147. return list;
  148. }
  149. moneyDetail(data) {
  150. const ddt = _.get(data, 'total_detail.discount_detail', {});
  151. const { sell_money: sm, freight: f, buy_num: bn, group_config, _id } = data.goods;
  152. const st = this.ctx.multiply(sm, bn);
  153. const ft = this.ctx.multiply(f, bn);
  154. const gt = this.ctx.plus(st, ft);
  155. const dd = {};
  156. for (const uc_id in ddt) {
  157. const detail = _.get(ddt, uc_id, {});
  158. const value = detail[_id];
  159. if (value) dd[uc_id] = value;
  160. }
  161. const dt = Object.values(dd).reduce((p, n) => this.ctx.plus(p, n), 0);
  162. const grp = this.ctx.minus(gt, dt);
  163. let obj = { sm, f, bn, st, ft, gt, dd, dt, grp };
  164. const gsm = _.get(group_config, 'money');
  165. if (gsm) {
  166. const gst = this.ctx.multiply(gsm, bn);
  167. const ggt = this.ctx.plus(gst, ft);
  168. const ggrp = this.ctx.minus(ggt, dt);
  169. obj = { ...obj, gsm, gst, ggt, ggrp };
  170. }
  171. return obj;
  172. }
  173. }
  174. module.exports = BillService;