package controllers import ( "context" "errors" "fmt" "github.com/gin-gonic/gin" uuid "github.com/satori/go.uuid" "github.com/shopspring/decimal" "github.com/xuri/excelize/v2" "gorm.io/gorm" "strconv" "strings" "time" "wms/conf" "wms/constvar" "wms/extend/code" "wms/extend/util" "wms/middleware" "wms/models" "wms/pkg/logx" "wms/pkg/mysqlx" "wms/pkg/structx" "wms/proto/client" "wms/proto/init_client" "wms/proto/inventory_order" "wms/proto/product_inventory" "wms/proto/purchase_wms" "wms/proto/supplier" "wms/request" "wms/response" "wms/service" "wms/utils/http" "wms/utils/upload" ) type OperationController struct { } // Add // @Tags 入库/出库 // @Summary 添加入库/出库 // @Produce application/json // @Param object body request.AddOperation true "入库/出库信息" // @Success 200 {object} util.Response "成功" // @Router /api-wms/v1/operation/operation [post] func (slf OperationController) Add(c *gin.Context) { var reqParams request.AddOperation var params models.Operation if err := c.BindJSON(&reqParams); err != nil { util.ResponseFormat(c, code.RequestParamError, "参数解析失败,数据类型错误") return } if err := structx.AssignTo(reqParams, ¶ms); err != nil { util.ResponseFormat(c, code.RequestParamError, "数据转换错误"+err.Error()) return } if err := slf.CheckParams(params); err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } if CheckDetailsRepeat(params.Details) { util.ResponseFormat(c, code.RequestParamError, "明细中不能存在重复的产品") return } if params.BaseOperationType != constvar.BaseOperationTypeAdjust { if err := slf.FormatLocation(¶ms); err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } var operationType *models.OperationType var err error if params.OperationTypeId == 0 { operationType, err := models.NewOperationTypeSearch(). SetWarehouseId(params.WarehouseId).SetBaseOperationType(params.BaseOperationType). First() if err != nil || params.WarehouseId != operationType.WarehouseId { util.ResponseFormat(c, code.RequestParamError, "参数缺失") return } } else { operationType, err = models.NewOperationTypeSearch().SetID(uint(params.OperationTypeId)).First() if err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } } params.BaseOperationType = operationType.BaseOperationType params.WarehouseId = operationType.WarehouseId } if params.DealerType == "" { CheckInventoryDealerType(¶ms) } userInfo := middleware.GetUserInfo(c) if userInfo == nil { util.ResponseFormat(c, code.SetStatusError, "登录用户信息为空,请登录后重试!") return } params.CreatedBy = userInfo.Username params.Status = constvar.OperationStatus_Ready //params.Number = strconv.FormatInt(time.Now().Unix(), 10) for _, detail := range params.Details { detail.BaseOperationType = params.BaseOperationType } var numberNum int64 if err := mysqlx.GetDB().Model(&models.Operation{}).Where("number=?", params.Number).Count(&numberNum).Error; err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } if numberNum > 0 { util.ResponseFormat(c, code.RequestParamError, "单号已存在") return } err := models.WithTransaction(func(db *gorm.DB) error { if err := models.NewOperationSearch().Create(¶ms); err != nil { return err } /*od := params.Details for _, v := range od { //material, err := models.NewMaterialSearch().SetID(v.ProductId).First() //if err != nil {return err} material := new(models.Material) material.ID = v.ProductId material.Cost = v.Cost material.SalePrice = v.SalePrice if err := models.NewMaterialSearch().SetOrm(db).Update(material); err != nil { return err } }*/ return nil }) if err != nil { logx.Errorf("Operation create err: %v", err) util.ResponseFormat(c, code.SaveFail, "添加失败:"+err.Error()) return } util.ResponseFormat(c, code.Success, "添加成功") } func CheckInventoryDealerType(params *models.Operation) { var dictType constvar.MiniDictType dictName := "其他" switch params.BaseOperationType { case constvar.BaseOperationTypeIncoming: dictType = constvar.StorageType //dictName = dictName + "入库" case constvar.BaseOperationTypeOutgoing: dictType = constvar.StockoutType //dictName = dictName + "出库" case constvar.BaseOperationTypeInternal: dictType = constvar.TransferType //dictName = dictName + "调拨" case constvar.BaseOperationTypeDisuse: dictType = constvar.DisuseType //dictName = dictName + "报废" case constvar.BaseOperationTypeAdjust: dictType = constvar.TakeStockType //dictName = dictName + "盘点" default: return } miniDict, err := models.NewMiniDictSearch().SetType(dictType).SetName(dictName).First() if err == gorm.ErrRecordNotFound { miniDict = &models.MiniDict{ Type: dictType, Name: dictName, } models.NewMiniDictSearch().Create(miniDict) } params.DealerType = miniDict.Name } // CheckDetailsRepeat 出库、入库、盘点涉及产品重复性检查(以产品编号、出库库房编号、入库库房编号) // 存在重复值 true; 不存在返回 false func CheckDetailsRepeat(details []*models.OperationDetails) bool { if len(details) == 0 { return false } var detailKeyMap = make(map[string]bool) for _, v := range details { key := fmt.Sprintf("%v%v%v", v.ProductId, v.FromLocationID, v.ToLocationID) detailKeyMap[key] = true } if len(detailKeyMap) != len(details) { // 长度相同,不存在重复值;长度不同存在存在不同 return true } return false } // FormatLocation 位置检查 func (slf OperationController) FormatLocation(params *models.Operation) error { operationType, err := models.NewOperationTypeSearch().SetID(uint(params.OperationTypeId)).First() if err != nil { return err } if operationType.BaseOperationType == constvar.BaseOperationTypeIncoming { location, err := models.NewLocationSearch().SetType(int(constvar.LocationTypeVendor)).First() if err != nil { return err } for k, v := range params.Details { params.Details[k].FromLocationID = location.Id if v.ToLocationID == 0 { params.Details[k].ToLocationID = params.LocationID } } } if operationType.BaseOperationType == constvar.BaseOperationTypeOutgoing { location, err := models.NewLocationSearch().SetType(int(constvar.LocationTypeCustomer)).First() if err != nil { return err } for k, v := range params.Details { params.Details[k].ToLocationID = location.Id if v.FromLocationID == 0 { return errors.New("请选择出库位置") } } } if operationType.BaseOperationType == constvar.BaseOperationTypeInternal { for _, v := range params.Details { if v.ToLocationID == 0 { return errors.New("请选择目标位置") } if v.FromLocationID == 0 { return errors.New("请选择源位置") } } } return nil } func (slf OperationController) CheckParams(params models.Operation) error { /*if params.SourceNumber == "" { return errors.New("请填入源单号") }*/ if params.Number == "" { return errors.New("请填入单号") } if params.OperationTypeId == 0 && int(params.BaseOperationType) == 0 { return errors.New("未识别该记录类型1") } else if params.OperationTypeId == 0 && params.BaseOperationType != 0 && params.WarehouseId == 0 { return errors.New("未识别该记录类型2") } if params.OperationDate == "" { return errors.New("请选择安排日期") } //if params.LocationID == 0 { // return errors.New("请选择源位置") //} if len(params.Details) <= 0 { return errors.New("请添加明细信息") } //检查明细部分 for _, v := range params.Details { if v.ProductId == "" { return errors.New("productID为空") } //if v.ProductName == "" { // return errors.New("产品名称异常") //} if v.Amount.IsNegative() { return errors.New("产品数量出错") } } return nil } // GetOperationInfo // @Tags 入库/出库 // @Summary 操作详情接口 // @Produce application/json // @Param id path string true "id" // @Param Authorization header string true "token" // @Success 200 {object} util.Response{data=models.Operation} "成功" // @Router /api-wms/v1/operation/getOperationInfo/{id} [get] func (slf OperationController) GetOperationInfo(c *gin.Context) { number := c.Param("id") if number == "" { util.ResponseFormat(c, code.RequestParamError, "参数解析失败,参数不能为空") return } id, _ := strconv.Atoi(number) operation, err := models.NewOperationSearch().SetID(id).SetPreload(true).First() if err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } if operation.BaseOperationType == constvar.BaseOperationTypeOutgoing || operation.BaseOperationType == constvar.BaseOperationTypeInternal { //查询实时在库库存 productIds := make([]string, 0) locationIds := make([]int, 0) for _, v := range operation.Details { productIds = append(productIds, v.ProductId) locationIds = append(locationIds, v.FromLocationID) } locationAmountMap, err := service.GetLocationAmounts(productIds, locationIds) if err != nil { util.ResponseFormat(c, code.RequestParamError, "查询产品库存错误") return } for _, v := range operation.Details { if locationAmountMap[v.ProductId] != nil { v.StockAmount = locationAmountMap[v.ProductId][v.FromLocationID] } } } util.ResponseFormat(c, code.Success, operation) } // List // @Tags 入库/出库 // @Summary 入库/出库列表 // @Produce application/json // @Param object body request.OperationList true "查询参数" // @Success 200 {object} util.ResponseList{data=[]models.Operation} "成功" // @Router /api-wms/v1/operation/list [post] func (slf OperationController) List(c *gin.Context) { var params request.OperationList if err := c.BindJSON(¶ms); err != nil { util.ResponseFormat(c, code.RequestParamError, "参数解析失败,数据类型错误:"+err.Error()) return } if err := slf.CheckListParams(¶ms); err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } search := models.NewOperationSearch() search.SetPage(params.Page, params.PageSize) if params.Number != "" { search.SetKeyword(params.Number) } if int(params.Status) != 0 { search.SetStatus(params.Status) } materials, err := models.NewMaterialSearch().FindNotTotal() if err != nil { util.ResponseFormat(c, code.RequestError, "查找失败:"+err.Error()) return } costMap := make(map[string]decimal.Decimal) salePriceMap := make(map[string]decimal.Decimal) for _, material := range materials { costMap[material.ID] = material.Cost salePriceMap[material.ID] = material.SalePrice } list, total, err := search.SetOperationTypeId(params.OperationTypeId). SetBaseOperationType(params.BaseOperationType). SetPreload(true).SetOrder("created_at desc").Find() if err != nil { util.ResponseFormat(c, code.RequestError, "查找失败:"+err.Error()) return } //userList, err := user.GetUserList() //userMap := make(map[string]string) //if err == nil { // for _, user := range userList { // userMap[user.UserName] = user.NickName // } //} userInfo := middleware.GetUserInfo(c) if userInfo == nil { util.ResponseFormat(c, code.SetStatusError, "登录用户信息为空,请登录后重试!") return } for _, v := range list { v.CreatedBy = userInfo.NickName //if err == nil { if userInfo != nil { // v.CreatedBy = userMap[v.CreatedBy] // v.CheckedBy = userMap[v.CheckedBy] v.CheckedBy = userInfo.NickName } for _, v1 := range v.Details { //v1.SalePrice = decimal.NewFromFloat(1.2345) //v1.Cost = decimal.NewFromFloat(9.678) v1.SalePrice = v1.Cost v1.Cost = v1.SalePrice } } util.ResponseFormatListWithPage(c, code.Success, list, int(total), params.Page, params.PageSize) } func (slf OperationController) CheckListParams(params *request.OperationList) error { if !params.PageInfo.Check() { return errors.New("数据分页信息错误") } if params.OperationTypeId == 0 && params.BaseOperationType == 0 { return errors.New("operationTypeId和baseOperationType不能同时为0") } return nil } // Update // @Tags 入库/出库 // @Summary 修改入库/出库信息 // @Produce application/json // @Param object body request.UpdateOperation true "入库信息" // @Success 200 {object} util.Response "成功" // @Router /api-wms/v1/operation/update [post] func (slf OperationController) Update(c *gin.Context) { var reqParams request.UpdateOperation var params models.Operation if err := c.BindJSON(&reqParams); err != nil { util.ResponseFormat(c, code.RequestParamError, "参数解析失败,数据类型错误:"+err.Error()) return } if reqParams.Status != constvar.OperationStatus_Ready { util.ResponseFormat(c, code.RequestParamError, "该入库信息已完成,无法进行修改") return } if err := structx.AssignTo(reqParams, ¶ms); err != nil { util.ResponseFormat(c, code.RequestParamError, "数据转换错误"+err.Error()) return } if err := slf.CheckParams(params); err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } if CheckDetailsRepeat(params.Details) { util.ResponseFormat(c, code.RequestParamError, "明细中不能存在重复的产品") return } if params.BaseOperationType != constvar.BaseOperationTypeAdjust { if err := slf.FormatLocation(¶ms); err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } } if err := models.WithTransaction(func(tx *gorm.DB) error { if err := models.NewOperationDetailsSearch().SetOrm(tx).SetOperationId(params.Id).Delete(); err != nil { return err } operationSearch := models.NewOperationSearch().SetOrm(tx) if err := operationSearch.Orm.Model(¶ms).Association("Details").Replace(params.Details); err != nil { return err } if err := models.NewOperationSearch().SetOrm(tx).SetID(params.Id).Save(¶ms); err != nil { return err } /*od := params.Details for _, v := range od { //material, err := models.NewMaterialSearch().SetID(v.ProductId).First() //if err != nil {return err} material := new(models.Material) material.ID = v.ProductId material.Cost = v.Cost material.SalePrice = v.SalePrice if err := models.NewMaterialSearch().SetOrm(tx).Update(material); err != nil { return err } }*/ return nil }); err != nil { util.ResponseFormat(c, code.RequestParamError, "修改失败:"+err.Error()) return } util.ResponseFormat(c, code.Success, "修改成功") } // Delete // // @Tags 入库/出库 // @Summary 删除入库/出库信息 // @Produce application/json // @Param id path int true "id" // @Success 200 {object} util.Response "成功" // @Router /api-wms/v1/operation/operation/{id} [delete] func (slf OperationController) Delete(c *gin.Context) { id, err := strconv.Atoi(c.Param("id")) if err != nil { util.ResponseFormat(c, code.RequestParamError, "错误的id值") return } if id == 0 { util.ResponseFormat(c, code.RequestParamError, "id为0") return } operation, err := models.NewOperationSearch().SetID(id).First() if err != nil { util.ResponseFormat(c, code.RequestParamError, "未找到相关出入库单据:"+err.Error()) return } userInfo := middleware.GetUserInfo(c) if operation.CreatedBy != userInfo.Username { util.ResponseFormat(c, code.RequestError, "别人的出入库单据无法进行删除") return } if operation.Status != constvar.OperationStatus_Ready { util.ResponseFormat(c, code.RequestError, "该出入库单据无法进行删除") return } if err := models.WithTransaction(func(tx *gorm.DB) error { if err := models.NewOperationDetailsSearch().SetOrm(tx).SetOperationId(id).Delete(); err != nil { return err } if err := models.NewOperationSearch().SetOrm(tx).SetID(id).Delete(); err != nil { return err } return nil }); err != nil { util.ResponseFormat(c, code.RequestParamError, "修改失败:"+err.Error()) return } util.ResponseFormat(c, code.Success, "删除成功") } // Finish // // @Tags 入库/出库 // @Summary 更改记录状态 // @Produce application/json // @Param id path int true "id" // @Success 200 {object} util.Response "成功" // @Router /api-wms/v1/operation/finish/{id} [put] func (slf OperationController) Finish(c *gin.Context) { id, err := strconv.Atoi(c.Param("id")) if err != nil { util.ResponseFormat(c, code.RequestParamError, "错误的id值") return } if id == 0 { util.ResponseFormat(c, code.RequestParamError, "id为0") return } operation, err := models.NewOperationSearch().SetPreload(true).SetID(id).First() if err != nil { util.ResponseFormat(c, code.RequestParamError, "未找到相关出入库信息:"+err.Error()) return } if operation.Status != constvar.OperationStatus_Ready { util.ResponseFormat(c, code.RequestError, "该出入库信息无法完成") return } listDetails, err := models.NewOperationDetailsSearch().SetOperationId(operation.Id).SetPreload(true).FindAll() if err != nil { util.ResponseFormat(c, code.RequestError, err.Error()) return } var mapLocAmount map[string]*models.LocationProductAmount locAmountList, err := models.NewLocationProductAmountSearch().Find() if err != nil { util.ResponseFormat(c, code.RequestError, err.Error()) return } if len(locAmountList) > 0 { mapLocAmount = make(map[string]*models.LocationProductAmount) for _, v := range locAmountList { mapLocAmount[strconv.Itoa(v.LocationId)+v.ProductId] = v } } userInfo := middleware.GetUserInfo(c) err = models.WithTransaction(func(tx *gorm.DB) error { if err := models.NewOperationSearch().SetOrm(tx).SetID(id).Update(&models.Operation{ Status: constvar.OperationStatus_Finish, CheckedBy: userInfo.Username, AuditDate: time.Now().Format("2006-01-02 15:04:05")}); err != nil { return err } if err := service.AddMoveHistory([]*models.Operation{operation}, tx); err != nil { return err } if operation.BaseOperationType == constvar.BaseOperationTypeIncoming { if err := service.FinishOperationInput(c, tx, operation, listDetails, mapLocAmount); err != nil { return err } } if operation.BaseOperationType == constvar.BaseOperationTypeOutgoing || operation.BaseOperationType == constvar.BaseOperationTypeDisuse { if err := service.FinishOperationOutput(tx, listDetails, mapLocAmount, operation); err != nil { return err } } if operation.BaseOperationType == constvar.BaseOperationTypeInternal { if err := service.FinishOperationInternal(tx, listDetails, operation); err != nil { return err } } if operation.BaseOperationType == constvar.BaseOperationTypeAdjust { if err := service.FinishOperationAdjust(tx, listDetails, mapLocAmount, operation); err != nil { return err } } return nil }) if err != nil { util.ResponseFormat(c, code.RequestError, err.Error()) return } //修改其他系统订单状态 if operation.BaseOperationType == constvar.BaseOperationTypeIncoming { if operation.Source != "" { go UpdatePurchaseStatus(operation.Source, operation.SourceNumber) go UpdateOutsourceOrder(operation.Source, operation.SourceNumber, operation.Id, listDetails) } } else if operation.BaseOperationType == constvar.BaseOperationTypeOutgoing { if operation.Source != "" { go UpdateOutStatus(operation.Source, operation.SourceNumber, 4) } } util.ResponseFormat(c, code.Success, "操作成功") } //var ( // ProductInventoryServiceConn *grpc.ClientConn // PurchaseServiceConn *grpc.ClientConn //) // //func InitProductInventoryServiceConn() { // var err error // ProductInventoryServiceConn, err = grpc.Dial(conf.GrpcServerConf.CrmAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) // if err != nil { // logx.Errorf("grpc dial product service error: %v", err.Error()) // return // } // PurchaseServiceConn, err = grpc.Dial(conf.GrpcServerConf.SrmAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) // if err != nil { // logx.Errorf("grpc dial product service error: %v", err.Error()) // return // } //} // //func CloseProductInventoryServiceConn() { // if ProductInventoryServiceConn != nil { // ProductInventoryServiceConn.Close() // } // if PurchaseServiceConn != nil { // PurchaseServiceConn.Close() // } //} func UpdateOutStatus(source, number string, status int64) { if source == "CRM" { cl := product_inventory.NewProductInventoryServiceClient(init_client.CrmConn) _, err := cl.UpdateSalesDetailStatus(context.Background(), &product_inventory.UpdateSalesDetailStatusRequest{ Number: number, SalesDetailStatus: "已出库", }) if err != nil { logx.Errorf("grpc dial UpdateSalesDetailStatus service error: %v", err) } } if source == "APS_APPLY_MATERIAL" { cl := inventory_order.NewInventoryOrderServiceClient(init_client.ApsConn) _, err := cl.UpdateMaterialApplyStatus(context.Background(), &inventory_order.UpdateMaterialApplyStatusRequest{ Number: number, Status: status, }) if err != nil { logx.Errorf("grpc dial UpdateSalesDetailStatus service error: %v", err) } } } func UpdatePurchaseStatus(source, number string) { if source == "SRM_PURCHASE" { count, err := models.NewOperationSearch().SetSourceNumber(number).SetStatus(constvar.OperationStatus_Ready).Count() if err != nil || count > 0 { return } cl := purchase_wms.NewPurchaseServiceClient(init_client.SrmConn) _, err = cl.UpdatePurchaseStatus(context.Background(), &purchase_wms.UpdatePurchaseStatusRequest{Number: number}) if err != nil { logx.Errorf("grpc dial UpdatePurchaseStatus service error: %v", err) } } } func UpdateOutsourceOrder(source, number string, operationId int, details []*models.OperationDetails) { if source == "APS_OUTSOURCING_RECEIVE" { products := make([]*inventory_order.OperationProduct, 0) for _, detail := range details { if operationId == detail.OperationID { var op inventory_order.OperationProduct op.ProductNumber = detail.ProductId op.Amount = detail.Amount.IntPart() products = append(products, &op) } } cl := inventory_order.NewInventoryOrderServiceClient(init_client.ApsConn) _, err := cl.UpdateOutsourceOrder(context.Background(), &inventory_order.UpdateOutsourceOrderRequest{ OutsourceNumber: number, Products: products, }) if err != nil { logx.Errorf("grpc dial UpdateOutsourceOrder service error: %v", err) } } } // ListTransfer // @Tags 入库/出库 // @Summary 库存调拨列表 // @Produce application/json // @Param object body request.OperationAllList true "参数" // @Success 200 {object} util.Response "成功" // @Router /api-wms/v1/operation/listTransfer [post] func (slf OperationController) ListTransfer(c *gin.Context) { var params request.OperationAllList if err := c.BindJSON(¶ms); err != nil { util.ResponseFormat(c, code.RequestParamError, "参数解析失败,数据类型错误:"+err.Error()) return } if !params.PageInfo.Check() { util.ResponseFormat(c, code.RequestParamError, "数据分页信息错误") return } search := models.NewOperationSearch() search.SetPage(params.Page, params.PageSize) search.SetPage(params.Page, params.PageSize) if params.Number != "" { search.SetKeyword(params.Number) } list, total, err := search.SetPreload(true).SetBaseOperationType(constvar.BaseOperationTypeInternal).SetOrder("created_at desc").Find() if err != nil { util.ResponseFormat(c, code.RequestError, "查找失败:"+err.Error()) return } util.ResponseFormatListWithPage(c, code.Success, list, int(total), params.Page, params.PageSize) } // GetLogisticCompanyList // @Tags 入库/出库 // @Summary 获取物流公司列表 // @Produce application/json // @Success 200 {object} util.ResponseList{data=[]models.LogisticCompany} "成功" // @Router /api-wms/v1/operation/getLogisticCompanyList [get] func (slf OperationController) GetLogisticCompanyList(c *gin.Context) { companies, err := models.NewLogisticCompanySearch().FindNotTotal() if err != nil { util.ResponseFormat(c, code.RequestError, "查找失败:"+err.Error()) return } util.ResponseFormat(c, code.Success, companies) } // Cancel // // @Tags 入库/出库 // @Summary 取消 // @Produce application/json // @Param id path int true "id" // @Success 200 {object} util.Response "成功" // @Router /api-wms/v1/operation/cancel/{id} [put] func (slf OperationController) Cancel(c *gin.Context) { id, err := strconv.Atoi(c.Param("id")) if err != nil { util.ResponseFormat(c, code.RequestParamError, "错误的id值") return } if id == 0 { util.ResponseFormat(c, code.RequestParamError, "id为0") return } operation, err := models.NewOperationSearch().SetPreload(true).SetID(id).First() if err != nil { util.ResponseFormat(c, code.RequestParamError, "未找到相关出入库信息:"+err.Error()) return } if operation.Status != constvar.OperationStatus_Ready { util.ResponseFormat(c, code.RequestError, "该出入库信息无法取消") return } operation.Status = constvar.OperationStatus_Cancel operation.AuditDate = time.Now().Format("2006-01-02 15:04:05") if err := models.NewOperationSearch().SetID(operation.Id).Save(operation); err != nil { util.ResponseFormat(c, code.SaveFail, err.Error()) return } //更新aps物料申请状态 if operation.Source == "APS_APPLY_MATERIAL" { go UpdateOutStatus(operation.Source, operation.SourceNumber, 3) } util.ResponseFormat(c, code.Success, "操作成功") } // OutputOperation // // @Tags 入库/出库 // @Summary 打印 // @Produce application/json // @Param id path int true "id" // @Success 200 {object} util.Response "成功" // @Router /api-wms/v1/operation/outputOperation/{id} [put] func (slf OperationController) OutputOperation(c *gin.Context) { id, err := strconv.Atoi(c.Param("id")) if err != nil { util.ResponseFormat(c, code.RequestParamError, "错误的id值") return } if id == 0 { util.ResponseFormat(c, code.RequestParamError, "id为0") return } // 获取操作消息 operation, err := models.NewOperationSearch().SetPreload(true).SetID(id).First() if err != nil { util.ResponseFormat(c, code.RequestParamError, "未找到相关出入库信息:"+err.Error()) return } if len(operation.Details) == 0 { util.ResponseFormat(c, code.RequestParamError, "没有相关产品明细的信息") return } var fileUrl string // 获取公司名称 companyName := conf.WebConf.CompanyName if companyName == "" { util.ResponseFormat(c, code.RequestParamError, "缺少工厂名称配置") return } // BaseOperationTypeIncoming 收货 if operation.BaseOperationType == constvar.BaseOperationTypeIncoming { if companyName == "jialian" { // 入库 oT, err := models.NewOperationTypeSearch().SetID(uint(operation.OperationTypeId)).First() if err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } warehouse, err := models.NewWarehouseSearch().SetID(oT.WarehouseId).First() if err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } if err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } if warehouse.FileTemplateCategoryIn == constvar.FileWarehouseCategory_JialianInput2 { fileUrl, err = JialianOperation1(warehouse.FileTemplateCategoryIn, operation) if err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } } else { fileUrl, err = JialianOperation2(constvar.FileTemplateCategory_JialianInput1, operation) if err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } } } else if companyName == "geruimi" { fileUrl, err = ExportInputSelfmade(constvar.FileTemplateCategory_Selfmade, operation) if err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } } } else if operation.BaseOperationType == constvar.BaseOperationTypeOutgoing { // 交货 if companyName == "jialian" { oT, err := models.NewOperationTypeSearch().SetID(uint(operation.OperationTypeId)).First() if err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } warehouse, err := models.NewWarehouseSearch().SetID(oT.WarehouseId).First() if err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } if err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } if warehouse.FileTemplateCategoryOut == constvar.FileWarehouseCategory_JialianOutput2 { fileUrl, err = JialianOperation1(warehouse.FileTemplateCategoryOut, operation) if err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } } else { fileUrl, err = JialianOperation2(constvar.FileTemplateCategory_JialianOutput1, operation) if err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } } } else if companyName == "geruimi" { fileUrl, err = ExportInputSelfmade(constvar.FileTemplateCategory_Output, operation) if err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } } } else { util.ResponseFormat(c, code.RequestParamError, "该记录不支持打印") return } m := make(map[string]string) m["url"] = fileUrl m["name"] = companyName util.ResponseFormat(c, code.Success, m) } func JialianOperation1(category constvar.FileTemplateCategory, operation *models.Operation) (string, error) { // 获取模板记录 template, err := models.NewFileTemplateAttachmentSearch().SetCategory(category).First() if err != nil { return "", errors.New("获取模版记录失败:" + err.Error()) } fmt.Println(template) // 根据模板路径获取模板消息 readerCloser, err := http.HttpGetWithReadCloser(template.FileUrl) if err != nil { return "", errors.New("获取模版失败:" + err.Error()) } // 读取模板内容 f, err := excelize.OpenReader(readerCloser) if err != nil { return "", errors.New("读取excel模版失败:" + err.Error()) } readerCloser.Close() defer f.Close() sheet := "Sheet1" if category == constvar.FileWarehouseCategory_JialianInput2 { sheet = "入库单" } else if category == constvar.FileWarehouseCategory_JialianOutput2 { sheet = "出库单" } // 第二行 f.SetCellValue(sheet, "K2", operation.Number) // 单号 // 第三行 f.SetCellValue(sheet, "D3", operation.CompanyName) // 公司 f.SetCellValue(sheet, "G3", "") // 部门 // 时间 date := operation.OperationDate f.SetCellValue(sheet, "I3", date[0:4]) // 年 f.SetCellValue(sheet, "L3", date[5:7]) // 月 f.SetCellValue(sheet, "N3", date[8:10]) // 日 var entityIDs []string for _, v := range operation.Details { entityIDs = append(entityIDs, v.ProductId) } attributeMap := make(map[string]uint) attributeValueMap := make(map[string]string) attributes, err := models.NewAttributeSearch().SetEntityType(1).FindNotTotal() if err != nil { return "", errors.New("获取动态属性失败:" + err.Error()) } for _, v := range attributes { attributeMap[v.Name] = v.ID } attributeValues, err := models.NewAttributeValueSearch().SetEntityIDs(entityIDs).FindNotTotal() if err != nil { return "", errors.New("获取动态属性值失败:" + err.Error()) } for _, v := range attributeValues { attributeValueMap[fmt.Sprintf("%d%s", v.AttributeID, v.EntityID)] = v.Value } // 第六行 循环填值 totalAmount := decimal.NewFromInt(0) // 总金额 totalPrice := decimal.NewFromInt(0) // 总价 rowIndex := 6 str := "" for i, v := range operation.Details { //设置表单最多6条数据 if i > 6 { break } str += v.Remark f.SetCellValue(sheet, "B"+strconv.Itoa(rowIndex), v.Product.Name) // 产品名称 f.SetCellValue(sheet, "C"+strconv.Itoa(rowIndex), v.Product.Specs) //规格 f.SetCellValue(sheet, "D"+strconv.Itoa(rowIndex), attributeValueMap[fmt.Sprintf("%d%s", attributeMap["颜色"], v.ProductId)]) //颜色 f.SetCellValue(sheet, "E"+strconv.Itoa(rowIndex), attributeValueMap[fmt.Sprintf("%d%s", attributeMap["面料"], v.ProductId)]) //面料 f.SetCellValue(sheet, "F"+strconv.Itoa(rowIndex), v.TotalGrossWeight.String()) //重量 f.SetCellValue(sheet, "G"+strconv.Itoa(rowIndex), v.Amount.String()) //数量 f.SetCellValue(sheet, "H"+strconv.Itoa(rowIndex), v.Product.SalePrice.String()) // 单价 // 金额 if !v.Product.SalePrice.IsZero() { ap := v.Amount.Mul(v.Product.SalePrice) totalPrice = totalPrice.Add(ap) price := ap.String() split := strings.Split(price, ".") if len(split) == 2 { for n, a := range split[1] { f.SetCellValue(sheet, string('N'+n)+strconv.Itoa(rowIndex), string(a)) } } s := split[0] n := 0 for j := len(s) - 1; j >= 0; j-- { if n == 6 { f.SetCellValue(sheet, string('M'-n)+strconv.Itoa(rowIndex), s[:j]) break } f.SetCellValue(sheet, string('M'-n)+strconv.Itoa(rowIndex), string(s[j])) n++ } } rowIndex++ totalAmount = totalAmount.Add(v.Amount) } //合计 if !totalPrice.IsZero() { price := totalPrice.String() split := strings.Split(price, ".") if len(split) == 2 { for n, a := range split[1] { f.SetCellValue(sheet, string('M'+n)+"12", string(a)) } } else { f.SetCellValue(sheet, "N12", "0") f.SetCellValue(sheet, "O12", "0") } s := split[0] n := 0 for j := len(s) - 1; j >= 0; j-- { if n == 6 { f.SetCellValue(sheet, string('M'-n)+"12", s[:j]) break } f.SetCellValue(sheet, string('M'-n)+"12", string(s[j])) n++ } } // 备注 第13行 //f.SetCellValue(sheet, "B13", "备注:"+operation.Comment) f.SetCellValue(sheet, "B13", "备注:"+str) // 第14行 f.SetCellValue(sheet, "C14", operation.Manager) // 审核 f.SetCellValue(sheet, "F14", operation.Accountant) // 保管 f.SetCellValue(sheet, "I14", operation.Custodian) //经办人 buf, err := f.WriteToBuffer() if err != nil { return "", err } fileUrl, err := upload.UploadFileToSeaWeed(string(constvar.FileType_File), uuid.NewV4().String()+".xlsx", buf.Bytes()) if err != nil { logx.Errorf("file upload err: %v", err) return "", err } return fileUrl, nil } func JialianOperation2(category constvar.FileTemplateCategory, operation *models.Operation) (string, error) { // 获取模板记录 template, err := models.NewFileTemplateAttachmentSearch().SetCategory(category).First() if err != nil { return "", errors.New("获取模版记录失败:" + err.Error()) } fmt.Println(template) // 根据模板路径获取模板消息 readerCloser, err := http.HttpGetWithReadCloser(template.FileUrl) if err != nil { return "", errors.New("获取模版失败:" + err.Error()) } // 读取模板内容 f, err := excelize.OpenReader(readerCloser) if err != nil { return "", errors.New("读取excel模版失败:" + err.Error()) } readerCloser.Close() defer f.Close() sheet := "Sheet1" f.SetCellValue(sheet, "A2", "单位(部门):"+operation.CompanyName) // 单位部门 f.SetCellValue(sheet, "J2", operation.OperationDate) // 时间 f.SetCellValue(sheet, "Q2", operation.Number) // 编号 totalAmount := decimal.NewFromInt(0) // 总金额 totalPrice := decimal.NewFromInt(0) // 总价 rowIndex := 5 str := "" for i, v := range operation.Details { //设置表单最多9条数据 if i > 9 { break } str += v.Remark f.SetCellValue(sheet, "A"+strconv.Itoa(rowIndex), v.Product.Name) // 品名 f.SetCellValue(sheet, "B"+strconv.Itoa(rowIndex), v.Product.Type) // 型号 f.SetCellValue(sheet, "C"+strconv.Itoa(rowIndex), v.Product.Unit) // 单位 f.SetCellValue(sheet, "D"+strconv.Itoa(rowIndex), v.Amount.String()) // 数量 unit := service.CreateMoreUnit(v.Amount, v.Product.MoreUnitList) for _, v1 := range unit { if !v1.Amount.IsZero() { f.SetCellValue(sheet, "E"+strconv.Itoa(rowIndex), v1.Amount) // 辅数量 f.SetCellValue(sheet, "F"+strconv.Itoa(rowIndex), v1.Unit) // 辅单位 break } } f.SetCellValue(sheet, "G"+strconv.Itoa(rowIndex), v.Product.SalePrice.String()) // 单价 // 金额 if !v.Product.SalePrice.IsZero() { ap := v.Amount.Mul(v.Product.SalePrice) totalPrice = totalPrice.Add(ap) price := ap.String() split := strings.Split(price, ".") if len(split) == 2 { for n, a := range split[1] { f.SetCellValue(sheet, string('O'+n)+strconv.Itoa(rowIndex), string(a)) } } s := split[0] n := 0 for j := len(s) - 1; j >= 0; j-- { if n == 6 { f.SetCellValue(sheet, string('N'-n)+strconv.Itoa(rowIndex), s[:j]) break } f.SetCellValue(sheet, string('N'-n)+strconv.Itoa(rowIndex), string(s[j])) n++ } } rowIndex++ totalAmount = totalAmount.Add(v.Amount) } //合计 if !totalPrice.IsZero() { price := totalPrice.String() split := strings.Split(price, ".") if len(split) == 2 { for n, a := range split[1] { f.SetCellValue(sheet, string('N'+n)+"14", string(a)) } } else { f.SetCellValue(sheet, "O14", "0") f.SetCellValue(sheet, "P14", "0") } s := split[0] n := 0 for j := len(s) - 1; j >= 0; j-- { if n == 6 { f.SetCellValue(sheet, string('N'-n)+"14", s[:j]) break } f.SetCellValue(sheet, string('N'-n)+"14", string(s[j])) n++ } } //f.SetCellValue(sheet, "Q5", operation.Comment) // 备注 f.SetCellValue(sheet, "Q5", str) // 备注 // 第14行 f.SetCellValue(sheet, "B15", operation.Manager) // 主管 f.SetCellValue(sheet, "D15", operation.Accountant) // 会计 f.SetCellValue(sheet, "H15", operation.Custodian) //保管员 f.SetCellValue(sheet, "Q15", operation.Custodian) //经手 buf, err := f.WriteToBuffer() if err != nil { return "", err } fileUrl, err := upload.UploadFileToSeaWeed(string(constvar.FileType_File), uuid.NewV4().String()+".xlsx", buf.Bytes()) if err != nil { logx.Errorf("file upload err: %v", err) return "", err } return fileUrl, nil } func ExportInputSelfmade(category constvar.FileTemplateCategory, operation *models.Operation) (string, error) { template, err := models.NewFileTemplateAttachmentSearch().SetCategory(category).First() if err != nil { return "", errors.New("获取模版记录失败:" + err.Error()) } readerCloser, err := http.HttpGetWithReadCloser(template.FileUrl) if err != nil { return "", errors.New("获取模版失败:" + err.Error()) } f, err := excelize.OpenReader(readerCloser) if err != nil { return "", errors.New("读取excel模版失败:" + err.Error()) } readerCloser.Close() defer f.Close() style, _ := f.NewStyle(&excelize.Style{ Border: []excelize.Border{ {Type: "left", Color: "#000000", Style: 2}, {Type: "top", Color: "#000000", Style: 2}, {Type: "bottom", Color: "#000000", Style: 2}, {Type: "right", Color: "#000000", Style: 2}, }, Alignment: &excelize.Alignment{ Horizontal: "center", Vertical: "center", }, }) styleLeft, _ := f.NewStyle(&excelize.Style{ Alignment: &excelize.Alignment{ Horizontal: "left", Vertical: "center", }, }) f.SetCellValue("Sheet1", "B3", operation.CompanyName) f.SetCellValue("Sheet1", "E3", operation.OperationDate) f.SetCellValue("Sheet1", "H3", operation.Number) rowIndex := 5 totalAmount := decimal.NewFromInt(0) for _, v := range operation.Details { f.SetCellValue("Sheet1", "A"+strconv.Itoa(rowIndex), v.ProductId) f.SetCellValue("Sheet1", "B"+strconv.Itoa(rowIndex), v.Product.Name) f.SetCellValue("Sheet1", "C"+strconv.Itoa(rowIndex), v.Product.Specs) f.SetCellValue("Sheet1", "D"+strconv.Itoa(rowIndex), v.Product.Unit) f.SetCellValue("Sheet1", "E"+strconv.Itoa(rowIndex), v.Amount.String()) f.SetCellValue("Sheet1", "H"+strconv.Itoa(rowIndex), v.ToLocation.Name) f.SetCellValue("Sheet1", "I"+strconv.Itoa(rowIndex), operation.Comment) rowIndex++ totalAmount = totalAmount.Add(v.Amount) } if err := f.MergeCell("Sheet1", "A"+strconv.Itoa(rowIndex), "C"+strconv.Itoa(rowIndex)); err != nil { return "", err } f.SetCellValue("Sheet1", "A"+strconv.Itoa(rowIndex), "合计") f.SetCellValue("Sheet1", "E"+strconv.Itoa(rowIndex), totalAmount.String()) f.SetCellStyle("Sheet1", "A4", "I"+strconv.Itoa(rowIndex), style) rowIndex++ f.SetCellValue("Sheet1", "A"+strconv.Itoa(rowIndex), "审核:") f.SetCellValue("Sheet1", "D"+strconv.Itoa(rowIndex), "检验:") f.SetCellValue("Sheet1", "G"+strconv.Itoa(rowIndex), "保管:") f.SetCellStyle("Sheet1", "A"+strconv.Itoa(rowIndex), "G"+strconv.Itoa(rowIndex), styleLeft) buf, err := f.WriteToBuffer() if err != nil { return "", err } fileUrl, err := upload.UploadFileToSeaWeed(string(constvar.FileType_File), uuid.NewV4().String()+".xlsx", buf.Bytes()) if err != nil { logx.Errorf("file upload err: %v", err) return "", err } return fileUrl, nil } func ExportOutputOperation(category constvar.FileTemplateCategory, operation *models.Operation) (string, error) { repositoryLevels := strings.Split(operation.Location.JointName, "/") template, err := models.NewFileTemplateAttachmentSearch().SetCategory(category).First() if err != nil { return "", errors.New("获取模版记录失败:" + err.Error()) } readerCloser, err := http.HttpGetWithReadCloser(template.FileUrl) if err != nil { return "", errors.New("获取模版失败:" + err.Error()) } f, err := excelize.OpenReader(readerCloser) if err != nil { return "", errors.New("读取excel模版失败:" + err.Error()) } readerCloser.Close() defer f.Close() style, _ := f.NewStyle(&excelize.Style{ Border: []excelize.Border{ {Type: "left", Color: "#000000", Style: 1}, {Type: "top", Color: "#000000", Style: 1}, {Type: "bottom", Color: "#000000", Style: 1}, {Type: "right", Color: "#000000", Style: 1}, }, Alignment: &excelize.Alignment{ Horizontal: "center", Vertical: "center", }, }) f.SetCellValue("Sheet1", "C2", repositoryLevels[0]) f.SetCellValue("Sheet1", "H2", operation.WaybillNumber) f.SetCellValue("Sheet1", "K2", operation.AuditDate) f.SetCellValue("Sheet1", "H3", operation.SourceNumber) f.SetCellValue("Sheet1", "K3", operation.Number) f.SetCellValue("Sheet1", "C4", operation.CompanyName) f.SetCellValue("Sheet1", "I4", operation.ReceiverAddr) f.SetCellValue("Sheet1", "C5", operation.ReceiverName) f.SetCellValue("Sheet1", "H5", operation.ReceiverPhone) rowIndex := 8 totalAmount := decimal.NewFromInt(0) totalWeight := decimal.NewFromInt(0) addRows := len(operation.Details) - 1 if addRows > 0 { f.InsertRows("Sheet1", 9, addRows) f.SetCellStyle("Sheet1", "A9", "K"+strconv.Itoa(rowIndex+addRows), style) } for k, v := range operation.Details { productTotalWeight := v.Amount.Mul(v.Product.Weight).Div(decimal.NewFromInt(1000)) f.SetCellValue("Sheet1", "A"+strconv.Itoa(rowIndex), k+1) f.SetCellValue("Sheet1", "B"+strconv.Itoa(rowIndex), v.Product.Name) f.SetCellValue("Sheet1", "H"+strconv.Itoa(rowIndex), v.Amount) f.SetCellValue("Sheet1", "I"+strconv.Itoa(rowIndex), v.Product.Weight) f.SetCellValue("Sheet1", "J"+strconv.Itoa(rowIndex), productTotalWeight) rowIndex++ totalAmount = totalAmount.Add(v.Amount) totalWeight = totalWeight.Add(productTotalWeight) } buf, err := f.WriteToBuffer() if err != nil { return "", err } fileUrl, err := upload.UploadFileToSeaWeed(string(constvar.FileType_File), uuid.NewV4().String()+".xlsx", buf.Bytes()) if err != nil { logx.Errorf("file upload err: %v", err) return "", err } return fileUrl, nil } // GetSupplierList // @Tags 入库/出库 // @Summary 获取物流公司列表 // @Produce application/json // // @Success 200 {object} util.Response "成功" // // @Router /api-wms/v1/operation/getSupplierList [get] func (slf OperationController) GetSupplierList(c *gin.Context) { cli := supplier.NewSupplierServiceClient(init_client.SrmConn) resp, err := cli.GetSupplierList(c, &supplier.SupplierListRequest{Status: 1}) if err != nil { util.ResponseFormat(c, code.RequestParamError, "grpc调用失败:"+err.Error()) return } util.ResponseFormat(c, code.Success, resp.List) } // GetClientList // @Tags 入库/出库 // @Summary 获取物流公司列表 // @Produce application/json // // @Success 200 {object} util.Response "成功" // // @Router /api-wms/v1/operation/getClientList [get] func (slf OperationController) GetClientList(c *gin.Context) { cli := client.NewClientServiceClient(init_client.CrmConn) resp, err := cli.GetClientList(c, &client.ClientListRequest{}) if err != nil { util.ResponseFormat(c, code.RequestParamError, "grpc调用失败:"+err.Error()) return } util.ResponseFormat(c, code.Success, resp.List) } // ListByCondition // @Tags 入库/出库 // @Summary 出入库明细 // @Produce application/json // @Param object body request.OperationCondition true "参数" // @Success 200 {object} util.Response "成功" // @Router /api-wms/v1/operation/listByCondition [post] func (slf OperationController) ListByCondition(c *gin.Context) { var params request.OperationCondition if err := c.BindJSON(¶ms); err != nil { util.ResponseFormat(c, code.RequestParamError, "参数解析失败,数据类型错误:"+err.Error()) return } if !params.PageInfo.Check() { util.ResponseFormat(c, code.RequestParamError, "数据分页信息错误") return } db := mysqlx.GetDB().Table("wms_operation"). Select("wms_operation.id as operation_id,wms_operation.number,wms_operation.base_operation_type,material.id AS product_id," + "material.`name` AS product_name,wms_operation_details.amount,material.unit,wms_operation_details.from_location_id," + "from_location.`name` AS from_location,wms_operation_details.to_location_id,to_location.`name` AS to_location," + "wms_operation.operation_date as date,wms_operation.`status`,material.weight,wms_operation.operation_type_name"). InnerJoins("inner join wms_operation_details ON wms_operation_details.operation_id = wms_operation.id"). InnerJoins("INNER JOIN material ON material.id = wms_operation_details.product_id"). InnerJoins("INNER JOIN wms_location AS from_location ON from_location.id = wms_operation_details.from_location_id"). InnerJoins("INNER JOIN wms_location AS to_location ON to_location.id = wms_operation_details.to_location_id") if params.Condition != "" { db = db.Where("wms_operation.number like ? or wms_operation.source_number like ? or from_location.`name` like ? or to_location.`name` like ? or material.`name` like ? ", "%"+params.Condition+"%", "%"+params.Condition+"%", "%"+params.Condition+"%", "%"+params.Condition+"%", "%"+params.Condition+"%") } if params.WarehouseId != 0 { db = db.Where("warehouse_id = ?", params.WarehouseId) } if params.LocationId != 0 { db = db.Where("location_id = ?", params.LocationId) } var ( records = make([]*response.InventoryHistory, 0) total int64 ) if err := db.Count(&total).Error; err != nil { util.ResponseFormat(c, code.RequestError, "查找失败:"+err.Error()) return } if err := db.Offset((params.Page - 1) * params.PageSize).Limit(params.PageSize).Order("wms_operation.created_at desc").Find(&records).Error; err != nil { util.ResponseFormat(c, code.RequestError, "查找失败:"+err.Error()) return } util.ResponseFormatListWithPage(c, code.Success, records, int(total), params.Page, params.PageSize) } // GetPersonnelList // @Tags 入库/出库 // @Summary 获取人员列表 // @Produce application/json // @Success 200 {object} util.Response "成功" // @Router /api-wms/v1/operation/getPersonnelList [get] func (slf OperationController) GetPersonnelList(c *gin.Context) { cli := inventory_order.NewInventoryOrderServiceClient(init_client.ApsConn) list, err := cli.GetWorkerList(context.Background(), &inventory_order.GetWorkerListRequest{}) if err != nil { util.ResponseFormat(c, code.RequestParamError, "内部错误") logx.Error("grpc调用失败, GetPersonnelList err : " + err.Error()) return } util.ResponseFormat(c, code.Success, list.List) }