From c1abef7dd5d9f1ba29fb841f58bd5e0a17cffa7b Mon Sep 17 00:00:00 2001 From: chenlin Date: Wed, 27 Dec 2023 19:23:03 +0800 Subject: [PATCH] =?UTF-8?q?1.=E6=96=B0=E5=A2=9E=E6=8E=A5=E5=8F=A3=EF=BC=9A?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=94=B6=E6=AC=BE=E7=8A=B6=E6=80=81=EF=BC=9B?= =?UTF-8?q?=202.=E6=96=B0=E5=A2=9E=E6=8E=A5=E5=8F=A3=EF=BC=9A=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E9=97=A8=E5=BA=97=E7=BB=8F=E8=90=A5=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=EF=BC=9B=203.=E6=96=B0=E5=A2=9E=E6=8E=A5=E5=8F=A3=EF=BC=9A?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E5=95=86=E5=93=81=E9=9B=B6=E5=94=AE=E6=AF=9B?= =?UTF-8?q?=E5=88=A9=E6=B1=87=E6=80=BB=EF=BC=9B=204.=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=B2=B3=E9=A9=AC=E4=BB=98=E5=AF=86=E9=92=A5=E8=AF=BB=E5=8F=96?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=EF=BC=9B=205.=E4=BC=98=E5=8C=96=E9=9B=B6?= =?UTF-8?q?=E5=94=AE=E8=AE=A2=E5=8D=95=E5=BA=93=E5=AD=98=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/apis/erpordermanage/erp_order.go | 290 ++++++++-- app/admin/apis/pay/wx_pay.go | 9 +- app/admin/models/commodity.go | 25 +- app/admin/models/erp_order.go | 615 +++++++++++++++++++-- app/admin/router/erpordermanage.go | 18 +- docs/docs.go | 244 +++++++- docs/swagger.json | 244 +++++++- docs/swagger.yaml | 170 +++++- 8 files changed, 1457 insertions(+), 158 deletions(-) diff --git a/app/admin/apis/erpordermanage/erp_order.go b/app/admin/apis/erpordermanage/erp_order.go index a89a3ac..2ebbed9 100644 --- a/app/admin/apis/erpordermanage/erp_order.go +++ b/app/admin/apis/erpordermanage/erp_order.go @@ -25,7 +25,7 @@ func ErpOrderCreate(c *gin.Context) { var req = new(model.ErpOrderCreateReq) if err := c.ShouldBindJSON(&req); err != nil { logger.Error("ShouldBindJSON err:", logger.Field("err", err)) - app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误") + app.Error(c, http.StatusBadRequest, err, "参数错误:"+err.Error()) return } @@ -38,22 +38,39 @@ func ErpOrderCreate(c *gin.Context) { jCashier, err := json.Marshal(req.Cashiers) if err != nil { logger.Error("cashiers marshal err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "操作失败") + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) return } sysUser, err := model.GetSysUserByCtx(c) if err != nil { logger.Error("sys user err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "操作失败") + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) return } - //if sysUser.StoreId == 0 {// 校验登陆账户所属门店 - // logger.Error("sys user store id null") - // app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误") - // return - //} + if sysUser.StoreId == 0 { // 校验登陆账户所属门店 + logger.Error("sys user store id null") + app.Error(c, http.StatusBadRequest, errors.New("para err"), "该账户未绑定门店") + return + } + + var Salesman1Info, Salesman2Info model.UserInfo + Salesman1Info, err = model.GetUserInfoByUid(req.Salesman1) + if err != nil { + logger.Error("GetUserInfoByUid err:", logger.Field("err", err)) + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) + return + } + + if req.Salesman2 != 0 { + Salesman1Info, err = model.GetUserInfoByUid(req.Salesman2) + if err != nil { + logger.Error("GetUserInfoByUid Salesman2 err:", logger.Field("err", err)) + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) + return + } + } erpOrder := &model.ErpOrder{ BillSn: model.NewErpBillSn(), @@ -67,9 +84,9 @@ func ErpOrderCreate(c *gin.Context) { MakerTime: time.Now(), CashierList: string(jCashier), Salesman1: req.Salesman1, - SalesmanName1: req.SalesmanName1, + SalesmanName1: Salesman1Info.ShopAssistantName, Salesman2: req.Salesman2, - SalesmanName2: req.SalesmanName2, + SalesmanName2: Salesman2Info.ShopAssistantName, MemberType: req.MemberType, State: model.ErpOrderStateUnAudit, PayStatus: model.NoCreatePayOrder, @@ -87,7 +104,7 @@ func ErpOrderCreate(c *gin.Context) { err = orm.Eloquent.Table("erp_commodity").Where("id IN (?)", commodityIds).Find(&commodities).Error if err != nil { logger.Error("commodities err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "操作失败") + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) return } @@ -137,13 +154,38 @@ func ErpOrderCreate(c *gin.Context) { return } - // 更新订单表总金额和数量 - erpOrder.TotalAmount += req.ErpOrderCommodities[i].Amount - erpOrder.TotalCount += req.ErpOrderCommodities[i].Count + // 更新订单表总退款金额和数量 + erpOrder.RejectedTotalAmount += req.ErpOrderCommodities[i].RejectedAmount + erpOrder.RejectedTotalCount += req.ErpOrderCommodities[i].RejectedCount + + // 销售毛利 // todo 待测试核实 + salesProfit := v.TotalAmount - float64(v.WholesalePrice*v.Count) + if salesProfit < 0 { + logger.Error("rejected salesProfit less than 0") + app.Error(c, http.StatusInternalServerError, err, "商品销售毛利小于0,请检查") + return + } + // 员工毛利 // todo 待测试核实 + StaffProfit := salesProfit - float64(v.StaffCostPrice*v.Count) + if StaffProfit < 0 { + logger.Error("rejected StaffProfit less than 0") + app.Error(c, http.StatusInternalServerError, err, "商品员工毛利小于0,请检查") + return + } + + erpOrder.SalesProfit += salesProfit + erpOrder.StaffProfit += StaffProfit } else if req.RetailType == model.RetailTypeSale { // 零售订单 v, ok := commodityMap[req.ErpOrderCommodities[i].ErpCommodityId] if ok { + // todo 零售订单需校验商品是否有库存 + if !model.CommodityIsHaveStock(req.ErpOrderCommodities[i], req.StoreId) { + logger.Error("商品" + "[" + v.Name + "]库存不足, 门店:" + req.StoreName) + app.Error(c, http.StatusInternalServerError, err, "商品"+"["+v.Name+"]库存不足") + return + } + req.ErpOrderCommodities[i].ID = 0 req.ErpOrderCommodities[i].ErpCommodityId = v.ID // 商品id req.ErpOrderCommodities[i].ErpCommodityName = v.Name // 商品名称 @@ -151,6 +193,11 @@ func ErpOrderCreate(c *gin.Context) { req.ErpOrderCommodities[i].ErpCategoryName = v.ErpCategoryName // 分类名称 req.ErpOrderCommodities[i].RetailPrice = v.RetailPrice // 指导零售价 req.ErpOrderCommodities[i].MemberDiscount = v.MemberDiscount // 会员优惠 + req.ErpOrderCommodities[i].StaffCostPrice = v.StaffCostPrice // 员工成本价加价 + req.ErpOrderCommodities[i].WholesalePrice = v.WholesalePrice // 指导采购价 + } else { + app.Error(c, http.StatusInternalServerError, err, "遍历商品信息报错") + return } if req.ErpOrderCommodities[i].PresentType == 2 && v.MinRetailPrice != 0 { // 赠送类型商品进行校验,最低零售价为0才能赠送 @@ -165,9 +212,27 @@ func ErpOrderCreate(c *gin.Context) { return } - // 更新订单表总退款金额和数量 - erpOrder.TotalAmount += req.ErpOrderCommodities[i].RejectedAmount - erpOrder.TotalCount += req.ErpOrderCommodities[i].RejectedCount + // 更新订单表总金额和数量 + erpOrder.TotalAmount += req.ErpOrderCommodities[i].TotalAmount + erpOrder.TotalCount += req.ErpOrderCommodities[i].Count + // 销售毛利 + salesProfit := req.ErpOrderCommodities[i].TotalAmount - + float64(req.ErpOrderCommodities[i].WholesalePrice*req.ErpOrderCommodities[i].Count) + if salesProfit < 0 { + logger.Error("salesProfit less than 0") + app.Error(c, http.StatusInternalServerError, err, "商品销售毛利小于0,请检查") + return + } + // 员工毛利 + StaffProfit := salesProfit - float64(req.ErpOrderCommodities[i].StaffCostPrice*req.ErpOrderCommodities[i].Count) + if StaffProfit < 0 { + logger.Error("StaffProfit less than 0") + app.Error(c, http.StatusInternalServerError, err, "商品员工毛利小于0,请检查") + return + } + + erpOrder.SalesProfit += salesProfit + erpOrder.StaffProfit += StaffProfit } } @@ -176,7 +241,7 @@ func ErpOrderCreate(c *gin.Context) { if err != nil { begin.Rollback() logger.Error("create erp order err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "操作失败") + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) return } @@ -190,7 +255,7 @@ func ErpOrderCreate(c *gin.Context) { if err != nil { begin.Rollback() logger.Error("Create") - app.Error(c, http.StatusInternalServerError, err, "操作失败") + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) return } @@ -198,7 +263,7 @@ func ErpOrderCreate(c *gin.Context) { if err != nil { begin.Rollback() logger.Error("commit err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "操作失败") + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) return } @@ -218,7 +283,7 @@ func ErpOrderEdit(c *gin.Context) { var req = new(model.ErpOrderCreateReq) if err := c.ShouldBindJSON(&req); err != nil { logger.Error("ShouldBindJSON err:", logger.Field("err", err)) - app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误") + app.Error(c, http.StatusBadRequest, err, "参数错误:"+err.Error()) return } @@ -231,14 +296,14 @@ func ErpOrderEdit(c *gin.Context) { jCashier, err := json.Marshal(req.Cashiers) if err != nil { logger.Error("cashiers marshal err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "操作失败") + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) return } sysUser, err := model.GetSysUserByCtx(c) if err != nil { logger.Error("sys user err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "操作失败") + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) return } @@ -246,7 +311,7 @@ func ErpOrderEdit(c *gin.Context) { err = orm.Eloquent.Table("erp_order").Where("bill_sn=?", req.BillSn).Find(&orderInfo).Error if err != nil { logger.Error("GetOrderState err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "操作失败") + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) return } @@ -256,11 +321,28 @@ func ErpOrderEdit(c *gin.Context) { return } - //if sysUser.StoreId == 0 {// 校验登陆账户所属门店 - // logger.Error("sys user store id null") - // app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误") - // return - //} + if sysUser.StoreId == 0 { // 校验登陆账户所属门店 + logger.Error("sys user store id null") + app.Error(c, http.StatusBadRequest, errors.New("para err"), "该账户未绑定门店") + return + } + + var Salesman1Info, Salesman2Info model.UserInfo + Salesman1Info, err = model.GetUserInfoByUid(req.Salesman1) + if err != nil { + logger.Error("GetUserInfoByUid err:", logger.Field("err", err)) + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) + return + } + + if req.Salesman2 != 0 { + Salesman1Info, err = model.GetUserInfoByUid(req.Salesman2) + if err != nil { + logger.Error("GetUserInfoByUid Salesman2 err:", logger.Field("err", err)) + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) + return + } + } erpOrder := &model.ErpOrder{ BillSn: req.BillSn, @@ -274,9 +356,9 @@ func ErpOrderEdit(c *gin.Context) { MakerTime: time.Now(), CashierList: string(jCashier), Salesman1: req.Salesman1, - SalesmanName1: req.SalesmanName1, + SalesmanName1: Salesman1Info.ShopAssistantName, Salesman2: req.Salesman2, - SalesmanName2: req.SalesmanName2, + SalesmanName2: Salesman2Info.ShopAssistantName, MemberType: req.MemberType, State: model.ErpOrderStateUnAudit, PayStatus: model.NoCreatePayOrder, @@ -295,7 +377,7 @@ func ErpOrderEdit(c *gin.Context) { err = orm.Eloquent.Table("erp_commodity").Where("id IN (?)", commodityIds).Find(&commodities).Error if err != nil { logger.Error("commodities err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "操作失败") + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) return } @@ -349,6 +431,23 @@ func ErpOrderEdit(c *gin.Context) { erpOrder.TotalAmount += req.ErpOrderCommodities[i].Amount erpOrder.TotalCount += req.ErpOrderCommodities[i].Count + // 销售毛利 // todo 待测试核实 + salesProfit := v.TotalAmount - float64(v.WholesalePrice*v.Count) + if salesProfit < 0 { + logger.Error("rejected salesProfit less than 0") + app.Error(c, http.StatusInternalServerError, err, "商品销售毛利小于0,请检查") + return + } + // 员工毛利 // todo 待测试核实 + StaffProfit := salesProfit - float64(v.StaffCostPrice*v.Count) + if StaffProfit < 0 { + logger.Error("rejected StaffProfit less than 0") + app.Error(c, http.StatusInternalServerError, err, "商品员工毛利小于0,请检查") + return + } + + erpOrder.SalesProfit += salesProfit + erpOrder.StaffProfit += StaffProfit } else if req.RetailType == model.RetailTypeSale { // 零售订单 v, ok := commodityMap[req.ErpOrderCommodities[i].ErpCommodityId] if ok { @@ -375,6 +474,25 @@ func ErpOrderEdit(c *gin.Context) { // 更新订单表总退款金额和数量 erpOrder.TotalAmount += req.ErpOrderCommodities[i].RejectedAmount erpOrder.TotalCount += req.ErpOrderCommodities[i].RejectedCount + + // 销售毛利 + salesProfit := req.ErpOrderCommodities[i].TotalAmount - + float64(req.ErpOrderCommodities[i].WholesalePrice*req.ErpOrderCommodities[i].Count) + if salesProfit < 0 { + logger.Error("salesProfit less than 0") + app.Error(c, http.StatusInternalServerError, err, "商品销售毛利小于0,请检查") + return + } + // 员工毛利 + StaffProfit := salesProfit - float64(req.ErpOrderCommodities[i].StaffCostPrice*req.ErpOrderCommodities[i].Count) + if StaffProfit < 0 { + logger.Error("StaffProfit less than 0") + app.Error(c, http.StatusInternalServerError, err, "商品员工毛利小于0,请检查") + return + } + + erpOrder.SalesProfit += salesProfit + erpOrder.StaffProfit += StaffProfit } } @@ -383,7 +501,7 @@ func ErpOrderEdit(c *gin.Context) { if err != nil { begin.Rollback() logger.Error("create erp order err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "操作失败") + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) return } @@ -397,7 +515,7 @@ func ErpOrderEdit(c *gin.Context) { if err != nil { begin.Rollback() logger.Error("Create") - app.Error(c, http.StatusInternalServerError, err, "操作失败") + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) return } @@ -405,7 +523,7 @@ func ErpOrderEdit(c *gin.Context) { if err != nil { begin.Rollback() logger.Error("commit err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "操作失败") + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) return } @@ -425,14 +543,14 @@ func ErpOrderList(c *gin.Context) { req := &model.ErpOrderListReq{} if err := c.ShouldBindJSON(&req); err != nil { logger.Error("ShouldBindJSON err:", logger.Field("err", err)) - app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误") + app.Error(c, http.StatusBadRequest, err, "参数错误:"+err.Error()) return } resp, err := req.List() if err != nil { logger.Error("erp commodity list err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "获取失败") + app.Error(c, http.StatusInternalServerError, err, "查询失败:"+err.Error()) return } @@ -452,7 +570,7 @@ func ErpOrderAudit(c *gin.Context) { var req = new(model.ErpOrderAuditReq) if err := c.ShouldBindJSON(&req); err != nil { logger.Error("ShouldBindJSON err:", logger.Field("err", err)) - app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误") + app.Error(c, http.StatusBadRequest, err, "参数错误:"+err.Error()) return } @@ -465,19 +583,22 @@ func ErpOrderAudit(c *gin.Context) { err = orm.Eloquent.Table("erp_order").Where("bill_sn = ?", req.BillSn).Find(&erpOrder).Error if err != nil { logger.Error("order err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "审核失败") + app.Error(c, http.StatusInternalServerError, err, "审核失败:"+err.Error()) return } orderState := model.ErpOrderStateAudited + stockState := model.OnSale switch req.State { case 1: // 审核 orderState = model.ErpOrderStateAudited + stockState = model.OnSale // 库存-销售锁定 case 2: // 取消审核 orderState = model.ErpOrderStateUnAudit + stockState = model.InStock // 库存-在库 default: logger.Error("req.State no in (1,2)", logger.Field("req.State", req.State)) - app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误") + app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误,req.State no in (1,2)") return } @@ -490,6 +611,7 @@ func ErpOrderAudit(c *gin.Context) { } if !model.CheckIsOnlinePay(cashiers) { nPayStatus = model.HavePaid // 已完成 + stockState = model.SoldOut // 库存-已售 } begin := orm.Eloquent.Begin() @@ -501,16 +623,16 @@ func ErpOrderAudit(c *gin.Context) { if err != nil { begin.Rollback() logger.Error("order err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "审核失败") + app.Error(c, http.StatusInternalServerError, err, "审核失败:"+err.Error()) return } // 更新库存 - err = model.UpdateStock(begin, erpOrder) + err = model.UpdateStock(begin, erpOrder, stockState) if err != nil { begin.Rollback() logger.Error("UpdateStock err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "审核失败") + app.Error(c, http.StatusInternalServerError, err, "审核失败:"+err.Error()) return } @@ -518,7 +640,7 @@ func ErpOrderAudit(c *gin.Context) { if err != nil { begin.Rollback() logger.Error("UpdateStock err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "删除失败") + app.Error(c, http.StatusInternalServerError, err, "审核失败:"+err.Error()) return } @@ -538,7 +660,7 @@ func ErpOrderDelete(c *gin.Context) { var req = new(model.ErpOrderDeleteReq) if err := c.ShouldBindJSON(&req); err != nil { logger.Error("ShouldBindJSON err:", logger.Field("err", err)) - app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误") + app.Error(c, http.StatusBadRequest, err, "参数错误:"+err.Error()) return } @@ -551,7 +673,7 @@ func ErpOrderDelete(c *gin.Context) { err = model.DeleteOrder(req) if err != nil { logger.Error("AddInvoice err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "操作失败") + app.Error(c, http.StatusInternalServerError, err, err.Error()) return } @@ -571,7 +693,7 @@ func ErpOrderAddInvoice(c *gin.Context) { var req = new(model.ErpOrderAddInvoiceReq) if err := c.ShouldBindJSON(&req); err != nil { logger.Error("ShouldBindJSON err:", logger.Field("err", err)) - app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误") + app.Error(c, http.StatusBadRequest, err, "参数错误:"+err.Error()) return } @@ -584,7 +706,7 @@ func ErpOrderAddInvoice(c *gin.Context) { err = model.SetInvoice(req) if err != nil { logger.Error("AddInvoice err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "操作失败") + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) return } @@ -604,7 +726,7 @@ func ErpOrderPay(c *gin.Context) { var req = new(model.ErpOrderPayReq) if err := c.ShouldBindJSON(&req); err != nil { logger.Error("ShouldBindJSON err:", logger.Field("err", err)) - app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误") + app.Error(c, http.StatusBadRequest, err, "参数错误:"+err.Error()) return } @@ -637,7 +759,7 @@ func ErpOrderQueryPayStatus(c *gin.Context) { var req = new(model.ErpOrderDeleteReq) if err := c.ShouldBindJSON(&req); err != nil { logger.Error("ShouldBindJSON err:", logger.Field("err", err)) - app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误") + app.Error(c, http.StatusBadRequest, err, "参数错误:"+err.Error()) return } @@ -650,7 +772,73 @@ func ErpOrderQueryPayStatus(c *gin.Context) { resp, err := model.QueryErpOrderPayStatus(req.BillSn) if err != nil { logger.Error("QueryErpOrderPayStatus err:", logger.Field("err", err)) - app.Error(c, http.StatusInternalServerError, err, "查询失败") + app.Error(c, http.StatusInternalServerError, err, "查询失败:"+err.Error()) + return + } + + app.OK(c, resp, "") + return +} + +// ErpOrderStoreManageData 查询门店经营数据 +// @Summary 查询门店经营数据 +// @Tags 零售报表 +// @Produce json +// @Accept json +// @Param request body models.ErpOrderStoreManageDataReq true "查询门店经营数据模型" +// @Success 200 {object} models.ErpOrderStoreManageDataResp +// @Router /api/v1/erp_order/store_manage_data [post] +func ErpOrderStoreManageData(c *gin.Context) { + var req = new(model.ErpOrderStoreManageDataReq) + 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.QueryStoreManageData(req) + if err != nil { + logger.Error("QueryStoreManageData err:", logger.Field("err", err)) + app.Error(c, http.StatusInternalServerError, err, "查询失败:"+err.Error()) + return + } + + app.OK(c, resp, "") + return +} + +// ErpOrderRetailMargin 查询商品零售毛利汇总 +// @Summary 查询商品零售毛利汇总 +// @Tags 零售报表 +// @Produce json +// @Accept json +// @Param request body models.ErpOrderRetailMarginReq true "查询商品零售毛利汇总数据模型" +// @Success 200 {object} models.ErpOrderStoreManageDataResp +// @Router /api/v1/erp_order/retail_margin [post] +func ErpOrderRetailMargin(c *gin.Context) { + var req = new(model.ErpOrderRetailMarginReq) + 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.QueryRetailMargin(req) + if err != nil { + logger.Error("QueryRetailMargin err:", logger.Field("err", err)) + app.Error(c, http.StatusInternalServerError, err, "查询失败:"+err.Error()) return } diff --git a/app/admin/apis/pay/wx_pay.go b/app/admin/apis/pay/wx_pay.go index 56e5ab1..c9e0ab7 100644 --- a/app/admin/apis/pay/wx_pay.go +++ b/app/admin/apis/pay/wx_pay.go @@ -694,8 +694,8 @@ type HmPayUnifiedOrderRsp struct { } func ParsePrivateKey() (*rsa.PrivateKey, error) { - fp := "/Users/max/Documents/code/deovo/mh_goadmin_server/config/hm_pay/private_key.pem" - //fp := "./configs/hm_pay/private_key.pem" + //fp := "/Users/max/Documents/code/deovo/mh_goadmin_server/config/hm_pay/private_key.pem" + fp := "./config/hm_pay/private_key.pem" privateKey, err := os.ReadFile(fp) if err != nil { logger.Errorf("read file err:", err) @@ -979,7 +979,7 @@ func HmJsPayBToCOrder(orderId string, totalFee float64, authCode, notifyUrl stri logger.Errorf("hm pay unified order pay data unmarshal error %#v", err) return nil, err } - fmt.Println("hmPayDetail:", hmPayDetail) + logger.Info("刷卡付响应参数::", logger.Field("HmPayBToCOrderDetail", hmPayDetail)) return &hmPayDetail, nil } @@ -1047,7 +1047,8 @@ func HmQueryOrder(orderId string) (*HmPayTradeQueryResp, error) { logger.Errorf("hm pay unified order pay data unmarshal error %#v", err) return nil, err } - fmt.Println("hmPayDetail:", hmPayDetail) + + logger.Info("订单查询响应参数::", logger.Field("HmPayTradeQueryResp", hmPayDetail)) return &hmPayDetail, nil } diff --git a/app/admin/models/commodity.go b/app/admin/models/commodity.go index 6e5dafe..a78280b 100644 --- a/app/admin/models/commodity.go +++ b/app/admin/models/commodity.go @@ -16,6 +16,16 @@ import ( "time" ) +const ( + NoIMEICommodity = 1 // 非串码商品类型 + + InStock = 1 // 在库 + SoldOut = 2 // 已售 + PurchaseReturn = 3 // 采购退货 + InAllot = 4 // 调拨中 + OnSale = 5 // 销售锁定中 +) + // ErpStock 库存列表 type ErpStock struct { Model @@ -57,7 +67,7 @@ type ErpStockCommodity struct { StaffCostPrice uint32 `json:"staff_cost_price"` // 员工成本价加价 WholesalePrice uint32 `json:"wholesale_price"` // 指导采购价 MemberDiscount float64 `json:"member_discount"` // 会员优惠 - State uint32 `json:"state"` // 状态:1-在库 2-已售 3-采购退货 4-调拨中 + State uint32 `json:"state"` // 状态:1-在库 2-已售 3-采购退货 4-调拨中 5-销售锁定中 Count uint32 `json:"count"` // 数量 StorageType uint32 `json:"storage_type"` // 入库方式:1-系统入库 2-采购入库 FirstStockTime time.Time `json:"first_stock_time"` // 首次入库时间 @@ -660,7 +670,9 @@ func (m *StockImporter) ImportStockData(colsMap []map[string]interface{}) error IMEI: "", Remark: "", MemberDiscount: v2.MemberDiscount, - } + MinRetailPrice: v2.MinRetailPrice, + RetailPrice: v2.RetailPrice, + } // todo 首次入库订单编号、订单编号还未赋值 if list[i].StockTime != "" { //导入时间不为空 local, _ := time.LoadLocation("Local") @@ -1609,6 +1621,7 @@ type ErpStockCommodityListReq struct { SerialNumber string `json:"serial_number"` // 商品编号 CommodityName string `json:"commodity_name"` // 商品名称 ErpCategoryId uint32 `json:"erp_category_id"` // 商品分类Id + IsIMEI uint32 `json:"is_imei""` // 是否串码:0-查全部 1-查串码类 2-查非串码 IMEI string `json:"imei"` // 串码 StoreId uint32 `json:"store_id"` // 门店编号 SupplierId uint32 `json:"supplier_id"` // 供应商id @@ -1721,6 +1734,14 @@ func (m *ErpStockCommodityListReq) buildQueryConditions(qs *gorm.DB) { qs = qs.Where("erp_commodity_id=?", m.ErpCommodityId) } + if m.IsIMEI != 0 { // 是否串码 + if m.IsIMEI == 1 { // 查串码数据 + qs = qs.Where("imei_type != ?", NoIMEICommodity) + } else if m.IsIMEI == 2 { // 查非串码数据 + qs = qs.Where("imei_type = ?", NoIMEICommodity) + } + } + if m.SerialNumber != "" { //商品编号 qs = qs.Where("commodity_serial_number=?", m.SerialNumber) } diff --git a/app/admin/models/erp_order.go b/app/admin/models/erp_order.go index 4ea7538..072f970 100644 --- a/app/admin/models/erp_order.go +++ b/app/admin/models/erp_order.go @@ -4,11 +4,14 @@ import ( "encoding/json" "errors" "fmt" + "github.com/xuri/excelize/v2" "go-admin/app/admin/apis/pay" orm "go-admin/common/global" "go-admin/logger" + "go-admin/tools/config" "gorm.io/gorm" "math/rand" + "strconv" "strings" "time" ) @@ -32,6 +35,7 @@ const ( OnlinePay = 1 // 线上支付(微信/支付宝/云闪付扫码) + NoPayOrder = "no_pay_order" PayInit = "pay_init" Paying = "paying" PayOk = "pay_ok" @@ -56,14 +60,16 @@ type ErpOrder struct { AuditTime time.Time `json:"audit_time"` // 审核时间 CashierList string `json:"cashier_list" gorm:"type:text"` // 付款方式,存储json数据 Salesman1 uint32 `json:"salesman_1"` // 销售员一用户ID - SalesmanName1 uint32 `json:"salesmanName_1"` // 销售员一用户姓名 + SalesmanName1 string `json:"salesman_Name1"` // 销售员一用户姓名 Salesman2 uint32 `json:"salesman_2"` // 销售员二用户ID - SalesmanName2 uint32 `json:"salesmanName_2"` // 销售员二用户姓名 + SalesmanName2 string `json:"salesman_Name2"` // 销售员二用户姓名 MemberType string `json:"member_type"` // 会员类型:general 普通; member 会员 State string `json:"state" gorm:"index"` // 订单状态:un_audit 待审核; audited 已审核 TotalRetailPrice float64 `json:"total_retail_price"` // 订单总指导零售价 TotalAmount float64 `json:"total_amount"` // 订单实收金额 TotalCount uint32 `json:"total_count"` // 订单商品数量 + SalesProfit float64 `json:"sales_profit"` // 销售毛利 + StaffProfit float64 `json:"staff_profit"` // 员工毛利 VmCount uint32 `json:"vm_count"` // 使用会员积分 SaleOrderId uint32 `json:"sale_order_id"` // 销售订单id PayStatus uint32 `json:"pay_status"` // 支付状态 0-未创建 ;1-待支付; 2-已支付 @@ -102,6 +108,8 @@ type ErpOrderCommodity struct { RejectedCount uint32 `json:"rejected_count"` // 退货数量 RejectedAmount float64 `json:"rejected_amount"` // 退货金额 RejectedOrderCommodityId uint32 `json:"rejected_order_commodity_id"` // 退货订单商品id + StaffCostPrice uint32 `json:"staff_cost_price"` // 员工成本价加价 + WholesalePrice uint32 `json:"wholesale_price"` // 指导采购价 } type ErpOrderCashier struct { @@ -128,11 +136,9 @@ type ErpOrderCreateReq struct { StoreId uint32 `json:"store_id" binding:"required"` // 门店id StoreName string `json:"store_name"` // 门店名称 Salesman1 uint32 `json:"salesman_1" binding:"required"` // 销售员一 - SalesmanName1 uint32 `json:"salesmanName_1"` // 销售员一用户姓名 Salesman2 uint32 `json:"salesman_2"` // 销售员二 - SalesmanName2 uint32 `json:"salesmanName_2"` // 销售员二用户姓名 RetailType string `json:"retail_type" binding:"required"` // 销售类型:sale 零售销售; rejected 零售退货 - Tel string `json:"tel" binding:"required"` // 会员手机号 + Tel string `json:"tel"` // 会员手机号 MemberType string `json:"member_type"` // 会员类型:general 普通; member 会员 TotalRetailPrice float64 `json:"total_retail_price"` // 订单总指导零售价 TotalAmount float64 `json:"total_amount"` // 订单实收金额 @@ -166,28 +172,105 @@ type ErpOrderListResp struct { ExportUrl string `json:"export_url"` } +// ErpOrderDeleteReq 删除零售订单入参 type ErpOrderDeleteReq struct { BillSn string `json:"bill_sn" binding:"required"` // 单据编号 } +// ErpOrderPayReq 收款入参 type ErpOrderPayReq struct { BillSn string `json:"bill_sn" binding:"required"` // 单据编号 AuthCode string `json:"auth_code" binding:"required"` // 用户付款码 } +// ErpOrderPayResp 收款出参 type ErpOrderPayResp struct { - Status string `json:"status"` // 支付成功:pay_ok;支付失败:pay_failed ;等待支付:paying; 未知状态:pay_unknown + Status string `json:"status"` // 支付成功:pay_ok;支付失败:pay_failed ;等待支付:paying; 未知状态:pay_unknown; 未创建支付订单:no_pay_order } +// ErpOrderAuditReq 审核零售订单入参 type ErpOrderAuditReq struct { BillSn string `json:"bill_sn" binding:"required"` // 单据编号 State int `json:"state" binding:"required"` // 审核操作: 1-审核 2-取消审核 } +// ErpOrderAddInvoiceReq 开发票入参 type ErpOrderAddInvoiceReq struct { BillSn string `json:"bill_sn" binding:"required"` // 单据编号 - InvoiceCode uint32 `json:"invoice_code" binding:"required"` // 发票代码 - InvoiceNumber uint32 `json:"invoice_number" binding:"required"` // 发票编码 + InvoiceCode string `json:"invoice_code" binding:"required"` // 发票代码 + InvoiceNumber string `json:"invoice_number" binding:"required"` // 发票编码 +} + +// ErpOrderStoreManageDataReq 查询门店经营入参 +type ErpOrderStoreManageDataReq struct { + StoreId uint32 `json:"store_id"` // 门店ID + StartTime string `json:"start_time"` // 开始时间 + EndTime string `json:"end_time"` // 结束时间 + PageIndex int `json:"pageIndex"` // 页码 + PageSize int `json:"pageSize"` // 页面条数 + IsExport uint32 `json:"is_export"` // 1-导出 +} + +// ErpOrderStoreManageDataResp 查询门店经营出参 +type ErpOrderStoreManageDataResp struct { + List []StoreManageData `json:"list"` + Total int `json:"total"` // 总条数 + PageIndex int `json:"pageIndex"` // 页码 + PageSize int `json:"pageSize"` // 每页展示条数 + ExportUrl string `json:"export_url"` +} + +// StoreManageData 门店经营数据 +type StoreManageData struct { + Date string `json:"date"` // 时间,如:"2023-12-25" + TotalSalesAmount float64 `json:"total_sales_amount"` // 销售额 + PromotionFee float64 `json:"promotion_fee"` // 推广费 + SalesProfit float64 `json:"sales_profit"` // 销售毛利 + StaffProfit float64 `json:"staff_profit"` // 员工毛利 + Count uint32 `json:"count"` // 销售数量 +} + +// ErpOrderRetailMarginReq 查询商品零售毛利汇总入参 +type ErpOrderRetailMarginReq struct { + StoreId uint32 `json:"store_id"` // 门店ID + RetailType string `json:"retail_type"` // 销售类型:sale 零售销售; rejected 零售退货 + ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 + ErpCategoryId uint32 `json:"erp_category_id" gorm:"index"` // 分类id + StartTime string `json:"start_time"` // 开始时间 + EndTime string `json:"end_time"` // 结束时间 + PageIndex int `json:"pageIndex"` // 页码 + PageSize int `json:"pageSize"` // 页面条数 + IsExport uint32 `json:"is_export"` // 1-导出 +} + +// ErpOrderRetailMarginResp 查询商品零售毛利汇总出参 +type ErpOrderRetailMarginResp struct { + List []RetailMarginData `json:"list"` + Total int `json:"total"` // 总条数 + PageIndex int `json:"pageIndex"` // 页码 + PageSize int `json:"pageSize"` // 每页展示条数 + TotalCount uint32 `json:"total_count"` // 总销售数量 + TotalSalesAmount float64 `json:"total_sales_amount"` // 总销售/退货金额 + TotalSalesCost float64 `json:"total_sales_cost"` // 总销售成本:销售采购价之和 + TotalSalesMargin float64 `json:"total_sales_margin"` // 总销售毛利:销售/退货金额-销售成本 + TotalGrossMargins string `json:"total_gross_margins"` // 总销售毛利:销售/退货金额-销售成本 + ExportUrl string `json:"export_url"` +} + +// RetailMarginData 商品零售毛利数据 +type RetailMarginData struct { + StoreId uint32 `json:"store_id" gorm:"index"` // 门店id + StoreName string `json:"store_name"` // 门店名称 + RetailType string `json:"retail_type"` // 销售类型:sale 零售销售; rejected 零售退货 + ErpCommodityId uint32 `json:"erp_commodity_id" gorm:"index"` // 商品id + ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 + ErpCategoryId uint32 `json:"erp_category_id" gorm:"index"` // 分类id + ErpCategoryName string `json:"erp_category_name"` // 分类名称 + Count uint32 `json:"count"` // 销售数量 + SalesAmount float64 `json:"sales_amount"` // 销售/退货金额 + SalesCost float64 `json:"sales_cost"` // 销售成本:销售采购价之和 + SalesMargin float64 `json:"sales_margin"` // 销售毛利:销售/退货金额-销售成本 + GrossMargins string `json:"gross_margins"` // 销售毛利率:销售毛利/销售/退货金额 } func (m *ErpOrderListReq) List() (*ErpOrderListResp, error) { @@ -290,7 +373,7 @@ func (m *ErpOrderListReq) List() (*ErpOrderListResp, error) { // 有串码,通过串码查找库存详情表,然后更改对应库存的状态为"在库"; // 非串码,通过门店id、商品id、商品名称查找库存详情表,找到状态为"已售"且时间最近的单,将其状态改为"在库" // 同时扣减库存表对应的数量,+1 -func UpdateStock(gdb *gorm.DB, erpOrder ErpOrder) error { +func UpdateStock(gdb *gorm.DB, erpOrder ErpOrder, state int) error { var commodities []ErpOrderCommodity err := orm.Eloquent.Table("erp_order_commodity").Where("erp_order_id = ?", erpOrder.ID). Find(&commodities).Error @@ -301,52 +384,56 @@ func UpdateStock(gdb *gorm.DB, erpOrder ErpOrder) error { if erpOrder.RetailType == RetailTypeSale { // 零售订单 for i, _ := range commodities { - if commodities[i].IMEIType == 2 { // 串码商品 + if commodities[i].IMEIType == 2 || commodities[i].IMEIType == 3 { // 串码商品 if commodities[i].IMEI == "" { return errors.New("串码为空") } err = gdb.Table("erp_stock_commodity").Where("imei = ?", commodities[i].IMEI). - Update("state", 2).Error // 状态更新为已售 + Update("state", state).Error // 状态更新为销售锁定中 if err != nil { logger.Error("commodities err:", logger.Field("err", err)) return err } - err = gdb.Table("erp_stock").Where("store_id = ? and erp_commodity_id = ?", - erpOrder.StoreId, commodities[i].ErpCommodityId). - Updates(map[string]interface{}{"count": gorm.Expr("count - ?", 1)}).Error // 库存数量-1 - if err != nil { - logger.Error("commodities err:", logger.Field("err", err)) - return err + if state == SoldOut { // 已售的订单才更新库存数量 + err = gdb.Table("erp_stock").Where("store_id = ? and erp_commodity_id = ?", + erpOrder.StoreId, commodities[i].ErpCommodityId). + Updates(map[string]interface{}{"count": gorm.Expr("count - ?", 1)}).Error // 库存数量-1 + if err != nil { + logger.Error("commodities err:", logger.Field("err", err)) + return err + } } } else { // 非串码商品 var stockCommodity []ErpStockCommodity // 通过门店id,商品id,查找状态为1-在库的非串码商品 err = orm.Eloquent.Table("erp_stock_commodity").Where("erp_commodity_id = ? and store_id = ? "+ "and state = ? and imei_type = ?", commodities[i].ErpCommodityId, erpOrder.StoreId, 1, 1). - Order("sort DESC").Find(&stockCommodity).Error + Order("id DESC").Find(&stockCommodity).Error if err != nil { logger.Error("commodities err:", logger.Field("err", err)) return err } - if stockCommodity == nil { - return errors.New("find no stock commodity") + if stockCommodity == nil || len(stockCommodity) == 0 { + return errors.New("find commodity no stock ") } err = gdb.Table("erp_stock_commodity").Where("id = ?", stockCommodity[0].ID). - Update("state", 2).Error // 状态更新为已售 + Update("state", state).Error // 状态更新为销售锁定中 if err != nil { logger.Error("commodities err:", logger.Field("err", err)) return err } - err = gdb.Table("erp_stock").Where("store_id = ? and erp_commodity_id = ?", - erpOrder.StoreId, commodities[i].ErpCommodityId). - Updates(map[string]interface{}{"count": gorm.Expr("count - ?", 1)}).Error // 库存数量-1 - if err != nil { - logger.Error("commodities err:", logger.Field("err", err)) - return err + if state == SoldOut { // 已售的订单才更新库存数量 + err = gdb.Table("erp_stock").Where("store_id = ? and erp_commodity_id = ?", + erpOrder.StoreId, commodities[i].ErpCommodityId). + Updates(map[string]interface{}{"count": gorm.Expr("count - ?", 1)}).Error // 库存数量-1 + if err != nil { + logger.Error("commodities err:", logger.Field("err", err)) + return err + } } } } @@ -358,7 +445,7 @@ func UpdateStock(gdb *gorm.DB, erpOrder ErpOrder) error { } err = gdb.Table("erp_stock_commodity").Where("imei = ?", commodities[i].IMEI). - Update("state", 1).Error // 状态更新为在库 + Update("state", InStock).Error // 状态更新为在库 if err != nil { logger.Error("RetailTypeRejected commodities err:", logger.Field("err", err)) return err @@ -376,7 +463,7 @@ func UpdateStock(gdb *gorm.DB, erpOrder ErpOrder) error { // 通过门店id,商品id,查找状态为2-已售的非串码商品 err = orm.Eloquent.Table("erp_stock_commodity").Where("erp_commodity_id = ? and store_id = ? "+ "and state = ? and imei_type = ?", commodities[i].ErpCommodityId, erpOrder.StoreId, 2, 1). - Order("sort DESC").Find(&stockCommodity).Error + Order("id DESC").Find(&stockCommodity).Error if err != nil { logger.Error("RetailTypeRejected commodities err:", logger.Field("err", err)) return err @@ -386,7 +473,7 @@ func UpdateStock(gdb *gorm.DB, erpOrder ErpOrder) error { } err = gdb.Table("erp_stock_commodity").Where("id = ?", stockCommodity[0].ID). - Update("state", 1).Error // 状态更新为在库 + Update("state", InStock).Error // 状态更新为在库 if err != nil { logger.Error("RetailTypeRejected commodities err:", logger.Field("err", err)) return err @@ -690,6 +777,11 @@ func ErpOrderPay(req *ErpOrderPayReq) (*ErpOrderPayResp, error) { return resp, errors.New("订单金额为0") } + // dev环境测试默认金额都为0.1元 + if config.ApplicationConfig.Mode == "dev" { + amount = 0.1 + } + // 查询是否有已支付的订单 if checkIsPayOk(req.BillSn) { // 更新零售订单表 @@ -761,8 +853,8 @@ func ErpOrderPay(req *ErpOrderPayReq) (*ErpOrderPayResp, error) { payStatus = PayFailed case "WAITING_PAYMENT": payStatus = Paying - default: - payStatus = PayUnknown + //default: + // payStatus = PayUnknown } updateInfo := map[string]interface{}{ "bank_order_no": hmPayResp.BankOrderNo, @@ -790,6 +882,13 @@ func ErpOrderPay(req *ErpOrderPayReq) (*ErpOrderPayResp, error) { logger.Error("update erp_order_record err:", logger.Field("err", err)) return resp, err } + + // 更新库存订单表 + err = updateErpStockCommodity(begin, req.BillSn) + if err != nil { + logger.Error("ErpOrderPay updateErpStockCommodity err:", logger.Field("err", err)) + return resp, err + } } err = begin.Commit().Error @@ -809,7 +908,7 @@ func QueryErpOrderPayStatus(billSn string) (*ErpOrderPayResp, error) { } // 查询待支付订单 var orderRecordInfo ErpOrderRecord - err := orm.Eloquent.Table("erp_order_record").Where("bill_sn = ? and status = ?", billSn, Paying). + err := orm.Eloquent.Table("erp_order_record").Where("bill_sn = ? and status in (?)", billSn, []string{Paying, PayOk}). Find(&orderRecordInfo).Error if err != nil { logger.Error("未查询到订单:", logger.Field("err", err)) @@ -817,6 +916,18 @@ func QueryErpOrderPayStatus(billSn string) (*ErpOrderPayResp, error) { return resp, errors.New("未查询到待支付订单") } + if orderRecordInfo.OutOrderNo == "" { + logger.Info("还未创建支付订单") + resp.Status = NoPayOrder + return resp, nil + } + + if orderRecordInfo.Status == PayOk { + logger.Info("订单已支付") + resp.Status = PayOk + return resp, nil + } + // 查询支付状态 hmQueryResp, err := pay.HmQueryOrder(orderRecordInfo.OutOrderNo) if err != nil { @@ -833,8 +944,8 @@ func QueryErpOrderPayStatus(billSn string) (*ErpOrderPayResp, error) { payStatus = PayFailed case "WAITING_PAYMENT": payStatus = Paying - default: - payStatus = PayUnknown + //default: + // payStatus = PayUnknown } updateInfo := map[string]interface{}{ "bank_order_no": hmQueryResp.BankOrderNo, @@ -855,13 +966,20 @@ func QueryErpOrderPayStatus(billSn string) (*ErpOrderPayResp, error) { if payStatus == PayOk { // 支付成功 // 更新零售订单表 - err = begin.Model(&ErpOrder{}).Where("id = ?", orderRecordInfo.ID). + err = begin.Model(&ErpOrder{}).Where("id = ?", orderRecordInfo.ErpOrderId). Update("pay_status", HavePaid).Error if err != nil { begin.Rollback() logger.Error("query update erp_order_record err:", logger.Field("err", err)) return resp, err } + + // 更新库存订单表 + err = updateErpStockCommodity(begin, billSn) + if err != nil { + logger.Error("ErpOrderPay updateErpStockCommodity err:", logger.Field("err", err)) + return resp, err + } } err = begin.Commit().Error @@ -873,3 +991,428 @@ func QueryErpOrderPayStatus(billSn string) (*ErpOrderPayResp, error) { resp.Status = payStatus return resp, nil } + +// updateErpStockCommodity 更新库存订单表 +func updateErpStockCommodity(gdb *gorm.DB, billSn string) error { + //通过单据号查询订单 + var erpOrderInfo []ErpOrder + err := orm.Eloquent.Table("erp_order").Where("bill_sn = ?", billSn).Find(&erpOrderInfo).Error + if err != nil { + logger.Error("erp_order 未查询到订单:", logger.Field("err", err)) + return err + } + + if len(erpOrderInfo) == 0 { + return errors.New("未查询到订单") + } + + err = UpdateStock(gdb, erpOrderInfo[0], SoldOut) + if err != nil { + gdb.Rollback() + logger.Error("updateErpStockCommodity UpdateStock err:", logger.Field("err", err)) + return errors.New("审核失败:" + err.Error()) + } + + return nil +} + +// CommodityIsHaveStock 查询商品是否有库存 +func CommodityIsHaveStock(req ErpOrderCommodity, storeId uint32) bool { + qs := orm.Eloquent.Table("erp_stock_commodity") + if req.IMEI != "" { // 串码商品 + qs.Where("imei = ? and state = ?", req.IMEI, InStock) + } else { // 非串码商品 + qs.Where("erp_commodity_id = ? and erp_store_id = ? and state = ? and IMEIType = ", + req.ErpCommodityId, storeId, InStock, NoIMEICommodity) + } + + var count int64 + err := qs.Count(&count).Error + if err != nil { + logger.Error("CommodityIsHaveStock err:", logger.Field("err", err)) + } + + return count > 0 +} + +// QueryStoreManageData 查询门店经营数据 +func QueryStoreManageData(req *ErpOrderStoreManageDataReq) (*ErpOrderStoreManageDataResp, error) { + resp := &ErpOrderStoreManageDataResp{ + PageIndex: req.PageIndex, + PageSize: req.PageSize, + } + page := req.PageIndex - 1 + if page < 0 { + page = 0 + } + if req.PageSize == 0 { + req.PageSize = 10 + } + var storeManageDataList []StoreManageData + + // 构建查询条件 + qs := orm.Eloquent.Model(&ErpOrder{}) + if req.StoreId != 0 { + qs = qs.Where("store_id = ?", req.StoreId) + } + if req.StartTime != "" && req.EndTime != "" { + startTime, _ := time.Parse("2006-01-02", req.StartTime) + endTime, _ := time.Parse("2006-01-02", req.EndTime) + qs = qs.Where("maker_time BETWEEN ? AND ?", startTime, endTime) + } + + qs = qs.Where("maker_time IS NOT NULL AND maker_time != '0000-00-00 00:00:00'") + es := qs + + // 获取总条数 + err := es.Select("DATE_FORMAT(maker_time, '%Y-%m-%d') AS date, SUM(total_amount) AS total_sales_amount, " + + "(SUM(total_retail_price) - SUM(total_amount)) AS promotion_fee, " + + "SUM(sales_profit) AS sales_profit, SUM(staff_profit) AS staff_profit, SUM(total_count) AS count"). + Group("date"). + Find(&storeManageDataList).Error + if err != nil { + logger.Error("QueryStoreManageData count err:", logger.Field("err", err)) + return nil, err + } + resp.Total = len(storeManageDataList) + storeManageDataList = nil + + if req.IsExport == 1 { //导出excel + // 查询数据 + err = qs.Select("DATE_FORMAT(maker_time, '%Y-%m-%d') AS date, SUM(total_amount) AS total_sales_amount, " + + "(SUM(total_retail_price) - SUM(total_amount)) AS promotion_fee, " + + "SUM(sales_profit) AS sales_profit, SUM(staff_profit) AS staff_profit, SUM(total_count) AS count"). + Group("date"). + Order("date DESC"). + Find(&storeManageDataList).Error + if err != nil { + logger.Error("QueryStoreManageData err:", logger.Field("err", err)) + return nil, err + } + + storeName := "所有门店" + if req.StoreId != 0 { + storeInfo, err := GetStore(req.StoreId) + if err != nil { + logger.Error("GetStore err:", logger.Field("err", err)) + return nil, err + } + storeName = storeInfo.Name + } + + filePath, err := StoreManageDataExport(storeManageDataList, storeName) + if err != nil { + logger.Error("StoreManageDataExport err:", logger.Field("err", err)) + return nil, err + } + + resp.ExportUrl = filePath + } else { + // 查询数据 + err = qs.Select("DATE_FORMAT(maker_time, '%Y-%m-%d') AS date, SUM(total_amount) AS total_sales_amount, " + + "(SUM(total_retail_price) - SUM(total_amount)) AS promotion_fee, " + + "SUM(sales_profit) AS sales_profit, SUM(staff_profit) AS staff_profit, SUM(total_count) AS count"). + Group("date"). + Order("date DESC"). + Offset(page * req.PageSize). + Limit(req.PageSize). + Find(&storeManageDataList).Error + if err != nil { + logger.Error("QueryStoreManageData err:", logger.Field("err", err)) + return nil, err + } + + resp.List = storeManageDataList + resp.PageIndex = req.PageIndex + resp.PageSize = req.PageSize + } + + return resp, nil +} + +// StoreManageDataExport 导出门店经营数据 +func StoreManageDataExport(list []StoreManageData, storeName string) (string, error) { + file := excelize.NewFile() + streamWriter, err := file.NewStreamWriter("Sheet1") + if err != nil { + fmt.Println(err) + } + + url := ExportUrl + fileName := time.Now().Format(TimeFormat) + storeName + "经营数据" + ".xlsx" + fmt.Println("url fileName:", url+fileName) + + title := []interface{}{"时间", "销售额", "推广费", "销售毛利", "员工毛利", "销售数量"} + cell, _ := excelize.CoordinatesToCellName(1, 1) + if err = streamWriter.SetRow(cell, title); err != nil { + fmt.Println(err) + } + var row []interface{} + nExcelStartRow := 0 + for rowId := 0; rowId < len(list); rowId++ { + row = []interface{}{ + list[rowId].Date, + list[rowId].TotalSalesAmount, + list[rowId].PromotionFee, + list[rowId].SalesProfit, + list[rowId].StaffProfit, + list[rowId].Count, + } + + cell, _ := excelize.CoordinatesToCellName(1, nExcelStartRow+2) + if err := streamWriter.SetRow(cell, row); err != nil { + fmt.Println(err) + } + nExcelStartRow++ + + } + if err := streamWriter.Flush(); err != nil { + fmt.Println(err) + } + 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 +} + +// RetailMarginDataExport 导出商品零售毛利汇总数据 +func RetailMarginDataExport(req *ErpOrderRetailMarginResp, storeName string) (string, error) { + file := excelize.NewFile() + streamWriter, err := file.NewStreamWriter("Sheet1") + if err != nil { + fmt.Println(err) + } + + url := ExportUrl + fileName := time.Now().Format(TimeFormat) + storeName + "商品零售毛利汇总" + ".xlsx" + fmt.Println("url fileName:", url+fileName) + + title := []interface{}{"店铺名称", "零售类型", "商品名称", "商品分类", "数量", "销售/退货金额", "销售成本", "销售毛利", "销售毛利率"} + cell, _ := excelize.CoordinatesToCellName(1, 1) + if err = streamWriter.SetRow(cell, title); err != nil { + fmt.Println(err) + } + + var row []interface{} + nExcelStartRow := 0 + for rowId := 0; rowId < len(req.List); rowId++ { + var saleType string + if req.List[rowId].RetailType == RetailTypeSale { + saleType = "零售销售" + } else if req.List[rowId].RetailType == RetailTypeRejected { + saleType = "零售退货" + } else { + logger.Error("订单类型异常") + return "", errors.New("RetailMarginDataExport, 订单类型异常:" + req.List[rowId].RetailType) + } + row = []interface{}{ + storeName, + saleType, + req.List[rowId].ErpCommodityName, + req.List[rowId].ErpCategoryName, + req.List[rowId].Count, + req.List[rowId].SalesAmount, + req.List[rowId].SalesCost, + req.List[rowId].SalesMargin, + req.List[rowId].GrossMargins, + } + + cell, _ := excelize.CoordinatesToCellName(1, nExcelStartRow+2) + if err := streamWriter.SetRow(cell, row); err != nil { + fmt.Println(err) + } + nExcelStartRow++ + } + + totalData := "汇总 记录数:" + strconv.FormatInt(int64(req.TotalCount), 10) + end := []interface{}{totalData, "", "", "", req.TotalCount, req.TotalSalesAmount, req.TotalSalesCost, + req.TotalSalesMargin, req.TotalGrossMargins} + cell, _ = excelize.CoordinatesToCellName(1, nExcelStartRow+2) + if err := streamWriter.SetRow(cell, end); err != nil { + fmt.Println(err) + } + + if err := streamWriter.Flush(); err != nil { + fmt.Println(err) + } + 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 +} + +// QueryRetailMargin 查询零售毛利汇总数据 +func QueryRetailMargin(req *ErpOrderRetailMarginReq) (*ErpOrderRetailMarginResp, error) { + resp := &ErpOrderRetailMarginResp{ + PageIndex: req.PageIndex, + PageSize: req.PageSize, + } + page := req.PageIndex - 1 + if page < 0 { + page = 0 + } + if req.PageSize == 0 { + req.PageSize = 10 + } + + qs := orm.Eloquent.Debug().Table("erp_order_commodity"). + Select("erp_order_commodity.*, erp_order.store_id, erp_order.store_name, erp_order.retail_type"). + Joins("JOIN erp_order ON erp_order_commodity.erp_order_id = erp_order.id") + + if req.StoreId != 0 { + qs.Where("erp_order.store_id = ?", req.StoreId) + } + if req.RetailType != "" { + qs.Where("erp_order.retail_type = ?", req.RetailType) + } + if req.ErpCommodityName != "" { + qs.Where("erp_order_commodity.erp_commodity_name like" + "%" + req.ErpCommodityName + "%") + } + if req.ErpCategoryId != 0 { + qs.Where("erp_order_commodity.erp_category_id = ?", req.ErpCategoryId) + } + if req.StartTime != "" { + startTime, err := time.Parse(QueryTimeFormat, req.StartTime) + if err == nil { + qs = qs.Where("erp_order.created_at>?", startTime) + } else { + logger.Errorf("QueryRetailMargin time start parse err:", err.Error()) + } + } + if req.EndTime != "" { + endTime, err := time.Parse(QueryTimeFormat, req.EndTime) + if err == nil { + qs = qs.Where("erp_order.created_at