package models import ( "errors" "fmt" "github.com/gin-gonic/gin" orm "go-admin/common/global" "go-admin/logger" "go-admin/tools" "gorm.io/gorm" "math/rand" "time" ) const ( ErpInventoryAllotOrderUnAudit = 1 // 库存调拨-待审核 ErpInventoryAllotOrderWaitSend = 2 // 库存调拨-待发货 ErpInventoryAllotOrderWaitReceive = 3 // 库存调拨-待收货 ErpInventoryAllotOrderFinished = 4 // 库存调拨-已完成 ) // ErpInventoryAllotOrder 库存调拨订单表 type ErpInventoryAllotOrder struct { Model SerialNumber string `json:"serial_number" gorm:"index"` // 单据编号 DeliverStoreId uint32 `json:"deliver_store_id" gorm:"index"` // 调出门店id DeliverStoreName string `json:"deliver_store_name"` // 调出门店名称 ReceiveStoreId uint32 `json:"receive_store_id" gorm:"index"` // 调入门店id ReceiveStoreName string `json:"receive_store_name"` // 调入门店名称 HandlerId uint32 `json:"handler_id" gorm:"index"` // 经手人id HandlerName string `json:"handler_name"` // 经手人名称 MakerTime *time.Time `json:"maker_time"` // 制单时间 MakerId uint32 `json:"maker_id" gorm:"index"` // 制单人id MakerName string `json:"maker_name"` // 制单人名称 AuditTime *time.Time `json:"audit_time"` // 审核时间 AuditorId uint32 `json:"auditor_id" gorm:"index"` // 审核人id AuditorName string `json:"auditor_name"` // 审核人名称 DeliverTime *time.Time `json:"deliver_time"` // 发货时间 ReceiveTime *time.Time `json:"receive_time"` // 收货时间/调入时间 LogisticsNumber string `json:"logistics_number"` // 物流单号 State uint32 `json:"state"` // 1-待审核 2-待发货 3-待收货 4-已完成 TotalCount uint32 `json:"total_count"` // 商品总数量 Remark string `json:"remark"` // 备注 Commodities []ErpInventoryAllotCommodity `json:"commodities" gorm:"-"` // 库存调拨商品信息 } // ErpInventoryAllotCommodity 库存调拨商品信息表 type ErpInventoryAllotCommodity struct { Model AllotOrderId uint32 `json:"allot_order_id" gorm:"index"` // 库存调拨订单表id CommodityId uint32 `json:"commodity_id" gorm:"index"` // 商品id CommodityName string `json:"commodity_name"` // 商品名称 CategoryId uint32 `json:"category_id" gorm:"index"` // 分类id CategoryName string `json:"category_name"` // 分类名称 IMEIType uint32 `json:"imei_type"` // 1-无串码 2-串码 IMEI string `json:"imei"` // 商品串码 Count uint32 `json:"count"` // 数量 Amount float64 `json:"amount"` // 金额(采购价) Remark string `json:"remark"` // 备注 } // InventoryAllotAddReq 新增库存调拨入参 type InventoryAllotAddReq struct { DeliverStoreId uint32 `json:"deliver_store_id" binding:"required"` // 调出门店id DeliverStoreName string `json:"deliver_store_name" binding:"required"` // 调出门店名称 ReceiveStoreId uint32 `json:"receive_store_id" binding:"required"` // 调入门店id ReceiveStoreName string `json:"receive_store_name" binding:"required"` // 调入门店名称 HandlerId uint32 `json:"handler_id" binding:"required"` // 经手人id HandlerName string `json:"handler_name" binding:"required"` // 经手人名称 Commodities []ErpInventoryAllotCommodity `json:"commodities" binding:"required"` // 库存调拨商品信息 } // InventoryAllotEditReq 编辑库存调拨入参 type InventoryAllotEditReq struct { SerialNumber string `json:"serial_number" binding:"required"` // 单据编号 InventoryAllotAddReq } // InventoryAllotAuditReq 审核入参 type InventoryAllotAuditReq struct { SerialNumber string `json:"serial_number" binding:"required"` // 单据编号 State int `json:"state" binding:"required"` // 审核操作: 1-审核 2-取消审核 } // InventoryAllotDeleteReq 删除入参 type InventoryAllotDeleteReq struct { SerialNumber string `json:"serial_number" binding:"required"` // 单据编号 } // InventoryAllotListReq 查询库存调拨列表入参 type InventoryAllotListReq struct { SerialNumber string `json:"serial_number"` // 单据编号 DeliverStoreId uint32 `json:"deliver_store_id"` // 调出门店id ReceiveStoreId uint32 `json:"receive_store_id"` // 调入门店id HandlerId uint32 `json:"handler_id"` // 经手人id State int `json:"state"` // 1-待审核 2-待发货 3-待收货 4-已完成 AuditTimeStart string `json:"audit_time_start"` // 审核开始时间 AuditTimeEnd string `json:"audit_time_end"` // 审核结束时间 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 } // InventoryAllotListResp 查询库存调拨列表出参 type InventoryAllotListResp struct { List []ErpInventoryAllotOrder `json:"list"` Total int `json:"total"` // 总条数 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 } // InventoryAllotDetailReq 查询库存调拨详情入参 type InventoryAllotDetailReq struct { SerialNumber string `json:"serial_number" binding:"required"` // 单据编号 } // InventoryAllotDeliverReq 调拨发货入参 type InventoryAllotDeliverReq struct { SerialNumber string `json:"serial_number" binding:"required"` // 单据编号 LogisticsNumber string `json:"logistics_number"` // 物流单号 Remark string `json:"remark"` // 备注 } // InventoryAllotReceiveReq 调拨收货入参 type InventoryAllotReceiveReq struct { SerialNumber string `json:"serial_number" binding:"required"` // 单据编号 } // 检查新增库存调拨信息 func checkAllotInventoryParam(req *InventoryAllotAddReq, editFlag bool) error { if len(req.Commodities) == 0 { return errors.New("商品信息为空") } noIMEICommodityMap := make(map[uint32]bool, 0) for _, item := range req.Commodities { // 校验数量 if item.Count <= 0 { return fmt.Errorf("商品[%s]数量[%d]错误,需大于0", item.CommodityName, item.Count) } else { if item.Count != 1 && item.IMEIType != NoIMEICommodity { // 串码商品数量不为1,则报错 return fmt.Errorf("串码商品[%s]数量[%d]错误,需为1", item.CommodityName, item.Count) } } // 校验编辑订单时是否有传商品ID if editFlag { if item.ID == 0 { return fmt.Errorf("商品[%s]ID为空", item.CommodityName) } } // 校验串码类型 switch item.IMEIType { case 1, 2, 3: // 串码商品校验串码是否为空 if item.IMEIType != NoIMEICommodity && item.IMEI == "" { return fmt.Errorf("[%s]是串码商品,其串码不能为空", item.CommodityName) } default: return fmt.Errorf("商品[%s]串码类型[%d]错误", item.CommodityName, item.IMEIType) } // 校验非串码商品是否重复 if item.IMEIType == NoIMEICommodity { _, ok := noIMEICommodityMap[item.CommodityId] if !ok { noIMEICommodityMap[item.CommodityId] = true } else { return fmt.Errorf("商品[%s]重复,相同的非串码商品只能有1条数据", item.CommodityName) } } // 检查调出门店是否有对应商品,调拨数量是否 < 商品库存数量 var stockCount int64 var err error if item.IMEIType == NoIMEICommodity { // 非串码商品 err = orm.Eloquent.Table("erp_stock_commodity"). Where("erp_commodity_id = ? AND store_id = ? AND state = 1", item.CommodityId, req.DeliverStoreId). Count(&stockCount).Error } else { // 串码商品 err = orm.Eloquent.Table("erp_stock_commodity"). Where("erp_commodity_id = ? AND store_id = ? AND state = 1 AND imei = ?", item.CommodityId, req.DeliverStoreId, item.IMEI).Count(&stockCount).Error } if err != nil { return errors.New("查询商品库存失败," + err.Error()) } if stockCount == 0 { return fmt.Errorf("商品[%s]库存数量为0", item.CommodityName) } if stockCount < int64(item.Count) { return fmt.Errorf("商品[%s]库存数量[%d]少于调拨数量[%d]", item.CommodityName, stockCount, item.Count) } } return nil } // newAllotInventorySn 生成库存调拨订单号 func newAllotInventorySn() string { nowTime := time.Now() rand.Seed(nowTime.UnixNano()) max := 1 for { if max > 5 { logger.Error("create NewProductInventorySn err") return "" } random := rand.Intn(9000) + 1000 sn := fmt.Sprintf("%s%d", nowTime.Format("060102"), random) exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_inventory_allot_order "+ "WHERE serial_number='%s'", "db"+sn)) if err != nil { logger.Error("exist sn err") } if !exist { return "db" + sn } max++ } } // AddInventoryAllot 新增库存调拨 func AddInventoryAllot(req *InventoryAllotAddReq, sysUser *SysUser) (*ErpInventoryAllotOrder, error) { // 检查库存调拨信息 if err := checkAllotInventoryParam(req, false); err != nil { return nil, err } var err error nowTime := time.Now() // 计算库存调拨的总数量 var nTotalCount uint32 for i, _ := range req.Commodities { nTotalCount += req.Commodities[i].Count // 数量 } // 组合库存调拨订单数据 inventoryAllotOrder := &ErpInventoryAllotOrder{ SerialNumber: newAllotInventorySn(), DeliverStoreId: req.DeliverStoreId, DeliverStoreName: req.DeliverStoreName, ReceiveStoreId: req.ReceiveStoreId, ReceiveStoreName: req.ReceiveStoreName, HandlerId: req.HandlerId, HandlerName: req.HandlerName, MakerTime: &nowTime, MakerId: uint32(sysUser.UserId), MakerName: sysUser.NickName, State: ErpInventoryAllotOrderUnAudit, TotalCount: nTotalCount, } // 创建库存调拨订单 begin := orm.Eloquent.Begin() err = begin.Create(inventoryAllotOrder).Error if err != nil { begin.Rollback() logger.Error("create allot order err:", logger.Field("err", err)) return nil, err } // 创建库存调拨商品信息,添加库存调拨订单id for i, _ := range req.Commodities { req.Commodities[i].AllotOrderId = inventoryAllotOrder.ID // 查询商品信息 commodityInfo, err := GetCommodity(req.Commodities[i].CommodityId) if err != nil { logger.Error("SetCategory err:", logger.Field("err", err)) return nil, err } req.Commodities[i].CategoryId = commodityInfo.ErpCategoryId req.Commodities[i].CategoryName = commodityInfo.ErpCategoryName // 如果是非串码,且数量大于1,则进行拆分 if req.Commodities[i].IMEIType == 1 && req.Commodities[i].Count > 1 { nCount := req.Commodities[i].Count for j := 0; j < int(nCount); j++ { req.Commodities[i].ID = 0 req.Commodities[i].Count = 1 err = begin.Create(&req.Commodities[i]).Error if err != nil { begin.Rollback() logger.Error("create allot commodity err:", logger.Field("err", err)) return nil, err } } } else { // 普通串码商品则只有1条数据 err = begin.Create(&req.Commodities[i]).Error if err != nil { begin.Rollback() logger.Error("create allot commodity err:", logger.Field("err", err)) return nil, err } } } err = begin.Commit().Error if err != nil { begin.Rollback() logger.Error("commit allot commodity err:", logger.Field("err", err)) return nil, err } return inventoryAllotOrder, nil } // EditAllotInventory 编辑库存调拨 func EditAllotInventory(req *InventoryAllotEditReq) (*ErpInventoryAllotOrder, error) { // 查询订单信息 var inventoryAllotOrder ErpInventoryAllotOrder err := orm.Eloquent.Table("erp_inventory_allot_order").Where("serial_number = ?", req.SerialNumber). Find(&inventoryAllotOrder).Error if err != nil { logger.Error("allot order err:", logger.Field("err", err)) return nil, err } // 未找到订单 if inventoryAllotOrder.ID == 0 { return nil, fmt.Errorf("未找到该订单,请检查单据编号[%s]", req.SerialNumber) } if inventoryAllotOrder.State != ErpInventoryAllotOrderUnAudit { // 只有待审核的订单才能编辑 return nil, errors.New("订单不是待审核状态") } // 检查库存调拨信息 if err = checkAllotInventoryParam(&req.InventoryAllotAddReq, true); err != nil { return nil, err } // 计算库存调拨的总数量 var nTotalCount uint32 for i, _ := range req.Commodities { nTotalCount += req.Commodities[i].Count // 数量 } begin := orm.Eloquent.Begin() // 1-更新库存调拨信息 inventoryAllotOrder.DeliverStoreId = req.DeliverStoreId inventoryAllotOrder.DeliverStoreName = req.DeliverStoreName inventoryAllotOrder.ReceiveStoreId = req.ReceiveStoreId inventoryAllotOrder.ReceiveStoreName = req.ReceiveStoreName inventoryAllotOrder.HandlerId = req.HandlerId inventoryAllotOrder.HandlerName = req.HandlerName inventoryAllotOrder.TotalCount = nTotalCount err = begin.Model(&ErpInventoryAllotOrder{}).Where("id = ?", inventoryAllotOrder.ID). Updates(inventoryAllotOrder).Error if err != nil { begin.Rollback() logger.Error("update allot order err:", logger.Field("err", err)) return nil, err } // 2-更新库存调拨商品表 err = updateAllotCommodityData(begin, inventoryAllotOrder.ID, req) if err != nil { begin.Rollback() logger.Error("update erp_inventory_allot_commodity err:", logger.Field("err", err)) return nil, err } err = begin.Commit().Error if err != nil { begin.Rollback() logger.Error("commit err:", logger.Field("err", err)) return nil, err } return &inventoryAllotOrder, nil } // updateAllotCommodityData 更新库存调拨商品信息 func updateAllotCommodityData(gdb *gorm.DB, orderId uint32, req *InventoryAllotEditReq) error { // 查询现有的零售订单信息 var commodities []ErpInventoryAllotCommodity err := orm.Eloquent.Table("erp_inventory_allot_commodity").Where("allot_order_id = ?", orderId).Find(&commodities).Error if err != nil { logger.Error("query erp_inventory_allot_commodity err:", logger.Field("err", err)) return err } // 删除所有的商品信息 if len(commodities) != 0 { err = gdb.Delete(&commodities).Error if err != nil { logger.Error("更新商品订单信息-删除 error") return errors.New("操作失败:" + err.Error()) } } // 新建所有的商品信息 for i, _ := range req.Commodities { req.Commodities[i].AllotOrderId = orderId // 查询商品信息 commodityInfo, err := GetCommodity(req.Commodities[i].CommodityId) if err != nil { logger.Error("SetCategory err:", logger.Field("err", err)) return err } req.Commodities[i].CategoryId = commodityInfo.ErpCategoryId req.Commodities[i].CategoryName = commodityInfo.ErpCategoryName req.Commodities[i].CommodityName = commodityInfo.Name // 如果是非串码,且数量大于1,则进行拆分 if req.Commodities[i].IMEIType == 1 && req.Commodities[i].Count > 1 { nCount := req.Commodities[i].Count for j := 0; j < int(nCount); j++ { req.Commodities[i].ID = 0 req.Commodities[i].Count = 1 err = gdb.Create(&req.Commodities[i]).Error if err != nil { logger.Error("create allot commodity err:", logger.Field("err", err)) return err } } } else { // 普通串码商品则只有1条数据 err = gdb.Create(&req.Commodities[i]).Error if err != nil { logger.Error("create allot commodity err:", logger.Field("err", err)) return err } } } return nil } // List 查询采购订单列表 func (m *InventoryAllotListReq) List(c *gin.Context) (*InventoryAllotListResp, error) { resp := &InventoryAllotListResp{ PageIndex: m.PageIndex, PageSize: m.PageSize, } page := m.PageIndex - 1 if page < 0 { page = 0 } if m.PageSize == 0 { m.PageSize = 10 } qs := orm.Eloquent.Table("erp_inventory_allot_order") // 非管理员才判断所属门店 if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { sysUser, err := GetSysUserByCtx(c) if err != nil { return nil, err } // 返回sysUser未过期的门店id列表 storeList := GetValidStoreIDs(sysUser.StoreData) if m.DeliverStoreId != 0 || m.ReceiveStoreId != 0 { if m.DeliverStoreId != 0 { if !Contains(storeList, m.DeliverStoreId) { return nil, errors.New("您没有该门店权限") } } if m.ReceiveStoreId != 0 { if !Contains(storeList, m.ReceiveStoreId) { return nil, errors.New("您没有该门店权限") } } } else { if len(storeList) > 0 { if len(storeList) == 1 { qs = qs.Where("deliver_store_id = ? or receive_store_id=?", storeList[0], storeList[0]) } else { qs = qs.Where("deliver_store_id IN (?) or receive_store_id IN (?)", storeList, storeList) } } else { return nil, errors.New("用户未绑定门店") } } } if m.SerialNumber != "" { qs = qs.Where("serial_number=?", m.SerialNumber) } else { if m.DeliverStoreId != 0 { qs = qs.Where("deliver_store_id=?", m.DeliverStoreId) } if m.ReceiveStoreId != 0 { qs = qs.Where("receive_store_id=?", m.ReceiveStoreId) } if m.HandlerId != 0 { qs = qs.Where("handler_id=?", m.HandlerId) } if m.State != 0 { qs = qs.Where("state=?", m.State) } if m.AuditTimeStart != "" { parse, err := time.Parse(QueryTimeFormat, m.AuditTimeStart) if err != nil { logger.Errorf("AllotInventoryList err:", err) return nil, err } qs = qs.Where("audit_time > ?", parse) } if m.AuditTimeEnd != "" { parse, err := time.Parse(QueryTimeFormat, m.AuditTimeEnd) if err != nil { logger.Errorf("AllotInventoryList err:", err) return nil, err } qs = qs.Where("audit_time < ?", parse) } } var count int64 err := qs.Count(&count).Error if err != nil { logger.Error("count err:", logger.Field("err", err)) return resp, err } resp.Total = int(count) var orders []ErpInventoryAllotOrder err = qs.Order("id DESC").Offset(page * m.PageSize).Limit(m.PageSize).Find(&orders).Error if err != nil && err != RecordNotFound { logger.Error("allot list err:", logger.Field("err", err)) return resp, err } // 校验时间,如果为01-01-01 08:05,则赋值为空 for i, v := range orders { if v.MakerTime != nil && v.MakerTime.IsZero() { orders[i].MakerTime = nil } if v.AuditTime != nil && v.AuditTime.IsZero() { orders[i].AuditTime = nil } } resp.List = orders return resp, nil } // AuditAllotInventory 审核库存调拨入库 state:1-审核,2-取消审核 func AuditAllotInventory(req *InventoryAllotAuditReq, c *gin.Context) error { // 查询订单信息 var inventoryAllotOrder ErpInventoryAllotOrder err := orm.Eloquent.Table("erp_inventory_allot_order").Where("serial_number = ?", req.SerialNumber). Find(&inventoryAllotOrder).Error if err != nil { logger.Error("order err:", logger.Field("err", err)) return err } sysUser, err := GetSysUserByCtx(c) if err != nil { logger.Error("sys user err:", logger.Field("err", err)) return err } // 校验入参门店是否包含在用户所有门店中,是否过期 if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { if !CheckUserStore(inventoryAllotOrder.DeliverStoreId, sysUser) { return errors.New("操作失败:您没有该门店权限") } } if inventoryAllotOrder.ID == 0 { return errors.New("未查询到订单信息") } begin := orm.Eloquent.Begin() // 判断入参state:1-审核,2-取消审核 orderState := 0 switch req.State { case 1: // 1-审核:待审核状态可以审核 if inventoryAllotOrder.State != ErpInventoryAllotOrderUnAudit { // 订单不是未审核状态 return errors.New("订单已审核,无需再次审核") } orderState = ErpInventoryAllotOrderWaitSend // 更新商品的库存状态 err = allotAuditAndUpdateStock(begin, inventoryAllotOrder) if err != nil { begin.Rollback() logger.Error("ProductAuditAndUpdateStock err:", logger.Field("err", err)) return fmt.Errorf("审核失败,%s", err.Error()) } case 2: // 2-取消审核 orderState = ErpInventoryAllotOrderUnAudit if inventoryAllotOrder.State == ErpInventoryAllotOrderUnAudit { // 订单未审核 return errors.New("订单是未审核状态,无需取消审核") } // 更新商品的库存状态 err = cancelAllotAuditAndUpdateStock(begin, inventoryAllotOrder) if err != nil { begin.Rollback() return fmt.Errorf("取消审核失败,%s", err.Error()) } default: logger.Error("order err, req.State is:", logger.Field("req.State", req.State)) return errors.New("参数有误") } // 更新库存调拨订单表 err = begin.Table("erp_inventory_allot_order").Where("id = ?", inventoryAllotOrder.ID). Updates(map[string]interface{}{ "state": orderState, "auditor_id": sysUser.UserId, "audit_time": time.Now(), "auditor_name": sysUser.NickName, }).Error if err != nil { begin.Rollback() logger.Error("update erp_inventory_allot_order err:", logger.Field("err", err)) return err } err = begin.Commit().Error if err != nil { begin.Rollback() logger.Errorf("commit err:", err) return err } return nil } // allotAuditAndUpdateStock 库存调拨审核后更新库存信息 func allotAuditAndUpdateStock(gdb *gorm.DB, allotOrder ErpInventoryAllotOrder) error { // 查询库存调拨商品信息 var commodities []ErpInventoryAllotCommodity err := orm.Eloquent.Table("erp_inventory_allot_commodity").Where("allot_order_id = ?", allotOrder.ID).Find(&commodities).Error if err != nil { logger.Error("query erp_inventory_allot_commodity err:", logger.Field("err", err)) return err } if len(commodities) == 0 { return errors.New("未查询到商品信息") } // 遍历库存调拨商品信息,将商品id相同的非串码商品进行合并,数量累加即可 trimCommodities := MergeCommodities(commodities) // 遍历库存调拨商品信息 for _, v := range trimCommodities { var stockCommodity []ErpStockCommodity err = orm.Eloquent.Table("erp_stock_commodity").Where("erp_commodity_id = ? AND store_id = ? "+ "AND state = ? AND imei = ?", v.CommodityId, allotOrder.DeliverStoreId, InStock, v.IMEI). Order("first_stock_time ASC").Find(&stockCommodity).Error if err != nil { return fmt.Errorf("审核失败,查询商品库存失败:[%s]", err.Error()) } if len(stockCommodity) == 0 { return fmt.Errorf("审核失败,未找到商品库存信息") } if v.Count > uint32(len(stockCommodity)) { return fmt.Errorf("审核失败,商品[%s]库存数量[%d]少于调拨数量[%d]", v.CommodityName, len(stockCommodity), v.Count) } // 更新库存商品表商品状态为:调拨中;调拨中归属门店不更新,只有完成调拨后才更新到调入门店 var nAmount float64 for i := 0; i < int(v.Count); i++ { //stockCommodity[i].StoreId = allotOrder.ReceiveStoreId //stockCommodity[i].StoreName = allotOrder.ReceiveStoreName stockCommodity[i].State = InAllot if err := gdb.Model(&ErpStockCommodity{}).Where("id = ?", stockCommodity[i].ID). Updates(stockCommodity[i]).Error; err != nil { return fmt.Errorf("审核失败,更新商品库存失败:%s", err.Error()) } nAmount += stockCommodity[i].WholesalePrice } if v.Count != 0 { nAmount = nAmount / float64(v.Count) // todo 目前只能计算平均采购价 } // 更新库存调拨商品信息表的调拨金额 err = gdb.Table("erp_inventory_allot_commodity").Where("allot_order_id = ? and commodity_id = ?", v.AllotOrderId, v.CommodityId). Updates(map[string]interface{}{ "amount": nAmount, }).Error if err != nil { logger.Errorf("update erp_inventory_allot_commodity amount err:", err) return err } // 更新库存商品数量 err = gdb.Exec(fmt.Sprintf( "UPDATE erp_stock SET count=count-%d WHERE store_id=%d AND erp_commodity_id=%d", v.Count, allotOrder.DeliverStoreId, v.CommodityId)).Error if err != nil { logger.Errorf("update stock err:", err) return err } // 更新调出门店的库存调拨数量 err = gdb.Exec(fmt.Sprintf( "UPDATE erp_stock SET dispatch_count = dispatch_count+%d WHERE store_id=%d AND erp_commodity_id=%d", v.Count, allotOrder.DeliverStoreId, v.CommodityId)).Error if err != nil { logger.Errorf("update stock err:", err) return err } //// 更新调入门店的库存调拨数量 //exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_stock WHERE store_id=%d AND erp_commodity_id=%d", // allotOrder.ReceiveStoreId, v.CommodityId)) //if err != nil { // logger.Errorf("exist err:", err) // return err //} //if exist { // err = gdb.Exec(fmt.Sprintf( // "UPDATE erp_stock SET dispatch_count = dispatch_count+%d WHERE store_id=%d AND erp_commodity_id=%d", // v.Count, allotOrder.ReceiveStoreId, v.CommodityId)).Error // if err != nil { // logger.Errorf("update stock err:", err) // return err // } //} else { // stock := &ErpStock{ // StoreId: allotOrder.ReceiveStoreId, // StoreName: allotOrder.ReceiveStoreName, // ErpCommodityId: v.CommodityId, // ErpCommodityName: v.CommodityName, // ErpCategoryId: stockCommodity[0].ErpCategoryId, // ErpCategoryName: stockCommodity[0].ErpCategoryName, // CommoditySerialNumber: stockCommodity[0].CommoditySerialNumber, // IMEIType: v.IMEIType, // RetailPrice: stockCommodity[0].RetailPrice, // MinRetailPrice: stockCommodity[0].MinRetailPrice, // Count: 0, // DispatchCount: v.Count, // } // err = gdb.Create(stock).Error // if err != nil { // logger.Errorf("create stock err:", err) // return err // } //} } return nil } // MergeCommodities 遍历库存调拨商品信息,将商品id相同的非串码商品进行合并,数量累加即可 func MergeCommodities(commodities []ErpInventoryAllotCommodity) []ErpInventoryAllotCommodity { // 用于存储合并后的商品信息 mergedCommodities := make([]ErpInventoryAllotCommodity, 0) // 用于记录无串码商品的合并信息 commodityMap := make(map[uint32]*ErpInventoryAllotCommodity) for _, commodity := range commodities { // 只处理无串码商品 if commodity.IMEIType == 1 { if existing, found := commodityMap[commodity.CommodityId]; found { // 如果相同商品 ID 的无串码商品已存在,则数量累加 existing.Count += commodity.Count commodityMap[commodity.CommodityId] = existing } else { // 否则,加入到 commodityMap 中 newCommodity := commodity commodityMap[commodity.CommodityId] = &newCommodity } } else { // 对于有串码的商品,直接加入到合并后的列表中 mergedCommodities = append(mergedCommodities, commodity) } } // 将合并后的无串码商品加入到合并后的列表中 for _, commodity := range commodityMap { mergedCommodities = append(mergedCommodities, *commodity) } return mergedCommodities } // MergeChangeCommodities 遍历库存变动商品信息,将商品id相同的非串码商品进行合并,数量累加即可 func MergeChangeCommodities(commodities []ErpInventoryChangeCommodity) []ErpInventoryChangeCommodity { // 用于存储合并后的商品信息 mergedCommodities := make([]ErpInventoryChangeCommodity, 0) // 用于记录无串码商品的合并信息 commodityMap := make(map[uint32]*ErpInventoryChangeCommodity) for _, commodity := range commodities { // 只处理无串码商品 if commodity.IMEIType == 1 { if existing, found := commodityMap[commodity.CommodityId]; found { // 如果相同商品 ID 的无串码商品已存在,则数量累加 existing.Count += commodity.Count commodityMap[commodity.CommodityId] = existing } else { // 否则,加入到 commodityMap 中 newCommodity := commodity commodityMap[commodity.CommodityId] = &newCommodity } } else { // 对于有串码的商品,直接加入到合并后的列表中 mergedCommodities = append(mergedCommodities, commodity) } } // 将合并后的无串码商品加入到合并后的列表中 for _, commodity := range commodityMap { mergedCommodities = append(mergedCommodities, *commodity) } return mergedCommodities } // cancelAllotAuditAndUpdateStock 库存调拨反审核后更新库存信息 func cancelAllotAuditAndUpdateStock(gdb *gorm.DB, allotOrder ErpInventoryAllotOrder) error { // 查询库存调拨商品信息 var commodities []ErpInventoryAllotCommodity if err := orm.Eloquent.Table("erp_inventory_allot_commodity").Where("allot_order_id = ?", allotOrder.ID).Find(&commodities).Error; err != nil { logger.Error("query erp_inventory_allot_commodity err:", logger.Field("err", err)) return err } if len(commodities) == 0 { return errors.New("取消审核失败,未查询到库存调拨商品信息") } // 遍历库存调拨商品信息,将商品id相同的非串码商品进行合并,数量累加即可 trimCommodities := MergeCommodities(commodities) switch allotOrder.State { // 2 待发货:状态改为待审核,调拨中库存恢复为在库 // 3 待收货:状态改为待审核,调拨中库存恢复为在库 case ErpInventoryAllotOrderWaitSend, ErpInventoryAllotOrderWaitReceive: // 遍历库存调拨商品信息 for _, v := range trimCommodities { var stockCommodity []ErpStockCommodity err := orm.Eloquent.Table("erp_stock_commodity").Where("erp_commodity_id = ? AND store_id = ? "+ "AND state = ? AND imei = ?", v.CommodityId, allotOrder.ReceiveStoreId, InAllot, v.IMEI). Find(&stockCommodity).Error if err != nil { return fmt.Errorf("查询商品库存失败:[%s]", err.Error()) } if len(stockCommodity) == 0 { return fmt.Errorf("未找到商品库存信息") } // 更新库存商品表商品状态为:在库 for i := 0; i < int(v.Count); i++ { //stockCommodity[i].StoreId = allotOrder.DeliverStoreId //stockCommodity[i].StoreName = allotOrder.DeliverStoreName stockCommodity[i].State = InStock err = gdb.Model(&ErpStockCommodity{}).Where("id = ?", stockCommodity[i].ID). Updates(stockCommodity[i]).Error if err != nil { return fmt.Errorf("更新商品库存失败:%s", err.Error()) } } // 更新库存商品数量 err = gdb.Exec(fmt.Sprintf( "UPDATE erp_stock SET count=count+%d WHERE store_id=%d AND erp_commodity_id=%d", v.Count, allotOrder.DeliverStoreId, v.CommodityId)).Error if err != nil { logger.Errorf("update stock err:", err) return fmt.Errorf("更新库存商品数量失败:%s", err.Error()) } // 更新库存商品调拨数量 err = gdb.Exec(fmt.Sprintf( "UPDATE erp_stock SET dispatch_count = dispatch_count-%d WHERE store_id=%d AND erp_commodity_id=%d", v.Count, allotOrder.DeliverStoreId, v.CommodityId)).Error if err != nil { logger.Errorf("update stock err:", err) return err } } case ErpInventoryAllotOrderFinished: // 已完成:状态改为待审核,调入门店对应商品的库存更新到调出门店,如果库存不足则报错 // 遍历库存调拨商品信息 for _, v := range trimCommodities { var stockCommodity []ErpStockCommodity err := orm.Eloquent.Table("erp_stock_commodity").Where("erp_commodity_id = ? AND store_id = ? "+ "AND state = ? AND imei = ?", v.CommodityId, allotOrder.ReceiveStoreId, InStock, v.IMEI). Find(&stockCommodity).Error if err != nil { return fmt.Errorf("查询商品库存失败:[%s]", err.Error()) } if len(stockCommodity) == 0 { return fmt.Errorf("未找到商品库存信息") } if v.Count > uint32(len(stockCommodity)) { return fmt.Errorf("商品[%s]库存数量[%d]不足", v.CommodityName, len(stockCommodity)) } // 更新库存商品表商品状态为:在库 for i := 0; i < int(v.Count); i++ { stockCommodity[i].StoreId = allotOrder.DeliverStoreId stockCommodity[i].StoreName = allotOrder.DeliverStoreName stockCommodity[i].State = InStock err = gdb.Model(&ErpStockCommodity{}).Where("id = ?", stockCommodity[i].ID). Updates(stockCommodity[i]).Error if err != nil { return fmt.Errorf("更新商品库存失败:%s", err.Error()) } } // 更新库存商品数量 // 调入门店减去对应调入的数量 err = gdb.Exec(fmt.Sprintf( "UPDATE erp_stock SET count=count-%d WHERE store_id=%d AND erp_commodity_id=%d", v.Count, allotOrder.ReceiveStoreId, v.CommodityId)).Error if err != nil { logger.Errorf("update stock err:", err) return err } // 调出门店加上对应调出的数量 err = gdb.Exec(fmt.Sprintf( "UPDATE erp_stock SET count=count+%d WHERE store_id=%d AND erp_commodity_id=%d", v.Count, allotOrder.DeliverStoreId, v.CommodityId)).Error if err != nil { logger.Errorf("update stock err:", err) return err } } default: return fmt.Errorf("订单当前状态未知[%d]", allotOrder.State) } // 更新库存调拨订单信息 allotOrder.State = ErpInventoryAllotOrderUnAudit err := gdb.Model(&ErpInventoryAllotOrder{}).Where("id = ?", allotOrder.ID). Updates(allotOrder).Error if err != nil { return fmt.Errorf("更新库存调拨订单失败:%s", err.Error()) } return nil } // DeliverAllotInventory 调拨发货 func DeliverAllotInventory(req *InventoryAllotDeliverReq, c *gin.Context) error { // 查询订单信息 var inventoryAllotOrder ErpInventoryAllotOrder err := orm.Eloquent.Table("erp_inventory_allot_order").Where("serial_number = ?", req.SerialNumber). Find(&inventoryAllotOrder).Error if err != nil { logger.Error("order err:", logger.Field("err", err)) return err } sysUser, err := GetSysUserByCtx(c) if err != nil { logger.Error("sys user err:", logger.Field("err", err)) return err } // 校验入参门店是否包含在用户所有门店中,是否过期 if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { if !CheckUserStore(inventoryAllotOrder.DeliverStoreId, sysUser) { return errors.New("操作失败:您没有该门店权限") } } if inventoryAllotOrder.ID == 0 { return errors.New("未查询到订单信息") } if inventoryAllotOrder.State != ErpInventoryAllotOrderWaitSend { return errors.New("订单不是待发货状态") } nowTime := time.Now() inventoryAllotOrder.LogisticsNumber = req.LogisticsNumber inventoryAllotOrder.Remark = req.Remark inventoryAllotOrder.State = ErpInventoryAllotOrderWaitReceive inventoryAllotOrder.DeliverTime = &nowTime err = orm.Eloquent.Model(&ErpInventoryAllotOrder{}).Where("id = ?", inventoryAllotOrder.ID). Updates(inventoryAllotOrder).Error if err != nil { return err } return nil } // ReceiveAllotInventory 调拨收货 func ReceiveAllotInventory(req *InventoryAllotReceiveReq, c *gin.Context) error { // 查询订单信息 var inventoryAllotOrder ErpInventoryAllotOrder if err := orm.Eloquent.Table("erp_inventory_allot_order").Where("serial_number = ?", req.SerialNumber). Find(&inventoryAllotOrder).Error; err != nil { logger.Error("order err:", logger.Field("err", err)) return err } sysUser, err := GetSysUserByCtx(c) if err != nil { logger.Error("sys user err:", logger.Field("err", err)) return err } // 校验入参门店是否包含在用户所有门店中,是否过期 if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { if !CheckUserStore(inventoryAllotOrder.ReceiveStoreId, sysUser) { return errors.New("操作失败:您没有该门店权限") } } if inventoryAllotOrder.ID == 0 { return errors.New("未查询到订单信息") } if inventoryAllotOrder.State != ErpInventoryAllotOrderWaitReceive { return errors.New("订单不是待收货状态") } // 查询库存调拨商品信息 var commodities []ErpInventoryAllotCommodity if err := orm.Eloquent.Table("erp_inventory_allot_commodity").Where("allot_order_id = ?", inventoryAllotOrder.ID).Find(&commodities).Error; err != nil { logger.Error("query erp_inventory_allot_commodity err:", logger.Field("err", err)) return err } if len(commodities) == 0 { return errors.New("未查询到库存调拨商品信息") } // 遍历库存调拨商品信息,将商品id相同的非串码商品进行合并,数量累加即可 trimCommodities := MergeCommodities(commodities) begin := orm.Eloquent.Begin() // 遍历库存调拨商品信息 for _, v := range trimCommodities { var stockCommodity []ErpStockCommodity err := orm.Eloquent.Table("erp_stock_commodity").Where("erp_commodity_id = ? AND store_id = ? "+ "AND state = ? AND imei = ?", v.CommodityId, inventoryAllotOrder.DeliverStoreId, InAllot, v.IMEI). Find(&stockCommodity).Error if err != nil { return fmt.Errorf("查询商品库存失败:[%s]", err.Error()) } if len(stockCommodity) == 0 { return fmt.Errorf("未找到商品库存信息") } // 更新库存商品表商品状态为:在库,同时更新所属门店为调入门店 for i := 0; i < int(v.Count); i++ { stockCommodity[i].StoreId = inventoryAllotOrder.ReceiveStoreId stockCommodity[i].StoreName = inventoryAllotOrder.ReceiveStoreName stockCommodity[i].State = InStock stockCommodity[i].StockTime = time.Now() err = begin.Model(&ErpStockCommodity{}).Where("id = ?", stockCommodity[i].ID). Updates(stockCommodity[i]).Error if err != nil { begin.Rollback() return fmt.Errorf("更新商品库存失败:%s", err.Error()) } } // 更新库存商品数量 err = begin.Exec(fmt.Sprintf( "UPDATE erp_stock SET count=count+%d WHERE store_id=%d AND erp_commodity_id=%d", v.Count, inventoryAllotOrder.ReceiveStoreId, v.CommodityId)).Error if err != nil { begin.Rollback() logger.Errorf("update stock err:", err) return err } // 更新库存商品调拨数量 err = begin.Exec(fmt.Sprintf( "UPDATE erp_stock SET dispatch_count = dispatch_count-%d WHERE store_id=%d AND erp_commodity_id=%d", v.Count, inventoryAllotOrder.DeliverStoreId, v.CommodityId)).Error if err != nil { logger.Errorf("update stock err:", err) return err } } // 更新调拨订单的状态为:已完成 nowTime := time.Now() inventoryAllotOrder.State = ErpInventoryAllotOrderFinished inventoryAllotOrder.ReceiveTime = &nowTime err = begin.Model(&ErpInventoryAllotOrder{}).Where("id = ?", inventoryAllotOrder.ID). Updates(inventoryAllotOrder).Error if err != nil { begin.Rollback() return err } err = begin.Commit().Error if err != nil { begin.Rollback() logger.Errorf("commit err:", err) return err } return nil }