bill.js 5.7 KB

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