1.商品实现多图/api/product/list
2.在商品管理下的商品列表接口里 分别统计:已落位 locatedCount, 未落位 unlocatedCount, 启用中 enabledCount, 已禁用 disabledCount 4个字段并进行返回 3.新增一个接口 对商品名称字段进行修改,对实拍图字段修改,实拍图可以为单图或者多图 4.波次任务列表- 缺少统计信息:分别为今日入库波次数 today_inbound_waves, 昨日入库波次数 ,yesterday_inbound_waves 今日入库数量,today_inbound_qty 昨日入库数量,yesterday_inbound_qty 今日出库数量,today_outbound_qty 昨日出库数量 yesterday_outbound_qty 5.采购单列表- 缺少导出到旺店通功能
This commit is contained in:
parent
4f6b68b58f
commit
4253c6ade0
@ -540,3 +540,30 @@ func parseNewImageFromForm(c *gin.Context) ([]string, error) {
|
|||||||
|
|
||||||
return images, nil
|
return images, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateProductNameAndImages 修改商品名称和实拍图
|
||||||
|
func (r *ProductApi) UpdateProductNameAndImages(c *gin.Context) {
|
||||||
|
var req systemReq.UpdateProductNameAndImagesRequest
|
||||||
|
|
||||||
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
|
ValidAndFail(constant.LoggerChannelRequest, "修改商品信息请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理live_image参数: 如果表单未绑定,尝试从原始表单解析
|
||||||
|
if len(req.LiveImage) == 0 {
|
||||||
|
image, err := parseImageFromForm(c)
|
||||||
|
if err != nil {
|
||||||
|
systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.LiveImage = image
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := productService.UpdateProductNameAndImages(req, database.GetDB(c)); err != nil {
|
||||||
|
utils.FailWithRequestLog(constant.LoggerChannelWork, "修改商品信息异常", err, c, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
systemRes.OkWithMessage("修改成功", c)
|
||||||
|
}
|
||||||
|
|||||||
@ -55,3 +55,25 @@ func (r *PurchaseApi) GetPurchaseOrderDetail(c *gin.Context) {
|
|||||||
|
|
||||||
systemRes.OkWithDetailed(result, "查询成功", c)
|
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExportPurchaseOrderToWDT 导出采购单到旺店通
|
||||||
|
func (r *PurchaseApi) ExportPurchaseOrderToWDT(c *gin.Context) {
|
||||||
|
var req systemReq.ExportPurchaseOrderToWDTRequest
|
||||||
|
|
||||||
|
if err := c.ShouldBindQuery(&req); err != nil {
|
||||||
|
ValidAndFail(constant.LoggerChannelRequest, "导出采购单请求参数异常", "参数错误: "+err.Error(), c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userInfo := utils.GetUserInfo(c)
|
||||||
|
result, err := purchaseService.ExportPurchaseOrderToWDT(req, userInfo.ID, userInfo.Role, database.GetDB(c))
|
||||||
|
if err != nil {
|
||||||
|
utils.FailWithRequestLog(constant.LoggerChannelWork, "导出采购单异常", err, c, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 200,
|
||||||
|
"data": result,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@ -190,3 +190,10 @@ type PushProductToShopRequest struct {
|
|||||||
Photos []string `form:"photos" json:"photos"` // 照片数组
|
Photos []string `form:"photos" json:"photos"` // 照片数组
|
||||||
Appearance int64 `form:"appearance" json:"appearance" binding:"required"` // 品相
|
Appearance int64 `form:"appearance" json:"appearance" binding:"required"` // 品相
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateProductNameAndImagesRequest 修改商品名称和实拍图请求
|
||||||
|
type UpdateProductNameAndImagesRequest struct {
|
||||||
|
ProductID int64 `form:"product_id" binding:"required"` // 商品ID
|
||||||
|
Name string `form:"name"` // 商品名称(可选)
|
||||||
|
LiveImage []string `form:"live_image[]"` // 商品实拍图(可选,支持单图或多图)
|
||||||
|
}
|
||||||
|
|||||||
@ -16,3 +16,14 @@ type GetPurchaseOrderListRequest struct {
|
|||||||
type GetPurchaseOrderDetailRequest struct {
|
type GetPurchaseOrderDetailRequest struct {
|
||||||
ID int64 `form:"id" binding:"required"`
|
ID int64 `form:"id" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExportPurchaseOrderToWDTRequest 导出采购单到旺店通请求
|
||||||
|
type ExportPurchaseOrderToWDTRequest struct {
|
||||||
|
IDs []int64 `form:"ids[]"` // 采购单ID列表(可选,不传则导出全部)
|
||||||
|
Status int8 `form:"status"` // 状态筛选(可选)
|
||||||
|
SupplierID int64 `form:"supplier_id"` // 供应商筛选(可选)
|
||||||
|
WarehouseID int64 `form:"warehouse_id"` // 仓库筛选(可选)
|
||||||
|
PoNo string `form:"po_no"` // 采购单号筛选(可选)
|
||||||
|
StartDate int64 `form:"start_date"` // 开始时间筛选(可选)
|
||||||
|
EndDate int64 `form:"end_date"` // 结束时间筛选(可选)
|
||||||
|
}
|
||||||
|
|||||||
@ -6,10 +6,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ProductListResponse struct {
|
type ProductListResponse struct {
|
||||||
List []ProductItem `json:"list"`
|
List []ProductItem `json:"list"`
|
||||||
Total int64 `json:"total"`
|
Total int64 `json:"total"`
|
||||||
Page int `json:"page"`
|
Page int `json:"page"`
|
||||||
PageSize int `json:"pageSize"`
|
PageSize int `json:"pageSize"`
|
||||||
|
LocatedCount int64 `json:"located_count"` // 已落位商品数
|
||||||
|
UnlocatedCount int64 `json:"unlocated_count"` // 未落位商品数
|
||||||
|
EnabledCount int64 `json:"enabled_count"` // 启用中商品数
|
||||||
|
DisabledCount int64 `json:"disabled_count"` // 已禁用商品数
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProductItem struct {
|
type ProductItem struct {
|
||||||
|
|||||||
@ -24,10 +24,16 @@ type DetailWithInfo struct {
|
|||||||
|
|
||||||
// WaveTaskListResponse 波次任务列表响应
|
// WaveTaskListResponse 波次任务列表响应
|
||||||
type WaveTaskListResponse struct {
|
type WaveTaskListResponse struct {
|
||||||
List []WaveTaskItem `json:"list"`
|
List []WaveTaskItem `json:"list"`
|
||||||
Total int64 `json:"total"`
|
Total int64 `json:"total"`
|
||||||
Page int `json:"page"`
|
Page int `json:"page"`
|
||||||
PageSize int `json:"pageSize"`
|
PageSize int `json:"pageSize"`
|
||||||
|
TodayInboundWaves int64 `json:"today_inbound_waves"` // 今日入库波次数
|
||||||
|
YesterdayInboundWaves int64 `json:"yesterday_inbound_waves"` // 昨日入库波次数
|
||||||
|
TodayInboundQty int64 `json:"today_inbound_qty"` // 今日入库数量
|
||||||
|
YesterdayInboundQty int64 `json:"yesterday_inbound_qty"` // 昨日入库数量
|
||||||
|
TodayOutboundQty int64 `json:"today_outbound_qty"` // 今日出库数量
|
||||||
|
YesterdayOutboundQty int64 `json:"yesterday_outbound_qty"` // 昨日出库数量
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaveTaskItem 波次任务列表项
|
// WaveTaskItem 波次任务列表项
|
||||||
|
|||||||
@ -149,9 +149,12 @@ func initRouter() (r *gin.Engine) {
|
|||||||
auth.GET("/getProCode", bookApi.GetProCode) // 获取商品条码
|
auth.GET("/getProCode", bookApi.GetProCode) // 获取商品条码
|
||||||
auth.POST("/syncBook", bookApi.SyncBook) // 同步图书
|
auth.POST("/syncBook", bookApi.SyncBook) // 同步图书
|
||||||
// 商品管理
|
// 商品管理
|
||||||
auth.GET("/product/list", productApi.GetProductList) // 商品列表
|
auth.GET("/product/list", productApi.GetProductList) // 商品列表
|
||||||
auth.GET("/product/detail", productApi.GetProductDetail) // 商品详情
|
auth.GET("/product/detail", productApi.GetProductDetail) // 商品详情
|
||||||
auth.POST("/product/save", productApi.SaveProduct) // 保存商品
|
auth.POST("/product/save", productApi.SaveProduct) // 保存商品
|
||||||
|
|
||||||
|
auth.POST("/product/updateNameAndImages", productApi.UpdateProductNameAndImages) // 修改商品名称和实拍图
|
||||||
|
|
||||||
auth.POST("/product/delete", productApi.DeleteProduct) // 删除商品
|
auth.POST("/product/delete", productApi.DeleteProduct) // 删除商品
|
||||||
auth.POST("/product/retry-out-task", productApi.RetryOutTask) // 重新出库
|
auth.POST("/product/retry-out-task", productApi.RetryOutTask) // 重新出库
|
||||||
auth.GET("/product/export", productApi.ExportProducts) // 导出商品
|
auth.GET("/product/export", productApi.ExportProducts) // 导出商品
|
||||||
@ -184,8 +187,9 @@ func initRouter() (r *gin.Engine) {
|
|||||||
auth.POST("/stock_check/adjust", processApi.AdjustInventory) // 盘库
|
auth.POST("/stock_check/adjust", processApi.AdjustInventory) // 盘库
|
||||||
auth.POST("/stock_check/return", processApi.ReturnInventory) // 盘库退货
|
auth.POST("/stock_check/return", processApi.ReturnInventory) // 盘库退货
|
||||||
// 采购订单管理
|
// 采购订单管理
|
||||||
auth.GET("/purchase-order/list", purchaseApi.GetPurchaseOrderList) // 获取采购订单列表
|
auth.GET("/purchase-order/list", purchaseApi.GetPurchaseOrderList) // 获取采购订单列表
|
||||||
auth.GET("/purchase-order/detail", purchaseApi.GetPurchaseOrderDetail) // 获取采购订单详情
|
auth.GET("/purchase-order/detail", purchaseApi.GetPurchaseOrderDetail) // 获取采购订单详情
|
||||||
|
auth.GET("/purchase-order/export-to-wdt", purchaseApi.ExportPurchaseOrderToWDT) // 导出采购单到旺店通
|
||||||
// 入库订单管理
|
// 入库订单管理
|
||||||
auth.GET("/receiving-order/list", receivingApi.GetReceivingOrderList) // 获取入库订单列表
|
auth.GET("/receiving-order/list", receivingApi.GetReceivingOrderList) // 获取入库订单列表
|
||||||
auth.GET("/receiving-order/detail", receivingApi.GetReceivingOrderDetail) // 获取入库订单详情
|
auth.GET("/receiving-order/detail", receivingApi.GetReceivingOrderDetail) // 获取入库订单详情
|
||||||
|
|||||||
@ -116,20 +116,116 @@ func (s *ProductService) GetProductList(req systemReq.GetProductListRequest, db
|
|||||||
item.ShopList = outTaskInfo.ShopList
|
item.ShopList = outTaskInfo.ShopList
|
||||||
}
|
}
|
||||||
productItems = append(productItems, item)
|
productItems = append(productItems, item)
|
||||||
/* // item.LiveImage[0] 按照 , 分割数组
|
|
||||||
liveImage := strings.Split(item.LiveImage[0], ",")
|
|
||||||
item.LiveImage[0] = liveImage[0]
|
|
||||||
productItems = append(productItems, item)*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
locatedCount, unlocatedCount, enabledCount, disabledCount := s.getProductStatistics(databaseConn, req, total)
|
||||||
|
|
||||||
return &systemRes.ProductListResponse{
|
return &systemRes.ProductListResponse{
|
||||||
List: productItems,
|
List: productItems,
|
||||||
Total: total,
|
Total: total,
|
||||||
Page: req.Page,
|
Page: req.Page,
|
||||||
PageSize: req.PageSize,
|
PageSize: req.PageSize,
|
||||||
|
LocatedCount: locatedCount,
|
||||||
|
UnlocatedCount: unlocatedCount,
|
||||||
|
EnabledCount: enabledCount,
|
||||||
|
DisabledCount: disabledCount,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getProductStatistics 获取商品统计数据
|
||||||
|
func (s *ProductService) getProductStatistics(db *gorm.DB, req systemReq.GetProductListRequest, total int64) (int64, int64, int64, int64) {
|
||||||
|
var enabledCount, disabledCount, locatedCount int64
|
||||||
|
|
||||||
|
buildBaseQuery := func() *gorm.DB {
|
||||||
|
query := db.Model(&models.Product{}).Where("product.is_del = ?", 0)
|
||||||
|
|
||||||
|
if len(req.IDs) > 0 {
|
||||||
|
query = query.Where("product.id IN ?", req.IDs)
|
||||||
|
}
|
||||||
|
if req.Keyword != "" {
|
||||||
|
query = query.Where("product.name LIKE ? OR product.barcode LIKE ?", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
|
||||||
|
}
|
||||||
|
if req.StartCreatedAt > 0 {
|
||||||
|
query = query.Where("product.created_at >= ?", req.StartCreatedAt)
|
||||||
|
}
|
||||||
|
if req.EndCreatedAt > 0 {
|
||||||
|
query = query.Where("product.created_at <= ?", req.EndCreatedAt)
|
||||||
|
}
|
||||||
|
if req.MinSalePrice > 0 {
|
||||||
|
query = query.Where("product.sale_price >= ?", req.MinSalePrice)
|
||||||
|
}
|
||||||
|
if req.MaxSalePrice > 0 {
|
||||||
|
query = query.Where("product.sale_price <= ?", req.MaxSalePrice)
|
||||||
|
}
|
||||||
|
|
||||||
|
hasInventoryFilter := req.WarehouseID > 0 || req.MinStock > 0 || req.MaxStock > 0 || req.LocationID > 0
|
||||||
|
if hasInventoryFilter {
|
||||||
|
query = query.Joins("LEFT JOIN inventory_detail inv_filter ON inv_filter.product_id = product.id AND inv_filter.is_del = ?", 0)
|
||||||
|
if req.WarehouseID > 0 {
|
||||||
|
query = query.Where("inv_filter.warehouse_id = ?", req.WarehouseID)
|
||||||
|
}
|
||||||
|
if req.MinStock > 0 {
|
||||||
|
query = query.Where("COALESCE(inv_filter.quantity, 0) > ?", req.MinStock)
|
||||||
|
}
|
||||||
|
if req.MaxStock > 0 {
|
||||||
|
query = query.Where("COALESCE(inv_filter.quantity, 0) < ?", req.MaxStock)
|
||||||
|
}
|
||||||
|
if req.LocationID > 0 {
|
||||||
|
query = query.Where("inv_filter.location_id = ?", req.LocationID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
buildBaseQuery().Where("product.status = ?", 1).Count(&enabledCount)
|
||||||
|
buildBaseQuery().Where("product.status = ?", 0).Count(&disabledCount)
|
||||||
|
|
||||||
|
locatedQuery := db.Table("product").
|
||||||
|
Joins("INNER JOIN inventory_detail inv ON inv.product_id = product.id AND inv.is_del = ?", 0).
|
||||||
|
Where("product.is_del = ?", 0)
|
||||||
|
|
||||||
|
if len(req.IDs) > 0 {
|
||||||
|
locatedQuery = locatedQuery.Where("product.id IN ?", req.IDs)
|
||||||
|
}
|
||||||
|
if req.Status != "" {
|
||||||
|
locatedQuery = locatedQuery.Where("product.status = ?", req.Status)
|
||||||
|
}
|
||||||
|
if req.Keyword != "" {
|
||||||
|
locatedQuery = locatedQuery.Where("product.name LIKE ? OR product.barcode LIKE ?", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
|
||||||
|
}
|
||||||
|
if req.StartCreatedAt > 0 {
|
||||||
|
locatedQuery = locatedQuery.Where("product.created_at >= ?", req.StartCreatedAt)
|
||||||
|
}
|
||||||
|
if req.EndCreatedAt > 0 {
|
||||||
|
locatedQuery = locatedQuery.Where("product.created_at <= ?", req.EndCreatedAt)
|
||||||
|
}
|
||||||
|
if req.MinSalePrice > 0 {
|
||||||
|
locatedQuery = locatedQuery.Where("product.sale_price >= ?", req.MinSalePrice)
|
||||||
|
}
|
||||||
|
if req.MaxSalePrice > 0 {
|
||||||
|
locatedQuery = locatedQuery.Where("product.sale_price <= ?", req.MaxSalePrice)
|
||||||
|
}
|
||||||
|
if req.WarehouseID > 0 {
|
||||||
|
locatedQuery = locatedQuery.Where("inv.warehouse_id = ?", req.WarehouseID)
|
||||||
|
}
|
||||||
|
if req.MinStock > 0 {
|
||||||
|
locatedQuery = locatedQuery.Where("inv.quantity > ?", req.MinStock)
|
||||||
|
}
|
||||||
|
if req.MaxStock > 0 {
|
||||||
|
locatedQuery = locatedQuery.Where("inv.quantity < ?", req.MaxStock)
|
||||||
|
}
|
||||||
|
if req.LocationID > 0 {
|
||||||
|
locatedQuery = locatedQuery.Where("inv.location_id = ?", req.LocationID)
|
||||||
|
}
|
||||||
|
|
||||||
|
locatedQuery.Distinct("product.id").Count(&locatedCount)
|
||||||
|
|
||||||
|
unlocatedCount := total - locatedCount
|
||||||
|
|
||||||
|
return locatedCount, unlocatedCount, enabledCount, disabledCount
|
||||||
|
}
|
||||||
|
|
||||||
// 获取商品任务信息
|
// 获取商品任务信息
|
||||||
func (s *ProductService) GetDistributionProductList(req systemReq.GetDistributionProductListRequest) (*systemRes.ProductListResponse, error) {
|
func (s *ProductService) GetDistributionProductList(req systemReq.GetDistributionProductListRequest) (*systemRes.ProductListResponse, error) {
|
||||||
databaseConn, err := database.GetTenantDB(req.UserID)
|
databaseConn, err := database.GetTenantDB(req.UserID)
|
||||||
@ -230,9 +326,7 @@ func (s *ProductService) GetDistributionProductList(req systemReq.GetDistributio
|
|||||||
var productItems []systemRes.ProductItem
|
var productItems []systemRes.ProductItem
|
||||||
for _, product := range products {
|
for _, product := range products {
|
||||||
item := systemRes.ConvertProductWithInfoToItem(product)
|
item := systemRes.ConvertProductWithInfoToItem(product)
|
||||||
/*if outTaskInfo, exists := outTaskInfoMap[product.ID]; exists {
|
|
||||||
item.ShopList = outTaskInfo.ShopList
|
|
||||||
}*/
|
|
||||||
productItems = append(productItems, item)
|
productItems = append(productItems, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -564,6 +658,49 @@ func (s *ProductService) GetProductFullInfo(req systemReq.GetProductFullInfoRequ
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateProductNameAndImages 修改商品名称和实拍图
|
||||||
|
func (s *ProductService) UpdateProductNameAndImages(req systemReq.UpdateProductNameAndImagesRequest, db ...*gorm.DB) error {
|
||||||
|
databaseConn := database.OptionalDB(db...)
|
||||||
|
|
||||||
|
if req.ProductID <= 0 {
|
||||||
|
return fmt.Errorf("商品ID不能为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
var product models.Product
|
||||||
|
if err := databaseConn.Where("id = ? AND is_del = ?", req.ProductID, 0).First(&product).Error; err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return fmt.Errorf("商品不存在")
|
||||||
|
}
|
||||||
|
return fmt.Errorf("查询商品失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
updates := make(map[string]interface{})
|
||||||
|
hasUpdate := false
|
||||||
|
|
||||||
|
if req.Name != "" {
|
||||||
|
updates["name"] = req.Name
|
||||||
|
hasUpdate = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.LiveImage) > 0 {
|
||||||
|
jsonBytes, _ := json.Marshal(req.LiveImage)
|
||||||
|
updates["live_image"] = datatypes.JSON(jsonBytes)
|
||||||
|
hasUpdate = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasUpdate {
|
||||||
|
return fmt.Errorf("至少需要提供一个要修改的字段")
|
||||||
|
}
|
||||||
|
|
||||||
|
updates["updated_at"] = time.Now().Unix()
|
||||||
|
|
||||||
|
if err := databaseConn.Model(&product).Updates(updates).Error; err != nil {
|
||||||
|
return fmt.Errorf("更新商品失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// getProductOutTaskInfo
|
// getProductOutTaskInfo
|
||||||
func (s *ProductService) getProductOutTaskInfo(db *gorm.DB, productIDs []int64) (map[int64]*OutTaskInfo, error) {
|
func (s *ProductService) getProductOutTaskInfo(db *gorm.DB, productIDs []int64) (map[int64]*OutTaskInfo, error) {
|
||||||
resultMap := make(map[int64]*OutTaskInfo)
|
resultMap := make(map[int64]*OutTaskInfo)
|
||||||
|
|||||||
@ -2,17 +2,211 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"psi/constant"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
"psi/config"
|
||||||
|
|
||||||
"psi/database"
|
"psi/database"
|
||||||
"psi/models"
|
"psi/models"
|
||||||
systemReq "psi/models/request"
|
systemReq "psi/models/request"
|
||||||
systemRes "psi/models/response"
|
systemRes "psi/models/response"
|
||||||
"psi/utils"
|
|
||||||
|
|
||||||
|
"psi/utils"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xuri/excelize/v2"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PurchaseService struct{}
|
type PurchaseService struct{}
|
||||||
|
|
||||||
|
// ExportPurchaseOrderToWDT 导出采购单到旺店通
|
||||||
|
func (s *PurchaseService) ExportPurchaseOrderToWDT(req systemReq.ExportPurchaseOrderToWDTRequest, creatorID int64, role int64, db ...*gorm.DB) (*systemRes.ExportProductResponse, error) {
|
||||||
|
databaseConn := database.OptionalDB(db...)
|
||||||
|
|
||||||
|
query := databaseConn.Model(&models.PurchaseOrder{}).Where("purchase_order.is_del = ?", 0)
|
||||||
|
|
||||||
|
if role == 128 {
|
||||||
|
query = query.Where("purchase_order.creator_id = ?", creatorID)
|
||||||
|
}
|
||||||
|
if len(req.IDs) > 0 {
|
||||||
|
query = query.Where("purchase_order.id IN ?", req.IDs)
|
||||||
|
}
|
||||||
|
if req.Status > 0 {
|
||||||
|
query = query.Where("purchase_order.status = ?", req.Status)
|
||||||
|
}
|
||||||
|
if req.SupplierID > 0 {
|
||||||
|
query = query.Where("purchase_order.supplier_id = ?", req.SupplierID)
|
||||||
|
}
|
||||||
|
if req.WarehouseID > 0 {
|
||||||
|
query = query.Where("purchase_order.warehouse_id = ?", req.WarehouseID)
|
||||||
|
}
|
||||||
|
if req.PoNo != "" {
|
||||||
|
query = query.Where("purchase_order.po_no LIKE ?", "%"+req.PoNo+"%")
|
||||||
|
}
|
||||||
|
if req.StartDate > 0 {
|
||||||
|
query = query.Where("purchase_order.created_at >= ?", req.StartDate)
|
||||||
|
}
|
||||||
|
if req.EndDate > 0 {
|
||||||
|
query = query.Where("purchase_order.created_at <= ?", req.EndDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
var total int64
|
||||||
|
if err := query.Count(&total).Error; err != nil {
|
||||||
|
return nil, utils.NewError("查询总数失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
if total == 0 {
|
||||||
|
return nil, fmt.Errorf("没有符合条件的采购单数据")
|
||||||
|
}
|
||||||
|
|
||||||
|
type PurchaseOrderExportData struct {
|
||||||
|
PoNo string `gorm:"column:po_no"`
|
||||||
|
SupplierName string `gorm:"column:supplier_name"`
|
||||||
|
WarehouseName string `gorm:"column:warehouse_name"`
|
||||||
|
ProductName string `gorm:"column:product_name"`
|
||||||
|
Barcode string `gorm:"column:barcode"`
|
||||||
|
Quantity int64 `gorm:"column:quantity"`
|
||||||
|
UnitPrice int64 `gorm:"column:unit_price"`
|
||||||
|
Amount int64 `gorm:"column:amount"`
|
||||||
|
OrderDate int64 `gorm:"column:order_date"`
|
||||||
|
ExpectedArrivalDate int64 `gorm:"column:expected_arrival_date"`
|
||||||
|
Status int8 `gorm:"column:status"`
|
||||||
|
Creator string `gorm:"column:creator"`
|
||||||
|
Remark string `gorm:"column:remark"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var orders []PurchaseOrderExportData
|
||||||
|
if err := query.Select(`purchase_order.po_no,
|
||||||
|
s.name as supplier_name,
|
||||||
|
w.name as warehouse_name,
|
||||||
|
p.name as product_name,
|
||||||
|
p.barcode,
|
||||||
|
poi.quantity,
|
||||||
|
poi.unit_price,
|
||||||
|
poi.amount,
|
||||||
|
purchase_order.order_date,
|
||||||
|
purchase_order.expected_arrival_date,
|
||||||
|
purchase_order.status,
|
||||||
|
purchase_order.creator,
|
||||||
|
purchase_order.remark`).
|
||||||
|
Joins("LEFT JOIN supplier s ON purchase_order.supplier_id = s.id AND s.is_del = 0").
|
||||||
|
Joins("LEFT JOIN warehouse w ON purchase_order.warehouse_id = w.id AND w.is_del = 0").
|
||||||
|
Joins("LEFT JOIN purchase_order_item poi ON poi.purchase_order_id = purchase_order.id AND poi.is_del = 0").
|
||||||
|
Joins("LEFT JOIN product p ON poi.product_id = p.id AND p.is_del = 0").
|
||||||
|
Order("purchase_order.created_at DESC").
|
||||||
|
Find(&orders).Error; err != nil {
|
||||||
|
return nil, utils.NewError("查询采购单数据失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
f := excelize.NewFile()
|
||||||
|
defer func() {
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
utils.ErrorLog(constant.LoggerChannelWork, map[string]interface{}{
|
||||||
|
"source": "关闭Excel文件",
|
||||||
|
"error": fmt.Sprintf("关闭失败: %v", err),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
sheetName := "Sheet1"
|
||||||
|
f.SetSheetName("Sheet1", sheetName)
|
||||||
|
|
||||||
|
headers := []string{"采购单号", "供应商", "仓库", "商品名称", "ISBN/条码", "采购数量", "单价(元)", "金额(元)", "订单日期", "预计到货日期", "状态", "创建人", "备注"}
|
||||||
|
for i, header := range headers {
|
||||||
|
cell, _ := excelize.CoordinatesToCellName(i+1, 1)
|
||||||
|
f.SetCellValue(sheetName, cell, header)
|
||||||
|
}
|
||||||
|
|
||||||
|
headerStyle, _ := f.NewStyle(&excelize.Style{
|
||||||
|
Font: &excelize.Font{
|
||||||
|
Bold: true,
|
||||||
|
Size: 12,
|
||||||
|
},
|
||||||
|
Alignment: &excelize.Alignment{
|
||||||
|
Horizontal: "center",
|
||||||
|
Vertical: "center",
|
||||||
|
},
|
||||||
|
Fill: excelize.Fill{
|
||||||
|
Type: "pattern",
|
||||||
|
Color: []string{"#E0E0E0"},
|
||||||
|
Pattern: 1,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
f.SetCellStyle(sheetName, "A1", "M1", headerStyle)
|
||||||
|
|
||||||
|
statusMap := map[int8]string{
|
||||||
|
1: "草稿",
|
||||||
|
2: "已提交",
|
||||||
|
3: "已审核",
|
||||||
|
4: "部分收货",
|
||||||
|
5: "已收货",
|
||||||
|
6: "已取消",
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, order := range orders {
|
||||||
|
row := idx + 2
|
||||||
|
|
||||||
|
f.SetCellValue(sheetName, fmt.Sprintf("A%d", row), order.PoNo)
|
||||||
|
f.SetCellValue(sheetName, fmt.Sprintf("B%d", row), order.SupplierName)
|
||||||
|
f.SetCellValue(sheetName, fmt.Sprintf("C%d", row), order.WarehouseName)
|
||||||
|
f.SetCellValue(sheetName, fmt.Sprintf("D%d", row), order.ProductName)
|
||||||
|
f.SetCellValue(sheetName, fmt.Sprintf("E%d", row), order.Barcode)
|
||||||
|
f.SetCellValue(sheetName, fmt.Sprintf("F%d", row), order.Quantity)
|
||||||
|
f.SetCellValue(sheetName, fmt.Sprintf("G%d", row), float64(order.UnitPrice)/100.0)
|
||||||
|
f.SetCellValue(sheetName, fmt.Sprintf("H%d", row), float64(order.Amount)/100.0)
|
||||||
|
|
||||||
|
orderDateStr := time.Unix(order.OrderDate, 0).Format("2006-01-02")
|
||||||
|
f.SetCellValue(sheetName, fmt.Sprintf("I%d", row), orderDateStr)
|
||||||
|
|
||||||
|
expectedDateStr := time.Unix(order.ExpectedArrivalDate, 0).Format("2006-01-02")
|
||||||
|
f.SetCellValue(sheetName, fmt.Sprintf("J%d", row), expectedDateStr)
|
||||||
|
|
||||||
|
statusText := statusMap[order.Status]
|
||||||
|
if statusText == "" {
|
||||||
|
statusText = "未知"
|
||||||
|
}
|
||||||
|
f.SetCellValue(sheetName, fmt.Sprintf("K%d", row), statusText)
|
||||||
|
|
||||||
|
f.SetCellValue(sheetName, fmt.Sprintf("L%d", row), order.Creator)
|
||||||
|
f.SetCellValue(sheetName, fmt.Sprintf("M%d", row), order.Remark)
|
||||||
|
}
|
||||||
|
|
||||||
|
colWidths := map[string]float64{
|
||||||
|
"A": 20,
|
||||||
|
"B": 15,
|
||||||
|
"C": 15,
|
||||||
|
"D": 30,
|
||||||
|
"E": 15,
|
||||||
|
"F": 12,
|
||||||
|
"G": 12,
|
||||||
|
"H": 12,
|
||||||
|
"I": 15,
|
||||||
|
"J": 15,
|
||||||
|
"K": 12,
|
||||||
|
"L": 12,
|
||||||
|
"M": 30,
|
||||||
|
}
|
||||||
|
for col, width := range colWidths {
|
||||||
|
f.SetColWidth(sheetName, col, col, width)
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
fileName := fmt.Sprintf("purchase_order_wdt_%s.xlsx", now.Format("20060102150405"))
|
||||||
|
filePath := fmt.Sprintf("excel/%s", fileName)
|
||||||
|
|
||||||
|
if err := f.SaveAs(filePath); err != nil {
|
||||||
|
return nil, fmt.Errorf("保存Excel文件失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &systemRes.ExportProductResponse{
|
||||||
|
Total: total,
|
||||||
|
FileName: fileName,
|
||||||
|
FilePath: config.AppConfig.Server.Host + filePath,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetPurchaseOrderList 获取采购订单列表
|
// GetPurchaseOrderList 获取采购订单列表
|
||||||
func (s *PurchaseService) GetPurchaseOrderList(req systemReq.GetPurchaseOrderListRequest, creatorID int64, role int64, db ...*gorm.DB) (*systemRes.PurchaseOrderListResponse, error) {
|
func (s *PurchaseService) GetPurchaseOrderList(req systemReq.GetPurchaseOrderListRequest, creatorID int64, role int64, db ...*gorm.DB) (*systemRes.PurchaseOrderListResponse, error) {
|
||||||
databaseConn := database.OptionalDB(db...)
|
databaseConn := database.OptionalDB(db...)
|
||||||
|
|||||||
@ -3,13 +3,13 @@ package service
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"psi/database"
|
"psi/database"
|
||||||
"psi/models"
|
"psi/models"
|
||||||
systemReq "psi/models/request"
|
systemReq "psi/models/request"
|
||||||
systemRes "psi/models/response"
|
systemRes "psi/models/response"
|
||||||
"psi/utils"
|
"psi/utils"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WaveService struct{}
|
type WaveService struct{}
|
||||||
@ -107,15 +107,73 @@ func (s *WaveService) GetWaveTaskList(req systemReq.GetWaveTaskListRequest, crea
|
|||||||
for _, task := range tasks {
|
for _, task := range tasks {
|
||||||
taskItems = append(taskItems, systemRes.ConvertWaveTaskToItem(task.WaveTask, task.WaveNo, task.OutOrderId, task.ReceivingOrderId, task.BatchNo))
|
taskItems = append(taskItems, systemRes.ConvertWaveTaskToItem(task.WaveTask, task.WaveNo, task.OutOrderId, task.ReceivingOrderId, task.BatchNo))
|
||||||
}
|
}
|
||||||
|
todayInboundWaves, yesterdayInboundWaves, todayInboundQty, yesterdayInboundQty, todayOutboundQty, yesterdayOutboundQty := s.getWaveTaskStatistics(databaseConn)
|
||||||
|
|
||||||
return &systemRes.WaveTaskListResponse{
|
return &systemRes.WaveTaskListResponse{
|
||||||
List: taskItems,
|
List: taskItems,
|
||||||
Total: total,
|
Total: total,
|
||||||
Page: req.Page,
|
Page: req.Page,
|
||||||
PageSize: req.PageSize,
|
PageSize: req.PageSize,
|
||||||
|
TodayInboundWaves: todayInboundWaves,
|
||||||
|
YesterdayInboundWaves: yesterdayInboundWaves,
|
||||||
|
TodayInboundQty: todayInboundQty,
|
||||||
|
YesterdayInboundQty: yesterdayInboundQty,
|
||||||
|
TodayOutboundQty: todayOutboundQty,
|
||||||
|
YesterdayOutboundQty: yesterdayOutboundQty,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getWaveTaskStatistics 获取波次任务统计数据
|
||||||
|
func (s *WaveService) getWaveTaskStatistics(db *gorm.DB) (int64, int64, int64, int64, int64, int64) {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).Unix()
|
||||||
|
yesterdayStart := time.Date(now.Year(), now.Month(), now.Day()-1, 0, 0, 0, 0, now.Location()).Unix()
|
||||||
|
yesterdayEnd := todayStart
|
||||||
|
|
||||||
|
var todayInboundWaves, yesterdayInboundWaves int64
|
||||||
|
var todayInboundQty, yesterdayInboundQty int64
|
||||||
|
var todayOutboundQty, yesterdayOutboundQty int64
|
||||||
|
|
||||||
|
db.Model(&models.WaveHeader{}).
|
||||||
|
Where("is_del = ? AND direction = ? AND created_at >= ?", 0, 1, todayStart).
|
||||||
|
Count(&todayInboundWaves)
|
||||||
|
|
||||||
|
db.Model(&models.WaveHeader{}).
|
||||||
|
Where("is_del = ? AND direction = ? AND created_at >= ? AND created_at < ?", 0, 1, yesterdayStart, yesterdayEnd).
|
||||||
|
Count(&yesterdayInboundWaves)
|
||||||
|
|
||||||
|
db.Table("wave_task_detail wtd").
|
||||||
|
Joins("INNER JOIN wave_task wt ON wt.id = wtd.wave_task_id AND wt.is_del = ?", 0).
|
||||||
|
Joins("INNER JOIN wave_header wh ON wh.id = wt.wave_id AND wh.is_del = ? AND wh.direction = ?", 0, 1).
|
||||||
|
Where("wtd.is_del = ? AND wt.created_at >= ?", 0, todayStart).
|
||||||
|
Select("COALESCE(SUM(wtd.actual_quantity), 0)").
|
||||||
|
Scan(&todayInboundQty)
|
||||||
|
|
||||||
|
db.Table("wave_task_detail wtd").
|
||||||
|
Joins("INNER JOIN wave_task wt ON wt.id = wtd.wave_task_id AND wt.is_del = ?", 0).
|
||||||
|
Joins("INNER JOIN wave_header wh ON wh.id = wt.wave_id AND wh.is_del = ? AND wh.direction = ?", 0, 1).
|
||||||
|
Where("wtd.is_del = ? AND wt.created_at >= ? AND wt.created_at < ?", 0, yesterdayStart, yesterdayEnd).
|
||||||
|
Select("COALESCE(SUM(wtd.actual_quantity), 0)").
|
||||||
|
Scan(&yesterdayInboundQty)
|
||||||
|
|
||||||
|
db.Table("wave_task_detail wtd").
|
||||||
|
Joins("INNER JOIN wave_task wt ON wt.id = wtd.wave_task_id AND wt.is_del = ?", 0).
|
||||||
|
Joins("INNER JOIN wave_header wh ON wh.id = wt.wave_id AND wh.is_del = ? AND wh.direction = ?", 0, 2).
|
||||||
|
Where("wtd.is_del = ? AND wt.created_at >= ?", 0, todayStart).
|
||||||
|
Select("COALESCE(SUM(wtd.actual_quantity), 0)").
|
||||||
|
Scan(&todayOutboundQty)
|
||||||
|
|
||||||
|
db.Table("wave_task_detail wtd").
|
||||||
|
Joins("INNER JOIN wave_task wt ON wt.id = wtd.wave_task_id AND wt.is_del = ?", 0).
|
||||||
|
Joins("INNER JOIN wave_header wh ON wh.id = wt.wave_id AND wh.is_del = ? AND wh.direction = ?", 0, 2).
|
||||||
|
Where("wtd.is_del = ? AND wt.created_at >= ? AND wt.created_at < ?", 0, yesterdayStart, yesterdayEnd).
|
||||||
|
Select("COALESCE(SUM(wtd.actual_quantity), 0)").
|
||||||
|
Scan(&yesterdayOutboundQty)
|
||||||
|
|
||||||
|
return todayInboundWaves, yesterdayInboundWaves, todayInboundQty, yesterdayInboundQty, todayOutboundQty, yesterdayOutboundQty
|
||||||
|
}
|
||||||
|
|
||||||
// GetWaveTaskDetail 获取波次任务详情
|
// GetWaveTaskDetail 获取波次任务详情
|
||||||
func (s *WaveService) GetWaveTaskDetail(id int64, creatorID int64, role int64, db ...*gorm.DB) (*systemRes.WaveTaskDetailResponse, error) {
|
func (s *WaveService) GetWaveTaskDetail(id int64, creatorID int64, role int64, db ...*gorm.DB) (*systemRes.WaveTaskDetailResponse, error) {
|
||||||
databaseConn := database.OptionalDB(db...)
|
databaseConn := database.OptionalDB(db...)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user