From d33ea3a622d39a2c425087fbb0014625133fbeb0 Mon Sep 17 00:00:00 2001 From: chenlin Date: Wed, 13 Nov 2024 15:49:57 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E5=95=86=E5=93=81=E8=B5=84=E6=96=99?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0"=E5=81=9C=E6=AD=A2=E9=87=87=E8=B4=AD"?= =?UTF-8?q?=E6=8C=89=E9=92=AE=EF=BC=9B=202=E3=80=81=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E9=94=80=E5=94=AE=E6=98=8E=E7=BB=86=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=EF=BC=9B=203=E3=80=81=E9=9B=B6=E5=94=AE=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E8=A1=A8=E5=A2=9E=E5=8A=A0"=E4=BC=9A=E5=91=98?= =?UTF-8?q?=E7=B1=BB=E5=88=AB"=E5=AD=97=E6=AE=B5=EF=BC=9B=204=E3=80=81?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E9=9B=B6=E5=94=AE=E8=AE=A2=E5=8D=95=E7=9A=84?= =?UTF-8?q?=E6=97=B6=E5=80=99=E5=90=8C=E6=AD=A5=E8=AE=B0=E5=BD=95=E4=BC=9A?= =?UTF-8?q?=E5=91=98=E7=B1=BB=E5=88=AB=EF=BC=9B=205=E3=80=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E7=BB=8F=E8=90=A5=E6=97=A5=E6=8A=A5=E8=A1=A8=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=EF=BC=8C=E6=A0=B9=E6=8D=AE=E7=94=A8=E6=88=B7=E6=9D=83?= =?UTF-8?q?=E9=99=90=E8=BF=94=E5=9B=9E=E4=B8=8D=E5=90=8C=E7=9A=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=EF=BC=9B=E5=BA=97=E9=95=BF=E5=92=8C=E5=BA=97=E5=91=98?= =?UTF-8?q?=E7=9C=8B=E5=88=B0=E7=9A=84=E9=94=80=E5=94=AE=E5=88=A9=E6=B6=A6?= =?UTF-8?q?=E6=98=AF=E5=91=98=E5=B7=A5=E6=AF=9B=E5=88=A9=EF=BC=8C=E5=BA=97?= =?UTF-8?q?=E5=91=98=E5=8F=AA=E8=83=BD=E7=9C=8B=E8=87=AA=E5=B7=B1=E7=9A=84?= =?UTF-8?q?=E4=B8=9A=E7=BB=A9=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/apis/basic/commodity.go | 2 + app/admin/apis/erpordermanage/erp_order.go | 33 + app/admin/models/commodity.go | 10 + app/admin/models/erp_order.go | 930 ++++++++++++++++++++- app/admin/models/purchase.go | 2 +- app/admin/models/user.go | 69 ++ app/admin/router/erpordermanage.go | 1 + docs/docs.go | 134 ++- docs/swagger.json | 134 ++- docs/swagger.yaml | 97 ++- 10 files changed, 1391 insertions(+), 21 deletions(-) diff --git a/app/admin/apis/basic/commodity.go b/app/admin/apis/basic/commodity.go index 422021d..1513546 100644 --- a/app/admin/apis/basic/commodity.go +++ b/app/admin/apis/basic/commodity.go @@ -101,6 +101,7 @@ func CommodityCreate(c *gin.Context) { Origin: req.Origin, Remark: req.Remark, Img: req.Img, + StopPurchase: req.StopPurchase, } err = commodity.SetErpCategory() if err != nil { @@ -265,6 +266,7 @@ func CommodityEdit(c *gin.Context) { Origin: req.Origin, Remark: req.Remark, Img: req.Img, + StopPurchase: req.StopPurchase, } commodity.ID = req.Id err = commodity.SetErpCategory() diff --git a/app/admin/apis/erpordermanage/erp_order.go b/app/admin/apis/erpordermanage/erp_order.go index 4efa29d..c01d385 100644 --- a/app/admin/apis/erpordermanage/erp_order.go +++ b/app/admin/apis/erpordermanage/erp_order.go @@ -623,3 +623,36 @@ func ErpOrderDailyReport(c *gin.Context) { app.OK(c, resp, "") return } + +// ErpOrderSaleDetail 查询销售明细 +// @Summary 查询销售明细 +// @Tags 零售报表 +// @Produce json +// @Accept json +// @Param request body models.ErpOrderSaleDetailReq true "查询销售明细数据模型" +// @Success 200 {object} models.ErpOrderRetailDetailResp +// @Router /api/v1/erp_order/sale_detail [post] +func ErpOrderSaleDetail(c *gin.Context) { + var req = new(model.ErpOrderSaleDetailReq) + if err := c.ShouldBindJSON(&req); err != nil { + logger.Error("ShouldBindJSON err:", logger.Field("err", err)) + app.Error(c, http.StatusBadRequest, err, "参数错误:"+err.Error()) + return + } + + err := tools.Validate(req) //必填参数校验 + if err != nil { + app.Error(c, http.StatusBadRequest, err, err.Error()) + return + } + + resp, err := model.QuerySaleDetail(req, c) + if err != nil { + logger.Error("QueryRetailDetail err:", logger.Field("err", err)) + app.Error(c, http.StatusInternalServerError, err, "查询失败:"+err.Error()) + return + } + + app.OK(c, resp, "") + return +} diff --git a/app/admin/models/commodity.go b/app/admin/models/commodity.go index e558512..6581537 100644 --- a/app/admin/models/commodity.go +++ b/app/admin/models/commodity.go @@ -124,6 +124,7 @@ type ErpCommodity struct { Remark string `json:"remark" gorm:"type:varchar(512)"` // 备注 StockCount uint32 `json:"stock_count" gorm:"-"` // 库存数量 Img string `json:"img"` // 图片 + StopPurchase uint32 `json:"stop_purchase"` // 0-未勾选,正常采购;1-勾选,停止采购 ErpCategory *ErpCategory `json:"erp_category" gorm:"-"` } @@ -2070,6 +2071,9 @@ func (m *ErpStockListReq) stockIsEmptyList(c *gin.Context) (*ErpStockListResp, e var stockList []ErpStock for _, commodity := range commodities { if commodity.TotalCount == 0 { + if commodity.StopPurchase == 1 { // 停止采购且没有库存 + continue + } var stock ErpStock stock.ErpCommodityId = commodity.ID stock.ErpCommodityName = commodity.Name @@ -2443,6 +2447,10 @@ func (m *ErpStockListReq) allCommodityList(c *gin.Context) (*ErpStockListResp, e //遍历商品资料,转换为库存列表数据 var stockList []ErpStock for _, commodity := range commodities { + if commodity.StopPurchase == 1 && commodity.TotalCount == 0 { // 停止采购且没有库存 + continue + } + var stock ErpStock stock.ID = uint32(commodity.ErpStockId) stock.ErpCommodityId = commodity.ID @@ -3240,6 +3248,7 @@ type CommodityCreateRequest struct { Origin string `json:"origin"` // 产地 Remark string `json:"remark"` // 备注 Img string `json:"img"` // 图片 + StopPurchase uint32 `json:"stop_purchase"` // 0-未勾选,正常采购;1-勾选,停止采购 } type CommodityEditRequest struct { @@ -3260,6 +3269,7 @@ type CommodityEditRequest struct { Origin string `json:"origin"` // 产地 Remark string `json:"remark"` // 备注 Img string `json:"img"` // 图片 + StopPurchase uint32 `json:"stop_purchase"` // 0-未勾选,正常采购;1-勾选,停止采购 } type CommodityDetailRequest struct { diff --git a/app/admin/models/erp_order.go b/app/admin/models/erp_order.go index 0707350..0bcb0a2 100644 --- a/app/admin/models/erp_order.go +++ b/app/admin/models/erp_order.go @@ -55,6 +55,7 @@ type ErpOrder struct { BillSn string `json:"bill_sn" gorm:"index"` // 单据编号 RetailType string `json:"retail_type"` // 销售类型:sale 零售销售; rejected 零售退货 Uid int `json:"uid"` // 用户id + UserType int `json:"user_type"` // 会员类别:1-普通会员 2-黄金会员 4-白金会员 5-黑金会员 6-尊享会员 7-黄金&尊享 8-白金&尊享 9-黑金&尊享 Tel string `json:"tel" gorm:"index"` // 客户手机号 StoreId uint32 `json:"store_id" gorm:"index"` // 门店id StoreName string `json:"store_name"` // 门店名称 @@ -5645,6 +5646,11 @@ func CreateErpOrder(req *ErpOrderCreateReq, c *gin.Context) error { } } + // 记录用户会员类型 + if erpOrder.Uid != 0 { + erpOrder.UserType, _ = GetUserType(erpOrder.Uid) + } + // 1-创建零售订单 err = begin.Create(erpOrder).Error if err != nil { @@ -6539,7 +6545,9 @@ func QueryOrderDailyReport(req *ErpOrderDailyReportReq, c *gin.Context) (*ErpOrd if err != nil { return nil, err } - if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { + + roleName := tools.GetRoleName(c) + if !(roleName == "admin" || roleName == "系统管理员") { // 返回sysUser未过期的门店id列表 storeList = GetValidStoreIDs(sysUser.StoreData) } else { @@ -6552,6 +6560,12 @@ func QueryOrderDailyReport(req *ErpOrderDailyReportReq, c *gin.Context) (*ErpOrd return nil, errors.New("用户未绑定门店") } + // 判断是否为店长和店员的账户,店长或员工展示员工成本和员工毛利 + showFlag := false + if roleName == "store-manager" || roleName == "shopper" { + showFlag = true + } + storeMap, err := GetAllStoreData() if err != nil { return nil, err @@ -6559,7 +6573,11 @@ func QueryOrderDailyReport(req *ErpOrderDailyReportReq, c *gin.Context) (*ErpOrd // 查询所有门店和分类的零售信息 var sumData []RetailDetailSumData - sumData, err = getSumDataByStoreAndCategory(req) + if roleName == "shopper" { //店员只查询自己的业绩 + sumData, err = getSalesmanSumDataByStoreAndCategory(req, c) + } else { + sumData, err = getSumDataByStoreAndCategory(req) + } if err != nil { return nil, err } @@ -6574,16 +6592,21 @@ func QueryOrderDailyReport(req *ErpOrderDailyReportReq, c *gin.Context) (*ErpOrd displayCategories := getDisplayCategories(storeMap[store].Name) for _, item := range sumData { - nTotalAmount += item.Amount - nTotalSalesProfit += item.SalesProfit - // 仅当门店ID匹配且分类ID在展示分类中时,才添加分类数据 if item.StoreId == store && containsCategory(displayCategories, item.ErpCategoryId) { + nTotalAmount += item.Amount nStoreAmount += item.Amount - nStoreSalesProfit += item.TotalSalesProfit + + if showFlag { // 员工毛利 + nTotalSalesProfit += item.StaffProfit + nStoreSalesProfit += item.StaffProfit + } else { // 销售毛利 + nTotalSalesProfit += item.SalesProfit + nStoreSalesProfit += item.SalesProfit + } // 处理单个分类数据 - categoryData := processCategoryData(item) + categoryData := processCategoryData(item, showFlag) categoryDataList = append(categoryDataList, categoryData) } } @@ -6636,7 +6659,7 @@ func getDisplayCategories(storeName string) []uint32 { } // 处理单个分类数据 -func processCategoryData(item RetailDetailSumData) CategorySalesData { +func processCategoryData(item RetailDetailSumData, flag bool) CategorySalesData { var categoryName string switch item.ErpCategoryId { case 1: @@ -6651,16 +6674,25 @@ func processCategoryData(item RetailDetailSumData) CategorySalesData { categoryName = item.ErpCategoryName } - return CategorySalesData{ - CategoryID: item.ErpCategoryId, - CategoryName: item.ErpCategoryName, - SaleTitle: categoryName + "销售金额", - TotalSaleAmount: tools.RoundToTwoDecimalPlaces(item.Amount), - CostTitle: categoryName + "销售成本", - TotalCostAmount: tools.RoundToTwoDecimalPlaces(item.WholesalePrice), - ProfitTitle: categoryName + "销售利润", - TotalProfitAmount: tools.RoundToTwoDecimalPlaces(item.SalesProfit), + var saleData CategorySalesData + saleData.CategoryID = item.ErpCategoryId + saleData.CategoryName = item.ErpCategoryName + saleData.SaleTitle = categoryName + "销售金额" + saleData.TotalSaleAmount = tools.RoundToTwoDecimalPlaces(item.Amount) + + if flag { // 店长或员工展示员工成本和员工毛利 + saleData.CostTitle = categoryName + "销售成本" + saleData.TotalCostAmount = tools.RoundToTwoDecimalPlaces(item.StaffPrice) + saleData.ProfitTitle = categoryName + "销售利润" + saleData.TotalProfitAmount = tools.RoundToTwoDecimalPlaces(item.StaffProfit) + } else { + saleData.CostTitle = categoryName + "销售成本" + saleData.TotalCostAmount = tools.RoundToTwoDecimalPlaces(item.WholesalePrice) + saleData.ProfitTitle = categoryName + "销售利润" + saleData.TotalProfitAmount = tools.RoundToTwoDecimalPlaces(item.SalesProfit) } + + return saleData } // 补充缺失的分类数据 @@ -6875,6 +6907,157 @@ func getSumDataByStoreAndCategory(req *ErpOrderDailyReportReq) ([]RetailDetailSu return finalResult, nil } +func getSalesmanSumDataByStoreAndCategory(req *ErpOrderDailyReportReq, c *gin.Context) ([]RetailDetailSumData, error) { + var result []RetailDetailSumData + + // 解析开始时间 + startTimeCondition := "" + if req.StartTime != "" { + parse, err := time.Parse(QueryTimeFormat, req.StartTime) + if err != nil { + logger.Errorf("开始时间解析错误: %v", err) + return result, err + } + startTimeCondition = fmt.Sprintf("AND eo.audit_time > '%s'", parse.Format(QueryTimeFormat)) + } + + // 解析结束时间 + endTimeCondition := "" + if req.EndTime != "" { + parse, err := time.Parse(QueryTimeFormat, req.EndTime) + if err != nil { + logger.Errorf("结束时间解析错误: %v", err) + return result, err + } + endTimeCondition = fmt.Sprintf("AND eo.audit_time < '%s'", parse.Format(QueryTimeFormat)) + } + + // 销售员条件 + salesmanCondition := fmt.Sprintf("AND JSON_CONTAINS(eo.salesman_list, '%s')", fmt.Sprintf(`{"userId":%d}`, tools.GetUserId(c))) + + // 获取所有1级分类 + firstCategories, err := GetAllFirstCategories() + if err != nil { + logger.Error("获取一级分类错误", logger.Field("err", err)) + return result, err + } + + // 获取所有一级分类对应的子分类IDs + categorySubIds := make(map[uint32][]uint32) + for _, category := range firstCategories { + subCategoryIDs, err := GetSubcategoryIds(category.ID) + if err != nil { + logger.Error("获取子分类ID错误", logger.Field("err", err), logger.Field("categoryID", category.ID)) + return result, err + } + categorySubIds[category.ID] = subCategoryIDs + } + + err = orm.Eloquent.Raw(fmt.Sprintf(` + SELECT + retail.store_id, + retail.store_name, + retail.erp_category_id, + retail.erp_category_name, + COALESCE(retail.count, 0) - COALESCE(rejected.count, 0) AS count, + COALESCE(retail.retail_price, 0) - COALESCE(rejected.retail_price, 0) AS retail_price, + COALESCE(retail.sale_price, 0) - COALESCE(rejected.sale_price, 0) AS sale_price, + COALESCE(retail.sale_discount, 0) - COALESCE(rejected.sale_discount, 0) AS sale_discount, + COALESCE(retail.member_discount, 0) - COALESCE(rejected.member_discount, 0) AS member_discount, + COALESCE(retail.amount, 0) - COALESCE(rejected.amount, 0) AS amount, + COALESCE(retail.wholesale_price, 0) - COALESCE(rejected.wholesale_price, 0) AS wholesale_price, + COALESCE(retail.staff_price, 0) - COALESCE(rejected.staff_price, 0) AS staff_price, + COALESCE(retail.sales_profit, 0) - COALESCE(rejected.sales_profit, 0) AS sales_profit, + COALESCE(retail.staff_profit, 0) - COALESCE(rejected.staff_profit, 0) AS staff_profit, + COALESCE(retail.total_amount, 0) - COALESCE(rejected.total_amount, 0) AS total_amount, + COALESCE(retail.total_sales_profit, 0) - COALESCE(rejected.total_sales_profit, 0) AS total_sales_profit, + COALESCE(retail.total_staff_profit, 0) - COALESCE(rejected.total_staff_profit, 0) AS total_staff_profit + FROM ( + SELECT eo.store_id, eo.store_name, oc.erp_category_id, oc.erp_category_name, + SUM(oc.count) AS count, + SUM(oc.retail_price) AS retail_price, + SUM(oc.sale_price) AS sale_price, + SUM(oc.sale_discount) AS sale_discount, + SUM(oc.member_discount) AS member_discount, + SUM(oc.amount) AS amount, + SUM(oc.wholesale_price) AS wholesale_price, + (SUM(oc.wholesale_price) + SUM(oc.staff_cost_price)) AS staff_price, + (SUM(oc.amount) - SUM(oc.wholesale_price)) AS sales_profit, + (SUM(oc.amount) - SUM(oc.wholesale_price) - SUM(oc.staff_cost_price)) AS staff_profit, + SUM(eo.total_amount) as total_amount, + SUM(eo.total_sales_profit) as total_sales_profit, + SUM(eo.total_staff_profit) as total_staff_profit + FROM erp_order eo + JOIN erp_order_commodity oc ON eo.id = oc.erp_order_id + WHERE eo.retail_type = ? %s %s %s + GROUP BY eo.store_id, eo.store_name, oc.erp_category_id, oc.erp_category_name + ) AS retail + LEFT JOIN ( + SELECT eo.store_id, eo.store_name, oc.erp_category_id, oc.erp_category_name, + SUM(oc.count) AS count, + SUM(oc.retail_price) AS retail_price, + SUM(oc.sale_price) AS sale_price, + SUM(oc.sale_discount) AS sale_discount, + SUM(oc.member_discount) AS member_discount, + SUM(oc.rejected_amount) AS amount, + SUM(oc.wholesale_price) AS wholesale_price, + (SUM(oc.wholesale_price) + SUM(oc.staff_cost_price)) AS staff_price, + SUM(oc.sales_profit) AS sales_profit, + SUM(oc.staff_profit) AS staff_profit, + SUM(eo.total_amount) as total_amount, + SUM(eo.total_sales_profit) as total_sales_profit, + SUM(eo.total_staff_profit) as total_staff_profit + FROM erp_order eo + JOIN erp_order_commodity oc ON eo.id = oc.erp_order_id + WHERE eo.retail_type = ? %s %s %s + GROUP BY eo.store_id, eo.store_name, oc.erp_category_id, oc.erp_category_name + ) AS rejected ON retail.store_id = rejected.store_id AND retail.erp_category_id = rejected.erp_category_id + `, startTimeCondition, endTimeCondition, salesmanCondition, startTimeCondition, endTimeCondition, salesmanCondition), RetailTypeSale, RetailTypeRejected).Scan(&result).Error + + if err != nil { + logger.Error("query data with subtraction err:", logger.Field("err", err)) + return result, err + } + + // 合并子分类数据(根据门店ID和父分类合并,并累加金额) + mergedResult := make(map[string]RetailDetailSumData) + + for i := range result { + for parentID, subCategories := range categorySubIds { + if containsCategory(subCategories, result[i].ErpCategoryId) { + key := fmt.Sprintf("%d-%d", result[i].StoreId, parentID) + + if existing, exists := mergedResult[key]; exists { + existing.Count += result[i].Count + existing.RetailPrice += result[i].RetailPrice + existing.SalePrice += result[i].SalePrice + existing.SaleDiscount += result[i].SaleDiscount + existing.MemberDiscount += result[i].MemberDiscount + existing.Amount += result[i].Amount + existing.WholesalePrice += result[i].WholesalePrice + existing.StaffPrice += result[i].StaffPrice + existing.SalesProfit += result[i].SalesProfit + existing.StaffProfit += result[i].StaffProfit + mergedResult[key] = existing + } else { + result[i].ErpCategoryId = parentID + //result[i].ErpCategoryName = getCategoryNameByCategoryID(parentID) + mergedResult[key] = result[i] + } + break + } + } + } + + // 转换合并结果为切片 + finalResult := make([]RetailDetailSumData, 0, len(mergedResult)) + for _, v := range mergedResult { + finalResult = append(finalResult, v) + } + + return finalResult, nil +} + // containsCategory 检查分类ID是否在子分类IDs列表中 func containsCategory(subCategoryIds []uint32, categoryId uint32) bool { for _, id := range subCategoryIds { @@ -6894,3 +7077,716 @@ func containsCategorySalesData(categoryDataList []CategorySalesData, categoryId } return false } + +// ErpOrderSaleDetailReq 销售明细汇总入参 +type ErpOrderSaleDetailReq struct { + BillSn string `json:"bill_sn"` // 单据编号 + RetailType string `json:"retail_type"` // 销售类型:sale 零售销售; rejected 零售退货 + UserType []uint32 `json:"user_type"` // 会员类别:0-未注册 1-普通会员 2-黄金会员 4-白金会员 5-黑金会员 6-尊享会员 7-黄金&尊享 8-白金&尊享 9-黑金&尊享 + Uid int `json:"uid"` // 用户ID + Tel string `json:"tel"` // 客户手机号 + StoreId uint32 `json:"store_id"` // 门店ID + ErpCategoryId []uint32 `json:"erp_category_id"` // 分类id + ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 + Salesman uint32 `json:"salesman"` // 销售人员ID + IMEI string `json:"imei"` // 串码 + StartTime string `json:"start_time"` // 开始时间 + EndTime string `json:"end_time"` // 结束时间 + BankTrxNo string `json:"bank_trx_no"` // 银联流水号 + PageIndex int `json:"pageIndex"` // 页码 + PageSize int `json:"pageSize"` // 页面条数 + IsExport uint32 `json:"is_export"` // 1-导出 +} + +// QuerySaleDetail 查询销售明细 +func QuerySaleDetail(req *ErpOrderSaleDetailReq, c *gin.Context) (*ErpOrderRetailDetailResp, error) { + resp := &ErpOrderRetailDetailResp{} + + // 通过银行流水号查询 + if req.BankTrxNo != "" { + var orderPayWay ErpOrderRecord + err := orm.Eloquent.Table("erp_order_record").Where("bank_trx_no = ? and status = ?", + req.BankTrxNo, PayOk).Find(&orderPayWay).Error + if err != nil { + logger.Error("query erp_order_record err:", logger.Field("err", err)) + return nil, err + } + req.BillSn = orderPayWay.BillSn + } + + var err error + if (len(req.ErpCategoryId) != 0 || req.ErpCommodityName != "" || req.IMEI != "") && req.BillSn == "" { // 商品分类or商品名称不为空且订单编号为空 + // 联表查询 + resp, err = querySaleDetailByJoin(req, c) + } else { + // 普通单表查询,然后补充收款数据和商品数据 + resp, err = querySaleDetailCommon(req, c) + } + if err != nil { + logger.Error("queryRetailDetailCommon err") + return nil, err + } + + return resp, nil +} + +// 联表查询 +func querySaleDetailByJoin(req *ErpOrderSaleDetailReq, c *gin.Context) (*ErpOrderRetailDetailResp, error) { + showConfig, err := GetErpOrderShowConfig() + if err != nil { + logger.Errorf("List err:", err) + showConfig.ShowAll = "ON" + } + + page := req.PageIndex - 1 + if page < 0 { + page = 0 + } + if req.PageSize == 0 { + req.PageSize = 10 + } + + resp := &ErpOrderRetailDetailResp{ + PageIndex: page + 1, + PageSize: req.PageSize, + } + + qs := orm.Eloquent.Debug().Table("erp_order_commodity"). + Select("erp_order_commodity.*, erp_order.*"). + Joins("JOIN erp_order ON erp_order_commodity.erp_order_id = erp_order.id") + + es := orm.Eloquent.Debug().Table("erp_order_commodity"). + Select("erp_order_commodity.*, erp_order.*"). + Joins("JOIN erp_order ON erp_order_commodity.erp_order_id = erp_order.id") + + orderSumQs := orm.Eloquent.Table("erp_order_commodity AS oc").Select("oc.*, eo.*"). + Joins("JOIN erp_order AS eo ON oc.erp_order_id = eo.id") + + rejectedOrderSumQs := orm.Eloquent.Table("erp_order_commodity AS oc").Select("oc.*, eo.*"). + Joins("JOIN erp_order AS eo ON oc.erp_order_id = eo.id") + + if len(req.ErpCategoryId) != 0 { // 商品分类 + categoryList, err := TransformErpCategoryIds(req.ErpCategoryId) + if err != nil { + return nil, err + } + qs = qs.Where("erp_order_commodity.erp_category_id in ?", categoryList) + es = es.Where("erp_order_commodity.erp_category_id in ?", categoryList) + orderSumQs = orderSumQs.Where("oc.erp_category_id in ?", categoryList) + rejectedOrderSumQs = rejectedOrderSumQs.Where("oc.erp_category_id in ?", categoryList) + } + if req.ErpCommodityName != "" { // 商品名称 + qs = qs.Where("erp_order_commodity.erp_commodity_name = ?", req.ErpCommodityName) + es = es.Where("erp_order_commodity.erp_commodity_name = ?", req.ErpCommodityName) + orderSumQs = orderSumQs.Where("oc.erp_commodity_name = ?", req.ErpCommodityName) + rejectedOrderSumQs = rejectedOrderSumQs.Where("oc.erp_commodity_name = ?", req.ErpCommodityName) + } + if req.RetailType != "" { // 销售类型 + qs = qs.Where("erp_order.retail_type=?", req.RetailType) + es = es.Where("erp_order.retail_type=?", req.RetailType) + orderSumQs = orderSumQs.Where("eo.retail_type=?", req.RetailType) + rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.retail_type=?", req.RetailType) + } + if req.Uid != 0 { // 用户ID + qs = qs.Where("erp_order.uid=?", req.Uid) + es = es.Where("erp_order.uid=?", req.Uid) + orderSumQs = orderSumQs.Where("eo.uid=?", req.Uid) + rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.uid=?", req.Uid) + } + if len(req.UserType) != 0 { // 会员类别 + qs = qs.Where("user_type in ?", req.UserType) + es = es.Where("user_type in ?", req.UserType) + orderSumQs = orderSumQs.Where("user_type in ?", req.UserType) + rejectedOrderSumQs = rejectedOrderSumQs.Where("user_type in ?", req.UserType) + } + if req.Tel != "" { // 用户手机号 + qs = qs.Where("erp_order.tel=?", req.Tel) + es = es.Where("erp_order.tel=?", req.Tel) + orderSumQs = orderSumQs.Where("eo.tel=?", req.Tel) + rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.tel=?", req.Tel) + } + if req.StoreId != 0 { // 门店ID + qs = qs.Where("erp_order.store_id=?", req.StoreId) + es = es.Where("erp_order.store_id=?", req.StoreId) + orderSumQs = orderSumQs.Where("eo.store_id=?", req.StoreId) + rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.store_id=?", req.StoreId) + } + if req.IMEI != "" { // 串码 + qs = qs.Where("erp_order_commodity.imei=?", req.IMEI) + es = es.Where("erp_order_commodity.imei=?", req.IMEI) + orderSumQs = orderSumQs.Where("oc.imei=?", req.IMEI) + rejectedOrderSumQs = rejectedOrderSumQs.Where("oc.imei=?", req.IMEI) + } + // 非管理员才判断所属门店 + if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { + sysUser, err := GetSysUserByCtx(c) + if err != nil { + return nil, errors.New("操作失败:" + err.Error()) + } + + // 返回sysUser未过期的门店id列表 + storeList := GetValidStoreIDs(sysUser.StoreData) + if len(storeList) > 0 { + if len(storeList) == 1 { + qs = qs.Where("erp_order.store_id = ?", storeList[0]) + es = es.Where("erp_order.store_id = ?", storeList[0]) + orderSumQs = orderSumQs.Where("eo.store_id = ?", storeList[0]) + rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.store_id = ?", storeList[0]) + } else { + qs = qs.Where("erp_order.store_id IN (?)", storeList) + es = es.Where("erp_order.store_id IN (?)", storeList) + orderSumQs = orderSumQs.Where("eo.store_id IN (?)", storeList) + rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.store_id IN (?)", storeList) + } + } else { + return nil, errors.New("用户未绑定门店") + } + } + if req.Salesman != 0 { // 销售员 + qs = qs.Where("JSON_CONTAINS(erp_order.salesman_list, ?)", fmt.Sprintf(`{"userId":%d}`, req.Salesman)) + es = es.Where("JSON_CONTAINS(erp_order.salesman_list, ?)", fmt.Sprintf(`{"userId":%d}`, req.Salesman)) + orderSumQs = orderSumQs.Where("JSON_CONTAINS(eo.salesman_list, ?)", fmt.Sprintf(`{"userId":%d}`, req.Salesman)) + rejectedOrderSumQs = rejectedOrderSumQs.Where("JSON_CONTAINS(eo.salesman_list, ?)", fmt.Sprintf(`{"userId":%d}`, req.Salesman)) + } + if req.StartTime != "" { // 审核开始时间 + parse, err := time.Parse(QueryTimeFormat, req.StartTime) + if err != nil { + logger.Errorf("err:", err) + } + qs = qs.Where("erp_order.audit_time > ?", parse) + es = es.Where("erp_order.audit_time > ?", parse) + orderSumQs = orderSumQs.Where("eo.audit_time > ?", parse) + rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.audit_time > ?", parse) + } + if req.EndTime != "" { // 审核结束时间 + parse, err := time.Parse(QueryTimeFormat, req.EndTime) + if err != nil { + logger.Errorf("err:", err) + } + //parse = parse.AddDate(0, 0, 1) + qs = qs.Where("erp_order.audit_time < ?", parse) + es = es.Where("erp_order.audit_time < ?", parse) + orderSumQs = orderSumQs.Where("eo.audit_time < ?", parse) + rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.audit_time < ?", parse) + } + if showConfig.ShowAll == "OFF" { + qs = qs.Where("erp_order.is_print = ? or erp_order.retail_type = ?", HavePrinted, RetailTypeRejected) + es = es.Where("erp_order.is_print = ? or erp_order.retail_type = ?", HavePrinted, RetailTypeRejected) + orderSumQs = orderSumQs.Where("eo.is_print = ? or eo.retail_type = ?", HavePrinted, RetailTypeRejected) + rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.is_print = ? or eo.retail_type = ?", HavePrinted, RetailTypeRejected) + } + qs.Where("erp_order.pay_status = ? or (erp_order.retail_type = ? and erp_order.state != ?)", + HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) + es.Where("erp_order.pay_status = ? or (erp_order.retail_type = ? and erp_order.state != ?)", + HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) + orderSumQs.Where("eo.pay_status = ? or (eo.retail_type = ? and eo.state != ?)", + HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) + rejectedOrderSumQs.Where("eo.pay_status = ? or (eo.retail_type = ? and eo.state != ?)", + HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) + + // 销售订单的汇总数据 + var sumData RetailDetailTotalData + sumData, err = getRetailDetailTotalDataJoinErpOrderSale(orderSumQs, RetailTypeSale) + if err != nil { + logger.Error("query sum data err:", logger.Field("err", err)) + return resp, err + } + // 退货订单的汇总数据 + var rejectedSumData RetailDetailTotalData + rejectedSumData, err = getRetailDetailTotalDataJoinErpOrderRejected(rejectedOrderSumQs, RetailTypeRejected) + if err != nil { + logger.Error("query sum data err:", logger.Field("err", err)) + return resp, err + } + // 计算销售订单和退货订单汇总后的销售数据 + sumData = subtractRetailData(sumData, rejectedSumData) + + // 销售订单提成汇总 + var totalPerData TotalPerData + totalPerQs := qs + totalPerData, err = getTotalPerData(totalPerQs, RetailTypeSale) + if err != nil { + logger.Error("query erp_order_sales sum data err:", logger.Field("err", err)) + return resp, err + } + // 退货订单提成汇总 + var rejectedTotalPerData TotalPerData + rejectedTotalPerQs := qs + rejectedTotalPerData, err = getTotalPerData(rejectedTotalPerQs, RetailTypeRejected) + if err != nil { + logger.Error("query erp_order_sales sum data err:", logger.Field("err", err)) + return resp, err + } + // 计算销售订单和退货订单汇总后的提成数据 + totalPerData = subtractTotalPerData(totalPerData, rejectedTotalPerData) + + // 处理汇总数据,四舍五入保留2位小数 + saleDetailRoundValues(&sumData, &totalPerData) + + sumData.TotalSalesProfit = 0 // 订单总销售毛利 + sumData.TotalStaffProfit = 0 // 订单总员工毛利 + sumData.TotalRetailPrice = 0 // 订单总指导零售价 + sumData.TotalDiscount = 0 // 订单总优惠 + sumData.TotalAmount = 0 // 订单实收金额 + sumData.StorePer = 0 // 门店提成 + + sumData.ScanAmount = 0 + sumData.CashAmount = 0 + sumData.PosAmount = 0 + sumData.StoreVmAmount = 0 + sumData.OtherAmount = 0 + RoundRetailDetailTotalData(&sumData) + + var result []RetailDetailByJoin + + if req.IsExport == 1 { //导出excel + err = qs.Order("erp_order.audit_time DESC").Find(&result).Error + if err != nil && err != RecordNotFound { + logger.Error("erp commodity list err:", logger.Field("err", err)) + return resp, err + } + + var count int64 + err = es.Count(&count).Error + if err != nil { + logger.Errorf("QueryRetailMargin count err:", err.Error()) + return nil, err + } + + orders := packData(result) + erpOrderSetBankTrxNo(orders) + fileUrl, err := saleDetailExport(orders, sumData, c) + if err != nil { + logger.Error("retailDetailExport err:", logger.Field("err", err)) + return resp, err + } + resp.ExportUrl = fileUrl + } else { + //err = qs.Order("erp_order.audit_time DESC").Offset(page * req.PageSize).Limit(req.PageSize).Find(&result).Error + err = qs.Order("erp_order.audit_time DESC").Find(&result).Error + if err != nil && err != RecordNotFound { + logger.Error("erp commodity list err:", logger.Field("err", err)) + return resp, err + } + + var count int64 + err = es.Count(&count).Error + if err != nil { + logger.Errorf("QueryRetailMargin count err:", err.Error()) + return nil, err + } + + orders := packData(result) + + erpOrderListSetSalesman(orders) + erpOrderSetBankTrxNo(orders) + pagedOrders := paginate(orders, page, req.PageSize) + + resp.List = pagedOrders + + //跟之前保持一致 + resp.Total = len(orders) + resp.PageIndex = page + 1 + resp.PageSize = req.PageSize + resp.SumData = sumData + } + + return resp, nil +} + +// 普通单表查询,然后补充收款数据和商品数据 +func querySaleDetailCommon(req *ErpOrderSaleDetailReq, c *gin.Context) (*ErpOrderRetailDetailResp, error) { + showConfig, err := GetErpOrderShowConfig() + if err != nil { + logger.Errorf("List err:", err) + showConfig.ShowAll = "ON" + } + + page := req.PageIndex - 1 + if page < 0 { + page = 0 + } + if req.PageSize == 0 { + req.PageSize = 10 + } + + resp := &ErpOrderRetailDetailResp{ + PageIndex: page + 1, + PageSize: req.PageSize, + } + + qs := orm.Eloquent.Table("erp_order") + totalPerQs := orm.Eloquent.Table("erp_order") + if showConfig.ShowAll == "OFF" { + qs = qs.Where("is_print = ? or retail_type = ?", HavePrinted, RetailTypeRejected) + totalPerQs = totalPerQs.Where("is_print = ? or retail_type = ?", HavePrinted, RetailTypeRejected) + } + if req.BillSn != "" { // 订单编号 + qs = qs.Where("bill_sn=?", req.BillSn) + totalPerQs = totalPerQs.Where("bill_sn=?", req.BillSn) + } else { + if req.RetailType != "" { // 销售类型 + qs = qs.Where("retail_type=?", req.RetailType) + totalPerQs = totalPerQs.Where("retail_type=?", req.RetailType) + } + if req.Uid != 0 { // 用户ID + qs = qs.Where("erp_order.uid=?", req.Uid) + totalPerQs = totalPerQs.Where("erp_order.uid=?", req.Uid) + } + if req.Tel != "" { // 用户手机号 + qs = qs.Where("tel=?", req.Tel) + totalPerQs = totalPerQs.Where("tel=?", req.Tel) + } + if req.StoreId != 0 { // 门店ID + qs = qs.Where("store_id=?", req.StoreId) + totalPerQs = totalPerQs.Where("store_id=?", req.StoreId) + } + // 非管理员才判断所属门店 + if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { + sysUser, err := GetSysUserByCtx(c) + if err != nil { + return nil, errors.New("操作失败:" + err.Error()) + } + + // 返回sysUser未过期的门店id列表 + storeList := GetValidStoreIDs(sysUser.StoreData) + if len(storeList) > 0 { + if len(storeList) == 1 { + qs = qs.Where("store_id = ?", storeList[0]) + totalPerQs = totalPerQs.Where("store_id = ?", storeList[0]) + } else { + qs = qs.Where("store_id IN (?)", storeList) + totalPerQs = totalPerQs.Where("store_id IN (?)", storeList) + } + } else { + return nil, errors.New("用户未绑定门店") + } + } + if req.Salesman != 0 { // 销售员 + qs = qs.Where("JSON_CONTAINS(salesman_list, ?)", fmt.Sprintf(`{"userId":%d}`, req.Salesman)) + totalPerQs = totalPerQs.Where("erp_order_sales.uid = ?", req.Salesman) + } + if req.StartTime != "" { // 审核开始时间 + parse, err := time.Parse(QueryTimeFormat, req.StartTime) + if err != nil { + logger.Errorf("err:", err) + } + qs = qs.Where("audit_time > ?", parse) + totalPerQs = totalPerQs.Where("audit_time > ?", parse) + } + if req.EndTime != "" { // 审核结束时间 + parse, err := time.Parse(QueryTimeFormat, req.EndTime) + if err != nil { + logger.Errorf("err:", err) + } + //parse = parse.AddDate(0, 0, 1) + qs = qs.Where("audit_time < ?", parse) + totalPerQs = totalPerQs.Where("audit_time < ?", parse) + } + if len(req.UserType) != 0 { + qs = qs.Where("user_type in ?", req.UserType) + totalPerQs = totalPerQs.Where("user_type in ?", req.UserType) + } + } + qs.Where("erp_order.pay_status = ? or (erp_order.retail_type = ? and erp_order.state != ?)", + HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) + totalPerQs.Where("erp_order.pay_status = ? or (erp_order.retail_type = ? and erp_order.state != ?)", + HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) + es := qs + rejectedTotalPerQs := totalPerQs + + // 销售订单的汇总数据 + orderSumQs := qs + var sumData RetailDetailTotalData + sumData, err = getRetailDetailTotalDataSale(orderSumQs, RetailTypeSale) + if err != nil { + logger.Error("query sum data err:", logger.Field("err", err)) + return resp, err + } + // 退货订单的汇总数据 + var rejectedSumData RetailDetailTotalData + rejectedOrderSumQs := qs + rejectedSumData, err = getRetailDetailTotalDataRejected(rejectedOrderSumQs, RetailTypeRejected) + if err != nil { + logger.Error("query sum data err:", logger.Field("err", err)) + return resp, err + } + // 计算销售订单和退货订单汇总后的销售数据 + sumData = subtractRetailData(sumData, rejectedSumData) + + // 销售订单提成汇总 + var totalPerData TotalPerData + totalPerData, err = getTotalPerData(totalPerQs, RetailTypeSale) + if err != nil { + logger.Error("query erp_order_sales sum data err:", logger.Field("err", err)) + return resp, err + } + // 退货订单提成汇总 + var rejectedTotalPerData TotalPerData + rejectedTotalPerData, err = getTotalPerData(rejectedTotalPerQs, RetailTypeRejected) + if err != nil { + logger.Error("query erp_order_sales sum data err:", logger.Field("err", err)) + return resp, err + } + // 计算销售订单和退货订单汇总后的提成数据 + totalPerData = subtractTotalPerData(totalPerData, rejectedTotalPerData) + + // 处理汇总数据,四舍五入保留2位小数 + saleDetailRoundValues(&sumData, &totalPerData) + sumData.TotalSalesProfitPer = totalPerData.TotalSalesProfitPer + sumData.TotalStaffProfitPer = totalPerData.TotalStaffProfitPer + sumData.SalesmanPer = totalPerData.SalesmanPer + RoundRetailDetailTotalData(&sumData) + + if req.IsExport == 1 { // 导出excel + var orders []ErpOrder + err = qs.Order("audit_time DESC").Find(&orders).Error + if err != nil && err != RecordNotFound { + logger.Error("erp commodity list err:", logger.Field("err", err)) + return resp, err + } + // 进行批量处理 + var wg sync.WaitGroup + wg.Add(2) + + go func() { + defer wg.Done() + ErpOrderRetailDetailSetCommodityAI(orders) // 商品信息处理 + }() + + go func() { + defer wg.Done() + erpOrderSetBankTrxNoAI(orders) // 银行交易号处理 + }() + + wg.Wait() // 等待所有处理完成 + + fileUrl, err := saleDetailExport(orders, sumData, c) + if err != nil { + logger.Error("retailDetailExport err:", logger.Field("err", err)) + return resp, err + } + resp.ExportUrl = fileUrl + } else { + var count int64 + err = es.Count(&count).Error + if err != nil { + logger.Error("count err:", logger.Field("err", err)) + return resp, err + } + + var orders []ErpOrder + err = qs.Order("audit_time DESC").Offset(page * req.PageSize).Limit(req.PageSize).Find(&orders).Error + if err != nil && err != RecordNotFound { + logger.Error("erp commodity list err:", logger.Field("err", err)) + return resp, err + } + + // 添加付款、销售员、商品信息 + ErpOrderRetailDetailSetCommodity(orders) + erpOrderListSetSalesman(orders) + erpOrderSetBankTrxNo(orders) + + resp.List = orders + + //跟之前保持一致 + resp.Total = int(count) + resp.PageIndex = page + 1 + resp.PageSize = req.PageSize + resp.SumData = sumData + } + + return resp, nil +} + +// 四舍五入保留2位小数 +func saleDetailRoundValues(data *RetailDetailTotalData, totalPerData *TotalPerData) { + roundMap := map[*float64]*float64{ + &data.StorePer: &data.StorePer, + &totalPerData.TotalSalesProfitPer: &totalPerData.TotalSalesProfitPer, + &totalPerData.TotalStaffProfitPer: &totalPerData.TotalStaffProfitPer, + &totalPerData.SalesmanPer: &totalPerData.SalesmanPer, + } + + for original, rounded := range roundMap { + *rounded = tools.RoundToTwoDecimalPlaces(*original) + } +} + +// 导出销售明细报表excel +func saleDetailExport(list []ErpOrder, sumData RetailDetailTotalData, c *gin.Context) (string, error) { + file := excelize.NewFile() + fSheet := "Sheet1" + + url := config.ExportConfig.Url + fileName := time.Now().Format(TimeFormat) + "销售明细" + ".xlsx" + fmt.Println("url fileName:", url+fileName) + + title := []interface{}{"订单编号", "订单类型", "店铺", "会员类别", "用户ID", "日期", "审核时间", "销售员1", "销售员2", + "一级分类", "二级分类", "三级分类", "商品名称", "供应商", "商品串码", "是否赠送", "销售数量", "指导价", "零售价", "零售优惠", + "会员优惠", "销售金额", "采购单价", "员工成本", "销售毛利", "员工毛利", "银联流水", "联系电话", "备注"} + + for i, _ := range title { + cell, _ := excelize.CoordinatesToCellName(1+i, 1) + err := file.SetCellValue(fSheet, cell, title[i]) + if err != nil { + logger.Error("file set value err:", logger.Field("err", err)) + } + } + + categoryMap, err := GetAllCategories() + if err != nil { + // 处理错误 + logger.Error("GetAllCategories err:", logger.Field("err", err)) + } + + var row []interface{} + nAmount := 0.0 + nExcelStartRow := 0 + for i := 0; i < len(list); i++ { + var saleType string + if list[i].RetailType == RetailTypeSale { + saleType = "零售销售" + } else if list[i].RetailType == RetailTypeRejected { + saleType = "零售退货" + } else { + logger.Error("订单类型异常") + return "", errors.New("RetailMarginDataExport, 订单类型异常:" + list[i].RetailType) + } + + var salesMan1, salesMan2 string + // 先判断商品数量,确定要写几行数据 + for rowId := 0; rowId < len(list[i].Commodities); rowId++ { + if list[i].RetailType == RetailTypeSale { + nAmount = list[i].Commodities[rowId].Amount + } else if list[i].RetailType == RetailTypeRejected { + nAmount = list[i].Commodities[rowId].RejectedAmount + list[i].CashierList = "" // 目前零售退货订单暂时不展示各个方式的付款金额 + } + + // 用户id,会员类别 + var userId, userType string + if list[i].Uid != 0 { + userId = strconv.Itoa(list[i].Uid) + userType = GetUserTypeString(list[i].UserType) + } else { + userId = "" + } + + // 审核时间 + var auditTime, auditDate string + if list[i].AuditTime != nil { + auditTime = list[i].AuditTime.Format(ExcelTimeFormat) + auditDate = list[i].AuditTime.Format(DateTimeFormat) // Extract date only + } else { + auditTime = "" + auditDate = "" + } + + // 商品分类 + categoryID := list[i].Commodities[rowId].ErpCategoryId + categoryLevels := GetCategoryLevelsFromMap(categoryID, categoryMap) + + // 是否赠送 + strPresentType := "非赠送" + if list[i].Commodities[rowId].PresentType == 2 { + strPresentType = "赠送" + } + + if len(list[i].Salesman) > 0 { + salesMan1 = list[i].Salesman[0].Name + if len(list[i].Salesman) == 2 { + salesMan2 = list[i].Salesman[1].Name + } + } + row = []interface{}{ + list[i].BillSn, // 订单编号 + saleType, // 订单类型 + list[i].StoreName, // 店铺 + userType, // 会员类别 + userId, // 用户ID + auditDate, // 日期 + auditTime, // 审核时间 + salesMan1, // 销售员1 + salesMan2, // 销售员2 + categoryLevels.Level1.Name, // 一级分类 + categoryLevels.Level2.Name, // 二级分类 + categoryLevels.Level3.Name, // 三级分类 + list[i].Commodities[rowId].ErpCommodityName, // 商品名称 + list[i].Commodities[rowId].ErpSupplierName, // 供应商 + list[i].Commodities[rowId].IMEI, // 商品串码 + strPresentType, // 是否赠送 + list[i].Commodities[rowId].Count, // 销售数量 + list[i].Commodities[rowId].RetailPrice, // 指导价 + list[i].Commodities[rowId].SalePrice, // 零售价 + list[i].Commodities[rowId].SaleDiscount, // 零售优惠 + list[i].Commodities[rowId].MemberDiscount, // 会员优惠 + nAmount, // 销售金额 + list[i].Commodities[rowId].WholesalePrice, // 采购单价 + list[i].Commodities[rowId].WholesalePrice + list[i].Commodities[rowId].StaffCostPrice, // 员工成本 + list[i].Commodities[rowId].SalesProfit, // 销售毛利 + list[i].Commodities[rowId].StaffProfit, // 员工毛利 + list[i].BankTrxNo, // 银联流水 + list[i].Tel, // 联系电话 + list[i].Commodities[rowId].Remark, // 备注 + } + + for j, _ := range row { + cell, _ := excelize.CoordinatesToCellName(1+j, nExcelStartRow+2) + err = file.SetCellValue(fSheet, cell, row[j]) + if err != nil { + logger.Error("file set value err:", logger.Field("err", err)) + } + } + nExcelStartRow++ + } + } + + totalData := "订单数:" + strconv.FormatInt(int64(len(list)), 10) + end := []interface{}{totalData, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", + sumData.Count, + sumData.RetailPrice, + sumData.SalePrice, + sumData.SaleDiscount, + sumData.MemberDiscount, + sumData.Amount, + "", + } + + for i, _ := range end { + cell, _ := excelize.CoordinatesToCellName(1+i, nExcelStartRow+2) + err := file.SetCellValue(fSheet, cell, end[i]) + if err != nil { + logger.Error("file set value err:", logger.Field("err", err)) + } + } + + // 设置所有单元格的样式: 居中、加边框 + style, _ := file.NewStyle(`{"alignment":{"horizontal":"center","vertical":"center"}, + "border":[{"type":"left","color":"000000","style":1}, + {"type":"top","color":"000000","style":1}, + {"type":"right","color":"000000","style":1}, + {"type":"bottom","color":"000000","style":1}]}`) + + endRow := fmt.Sprintf("AC"+"%d", nExcelStartRow+2) + + // 应用样式到整个表格 + _ = file.SetCellStyle("Sheet1", "A1", endRow, style) + + // 设置单元格大小 + file.SetColWidth("Sheet1", "A", "A", 15) + file.SetColWidth("Sheet1", "C", "C", 25) + file.SetColWidth("Sheet1", "D", "D", 19) + file.SetColWidth("Sheet1", "F", "F", 15) + file.SetColWidth("Sheet1", "G", "G", 19) + file.SetColWidth("Sheet1", "M", "M", 25) + file.SetColWidth("Sheet1", "O", "O", 18) + file.SetColWidth("Sheet1", "AA", "AA", 30) + file.SetColWidth("Sheet1", "AB", "AB", 15) + + fmt.Println("save fileName:", config.ExportConfig.Path+fileName) + if err := file.SaveAs(config.ExportConfig.Path + fileName); err != nil { + fmt.Println(err) + } + return url + fileName, nil +} diff --git a/app/admin/models/purchase.go b/app/admin/models/purchase.go index b5f626f..5408b7c 100644 --- a/app/admin/models/purchase.go +++ b/app/admin/models/purchase.go @@ -1954,7 +1954,7 @@ func getErpPurchaseDemandAll(req *GetErpPurchaseDemandReq, c *gin.Context) (*Get return nil, errors.New("无有效门店,请配置门店信息") } - qs := orm.Eloquent.Debug().Table("erp_commodity") + qs := orm.Eloquent.Debug().Table("erp_commodity").Where("stop_purchase != 1") if req.ErpCategoryId != 0 { categoryInfo, err := GetErpCategory(req.ErpCategoryId) if err != nil { diff --git a/app/admin/models/user.go b/app/admin/models/user.go index 0abdca6..db59ca5 100644 --- a/app/admin/models/user.go +++ b/app/admin/models/user.go @@ -4672,3 +4672,72 @@ func (m *CancelPrivilegeMembersReq) CancelPrivilegeMembers() (*PrivilegeMember, return &userInfo, nil } + +// GetUserType 获取会员类别:1-普通会员 2-黄金会员 4-白金会员 5-黑金会员 6-尊享会员 7-黄金&尊享 8-白金&尊享 9-黑金&尊享 +func GetUserType(uid int) (int, error) { + userType := 1 // 默认普通会员 + + // 获取尊享会员信息 + var privilegeUserInfo PrivilegeMember + if err := orm.Eloquent.Table("privilege_member").Where("uid = ?", uid).Find(&privilegeUserInfo).Error; err != nil { + logger.Errorf("获取尊享会员信息出错:", logger.Field("err", err)) + return userType, err + } + + // 获取用户信息 + var userInfo UserInfo + if err := orm.Eloquent.Table("user").Where("uid = ?", uid).Find(&userInfo).Error; err != nil { + logger.Errorf("获取用户信息出错:", logger.Field("err", err)) + return userType, err + } + + // 判断用户类别 + switch userInfo.MemberLevel { + case MemberLevelGold, MemberLevelPeriod: // 黄金会员或短期会员 + userType = 2 + case MemberLevelPlatinum: // 白金会员 + userType = 4 + case MemberLevelBlackGold: // 黑金会员 + userType = 5 + } + + // 如果是尊享会员,增加相应标志 + if privilegeUserInfo.MemberLevel == MemberLevelPrivilege { + switch userType { + case 2: // 黄金会员 & 尊享会员 + userType = 7 + case 4: // 白金会员 & 尊享会员 + userType = 8 + case 5: // 黑金会员 & 尊享会员 + userType = 9 + default: // 仅尊享会员 + userType = 6 + } + } + + return userType, nil +} + +// GetUserTypeString 将userType转换为对应的简写字符串 +func GetUserTypeString(userType int) string { + switch userType { + case 1: + return "普通会员" + case 2: + return "付费会员(黄金)" + case 4: + return "付费会员(白金)" + case 5: + return "付费会员(黑金)" + case 6: + return "付费会员(尊享)" + case 7: + return "付费会员(黄金&尊享)" + case 8: + return "付费会员(白金&尊享)" + case 9: + return "付费会员(黑金&尊享)" + default: + return "普通会员" + } +} diff --git a/app/admin/router/erpordermanage.go b/app/admin/router/erpordermanage.go index 455c0fc..f1fb4dc 100644 --- a/app/admin/router/erpordermanage.go +++ b/app/admin/router/erpordermanage.go @@ -24,4 +24,5 @@ func registerErpOrderManageRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJW r.POST("receipt_data", erpordermanage.ErpOrderReceiptData) // 查询小票数据 r.POST("show_all_data", erpordermanage.ErpOrderShowAllData) // 展示所有订单 r.POST("daily_report", erpordermanage.ErpOrderDailyReport) // 经营日报表 + r.POST("sale_detail", erpordermanage.ErpOrderSaleDetail) // 查询销售明细 } diff --git a/docs/docs.go b/docs/docs.go index b38525d..411ab2f 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -1853,7 +1853,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "零售订单" + "零售报表" ], "summary": "经营日报表", "parameters": [ @@ -2141,6 +2141,39 @@ const docTemplate = `{ } } }, + "/api/v1/erp_order/sale_detail": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "零售报表" + ], + "summary": "查询销售明细", + "parameters": [ + { + "description": "查询销售明细数据模型", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.ErpOrderSaleDetailReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ErpOrderRetailDetailResp" + } + } + } + } + }, "/api/v1/erp_order/show_all_data": { "post": { "consumes": [ @@ -7139,6 +7172,10 @@ const docTemplate = `{ "description": "员工成本价加价", "type": "number" }, + "stop_purchase": { + "description": "0-未勾选,正常采购;1-勾选,停止采购", + "type": "integer" + }, "wholesale_price": { "description": "指导采购价", "type": "number" @@ -7245,6 +7282,10 @@ const docTemplate = `{ "description": "员工成本价加价", "type": "number" }, + "stop_purchase": { + "description": "0-未勾选,正常采购;1-勾选,停止采购", + "type": "integer" + }, "wholesale_price": { "description": "指导采购价", "type": "number" @@ -7785,6 +7826,10 @@ const docTemplate = `{ "$ref": "#/definitions/models.CategorySalesData" } }, + "store_amount": { + "description": "门店销售金额", + "type": "number" + }, "store_id": { "description": "门店ID", "type": "integer" @@ -7792,6 +7837,10 @@ const docTemplate = `{ "store_name": { "description": "门店名称", "type": "string" + }, + "store_sales_profit": { + "description": "门店销售毛利(销售毛利;店员看员工毛利)", + "type": "number" } } }, @@ -8473,6 +8522,10 @@ const docTemplate = `{ "description": "库存数量", "type": "integer" }, + "stop_purchase": { + "description": "0-未勾选,正常采购;1-勾选,停止采购", + "type": "integer" + }, "updatedAt": { "description": "更新时间", "type": "string" @@ -9353,6 +9406,10 @@ const docTemplate = `{ "description": "更新时间", "type": "string" }, + "user_type": { + "description": "会员类别:1-普通会员 2-黄金会员 4-白金会员 5-黑金会员 6-尊享会员 7-黄金\u0026尊享 8-白金\u0026尊享 9-黑金\u0026尊享", + "type": "integer" + }, "vm_count": { "description": "使用会员积分", "type": "integer" @@ -10154,6 +10211,81 @@ const docTemplate = `{ } } }, + "models.ErpOrderSaleDetailReq": { + "type": "object", + "properties": { + "bank_trx_no": { + "description": "银联流水号", + "type": "string" + }, + "bill_sn": { + "description": "单据编号", + "type": "string" + }, + "end_time": { + "description": "结束时间", + "type": "string" + }, + "erp_category_id": { + "description": "分类id", + "type": "array", + "items": { + "type": "integer" + } + }, + "erp_commodity_name": { + "description": "商品名称", + "type": "string" + }, + "imei": { + "description": "串码", + "type": "string" + }, + "is_export": { + "description": "1-导出", + "type": "integer" + }, + "pageIndex": { + "description": "页码", + "type": "integer" + }, + "pageSize": { + "description": "页面条数", + "type": "integer" + }, + "retail_type": { + "description": "销售类型:sale 零售销售; rejected 零售退货", + "type": "string" + }, + "salesman": { + "description": "销售人员ID", + "type": "integer" + }, + "start_time": { + "description": "开始时间", + "type": "string" + }, + "store_id": { + "description": "门店ID", + "type": "integer" + }, + "tel": { + "description": "客户手机号", + "type": "string" + }, + "uid": { + "description": "用户ID", + "type": "integer" + }, + "user_type": { + "description": "会员类别:0-未注册 1-普通会员 2-黄金会员 4-白金会员 5-黑金会员 6-尊享会员 7-黄金\u0026尊享 8-白金\u0026尊享 9-黑金\u0026尊享", + "type": "array", + "items": { + "type": "integer" + } + } + } + }, "models.ErpOrderSales": { "type": "object", "required": [ diff --git a/docs/swagger.json b/docs/swagger.json index 83294da..371a249 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1842,7 +1842,7 @@ "application/json" ], "tags": [ - "零售订单" + "零售报表" ], "summary": "经营日报表", "parameters": [ @@ -2130,6 +2130,39 @@ } } }, + "/api/v1/erp_order/sale_detail": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "零售报表" + ], + "summary": "查询销售明细", + "parameters": [ + { + "description": "查询销售明细数据模型", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.ErpOrderSaleDetailReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ErpOrderRetailDetailResp" + } + } + } + } + }, "/api/v1/erp_order/show_all_data": { "post": { "consumes": [ @@ -7128,6 +7161,10 @@ "description": "员工成本价加价", "type": "number" }, + "stop_purchase": { + "description": "0-未勾选,正常采购;1-勾选,停止采购", + "type": "integer" + }, "wholesale_price": { "description": "指导采购价", "type": "number" @@ -7234,6 +7271,10 @@ "description": "员工成本价加价", "type": "number" }, + "stop_purchase": { + "description": "0-未勾选,正常采购;1-勾选,停止采购", + "type": "integer" + }, "wholesale_price": { "description": "指导采购价", "type": "number" @@ -7774,6 +7815,10 @@ "$ref": "#/definitions/models.CategorySalesData" } }, + "store_amount": { + "description": "门店销售金额", + "type": "number" + }, "store_id": { "description": "门店ID", "type": "integer" @@ -7781,6 +7826,10 @@ "store_name": { "description": "门店名称", "type": "string" + }, + "store_sales_profit": { + "description": "门店销售毛利(销售毛利;店员看员工毛利)", + "type": "number" } } }, @@ -8462,6 +8511,10 @@ "description": "库存数量", "type": "integer" }, + "stop_purchase": { + "description": "0-未勾选,正常采购;1-勾选,停止采购", + "type": "integer" + }, "updatedAt": { "description": "更新时间", "type": "string" @@ -9342,6 +9395,10 @@ "description": "更新时间", "type": "string" }, + "user_type": { + "description": "会员类别:1-普通会员 2-黄金会员 4-白金会员 5-黑金会员 6-尊享会员 7-黄金\u0026尊享 8-白金\u0026尊享 9-黑金\u0026尊享", + "type": "integer" + }, "vm_count": { "description": "使用会员积分", "type": "integer" @@ -10143,6 +10200,81 @@ } } }, + "models.ErpOrderSaleDetailReq": { + "type": "object", + "properties": { + "bank_trx_no": { + "description": "银联流水号", + "type": "string" + }, + "bill_sn": { + "description": "单据编号", + "type": "string" + }, + "end_time": { + "description": "结束时间", + "type": "string" + }, + "erp_category_id": { + "description": "分类id", + "type": "array", + "items": { + "type": "integer" + } + }, + "erp_commodity_name": { + "description": "商品名称", + "type": "string" + }, + "imei": { + "description": "串码", + "type": "string" + }, + "is_export": { + "description": "1-导出", + "type": "integer" + }, + "pageIndex": { + "description": "页码", + "type": "integer" + }, + "pageSize": { + "description": "页面条数", + "type": "integer" + }, + "retail_type": { + "description": "销售类型:sale 零售销售; rejected 零售退货", + "type": "string" + }, + "salesman": { + "description": "销售人员ID", + "type": "integer" + }, + "start_time": { + "description": "开始时间", + "type": "string" + }, + "store_id": { + "description": "门店ID", + "type": "integer" + }, + "tel": { + "description": "客户手机号", + "type": "string" + }, + "uid": { + "description": "用户ID", + "type": "integer" + }, + "user_type": { + "description": "会员类别:0-未注册 1-普通会员 2-黄金会员 4-白金会员 5-黑金会员 6-尊享会员 7-黄金\u0026尊享 8-白金\u0026尊享 9-黑金\u0026尊享", + "type": "array", + "items": { + "type": "integer" + } + } + } + }, "models.ErpOrderSales": { "type": "object", "required": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 5f86cd7..2deccec 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -591,6 +591,9 @@ definitions: staff_cost_price: description: 员工成本价加价 type: number + stop_purchase: + description: 0-未勾选,正常采购;1-勾选,停止采购 + type: integer wholesale_price: description: 指导采购价 type: number @@ -667,6 +670,9 @@ definitions: staff_cost_price: description: 员工成本价加价 type: number + stop_purchase: + description: 0-未勾选,正常采购;1-勾选,停止采购 + type: integer wholesale_price: description: 指导采购价 type: number @@ -1068,12 +1074,18 @@ definitions: items: $ref: '#/definitions/models.CategorySalesData' type: array + store_amount: + description: 门店销售金额 + type: number store_id: description: 门店ID type: integer store_name: description: 门店名称 type: string + store_sales_profit: + description: 门店销售毛利(销售毛利;店员看员工毛利) + type: number type: object models.DecisionReportData: properties: @@ -1572,6 +1584,9 @@ definitions: stock_count: description: 库存数量 type: integer + stop_purchase: + description: 0-未勾选,正常采购;1-勾选,停止采购 + type: integer updatedAt: description: 更新时间 type: string @@ -2218,6 +2233,9 @@ definitions: updatedAt: description: 更新时间 type: string + user_type: + description: 会员类别:1-普通会员 2-黄金会员 4-白金会员 5-黑金会员 6-尊享会员 7-黄金&尊享 8-白金&尊享 9-黑金&尊享 + type: integer vm_count: description: 使用会员积分 type: integer @@ -2800,6 +2818,62 @@ definitions: description: 总销售毛利:销售/退货金额-销售成本 type: number type: object + models.ErpOrderSaleDetailReq: + properties: + bank_trx_no: + description: 银联流水号 + type: string + bill_sn: + description: 单据编号 + type: string + end_time: + description: 结束时间 + type: string + erp_category_id: + description: 分类id + items: + type: integer + type: array + erp_commodity_name: + description: 商品名称 + type: string + imei: + description: 串码 + type: string + is_export: + description: 1-导出 + type: integer + pageIndex: + description: 页码 + type: integer + pageSize: + description: 页面条数 + type: integer + retail_type: + description: 销售类型:sale 零售销售; rejected 零售退货 + type: string + salesman: + description: 销售人员ID + type: integer + start_time: + description: 开始时间 + type: string + store_id: + description: 门店ID + type: integer + tel: + description: 客户手机号 + type: string + uid: + description: 用户ID + type: integer + user_type: + description: 会员类别:0-未注册 1-普通会员 2-黄金会员 4-白金会员 5-黑金会员 6-尊享会员 7-黄金&尊享 8-白金&尊享 + 9-黑金&尊享 + items: + type: integer + type: array + type: object models.ErpOrderSales: properties: createdAt: @@ -9659,7 +9733,7 @@ paths: $ref: '#/definitions/models.ErpOrderDailyReportResp' summary: 经营日报表 tags: - - 零售订单 + - 零售报表 /api/v1/erp_order/delete: post: consumes: @@ -9828,6 +9902,27 @@ paths: summary: 查询商品零售毛利汇总 tags: - 零售报表 + /api/v1/erp_order/sale_detail: + post: + consumes: + - application/json + parameters: + - description: 查询销售明细数据模型 + in: body + name: request + required: true + schema: + $ref: '#/definitions/models.ErpOrderSaleDetailReq' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.ErpOrderRetailDetailResp' + summary: 查询销售明细 + tags: + - 零售报表 /api/v1/erp_order/show_all_data: post: consumes: