736 lines
24 KiB
Go
736 lines
24 KiB
Go
package service
|
||
|
||
import (
|
||
"fmt"
|
||
"psi/constant"
|
||
"psi/database"
|
||
"psi/models"
|
||
systemReq "psi/models/request"
|
||
systemRes "psi/models/response"
|
||
"psi/utils"
|
||
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
type InventoryService struct{}
|
||
|
||
// GetInventoryList 获取库存汇总列表
|
||
func (s *InventoryService) GetInventoryList(req systemReq.GetInventoryListRequest, db ...*gorm.DB) (*systemRes.InventoryListResponse, error) {
|
||
databaseConn := database.OptionalDB(db...)
|
||
|
||
if req.Page < 1 {
|
||
req.Page = 1
|
||
}
|
||
if req.PageSize < 1 || req.PageSize > 100 {
|
||
req.PageSize = 20
|
||
}
|
||
|
||
query := databaseConn.Table("inventory").
|
||
Select(`
|
||
inventory.product_id,
|
||
p.name as product_name,
|
||
p.barcode,
|
||
p.appearance,
|
||
p.price,
|
||
inventory.warehouse_id,
|
||
w.name as warehouse_name,
|
||
w.code as warehouse_code,
|
||
l.code as location_code,
|
||
i.quantity as total_quantity,
|
||
i.locked_quantity as locked_quantity
|
||
`).
|
||
Joins("LEFT JOIN inventory_detail i ON i.product_id = inventory.product_id").
|
||
Joins("LEFT JOIN location l ON l.id = i.location_id").
|
||
Joins("LEFT JOIN product p ON inventory.product_id = p.id AND p.is_del = 0").
|
||
Joins("LEFT JOIN warehouse w ON inventory.warehouse_id = w.id AND w.is_del = 0").
|
||
Where("inventory.is_del = ?", 0)
|
||
|
||
if req.ProductId > 0 {
|
||
query = query.Where("inventory.product_id = ?", req.ProductId)
|
||
}
|
||
if req.WarehouseID > 0 {
|
||
query = query.Where("inventory.warehouse_id = ?", req.WarehouseID)
|
||
}
|
||
if req.LocationID > 0 {
|
||
query = query.Where("i.location_id = ?", req.LocationID)
|
||
}
|
||
if req.ISBN != "" {
|
||
query = query.Where("p.barcode LIKE ?", "%"+req.ISBN+"%")
|
||
}
|
||
if req.Name != "" {
|
||
query = query.Where("p.name LIKE ?", "%"+req.Name+"%")
|
||
}
|
||
|
||
var total int64
|
||
if err := query.Count(&total).Error; err != nil {
|
||
return nil, utils.NewError("查询总数失败")
|
||
}
|
||
|
||
if total == 0 {
|
||
return &systemRes.InventoryListResponse{
|
||
List: []systemRes.InventoryItem{},
|
||
Total: 0,
|
||
Page: req.Page,
|
||
PageSize: req.PageSize,
|
||
}, nil
|
||
}
|
||
|
||
type InventorySummary struct {
|
||
ProductID int64 `gorm:"column:product_id"`
|
||
ProductName string `gorm:"column:product_name"`
|
||
Appearance int64 `gorm:"column:appearance"`
|
||
Barcode string `gorm:"column:barcode"`
|
||
Price int64 `gorm:"column:price"`
|
||
WarehouseID int64 `gorm:"column:warehouse_id"`
|
||
WarehouseName string `gorm:"column:warehouse_name"`
|
||
WarehouseCode string `gorm:"column:warehouse_code"`
|
||
LocationCode string `gorm:"column:location_code"`
|
||
TotalQuantity int64 `gorm:"column:total_quantity"`
|
||
LockedQuantity int64 `gorm:"column:locked_quantity"`
|
||
}
|
||
|
||
var summaries []InventorySummary
|
||
offset := (req.Page - 1) * req.PageSize
|
||
if err := query.Order("i.updated_at DESC").Offset(offset).Limit(req.PageSize).Find(&summaries).Error; err != nil {
|
||
return nil, utils.NewError("查询库存汇总列表失败")
|
||
}
|
||
|
||
items := make([]systemRes.InventoryItem, 0, len(summaries))
|
||
for _, summary := range summaries {
|
||
items = append(items, systemRes.ConvertInventoryToItem(
|
||
summary.ProductID,
|
||
summary.ProductName,
|
||
summary.Appearance,
|
||
summary.Barcode,
|
||
summary.Price,
|
||
summary.WarehouseID,
|
||
summary.WarehouseName,
|
||
summary.WarehouseCode,
|
||
summary.LocationCode,
|
||
summary.TotalQuantity,
|
||
summary.LockedQuantity,
|
||
))
|
||
}
|
||
|
||
return &systemRes.InventoryListResponse{
|
||
List: items,
|
||
Total: total,
|
||
Page: req.Page,
|
||
PageSize: req.PageSize,
|
||
}, nil
|
||
}
|
||
|
||
// GetInventoryGroupedList 获取按仓库库位分组的库存列表
|
||
func (s *InventoryService) GetInventoryGroupedList(req systemReq.GetInventoryGroupedListRequest, db ...*gorm.DB) (*systemRes.InventoryGroupedListResponse, error) {
|
||
databaseConn := database.OptionalDB(db...)
|
||
|
||
if req.Page < 1 {
|
||
req.Page = 1
|
||
}
|
||
if req.PageSize < 1 || req.PageSize > 100 {
|
||
req.PageSize = 20
|
||
}
|
||
|
||
// 第一步:查询仓库库位分组统计信息
|
||
groupQuery := databaseConn.Table("inventory_detail").
|
||
Select(`
|
||
inventory_detail.warehouse_id,
|
||
w.name as warehouse_name,
|
||
w.code as warehouse_code,
|
||
inventory_detail.location_id,
|
||
l.code as location_code,
|
||
COALESCE(SUM(inventory_detail.quantity), 0) as total_quantity,
|
||
COALESCE(SUM(inventory_detail.locked_quantity), 0) as locked_quantity,
|
||
COUNT(DISTINCT inventory_detail.product_id) as item_count
|
||
`).
|
||
Joins("LEFT JOIN warehouse w ON inventory_detail.warehouse_id = w.id AND w.is_del = 0").
|
||
Joins("LEFT JOIN location l ON inventory_detail.location_id = l.id AND l.is_del = 0").
|
||
Where("inventory_detail.is_del = ?", 0).
|
||
Group("inventory_detail.warehouse_id, w.name, w.code, inventory_detail.location_id, l.code")
|
||
|
||
if req.WarehouseID > 0 {
|
||
groupQuery = groupQuery.Where("inventory_detail.warehouse_id = ?", req.WarehouseID)
|
||
}
|
||
if req.LocationID > 0 {
|
||
groupQuery = groupQuery.Where("inventory_detail.location_id = ?", req.LocationID)
|
||
}
|
||
|
||
var total int64
|
||
if err := groupQuery.Count(&total).Error; err != nil {
|
||
return nil, utils.NewError("查询总数失败")
|
||
}
|
||
|
||
if total == 0 {
|
||
return &systemRes.InventoryGroupedListResponse{
|
||
List: []systemRes.InventoryGroupItem{},
|
||
Total: 0,
|
||
Page: req.Page,
|
||
PageSize: req.PageSize,
|
||
}, nil
|
||
}
|
||
|
||
type GroupSummary struct {
|
||
WarehouseID int64 `gorm:"column:warehouse_id"`
|
||
WarehouseName string `gorm:"column:warehouse_name"`
|
||
WarehouseCode string `gorm:"column:warehouse_code"`
|
||
LocationID int64 `gorm:"column:location_id"`
|
||
LocationCode string `gorm:"column:location_code"`
|
||
TotalQuantity int64 `gorm:"column:total_quantity"`
|
||
LockedQuantity int64 `gorm:"column:locked_quantity"`
|
||
ItemCount int `gorm:"column:item_count"`
|
||
}
|
||
|
||
var groupSummaries []GroupSummary
|
||
offset := (req.Page - 1) * req.PageSize
|
||
if err := groupQuery.Order("inventory_detail.warehouse_id ASC, inventory_detail.location_id ASC").Offset(offset).Limit(req.PageSize).Find(&groupSummaries).Error; err != nil {
|
||
return nil, utils.NewError("查询库存分组列表失败")
|
||
}
|
||
|
||
// 第二步:为每个分组查询详细的库存记录
|
||
groupList := make([]systemRes.InventoryGroupItem, 0, len(groupSummaries))
|
||
for _, group := range groupSummaries {
|
||
// 查询该仓库库位下的所有库存明细
|
||
detailQuery := databaseConn.Table("inventory_detail").
|
||
Select(`
|
||
inventory_detail.*,
|
||
p.name as product_name,
|
||
p.barcode,
|
||
p.appearance,
|
||
p.sale_price
|
||
`).
|
||
Joins("LEFT JOIN product p ON inventory_detail.product_id = p.id AND p.is_del = 0").
|
||
Where("inventory_detail.warehouse_id = ? AND inventory_detail.location_id = ? AND inventory_detail.is_del = ?",
|
||
group.WarehouseID, group.LocationID, 0)
|
||
|
||
if req.ProductId > 0 {
|
||
detailQuery = detailQuery.Where("inventory_detail.product_id = ?", req.ProductId)
|
||
}
|
||
|
||
var details []struct {
|
||
models.InventoryDetail
|
||
ProductName string `gorm:"column:product_name"`
|
||
Barcode string `gorm:"column:barcode"`
|
||
Appearance int64 `gorm:"column:appearance"`
|
||
SalePrice int64 `gorm:"column:sale_price"`
|
||
}
|
||
|
||
if err := detailQuery.Order("inventory_detail.updated_at DESC").Find(&details).Error; err != nil {
|
||
utils.ErrorLog(constant.LoggerChannelWork, map[string]interface{}{
|
||
"source": "查询库存明细",
|
||
"warehouse_id": group.WarehouseID,
|
||
"location_id": group.LocationID,
|
||
"error": fmt.Sprintf("查询失败: %v", err),
|
||
})
|
||
continue
|
||
}
|
||
|
||
detailItems := make([]systemRes.InventoryDetailItem, 0, len(details))
|
||
for _, detail := range details {
|
||
detailItems = append(detailItems, systemRes.InventoryDetailItem{
|
||
ID: detail.ID,
|
||
WarehouseID: detail.WarehouseID,
|
||
WarehouseName: group.WarehouseName,
|
||
LocationID: detail.LocationID,
|
||
LocationCode: group.LocationCode,
|
||
BatchNo: detail.BatchNo,
|
||
ProductionDate: detail.ProductionDate,
|
||
ExpiryDate: detail.ExpiryDate,
|
||
Quantity: detail.Quantity,
|
||
LockedQuantity: detail.LockedQuantity,
|
||
CreatedAt: detail.CreatedAt,
|
||
UpdatedAt: detail.UpdatedAt,
|
||
ProductID: detail.ProductID,
|
||
ProductName: detail.ProductName,
|
||
Barcode: detail.Barcode,
|
||
Appearance: detail.Appearance,
|
||
SalePrice: detail.SalePrice,
|
||
})
|
||
}
|
||
|
||
groupList = append(groupList, systemRes.InventoryGroupItem{
|
||
WarehouseID: group.WarehouseID,
|
||
WarehouseName: group.WarehouseName,
|
||
WarehouseCode: group.WarehouseCode,
|
||
LocationID: group.LocationID,
|
||
LocationCode: group.LocationCode,
|
||
TotalQuantity: group.TotalQuantity,
|
||
LockedQuantity: group.LockedQuantity,
|
||
ItemCount: group.ItemCount,
|
||
Details: detailItems,
|
||
})
|
||
}
|
||
|
||
return &systemRes.InventoryGroupedListResponse{
|
||
List: groupList,
|
||
Total: total,
|
||
Page: req.Page,
|
||
PageSize: req.PageSize,
|
||
}, nil
|
||
}
|
||
|
||
// GetInventorySummary 获取库存统计信息
|
||
func (s *InventoryService) GetInventorySummary(db ...*gorm.DB) (*systemRes.InventorySummaryResponse, error) {
|
||
databaseConn := database.OptionalDB(db...)
|
||
|
||
response := &systemRes.InventorySummaryResponse{}
|
||
|
||
// 1. 查询商品总种类数(同一个barcode算一个)
|
||
productTypeQuery := databaseConn.Table("inventory_detail").
|
||
Joins("LEFT JOIN product p ON inventory_detail.product_id = p.id AND p.is_del = ?", 0).
|
||
Where("inventory_detail.is_del = ?", 0)
|
||
|
||
var productTypeCount int64
|
||
if err := productTypeQuery.Distinct("p.barcode").Count(&productTypeCount).Error; err != nil {
|
||
utils.ErrorLog(constant.LoggerChannelWork, map[string]interface{}{
|
||
"source": "查询商品种类数",
|
||
"error": fmt.Sprintf("查询失败: %v", err),
|
||
})
|
||
return nil, utils.NewError("查询商品种类数失败")
|
||
}
|
||
response.ProductTypeCount = productTypeCount
|
||
|
||
// 2. 查询商品总数(不去重,按库存明细记录数)
|
||
productCountQuery := databaseConn.Table("inventory_detail").
|
||
Where("inventory_detail.is_del = ?", 0)
|
||
|
||
var productCount int64
|
||
if err := productCountQuery.Count(&productCount).Error; err != nil {
|
||
utils.ErrorLog(constant.LoggerChannelWork, map[string]interface{}{
|
||
"source": "查询商品总数",
|
||
"error": fmt.Sprintf("查询失败: %v", err),
|
||
})
|
||
return nil, utils.NewError("查询商品总数失败")
|
||
}
|
||
response.ProductCount = productCount
|
||
|
||
// 3. 查询库存总量
|
||
totalQuantityQuery := databaseConn.Table("inventory_detail").
|
||
Select("COALESCE(SUM(quantity), 0)").
|
||
Where("inventory_detail.is_del = ?", 0)
|
||
|
||
var totalQuantity int64
|
||
if err := totalQuantityQuery.Scan(&totalQuantity).Error; err != nil {
|
||
utils.ErrorLog(constant.LoggerChannelWork, map[string]interface{}{
|
||
"source": "查询库存总量",
|
||
"error": fmt.Sprintf("查询失败: %v", err),
|
||
})
|
||
return nil, utils.NewError("查询库存总量失败")
|
||
}
|
||
response.TotalQuantity = totalQuantity
|
||
|
||
// 4. 查询有货商品数量(库存大于0的商品种类,按barcode去重)
|
||
availableProductQuery := databaseConn.Table("inventory_detail").
|
||
Joins("LEFT JOIN product p ON inventory_detail.product_id = p.id AND p.is_del = ?", 0).
|
||
Where("inventory_detail.is_del = ?", 0).
|
||
Where("inventory_detail.quantity > 0")
|
||
|
||
var availableProductCount int64
|
||
if err := availableProductQuery.Distinct("p.barcode").Count(&availableProductCount).Error; err != nil {
|
||
utils.ErrorLog(constant.LoggerChannelWork, map[string]interface{}{
|
||
"source": "查询有货商品数量",
|
||
"error": fmt.Sprintf("查询失败: %v", err),
|
||
})
|
||
return nil, utils.NewError("查询有货商品数量失败")
|
||
}
|
||
response.AvailableProductCount = availableProductCount
|
||
|
||
// 5. 查询库位数(使用过的库位数量)
|
||
locationQuery := databaseConn.Table("inventory_detail").
|
||
Select("COUNT(DISTINCT location_id)").
|
||
Where("inventory_detail.is_del = ?", 0)
|
||
|
||
var locationCount int64
|
||
if err := locationQuery.Scan(&locationCount).Error; err != nil {
|
||
utils.ErrorLog(constant.LoggerChannelWork, map[string]interface{}{
|
||
"source": "查询库位数",
|
||
"error": fmt.Sprintf("查询失败: %v", err),
|
||
})
|
||
return nil, utils.NewError("查询库位数失败")
|
||
}
|
||
response.LocationCount = locationCount
|
||
|
||
return response, nil
|
||
}
|
||
|
||
// GetInventoryDetail 获取库存明细
|
||
func (s *InventoryService) GetInventoryDetail(productID int64, db ...*gorm.DB) (*systemRes.InventoryDetailResponse, error) {
|
||
databaseConn := database.OptionalDB(db...)
|
||
|
||
var product models.Product
|
||
if err := databaseConn.Where("id = ? AND is_del = ?", productID, 0).First(&product).Error; err != nil {
|
||
return nil, utils.NewError("商品不存在")
|
||
}
|
||
|
||
query := databaseConn.Model(&models.InventoryDetail{}).
|
||
Select("inventory_detail.*, w.name as warehouse_name, l.code as location_code").
|
||
Joins("LEFT JOIN warehouse w ON inventory_detail.warehouse_id = w.id AND w.is_del = 0").
|
||
Joins("LEFT JOIN location l ON inventory_detail.location_id = l.id AND l.is_del = 0").
|
||
Where("inventory_detail.product_id = ? AND inventory_detail.is_del = ?", productID, 0)
|
||
|
||
var total int64
|
||
if err := query.Count(&total).Error; err != nil {
|
||
return nil, utils.NewError("查询总数失败")
|
||
}
|
||
|
||
var details []struct {
|
||
models.InventoryDetail
|
||
WarehouseName string `gorm:"column:warehouse_name"`
|
||
LocationCode string `gorm:"column:location_code"`
|
||
}
|
||
|
||
if err := query.Order("inventory_detail.updated_at DESC").Find(&details).Error; err != nil {
|
||
return nil, utils.NewError("查询库存明细失败")
|
||
}
|
||
|
||
// 查询该商品关联的任务信息
|
||
type TaskInfo struct {
|
||
WaveNo string `gorm:"column:wave_no"`
|
||
WaveTaskNo string `gorm:"column:wave_task_no"`
|
||
OutTaskNo int64 `gorm:"column:out_task_no"`
|
||
SalesOrderNo string `gorm:"column:sales_order_no"`
|
||
ShippingNo string `gorm:"column:shipping_no"`
|
||
}
|
||
var taskInfo TaskInfo
|
||
databaseConn.Table("wave_task_detail wtd").
|
||
Select(`COALESCE(wh.wave_no, '') as wave_no,
|
||
COALESCE(wt.task_no, '') as wave_task_no,
|
||
COALESCE(ot.out_task_id, 0) as out_task_no,
|
||
COALESCE(so.so_no, '') as sales_order_no,
|
||
COALESCE(sh.shipping_no, '') as shipping_no`).
|
||
Joins("JOIN wave_task wt ON wt.id = wtd.wave_task_id AND wt.is_del = 0").
|
||
Joins("JOIN wave_header wh ON wh.id = wt.wave_id AND wh.is_del = 0").
|
||
Joins("LEFT JOIN out_task ot ON ot.wave_task_id = wt.id AND ot.is_del = 0").
|
||
Joins("LEFT JOIN sales_order so ON so.id = wh.related_order_id AND so.is_del = 0").
|
||
Joins("LEFT JOIN outbound_order obo ON obo.wave_task_id = wt.id AND obo.is_del = 0").
|
||
Joins("LEFT JOIN outbound_order_item oboi ON oboi.out_order_id = obo.id AND oboi.product_id = ? AND oboi.is_del = 0", productID).
|
||
Joins("LEFT JOIN shipping_order_item soi ON soi.outbound_order_item_id = oboi.id AND soi.is_del = 0").
|
||
Joins("LEFT JOIN shipping_order sh ON sh.id = soi.shipping_order_id AND sh.is_del = 0").
|
||
Where("wtd.product_id = ? AND wtd.is_del = 0", productID).
|
||
Order("wt.id DESC").
|
||
Limit(1).
|
||
Scan(&taskInfo)
|
||
|
||
items := make([]systemRes.InventoryDetailItem, 0, len(details))
|
||
for _, detail := range details {
|
||
items = append(items, systemRes.ConvertInventoryDetailToItem(
|
||
detail.InventoryDetail,
|
||
detail.WarehouseName,
|
||
detail.LocationCode,
|
||
taskInfo.WaveNo,
|
||
taskInfo.WaveTaskNo,
|
||
taskInfo.OutTaskNo,
|
||
taskInfo.SalesOrderNo,
|
||
taskInfo.ShippingNo,
|
||
))
|
||
}
|
||
|
||
return &systemRes.InventoryDetailResponse{
|
||
ProductID: productID,
|
||
ProductName: product.Name,
|
||
List: items,
|
||
Total: total,
|
||
}, nil
|
||
}
|
||
|
||
// GetInventoryLogList 获取库存流水列表
|
||
func (s *InventoryService) GetInventoryLogList(req systemReq.GetInventoryLogListRequest, db ...*gorm.DB) (*systemRes.InventoryLogListResponse, error) {
|
||
databaseConn := database.OptionalDB(db...)
|
||
|
||
if req.Page < 1 {
|
||
req.Page = 1
|
||
}
|
||
if req.PageSize < 1 || req.PageSize > 100 {
|
||
req.PageSize = 20
|
||
}
|
||
|
||
query := databaseConn.Table("inventory_log").
|
||
Select(`
|
||
inventory_log.*,
|
||
w.name as warehouse_name,
|
||
l.code as location_code,
|
||
p.name as product_name,
|
||
p.barcode
|
||
`).
|
||
Joins("LEFT JOIN warehouse w ON inventory_log.warehouse_id = w.id AND w.is_del = 0").
|
||
Joins("LEFT JOIN location l ON inventory_log.location_id = l.id AND l.is_del = 0").
|
||
Joins("LEFT JOIN product p ON inventory_log.product_id = p.id AND p.is_del = 0").
|
||
Where("inventory_log.is_del = ?", 0)
|
||
|
||
if req.ISBN != "" {
|
||
query = query.Where("p.barcode LIKE ?", "%"+req.ISBN+"%")
|
||
}
|
||
if req.BookName != "" {
|
||
query = query.Where("p.name LIKE ?", "%"+req.BookName+"%")
|
||
}
|
||
if req.WarehouseID > 0 {
|
||
query = query.Where("inventory_log.warehouse_id = ?", req.WarehouseID)
|
||
}
|
||
if req.LocationID > 0 {
|
||
query = query.Where("inventory_log.location_id = ?", req.LocationID)
|
||
}
|
||
if req.ChangeType > 0 {
|
||
query = query.Where("inventory_log.change_type = ?", req.ChangeType)
|
||
}
|
||
if req.RelatedOrderNo != "" {
|
||
query = query.Where("inventory_log.related_order_no LIKE ?", "%"+req.RelatedOrderNo+"%")
|
||
}
|
||
if req.StartDate > 0 {
|
||
query = query.Where("inventory_log.created_at >= ?", req.StartDate)
|
||
}
|
||
if req.EndDate > 0 {
|
||
query = query.Where("inventory_log.created_at <= ?", req.EndDate)
|
||
}
|
||
|
||
var total int64
|
||
if err := query.Count(&total).Error; err != nil {
|
||
return nil, utils.NewError("查询总数失败")
|
||
}
|
||
|
||
if total == 0 {
|
||
return &systemRes.InventoryLogListResponse{
|
||
List: []systemRes.InventoryLogItem{},
|
||
Total: 0,
|
||
Page: req.Page,
|
||
PageSize: req.PageSize,
|
||
}, nil
|
||
}
|
||
|
||
var logs []struct {
|
||
models.InventoryLog
|
||
WarehouseName string `gorm:"column:warehouse_name"`
|
||
LocationCode string `gorm:"column:location_code"`
|
||
ProductName string `gorm:"column:product_name"`
|
||
Barcode string `gorm:"column:barcode"`
|
||
}
|
||
|
||
offset := (req.Page - 1) * req.PageSize
|
||
if err := query.Order("inventory_log.created_at DESC").Offset(offset).Limit(req.PageSize).Find(&logs).Error; err != nil {
|
||
return nil, utils.NewError("查询库存流水列表失败")
|
||
}
|
||
|
||
items := make([]systemRes.InventoryLogItem, 0, len(logs))
|
||
for _, log := range logs {
|
||
items = append(items, systemRes.ConvertInventoryLogToItem(
|
||
log.InventoryLog,
|
||
log.WarehouseName,
|
||
log.LocationCode,
|
||
log.ProductName,
|
||
log.Barcode,
|
||
))
|
||
}
|
||
|
||
return &systemRes.InventoryLogListResponse{
|
||
List: items,
|
||
Total: total,
|
||
Page: req.Page,
|
||
PageSize: req.PageSize,
|
||
}, nil
|
||
}
|
||
|
||
// InventoryStatist 获取ISBN/品相的库存总数(跨所有商品和仓库)
|
||
func (s *InventoryService) InventoryStatist(req systemReq.InventoryStatistRequest, db ...*gorm.DB) (*systemRes.InventoryStatistResponse, error) {
|
||
databaseConn := database.OptionalDB(db...)
|
||
|
||
if req.Barcode == "" && req.Appearance <= 0 {
|
||
return nil, utils.NewError("条码和品相至少提供一个")
|
||
}
|
||
|
||
type InventoryTotal struct {
|
||
Barcode string `gorm:"column:barcode"`
|
||
Appearance int64 `gorm:"column:appearance"`
|
||
TotalQuantity int64 `gorm:"column:total_quantity"`
|
||
ProductCount int `gorm:"column:product_count"`
|
||
}
|
||
|
||
query := databaseConn.Table("inventory").
|
||
Select(`
|
||
p.barcode,
|
||
p.appearance,
|
||
SUM(inventory.quantity) as total_quantity,
|
||
COUNT(DISTINCT inventory.product_id) as product_count
|
||
`).
|
||
Joins("LEFT JOIN product p ON inventory.product_id = p.id AND p.is_del = 0").
|
||
Where("inventory.is_del = ?", 0)
|
||
|
||
if req.Barcode != "" {
|
||
query = query.Where("p.barcode = ?", req.Barcode)
|
||
}
|
||
if req.Appearance > 0 {
|
||
query = query.Where("p.Appearance = ?", req.Appearance)
|
||
}
|
||
|
||
var total InventoryTotal
|
||
if err := query.Group("p.barcode, p.appearance").Limit(1).Scan(&total).Error; err != nil {
|
||
return nil, utils.NewError("查询库存总数失败")
|
||
}
|
||
|
||
return &systemRes.InventoryStatistResponse{
|
||
Barcode: total.Barcode,
|
||
Appearance: total.Appearance,
|
||
TotalQuantity: total.TotalQuantity,
|
||
ProductCount: total.ProductCount,
|
||
}, nil
|
||
}
|
||
|
||
// GetStockCheckList 获取盘库列表(不包含明细)
|
||
func (s *InventoryService) GetStockCheckList(req systemReq.GetStockCheckListRequest, db ...*gorm.DB) (*systemRes.StockCheckListResponse, error) {
|
||
databaseConn := database.OptionalDB(db...)
|
||
|
||
if req.Page < 1 {
|
||
req.Page = 1
|
||
}
|
||
if req.PageSize < 1 || req.PageSize > 100 {
|
||
req.PageSize = 20
|
||
}
|
||
|
||
query := databaseConn.Table("stock_check").
|
||
Select(`
|
||
stock_check.*,
|
||
w.name as warehouse_name
|
||
`).
|
||
Joins("LEFT JOIN warehouse w ON stock_check.warehouse_id = w.id AND w.is_del = 0").
|
||
Where("stock_check.is_del = ?", 0)
|
||
|
||
if req.WarehouseID > 0 {
|
||
query = query.Where("stock_check.warehouse_id = ?", req.WarehouseID)
|
||
}
|
||
if req.Status > 0 {
|
||
query = query.Where("stock_check.status = ?", req.Status)
|
||
}
|
||
if req.CheckNo != "" {
|
||
query = query.Where("stock_check.check_no LIKE ?", "%"+req.CheckNo+"%")
|
||
}
|
||
if req.StartDate > 0 {
|
||
query = query.Where("stock_check.created_at >= ?", req.StartDate)
|
||
}
|
||
if req.EndDate > 0 {
|
||
query = query.Where("stock_check.created_at <= ?", req.EndDate)
|
||
}
|
||
|
||
var total int64
|
||
if err := query.Count(&total).Error; err != nil {
|
||
return nil, utils.NewError("查询总数失败")
|
||
}
|
||
|
||
if total == 0 {
|
||
return &systemRes.StockCheckListResponse{
|
||
List: []systemRes.StockCheckItem{},
|
||
Total: 0,
|
||
Page: req.Page,
|
||
PageSize: req.PageSize,
|
||
}, nil
|
||
}
|
||
|
||
type StockCheckWithWarehouse struct {
|
||
models.StockCheck
|
||
WarehouseName string `gorm:"column:warehouse_name"`
|
||
}
|
||
|
||
var checks []StockCheckWithWarehouse
|
||
offset := (req.Page - 1) * req.PageSize
|
||
if err := query.Order("stock_check.created_at DESC").Offset(offset).Limit(req.PageSize).Find(&checks).Error; err != nil {
|
||
return nil, utils.NewError("查询盘库列表失败")
|
||
}
|
||
|
||
items := make([]systemRes.StockCheckItem, 0, len(checks))
|
||
for _, check := range checks {
|
||
item := systemRes.ConvertStockCheckToItem(check.StockCheck, check.WarehouseName)
|
||
items = append(items, item)
|
||
}
|
||
|
||
return &systemRes.StockCheckListResponse{
|
||
List: items,
|
||
Total: total,
|
||
Page: req.Page,
|
||
PageSize: req.PageSize,
|
||
}, nil
|
||
}
|
||
|
||
// GetStockCheckDetail 获取盘库明细列表
|
||
func (s *InventoryService) GetStockCheckDetail(req systemReq.GetStockCheckDetailRequest, db ...*gorm.DB) (*systemRes.StockCheckDetailResponse, error) {
|
||
databaseConn := database.OptionalDB(db...)
|
||
|
||
if req.Page < 1 {
|
||
req.Page = 1
|
||
}
|
||
if req.PageSize < 1 || req.PageSize > 100 {
|
||
req.PageSize = 20
|
||
}
|
||
|
||
if req.StockCheckID == 0 {
|
||
return nil, utils.NewError("盘库单ID不能为空")
|
||
}
|
||
|
||
query := databaseConn.Table("stock_check_item").
|
||
Select(`
|
||
stock_check_item.*,
|
||
p.name as product_name,
|
||
p.barcode,
|
||
l.code as location_code
|
||
`).
|
||
Joins("LEFT JOIN product p ON stock_check_item.product_id = p.id AND p.is_del = 0").
|
||
Joins("LEFT JOIN location l ON stock_check_item.location_id = l.id AND l.is_del = 0").
|
||
Where("stock_check_item.stock_check_id = ? AND stock_check_item.is_del = ?", req.StockCheckID, 0)
|
||
|
||
var total int64
|
||
if err := query.Count(&total).Error; err != nil {
|
||
return nil, utils.NewError("查询总数失败")
|
||
}
|
||
|
||
if total == 0 {
|
||
return &systemRes.StockCheckDetailResponse{
|
||
List: []systemRes.StockCheckDetailItem{},
|
||
Total: 0,
|
||
Page: req.Page,
|
||
PageSize: req.PageSize,
|
||
}, nil
|
||
}
|
||
|
||
type StockCheckItemWithInfo struct {
|
||
models.StockCheckItem
|
||
ProductName string `gorm:"column:product_name"`
|
||
Barcode string `gorm:"column:barcode"`
|
||
LocationCode string `gorm:"column:location_code"`
|
||
}
|
||
|
||
var items []StockCheckItemWithInfo
|
||
offset := (req.Page - 1) * req.PageSize
|
||
if err := query.Order("stock_check_item.created_at ASC").Offset(offset).Limit(req.PageSize).Find(&items).Error; err != nil {
|
||
return nil, utils.NewError("查询盘库明细失败")
|
||
}
|
||
|
||
details := make([]systemRes.StockCheckDetailItem, 0, len(items))
|
||
for _, item := range items {
|
||
detail := systemRes.StockCheckDetailItem{
|
||
ID: item.ID,
|
||
ProductID: item.ProductID,
|
||
ProductName: item.ProductName,
|
||
Barcode: item.Barcode,
|
||
LocationID: item.LocationID,
|
||
LocationCode: item.LocationCode,
|
||
BatchNo: item.BatchNo,
|
||
ProductionDate: item.ProductionDate,
|
||
ExpiryDate: item.ExpiryDate,
|
||
SystemQuantity: item.SystemQuantity,
|
||
ActualQuantity: item.ActualQuantity,
|
||
DifferenceQuantity: item.DifferenceQuantity,
|
||
Status: item.Status,
|
||
StatusText: systemRes.GetStockCheckItemStatusText(item.Status),
|
||
CheckOperator: item.CheckOperator,
|
||
CheckOperatorID: item.CheckOperatorID,
|
||
CheckTime: item.CheckTime,
|
||
Remark: item.Remark,
|
||
CreatedAt: item.CreatedAt,
|
||
UpdatedAt: item.UpdatedAt,
|
||
}
|
||
details = append(details, detail)
|
||
}
|
||
|
||
return &systemRes.StockCheckDetailResponse{
|
||
List: details,
|
||
Total: total,
|
||
Page: req.Page,
|
||
PageSize: req.PageSize,
|
||
}, nil
|
||
}
|