mh_goadmin_server/app/admin/models/inventory_allot.go
chenlin 59766f70b3 优化需求:
(1)调拨中(调入门店)改成调拨中(调出门店);
缺陷修复:
(1)店员邀请统计字段未赋值情况修复;
(2)零售退货订单反审核校验库存情况;
(3)供应商列表接口修改,增加翻页逻辑;
新增接口:
(1)新增通过名称模糊查询商品库存详情接口;
2024-07-01 09:44:35 +08:00

1109 lines
39 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 审核库存调拨入库 state1-审核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()
// 判断入参state1-审核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
}