'use strict'; const { CrudService } = require('naf-framework-mongoose-free/lib/service'); const { BusinessError, ErrorCode } = require('naf-core').Error; const _ = require('lodash'); const assert = require('assert'); const Transaction = require('mongoose-transactions'); const { ObjectId } = require('mongoose').Types; // 对账 class BillService extends CrudService { constructor(ctx) { super(ctx, 'bill'); this.orderDetailModel = this.ctx.model.Trade.OrderDetail; this.afterSaleModel = this.ctx.model.Trade.AfterSale; this.tran = new Transaction(); } /** * 对账标记 * @param {Object} body 参数体 * @param body.order 对账的订单内容 // ** _id:orderDetail的id // ** spec_id: 商品规格id * @param body.afterSale 对账的售后id集合 */ async outBill({ order = [], afterSale = [] }) { const data = { out_bill: '0' }; await this.orderDetailModel.updateMany({ _id: order.map(i => ObjectId(i)) }, data); await this.afterSaleModel.updateMany({ _id: afterSale.map(i => ObjectId(i)) }, data); } /** * 生成店铺对账单 ** 根据店铺id,开始时间和结束时间,形成对账单 * @param {Object} query 查询条件 */ async getBill(query) { const { shop, start, end } = query; assert(start, '缺少开始时间'); assert(end, '缺少结束时间'); const orderList = await this.makeBillOrderPart(query); const afterSaleList = await this.makeBillAfterSalePart(query); const ot = orderList.reduce((p, n) => this.ctx.plus(p, n.total), 0); const oa = afterSaleList.reduce((p, n) => this.ctx.plus(p, n.money), 0); const total = this.ctx.minus(ot, oa); return { total, orderList, afterSaleList }; } /** * 对账单售后部分 * @param {Object} query 查询条件 */ async makeBillAfterSalePart(query) { const { shop, start, end } = query; const pipline = []; // 商店过滤&时间过滤 const q = { $match: { $and: [{ apply_time: { $gte: start } }, { apply_time: { $lte: end } }], status: { $in: [ '-1', '-2' ] } } }; // 退款,退货的完成状态, status:{$in:['-1','-2']}, out_bill:{$ne:'0'} if (shop) q.$match.shop = shop; pipline.push(q); // 组织数据 // 单号,商品,规格,退款金额,退款时间 // 单号是要用orderDetail关联获取;商品&规格&退款时间&退款金额有; pipline.push({ $addFields: { orderDetail_id: { $toObjectId: '$order_detail' } } }); pipline.push({ $lookup: { from: 'orderDetail', localField: 'orderDetail_id', foreignField: '_id', pipeline: [{ $project: { no: 1, _id: 1 } }], as: 'orderDetailInfo', }, }); // 组织数据 pipline.push({ $unwind: '$orderDetailInfo' }); pipline.push({ $project: { no: '$orderDetailInfo.no', goods: '$goods.goods.name', spec: '$goods.name', end_time: 1, money: { $toString: '$money' }, rowKey: { $concat: [{ $toString: '$_id' }, '_', '$goods.goods._id', '_', '$goods._id', '_', '$apply_time' ] }, }, }); const afterSaleList = await this.afterSaleModel.aggregate(pipline); return afterSaleList; } /** * 对账单订单部分 * @param {Object} query 查询条件 */ async makeBillOrderPart(query) { const { shop, start, end } = query; const pipline = []; // 商店过滤&时间过滤 const q = { $match: { $and: [{ pay_time: { $gte: start } }, { pay_time: { $lte: end } }] } }; // , out_bill:{$ne:'0'} if (shop) q.$match.shop = shop; pipline.push(q); // #region 整理数据 // 整理最外层的orderDetail,没用的裁掉 const $project = { type: 1, total_detail: 1, no: 1, pay_time: 1, goods: { _id: 1, sell_money: 1, buy_num: 1, freight: 1, group_config: { money: 1 }, name: 1, goods: { name: 1, _id: 1 }, }, }; pipline.push({ $project }); // 按规格平铺开 pipline.push({ $unwind: '$goods' }); // #endregion const orderList = await this.orderDetailModel.aggregate(pipline); const dictData = await this.ctx.model.Dev.DictData.find({ code: 'order_type' }); const list = []; for (const order of orderList) { const o = _.cloneDeep(order); const type = dictData.find(f => f.value === o.type); const moneyDetail = this.moneyDetail(order); const obj = { _id: _.get(o, '_id'), no: _.get(o, 'no'), type: _.get(type, 'label'), pay_time: _.get(o, 'pay_time'), goods_id: _.get(o, 'goods._id'), goods: _.get(o, 'goods.goods.name'), spec: _.get(o, 'goods.name'), spec_id: _.get(o, 'goods._id'), freight: _.get(o, 'goods.freight'), buy_num: _.get(o, 'goods.buy_num'), price: o.type === '1' ? _.get(o, 'goods.group_config.money') : _.get(o, 'goods.sell_money'), discount: _.get(moneyDetail, 'dt'), total: o.type === '1' ? _.get(moneyDetail, 'ggrp') : _.get(moneyDetail, 'grp'), rowKey: `${_.get(o, '_id')}_${_.get(o, 'goods.goods._id')}_${_.get(o, 'goods._id')}`, }; list.push(obj); } return list; } moneyDetail(data) { const ddt = _.get(data, 'total_detail.discount_detail', {}); const { sell_money: sm, freight: f, buy_num: bn, group_config, _id } = data.goods; const st = this.ctx.multiply(sm, bn); const ft = this.ctx.multiply(f, bn); const gt = this.ctx.plus(st, ft); const dd = {}; for (const uc_id in ddt) { const detail = _.get(ddt, uc_id, {}); const value = detail[_id]; if (value) dd[uc_id] = value; } const dt = Object.values(dd).reduce((p, n) => this.ctx.plus(p, n), 0); const grp = this.ctx.minus(gt, dt); let obj = { sm, f, bn, st, ft, gt, dd, dt, grp }; const gsm = _.get(group_config, 'money'); if (gsm) { const gst = this.ctx.multiply(gsm, bn); const ggt = this.ctx.plus(gst, ft); const ggrp = this.ctx.minus(ggt, dt); obj = { ...obj, gsm, gst, ggt, ggrp }; } return obj; } } module.exports = BillService;