package v1 import ( "aps_crm/constvar" "aps_crm/model" "aps_crm/model/grpc_init" "aps_crm/model/request" "aps_crm/model/response" "aps_crm/pkg/contextx" "aps_crm/pkg/ecode" "aps_crm/pkg/logx" "aps_crm/pkg/structx" "aps_crm/proto/crm_aps" "aps_crm/proto/product_inventory" "aps_crm/utils" "github.com/gin-gonic/gin" "github.com/shopspring/decimal" "gorm.io/gorm" "strconv" "strings" ) type SalesDetailsApi struct{} // Add // // @Tags SalesDetails // @Summary 添加销售明细 // @Produce application/json // @Param object body request.AddSalesDetails true "查询参数" // @Success 200 {object} contextx.Response{data=request.AddSalesDetails} // @Router /api/salesDetails/add [post] func (s *SalesDetailsApi) Add(c *gin.Context) { var params request.AddSalesDetails ctx, ok := contextx.NewContext(c, ¶ms) if !ok { return } errCode, salesDetails := checkSalesDetailsParams(params.SalesDetails) if errCode != ecode.OK { ctx.Fail(errCode) return } count, err := model.NewSalesDetailsSearch().SetNumber(params.Number).Count() if err != nil { ctx.FailWithMsg(ecode.UnknownErr, "编码验证失败") return } if count > 0 { ctx.FailWithMsg(ecode.UnknownErr, "编码已存在") return } if salesDetails.MemberId == 0 { userInfo := utils.GetUserInfo(c) if userInfo.UserType == constvar.UserTypeSub { salesDetails.MemberId = userInfo.CrmUserId } } errCode = salesDetailsService.AddSalesDetails(&salesDetails) if errCode != ecode.OK { ctx.Fail(errCode) return } ctx.OkWithDetailed(salesDetails) } // Delete // // @Tags SalesDetails // @Summary 删除销售明细 // @Produce application/json // @Param id path int true "查询参数" // @Success 200 {object} contextx.Response{} // @Router /api/salesDetails/delete/{id} [delete] func (s *SalesDetailsApi) Delete(c *gin.Context) { ctx, ok := contextx.NewContext(c, nil) if !ok { return } id, _ := strconv.Atoi(c.Param("id")) errCode := salesDetailsService.DeleteSalesDetails(id) if errCode != ecode.OK { ctx.Fail(errCode) return } ctx.Ok() } // BatchDelete // @Tags SalesDetails 销售明细 // @Summary 批量删除销售明细 // @Produce application/json // @Param object body request.CommonIds true "参数" // @Success 200 {object} contextx.Response{} // @Router /api/salesDetails/delete [delete] func (s *SalesDetailsApi) BatchDelete(c *gin.Context) { var params request.CommonIds ctx, ok := contextx.NewContext(c, ¶ms) if !ok { return } errCode := salesDetailsService.BatchDeleteSalesDetails(params.Ids) if errCode != ecode.OK { ctx.Fail(errCode) return } ctx.Ok() } // Update // // @Tags SalesDetails // @Summary 更新销售明细 // @Produce application/json // @Param object body request.UpdateSalesDetails true "查询参数" // @Success 200 {object} contextx.Response{} // @Router /api/salesDetails/update [put] func (s *SalesDetailsApi) Update(c *gin.Context) { var params request.UpdateSalesDetails ctx, ok := contextx.NewContext(c, ¶ms) if !ok { return } errCode, salesDetails := checkSalesDetailsParams(params.SalesDetails) if errCode != ecode.OK { ctx.Fail(errCode) return } salesDetails.Id = params.Id errCode = salesDetailsService.UpdateSalesDetails(&salesDetails) if errCode != ecode.OK { ctx.Fail(errCode) return } ctx.Ok() } func checkSalesDetailsParams(salesDetails request.SalesDetails) (errCode int, salesDetailsModel model.SalesDetails) { salesDetailsModel.ClientId = salesDetails.ClientId salesDetailsModel.Number = salesDetails.Number salesDetailsModel.SaleChanceId = salesDetails.SaleChanceId salesDetailsModel.SaleType = salesDetails.SaleType salesDetailsModel.SignTime = salesDetails.SignTime salesDetailsModel.MemberId = salesDetails.MemberId salesDetailsModel.DeliveryDate = salesDetails.DeliveryDate salesDetailsModel.WechatOrderStatusId = salesDetails.WechatOrderStatusId salesDetailsModel.Address = salesDetails.Address salesDetailsModel.Phone = salesDetails.Phone salesDetailsModel.Remark = salesDetails.Remark salesDetailsModel.Addressee = salesDetails.Addressee salesDetailsModel.Conditions = salesDetails.Conditions salesDetailsModel.Products = salesDetails.Products salesDetailsModel.LogisticCompany = salesDetails.LogisticCompany salesDetailsModel.LogisticNumber = salesDetails.LogisticNumber salesDetailsModel.LogisticCost = salesDetails.LogisticCost salesDetailsModel.CodeStandID = salesDetails.CodeStandID salesDetailsModel.DeliverType = salesDetails.DeliverType salesDetailsModel.QuotationId = salesDetails.QuotationId salesDetailsModel.Status = salesDetails.Status if salesDetails.Source == "" { salesDetailsModel.Source = "CRM自建" } else { salesDetailsModel.Source = salesDetails.Source } salesDetailsModel.ProjectId = salesDetails.ProjectId return ecode.OK, salesDetailsModel } // List // // @Tags SalesDetails // @Summary 销售明细单列表 // @Produce application/json // @Param object body request.GetSalesDetailsList true "参数" // @Success 200 {object} contextx.Response{data=response.SalesDetailsResponse} // @Router /api/salesDetails/list [post] func (con *SalesDetailsApi) List(c *gin.Context) { var params request.GetSalesDetailsList ctx, ok := contextx.NewContext(c, ¶ms) if !ok { return } var memberIds []int userInfo := utils.GetUserInfo(c) if userInfo.UserType == constvar.UserTypeSub { memberIds = userInfo.SubUserIds } salesDetailss, total, errCode := salesDetailsService.GetSalesDetailsList(params, memberIds) if errCode != ecode.OK { ctx.Fail(errCode) return } ctx.OkWithDetailed(response.SalesDetailsResponse{ List: salesDetailss, Count: int(total), }) } // UpdateStatus // // @Tags SalesDetails // @Summary 更新销售明细状态 // @Produce application/json // @Param object body request.UpdateSalesDetailsStatus true "查询参数" // @Success 200 {object} contextx.Response{} // @Router /api/salesDetails/updateStatus [post] func (s *SalesDetailsApi) UpdateStatus(c *gin.Context) { var params request.UpdateSalesDetailsStatus ctx, ok := contextx.NewContext(c, ¶ms) if !ok { return } m := make(map[string]interface{}) m["status"] = params.Status err := model.NewSalesDetailsSearch().SetId(params.Id).UpdateByMap(m) if err != nil { ctx.FailWithMsg(ecode.UnknownErr, "更新失败") return } ctx.Ok() } // GetProductInventoryInfo // // @Tags SalesDetails // @Summary 获取产品发货信息 // @Produce application/json // @Param number path string true "明细编码" // @Success 200 {object} response.ListResponse // // @Router /api/salesDetails/getProductInventoryInfo/{number} [get] func (s *SalesDetailsApi) GetProductInventoryInfo(c *gin.Context) { ctx, ok := contextx.NewContext(c, nil) if !ok { return } number := c.Param("number") client := product_inventory.NewProductInventoryServiceClient(grpc_init.ProductInventoryServiceConn) info, err := client.GetOrderInputAndOutputInfo(ctx.GetCtx(), &product_inventory.GetOrderInputAndOutputInfoRequest{Number: number}) if err != nil { if strings.Contains(err.Error(), "record not found") { ctx.Ok() return } logx.Errorf("GetProductInfo err: %v", err.Error()) ctx.FailWithMsg(ecode.UnknownErr, "grpc调用错误") return } var list []response.ProductInfo err = structx.AssignTo(info.OutputList, &list) if err != nil { ctx.FailWithMsg(ecode.UnknownErr, "转换错误") return } products, err := salesDetailsService.GetProducts(number) m := make(map[string]*model.Product) for _, product := range products { m[product.Number] = product } for k, v := range list { if m[v.Number] == nil { continue } list[k].OrderAmount = m[v.Number].Amount.String() } ctx.OkWithDetailed(list) } type GetWarehouseProductInfoReq struct { SaleDetailID int `json:"saleDetailID,omitempty"` SaleDetailNumber string `json:"saleDetailNumber,omitempty"` } // GetDeliveryPrepareInfo // @Tags SalesDetails // @Summary 获取产品入库信息 // @Produce application/json // @Param object body GetWarehouseProductInfoReq true "明细编码" // @Success 200 {object} response.ListResponse // @Router /api/salesDetails/getDeliveryPrepareInfo [post] func (s *SalesDetailsApi) GetDeliveryPrepareInfo(c *gin.Context) { var params GetWarehouseProductInfoReq ctx, ok := contextx.NewContext(c, ¶ms) if !ok { return } if params.SaleDetailID == 0 { ctx.FailWithMsg(ecode.ParamsErr, "参数缺失") return } salesDetails, err := salesDetailsService.GetSalesDetails(params.SaleDetailID) if err == gorm.ErrRecordNotFound || salesDetails.Number != params.SaleDetailNumber { ctx.FailWithMsg(ecode.ParamsErr, "销售订单不存在") return } productMap := model.ProductMap(salesDetails.Products) client := product_inventory.NewProductInventoryServiceClient(grpc_init.ProductInventoryServiceConn) grpcResp, err := client.GetOrderInputAndOutputInfo(ctx.GetCtx(), &product_inventory.GetOrderInputAndOutputInfoRequest{ Number: params.SaleDetailNumber, }) if err != nil { if strings.Contains(err.Error(), "record not found") { ctx.Ok() return } logx.Errorf("GetOrderInputAndOutputInfo err: %v", err.Error()) ctx.FailWithMsg(ecode.UnknownErr, "grpc调用错误") return } grpcOutputList := grpcResp.OutputList grpcInputList := grpcResp.InputList inputProductMap := make(map[string]*response.StoreInfo) outputProductMap := make(map[string]*response.OutputSimpleInfo) for _, v := range grpcOutputList { if productMap[v.Number] == nil { continue } if outputProductMap[v.Number] == nil { simpleInfo := &response.OutputSimpleInfo{ Number: v.Number, } amount, _ := decimal.NewFromString(v.Amount) simpleInfo.Amount = amount outputProductMap[v.Number] = simpleInfo } else { amount, _ := decimal.NewFromString(v.Amount) outputProductMap[v.Number].Amount = outputProductMap[v.Number].Amount.Add(amount) } } for _, v := range grpcInputList { if productMap[v.Number] == nil { continue } if inputProductMap[v.Number] == nil { storeInfo := &response.StoreInfo{ Number: v.Number, Name: v.Name, OrderAmount: productMap[v.Number].Amount, } finishAmount, _ := decimal.NewFromString(v.Amount) storeInfo.FinishAmount = finishAmount storeInfo.AvailableAmount = finishAmount storeInfo.LeftAmount = storeInfo.OrderAmount inputProductMap[v.Number] = storeInfo } else { finishAmount, _ := decimal.NewFromString(v.Amount) inputProductMap[v.Number].FinishAmount = inputProductMap[v.Number].FinishAmount.Add(finishAmount) inputProductMap[v.Number].AvailableAmount = inputProductMap[v.Number].FinishAmount } } storeList := make([]*response.StoreInfo, 0, len(salesDetails.Products)) for _, product := range salesDetails.Products { storeInfo := inputProductMap[product.Number] if storeInfo == nil { //没有入库信息 storeInfo = &response.StoreInfo{ Name: product.Name, Number: product.Number, OrderAmount: product.Amount, FinishAmount: decimal.Decimal{}, LeftAmount: product.Amount, AvailableAmount: decimal.Decimal{}, } } else { //有入库数量再查出库,算出未发货数量 if outputProductMap[product.Number] != nil { outputInfo := outputProductMap[product.Number] storeInfo.LeftAmount = storeInfo.LeftAmount.Sub(outputInfo.Amount) //剩余发货数量 = 订单数量 - 已发货数量 storeInfo.AvailableAmount = storeInfo.AvailableAmount.Sub(outputInfo.Amount) //可用数量 = 入库完成数量 - 已发货数量 } } storeList = append(storeList, storeInfo) } ctx.OkWithDetailed(storeList) } // ConfirmOutput // @Tags SalesDetails // @Summary 确认发货 // @Produce application/json // @Param object body request.ConfirmOutput true "明细编码" // @Success 200 {object} response.ListResponse // @Router /api/salesDetails/confirmOutput [post] func (s *SalesDetailsApi) ConfirmOutput(c *gin.Context) { var params request.ConfirmOutput ctx, ok := contextx.NewContext(c, ¶ms) if !ok { return } if len(params.Products) == 0 || params.SaleDetailNumber == "" { ctx.FailWithMsg(ecode.ParamsErr, "参数缺失") return } var flag bool for _, p := range params.Products { if p.OutputAmount.GreaterThan(decimal.Zero) { flag = true } } if !flag { ctx.FailWithMsg(ecode.ParamsErr, "发货数量缺失") return } products := make([]*product_inventory.OutputProduct, 0, len(params.Products)) for _, product := range params.Products { products = append(products, &product_inventory.OutputProduct{ Number: product.Number, Amount: product.OutputAmount.String(), }) } client := product_inventory.NewProductInventoryServiceClient(grpc_init.ProductInventoryServiceConn) _, err := client.OrderProductOutput(ctx.GetCtx(), &product_inventory.OrderProductOutputRequest{ OrderNumber: params.SaleDetailNumber, Products: products, }) if err != nil { logx.Errorf("product_inventory.OrderProductOutput err:%v, params:%v", err, params) ctx.FailWithMsg(ecode.UnknownErr, "发货失败"+err.Error()) return } ctx.OkWithDetailed(nil) } // confirmOutputOver // @Tags SalesDetails // @Summary 确认发货 // @Produce application/json // @Param object body request.ConfirmOutputOver true "明细编码" // @Success 200 {object} response.ListResponse // @Router /api/salesDetails/confirmOutputOver [post] func (s *SalesDetailsApi) ConfirmOutputOver(c *gin.Context) { var params request.ConfirmOutput ctx, ok := contextx.NewContext(c, ¶ms) if !ok { return } if params.SaleDetailNumber == "" { ctx.FailWithMsg(ecode.ParamsErr, "参数缺失") return } salesDetails, err := salesDetailsService.GetSalesDetailsByNumber(params.SaleDetailNumber) if err == gorm.ErrRecordNotFound || salesDetails.Number != params.SaleDetailNumber { ctx.FailWithMsg(ecode.ParamsErr, "销售订单不存在") return } err = model.NewSalesDetailsSearch().SetId(salesDetails.Id).UpdateByMap(map[string]interface{}{"status": constvar.WaitOutbound}) if err != nil { ctx.FailWithMsg(ecode.DBErr, "修改失败") return } ctx.OkWithDetailed(nil) } // GetDeliveryList // @Tags SalesDetails // @Summary 发货明细 // @Produce application/json // @Param object body GetWarehouseProductInfoReq true "明细编码" // @Success 200 {object} response.ListResponse // @Router /api/salesDetails/getDeliveryList [post] func (s *SalesDetailsApi) GetDeliveryList(c *gin.Context) { var params GetWarehouseProductInfoReq ctx, ok := contextx.NewContext(c, ¶ms) if !ok { return } if params.SaleDetailID == 0 { ctx.FailWithMsg(ecode.ParamsErr, "参数缺失") return } salesDetails, err := salesDetailsService.GetSalesDetails(params.SaleDetailID) if err == gorm.ErrRecordNotFound || salesDetails.Number != params.SaleDetailNumber { ctx.FailWithMsg(ecode.ParamsErr, "销售订单不存在") return } productMap := model.ProductMap(salesDetails.Products) client := product_inventory.NewProductInventoryServiceClient(grpc_init.ProductInventoryServiceConn) grpcResp, err := client.GetOrderInputAndOutputInfo(ctx.GetCtx(), &product_inventory.GetOrderInputAndOutputInfoRequest{ Number: params.SaleDetailNumber, }) if err != nil { if strings.Contains(err.Error(), "record not found") { ctx.Ok() return } logx.Errorf("GetOrderInputAndOutputInfo err: %v", err.Error()) ctx.FailWithMsg(ecode.UnknownErr, "grpc调用错误") return } grpcOutputList := grpcResp.OutputList outputList := make([]*response.OutputInfo, 0, len(grpcOutputList)) for _, v := range grpcOutputList { outputInfo := &response.OutputInfo{ Number: v.Number, Name: v.Name, OrderAmount: productMap[v.Number].Amount.String(), Unit: v.Unit, Invoice: v.Invoice, Carrier: v.Carrier, Waybill: v.Waybill, SalePrice: v.SalePrice, Valorem: v.Valorem, Warehouse: v.Warehouse, Amount: v.Amount, Status: int(v.Status), Specs: productMap[v.Number].Specs, CreateTime: v.CreateTime, } outputList = append(outputList, outputInfo) } ctx.OkWithDetailed(outputList) } // GetApsProjectList // // @Tags SalesDetails // @Summary 获取aps项目列表 // @Produce application/json // @Success 200 {object} response.Response // // @Router /api/salesDetails/getApsProjectList [get] func (s *SalesDetailsApi) GetApsProjectList(c *gin.Context) { ctx, ok := contextx.NewContext(c, nil) if !ok { return } client := crm_aps.NewCrmAndApsGrpcServiceClient(grpc_init.CrmApsGrpcServiceConn) projectList, err := client.GetApsProjectList(c, &crm_aps.GetApsProjectListRequest{}) if err != nil { logx.Errorf("grpc GetApsProjectList err: %v", err.Error()) ctx.FailWithMsg(ecode.UnknownErr, "获取aps项目列表失败") return } ctx.OkWithDetailed(projectList.List) } // SendSalesDetailsToOtherSystem // // @Tags SalesDetails // @Summary 推送销售明细信息到其他系统 // @Produce application/json // @Param object body request.SalesDetails true "查询参数" // @Success 200 {object} response.ListResponse // // @Router /api/salesDetails/sendSalesDetailsToOtherSystem [post] func (s *SalesDetailsApi) SendSalesDetailsToOtherSystem(c *gin.Context) { var params request.SalesDetails ctx, ok := contextx.NewContext(c, ¶ms) if !ok { return } clientName := "" if params.ClientId > 0 { first, err := model.NewClientSearch(nil).SetId(params.ClientId).First() if err != nil { ctx.FailWithMsg(ecode.UnknownErr, "客户信息查询失败") return } clientName = first.Name } m := make(map[string]interface{}) m["status"] = params.Status m["project_id"] = params.ProjectId err := model.NewSalesDetailsSearch().SetNumber(params.Number).UpdateByMap(m) if err != nil { ctx.FailWithMsg(ecode.UnknownErr, "状态更新失败") return } //推送到wms //wmsProducts := make([]*product_inventory.InventoryProduct, 0) //for _, product := range params.Products { // var p product_inventory.InventoryProduct // p.Id = product.Number // p.Amount = product.Amount.String() // wmsProducts = append(wmsProducts, &p) //} //clientWms := product_inventory.NewProductInventoryServiceClient(grpc_init.ProductInventoryServiceConn) //_, err = clientWms.CreateOperation(ctx.GetCtx(), &product_inventory.CreateOperationRequest{ // Number: params.Number, // Addressee: params.Addressee, // Address: params.Address, // Phone: params.Phone, // DeliverType: int32(params.DeliverType), // Source: "CRM", // ClientId: int64(params.ClientId), // ClientName: clientName, // ProductList: wmsProducts, //}) //if err != nil { // logx.Errorf("grpc CreateOperation err: %v", err.Error()) //} //推送到aps ApsProducts := make([]*crm_aps.SalesDetailsProduct, 0) var total decimal.Decimal for _, product := range params.Products { var sp crm_aps.SalesDetailsProduct sp.ProductId = product.Number sp.Amount = product.Amount.IntPart() ApsProducts = append(ApsProducts, &sp) total = total.Add(product.Amount) } clientAps := crm_aps.NewCrmAndApsGrpcServiceClient(grpc_init.CrmApsGrpcServiceConn) _, err = clientAps.SendSalesDetailsToApsProject(c, &crm_aps.SendSalesDetailsToApsProjectRequest{ Number: params.Number, ClientName: clientName, MemberName: params.Member.Username, SignTime: params.SignTime, DeliveryDate: params.DeliveryDate, Source: "CRM", ProductTotal: total.IntPart(), ProjectId: params.ProjectId, Products: ApsProducts, DeliverType: int64(params.DeliverType), }) if err != nil { //状态还原 m["status"] = constvar.WaitConfirmed _ = model.NewSalesDetailsSearch().SetNumber(params.Number).UpdateByMap(m) logx.Errorf("grpc SendSalesDetailsToApsProject err: %v", err.Error()) ctx.FailWithMsg(ecode.UnknownErr, "推送失败,请检查参数是否正确") return } ctx.Ok() }