package service import ( "fmt" "github.com/shopspring/decimal" "github.com/xuri/excelize/v2" "strconv" "time" "wms/constvar" "wms/models" "wms/request" "wms/response" ) type Detail struct { ProductId string `json:"productId"` Amount decimal.Decimal `json:"amount"` Status constvar.OperationStatus `json:"status"` //ProductName string `json:"productName"` } type InventoryReportFormsService struct{} func NewInventoryReportFormsService() *InventoryReportFormsService { return &InventoryReportFormsService{} } func (slf *InventoryReportFormsService) Query(params request.GetInventoryForms) (result []*response.InventoryForms, err error) { var ( materials = make([]*models.Material, 0) ) search, productIds, productAmounts, locationIds, err := slf.BuildSearch(params) if err != nil { return nil, err } if params.Page*params.PageSize > 0 { search.Orm = search.Orm.Offset((params.Page - 1) * params.PageSize).Limit(params.PageSize) } if err := search.Orm.Find(&materials).Error; err != nil { return nil, err } //查询在库数量 if len(productIds) == 0 { for _, material := range materials { productIds = append(productIds, material.ID) } productAmounts, err = models.NewLocationProductAmountSearch().SetProductIds(productIds).SetLocationIds(locationIds).Find() if err != nil { return nil, err } } //查询出入库就绪数量 var inHouse []Detail var outHouse []Detail dbIn := models.NewOperationDetailsSearch().Orm.Model(&models.OperationDetails{}). Select("wms_operation_details.product_id,wms_operation_details.amount,wms_operation.status as status"). Joins("left join wms_operation ON wms_operation_details.operation_id=wms_operation.id"). Where("wms_operation.base_operation_type in (?)", []constvar.BaseOperationType{constvar.BaseOperationTypeIncoming, constvar.BaseOperationTypeInternal}). Where("wms_operation.status in (?)", []constvar.OperationStatus{constvar.OperationStatus_Finish}) dbOut := models.NewOperationDetailsSearch().Orm.Model(&models.OperationDetails{}). Select("wms_operation_details.product_id,wms_operation_details.amount,wms_operation.status as status"). Joins("left join wms_operation ON wms_operation_details.operation_id=wms_operation.id"). Where("wms_operation.base_operation_type in (?)", []constvar.BaseOperationType{constvar.BaseOperationTypeOutgoing, constvar.BaseOperationTypeInternal, constvar.BaseOperationTypeDisuse}). Where("wms_operation.status in (?)", []constvar.OperationStatus{constvar.OperationStatus_Ready, constvar.OperationStatus_Finish}) if len(locationIds) > 0 { dbIn.Where("wms_operation_details.to_location_id in (?)", locationIds) dbOut.Where("wms_operation_details.from_location_id in (?)", locationIds) } if len(productIds) > 0 { dbIn.Where("wms_operation_details.product_id in (?)", productIds) dbOut.Where("wms_operation_details.product_id in (?)", productIds) } err = dbIn.Find(&inHouse).Error if err != nil { return } err = dbOut.Find(&outHouse).Error if err != nil { return } for _, material := range materials { data := new(response.InventoryForms) data.ProduceId = material.ID data.ProductName = material.Name data.Cost = material.Cost data.Unit = material.Unit data.Value = material.Amount.Mul(material.Cost) data.ProductType = material.CategoryName for _, amount := range productAmounts { if material.ID == amount.ProductId { data.Amount = data.Amount.Add(amount.Amount) } } for _, details := range inHouse { if material.ID == details.ProductId { data.In = data.In.Add(details.Amount) } } available := decimal.NewFromInt(0) for _, details := range outHouse { if material.ID == details.ProductId { if details.Status == constvar.OperationStatus_Ready { available = available.Add(details.Amount) } else { data.Out = data.Out.Add(details.Amount) } } } data.AvailableNumber = data.Amount.Sub(available) result = append(result, data) } return } func (slf *InventoryReportFormsService) BuildSearch(params request.GetInventoryForms) ( search *models.MaterialSearch, productIds []string, productAmounts []*models.LocationProductAmount, locationIds []int, err error) { productAmounts = make([]*models.LocationProductAmount, 0) if params.WarehouseCode != "" { locations, err := models.NewLocationSearch().SetJointName(params.WarehouseCode).FindNotTotal() if err != nil { return nil, nil, nil, nil, err } for _, location := range locations { locationIds = append(locationIds, location.Id) } productAmounts, err = models.NewLocationProductAmountSearch().SetLocationIds(locationIds).SetQuery("amount > 0").Find() if err != nil { return nil, nil, nil, nil, err } for _, amount := range productAmounts { productIds = append(productIds, amount.ProductId) } } //查询产品 search = models.NewMaterialSearch() search.Orm = search.Orm.Model(&models.Material{}). Select(`material.id, material.name, material.cost, material.amount, material.unit, wms_product_category.name as category_name `). Joins("left join wms_product_category on material.category_id = wms_product_category.id") if len(params.CategoryIds) > 0 { search.Orm.Where("material.category_id in (?)", params.CategoryIds) } if params.KeyWord != "" { search.Orm.Where("material.name like ?", "%"+params.KeyWord+"%").Or("wms_product_category.name like ?", "%"+params.KeyWord+"%") } if len(productIds) > 0 { search.Orm.Where("material.id in (?)", productIds) } return search, productIds, productAmounts, locationIds, nil } func (slf *InventoryReportFormsService) Count(params request.GetInventoryForms) (total int64, err error) { search, _, _, _, err := slf.BuildSearch(params) if err != nil { return 0, err } if err := search.Orm.Count(&total).Error; err != nil { return 0, err } return total, nil } func (slf *InventoryReportFormsService) FetchAll(params request.GetInventoryForms) (list []*response.InventoryForms, err error) { total, err := slf.Count(params) if err != nil { return nil, err } list = make([]*response.InventoryForms, 0, total) params.PageSize = 500 page := 1 for { params.Page = page data, err := slf.Query(params) if err != nil { return nil, err } if len(data) == 0 { break } list = append(list, data...) page++ } return } func (slf *InventoryReportFormsService) Export(dataList []*response.InventoryForms) (filename string, err error) { var fileName string f := excelize.NewFile() // 自定义表头 headers := []string{"产品", "产品类别", "单位成本", "总价值", "在库", "可用库存", "入库", "出库", "单位"} // 设置表头 for i, header := range headers { cell := getColumnAlphabet(i+1) + "1" f.SetCellValue("Sheet1", cell, header) } for i, v := range dataList { column := strconv.Itoa(i + 2) f.SetCellValue("Sheet1", "A"+column, v.ProductName) f.SetCellValue("Sheet1", "B"+column, v.ProductType) f.SetCellValue("Sheet1", "C"+column, v.Cost) f.SetCellValue("Sheet1", "D"+column, v.Value) f.SetCellValue("Sheet1", "E"+column, v.Amount) f.SetCellValue("Sheet1", "F"+column, v.AvailableNumber) f.SetCellValue("Sheet1", "G"+column, v.In) f.SetCellValue("Sheet1", "H"+column, v.Out) f.SetCellValue("Sheet1", "I"+column, v.Unit) } fileName = fmt.Sprintf("库存报表%s.xlsx", time.Now().Format("2006-01-02-1504")) if err := f.SaveAs(fileName); err != nil { return fileName, err } return fileName, nil } // 根据索引获取 Excel 列名(A、B、C...) func getColumnAlphabet(index int) string { const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" if index <= 26 { return string(alphabet[index-1]) } return getColumnAlphabet((index-1)/26) + getColumnAlphabet((index-1)%26+1) }