From a51445ecb0db2eab08591e95946567cd6fd3f893 Mon Sep 17 00:00:00 2001 From: chenlin Date: Mon, 22 Apr 2024 18:12:19 +0800 Subject: [PATCH] =?UTF-8?q?1.=E6=96=B0=E5=A2=9E=E6=8E=A5=E5=8F=A3=EF=BC=9A?= =?UTF-8?q?=E4=BA=A7=E5=93=81=E5=BA=93=E5=AD=98=E6=B1=87=E6=80=BB=EF=BC=88?= =?UTF-8?q?=E6=8C=89=E9=97=A8=E5=BA=97=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/apis/inventorymanage/report.go | 24 +- app/admin/models/commodity.go | 2 +- app/admin/models/inventory_allot.go | 45 ++++ app/admin/models/inventory_report.go | 293 ++++++++++++++++++++++- 4 files changed, 350 insertions(+), 14 deletions(-) diff --git a/app/admin/apis/inventorymanage/report.go b/app/admin/apis/inventorymanage/report.go index e416f57..373a3a2 100644 --- a/app/admin/apis/inventorymanage/report.go +++ b/app/admin/apis/inventorymanage/report.go @@ -21,12 +21,18 @@ import ( func InventoryReportByProduct(c *gin.Context) { req := &models.InventoryReportByProductReq{} if err := c.ShouldBindJSON(&req); err != nil { - //logger.Error(err) - app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误") + app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误:"+err.Error()) return } - app.OK(c, "", "OK") + resp, err := req.ReportByProductList(c) + if err != nil { + //logger.Error("erp commodity list err:", err) + app.Error(c, http.StatusInternalServerError, err, "查询失败:"+err.Error()) + return + } + + app.OK(c, resp, "OK") return } @@ -41,8 +47,7 @@ func InventoryReportByProduct(c *gin.Context) { func InventoryReportByAllot(c *gin.Context) { req := &models.InventoryReportByAllotReq{} if err := c.ShouldBindJSON(&req); err != nil { - //logger.Error(err) - app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误") + app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误:"+err.Error()) return } @@ -61,8 +66,7 @@ func InventoryReportByAllot(c *gin.Context) { func InventoryReportAllotDetail(c *gin.Context) { req := &models.InventoryReportAllotDetailReq{} if err := c.ShouldBindJSON(&req); err != nil { - //logger.Error(err) - app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误") + app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误:"+err.Error()) return } @@ -81,8 +85,7 @@ func InventoryReportAllotDetail(c *gin.Context) { func InventoryReportByOther(c *gin.Context) { req := &models.InventoryReportByOtherReq{} if err := c.ShouldBindJSON(&req); err != nil { - //logger.Error(err) - app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误") + app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误:"+err.Error()) return } @@ -101,8 +104,7 @@ func InventoryReportByOther(c *gin.Context) { func InventoryReportOtherDetail(c *gin.Context) { req := &models.InventoryReportOtherDetailReq{} if err := c.ShouldBindJSON(&req); err != nil { - //logger.Error(err) - app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误") + app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误:"+err.Error()) return } diff --git a/app/admin/models/commodity.go b/app/admin/models/commodity.go index 1a16030..c4af5c5 100644 --- a/app/admin/models/commodity.go +++ b/app/admin/models/commodity.go @@ -51,7 +51,7 @@ type ErpStock struct { RetailPrice float64 `json:"retail_price"` // 指导零售价 MinRetailPrice float64 `json:"min_retail_price"` // 最低零售价 Count uint32 `json:"count"` // 数量 - DispatchCount uint32 `json:"dispatch_count"` // 调拨中数量 + DispatchCount uint32 `json:"dispatch_count"` // 调拨中数量(调拨中调入) Commodities []ErpStockCommodity `json:"commodities" gorm:"-"` } diff --git a/app/admin/models/inventory_allot.go b/app/admin/models/inventory_allot.go index 0c5b041..8ed7cc0 100644 --- a/app/admin/models/inventory_allot.go +++ b/app/admin/models/inventory_allot.go @@ -617,6 +617,43 @@ func allotAuditAndUpdateStock(gdb *gorm.DB, allotOrder ErpInventoryAllotOrder) e 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: v.Count, + DispatchCount: 0, + } + err = gdb.Create(stock).Error + if err != nil { + logger.Errorf("create stock err:", err) + return err + } + } } return nil @@ -673,6 +710,14 @@ func cancelAllotAuditAndUpdateStock(gdb *gorm.DB, allotOrder ErpInventoryAllotOr 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.ReceiveStoreId, v.CommodityId)).Error + if err != nil { + logger.Errorf("update stock err:", err) + return err + } } case ErpInventoryAllotOrderFinished: // 已完成:状态改为待审核,调入门店对应商品的库存更新到调出门店,如果库存不足则报错 // 遍历库存调拨商品信息 diff --git a/app/admin/models/inventory_report.go b/app/admin/models/inventory_report.go index fdacc96..14d4eb5 100644 --- a/app/admin/models/inventory_report.go +++ b/app/admin/models/inventory_report.go @@ -1,6 +1,17 @@ package models -import "time" +import ( + "errors" + "fmt" + "github.com/gin-gonic/gin" + "github.com/xuri/excelize/v2" + orm "go-admin/common/global" + "go-admin/logger" + "go-admin/tools" + "go-admin/tools/config" + "strconv" + "time" +) // InventoryReportByProductReq 产品库存汇总(按门店)入参 type InventoryReportByProductReq struct { @@ -15,7 +26,7 @@ type InventoryReportByProductReq struct { // InventoryReportByProductResp 产品库存汇总(按门店)出参 type InventoryReportByProductResp struct { - Total int `json:"total"` // 总条数/记录数 + Total uint32 `json:"total"` // 总条数/记录数 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 TotalEffectiveCount uint32 `json:"total_effective_count"` // 有效库存数 @@ -209,3 +220,281 @@ type ReportOtherDetailData struct { PurchasePrice float64 `json:"purchase_price"` // 入库采购价 EmployeePrice float64 `json:"employee_price"` // 入库员工成本价 } + +// ReportByProductList 产品库存汇总(按门店) +func (m *InventoryReportByProductReq) ReportByProductList(c *gin.Context) (*InventoryReportByProductResp, error) { + // 非管理员才判断所属门店 + if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { + sysUser, err := GetSysUserByCtx(c) + if err != nil { + return nil, errors.New("操作失败:" + err.Error()) + } + + // 返回sysUser未过期的门店id列表 + storeList := GetValidStoreIDs(sysUser.StoreData) + if len(storeList) > 0 { + m.StoreId = CompareLists(storeList, m.StoreId) + if len(m.StoreId) == 0 { // 没有匹配的数据,表示入参门店不是用户有权限的门店 + return &InventoryReportByProductResp{}, nil + } + } else { + return nil, errors.New("用户未绑定门店") + } + } + + resp := &InventoryReportByProductResp{ + 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.Debug().Table("erp_stock") + + if len(m.StoreId) > 0 { // 门店复选 + var storeIDs []uint32 + for _, store := range m.StoreId { + storeIDs = append(storeIDs, store) + } + qs = qs.Where("store_id IN (?)", storeIDs) + } + + if len(m.CategoryID) > 0 { // 商品分类id + var categoryIDs []uint32 + for _, category := range m.CategoryID { + categoryIDs = append(categoryIDs, category) + } + qs = qs.Where("category_id IN (?)", categoryIDs) + } + + if len(m.CommoditySerialNumber) > 0 { // 商品编号 + var serialNumbers []string + for _, serialNumber := range m.CommoditySerialNumber { + serialNumbers = append(serialNumbers, serialNumber) + } + qs = qs.Where("commodity_serial_number IN (?)", serialNumbers) + } + + if len(m.CommodityName) > 0 { // 商品名称 + var commodityNames []string + for _, commodityName := range m.CommodityName { + commodityNames = append(commodityNames, commodityName) + } + qs = qs.Where("commodity_name IN (?)", commodityNames) + } + + var count int64 + err := qs.Count(&count).Error + if err != nil { + logger.Error("查询无库存列表数量失败", logger.Field("err", err)) + return nil, err + } + + var commodities []ErpStock + if m.IsExport == 1 { // 导出excel + err = qs.Find(&commodities).Error + } else { + err = qs.Offset(page * m.PageSize).Limit(m.PageSize).Find(&commodities).Error + } + if err != nil && err != RecordNotFound { + logger.Error("查询无库存列表失败", logger.Field("err", err)) + return nil, err + } + + var reportList []ReportByProductData + var nTotalEffectiveCount, nTotalTransferCount uint32 + var nTotalEffectiveAmount, nTotalTransferAmount float64 + // 遍历库存列表,计算有效库存金额,调入中金额 + for _, item := range commodities { + var reportData ReportByProductData + reportData.StoreId = item.StoreId + reportData.StoreName = item.StoreName + reportData.CommoditySerialNumber = item.CommoditySerialNumber + reportData.CommodityId = item.ErpCommodityId + reportData.CommodityName = item.ErpCommodityName + reportData.CategoryID = item.ErpCategoryId + reportData.CategoryName = item.ErpCategoryName + reportData.EffectiveCount = item.Count + reportData.TransferCount = item.DispatchCount + reportData.Count = item.Count + item.DispatchCount + + // 查询有效库存金额 + if item.Count != 0 { + reportData.EffectiveAmount, err = getStockCommodityAmount(item.StoreId, item.ErpCommodityId) + if err != nil { + return nil, err + } + } + // 查询调入中金额 + if item.DispatchCount != 0 { + reportData.TransferAmount, err = getDispatchCommodityAmount(item.StoreId, item.ErpCommodityId) + if err != nil { + return nil, err + } + } + + nTotalEffectiveCount += reportData.EffectiveCount + nTotalTransferCount += reportData.TransferCount + + nTotalEffectiveAmount += reportData.EffectiveAmount + nTotalTransferAmount += reportData.TransferAmount + + reportList = append(reportList, reportData) + } + + resp.TotalEffectiveCount = nTotalEffectiveCount + resp.TotalTransferCount = nTotalTransferCount + resp.TotalCount = resp.TotalTransferCount + resp.TotalEffectiveCount + resp.TotalEffectiveAmount = nTotalEffectiveAmount + resp.TotalTransferAmount = nTotalTransferAmount + resp.Total = resp.TotalCount + resp.List = reportList + + if m.IsExport == 1 { // 导出excel + resp.ExportUrl, err = reportByProductExport(resp) + if err != nil { + return nil, err + } + resp.List = []ReportByProductData{} + } + + return resp, nil +} + +// 查询有效库存金额 +func getStockCommodityAmount(storeId, commodityId uint32) (float64, error) { + var nCount int64 + err := orm.Eloquent.Debug().Table("erp_stock_commodity"). + Where("store_id = ? and erp_commodity_id = ? and state = ?", + storeId, commodityId, InStock).Count(&nCount).Error + if err != nil { + return 0, err + } + if nCount == 0 { + return 0, nil + } + + var nStockCommodityAmount float64 + err = orm.Eloquent.Debug().Table("erp_stock_commodity").Select("SUM(wholesale_price) AS "+ + "nStockCommodityAmount").Where("store_id = ? and erp_commodity_id = ? and state = ?", + storeId, commodityId, InStock).Find(&nStockCommodityAmount).Error + if err != nil { + return 0, err + } + + return nStockCommodityAmount, nil +} + +// 查询调入中金额 +func getDispatchCommodityAmount(storeId, commodityId uint32) (float64, error) { + var nCount int64 + err := orm.Eloquent.Debug().Table("erp_stock_commodity"). + Where("store_id = ? and erp_commodity_id = ? and state = ?", + storeId, commodityId, InAllot).Count(&nCount).Error + if err != nil { + return 0, err + } + if nCount == 0 { + return 0, nil + } + + var nDispatchCommodityAmount float64 + err = orm.Eloquent.Debug().Table("erp_stock_commodity").Select("SUM(wholesale_price) AS "+ + "nStockCommodityAmount").Where("store_id = ? and erp_commodity_id = ? and state = ?", + storeId, commodityId, InAllot).Find(&nDispatchCommodityAmount).Error + if err != nil { + return 0, err + } + + return nDispatchCommodityAmount, nil +} + +// reportByProductExport 产品库存汇总(按门店)导出excel +func reportByProductExport(req *InventoryReportByProductResp) (string, error) { + file := excelize.NewFile() + fSheet := "Sheet1" + + url := ExportUrl + fileName := time.Now().Format(TimeFormat) + "产品库存汇总(按门店)" + ".xlsx" + fmt.Println("url fileName:", url+fileName) + + // 组合标题栏数据 + title := []interface{}{"门店", "商品编号", "商品名称", "商品分类", "有效库存数", "调入中数量", "总数量", "有效库存金额", "调入中金额"} + for i, _ := range title { + cell, _ := excelize.CoordinatesToCellName(1+i, 1) + err := file.SetCellValue(fSheet, cell, title[i]) + if err != nil { + logger.Error("file set value err:", logger.Field("err", err)) + } + } + + var row1 []interface{} + nExcelStartRow := 0 + for _, reportData := range req.List { + row1 = []interface{}{ + reportData.StoreName, // 门店 + reportData.CommoditySerialNumber, // 商品编号 + reportData.CommodityName, // 商品名称 + reportData.CategoryName, // 商品分类 + reportData.EffectiveCount, // 有效库存数 + reportData.TransferCount, // 调入中数量 + reportData.Count, // 总数量 + reportData.EffectiveAmount, // 有效库存金额 + reportData.TransferAmount, // 调入中金额 + } + + for j, _ := range row1 { + cell, _ := excelize.CoordinatesToCellName(1+j, nExcelStartRow+2) + err := file.SetCellValue(fSheet, cell, row1[j]) + if err != nil { + logger.Error("file set value err:", logger.Field("err", err)) + } + } + nExcelStartRow++ + } + + totalData := "记录数:" + strconv.FormatInt(int64(req.Total), 10) + end := []interface{}{totalData, "", "", "", req.TotalEffectiveCount, req.TotalTransferCount, req.TotalCount, + req.TotalEffectiveAmount, req.TotalTransferAmount} + for i, _ := range end { + cell, _ := excelize.CoordinatesToCellName(1+i, nExcelStartRow+2) + err := file.SetCellValue(fSheet, cell, end[i]) + if err != nil { + logger.Error("file set value err:", logger.Field("err", err)) + } + } + + // 设置所有单元格的样式: 居中、加边框 + style, _ := file.NewStyle(`{"alignment":{"horizontal":"center","vertical":"center"}, + "border":[{"type":"left","color":"000000","style":1}, + {"type":"top","color":"000000","style":1}, + {"type":"right","color":"000000","style":1}, + {"type":"bottom","color":"000000","style":1}]}`) + + //设置单元格高度 + file.SetRowHeight("Sheet1", 1, 20) + + // 设置单元格大小 + file.SetColWidth("Sheet1", "A", "A", 30) + file.SetColWidth("Sheet1", "C", "C", 25) + file.SetColWidth("Sheet1", "D", "D", 15) + file.SetColWidth("Sheet1", "E", "E", 15) + file.SetColWidth("Sheet1", "F", "F", 15) + file.SetColWidth("Sheet1", "H", "H", 15) + file.SetColWidth("Sheet1", "I", "I", 15) + + endRow := fmt.Sprintf("I"+"%d", nExcelStartRow+2) + // 应用样式到整个表格 + _ = file.SetCellStyle("Sheet1", "A1", endRow, style) + + fmt.Println("save fileName:", config.ExportConfig.Path+fileName) + if err := file.SaveAs(config.ExportConfig.Path + fileName); err != nil { + fmt.Println(err) + } + return url + fileName, nil +}