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) if *material.MoreUnit { data.AmountMoreUnits = CreateMoreUnit(data.Amount, material.MoreUnitList) data.AvailableNumberMoreUnits = CreateMoreUnit(data.AvailableNumber, material.MoreUnitList) data.InMoreUnits = CreateMoreUnit(data.In, material.MoreUnitList) data.OutMoreUnits = CreateMoreUnit(data.Out, material.MoreUnitList) } 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) locationSearch := models.NewLocationSearch() if params.WarehouseCode != "" { locationSearch.SetJointName(params.WarehouseCode) } locations, err := locationSearch.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, material.more_unit, material.more_unit_value, 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" f.SetCellValue("Sheet1", cell, header) } f.SetCellValue("Sheet1", getColumnAlphabet(0)+"1", "产品") f.SetCellValue("Sheet1", getColumnAlphabet(1)+"1", "产品类别") f.SetCellValue("Sheet1", getColumnAlphabet(2)+"1", "单位成本") f.SetCellValue("Sheet1", getColumnAlphabet(3)+"1", "总价值") f.SetCellValue("Sheet1", getColumnAlphabet(4)+"1", "在库") f.MergeCell("Sheet1", getColumnAlphabet(4)+"1", getColumnAlphabet(7)+"1") // 合并单元格 f.SetCellValue("Sheet1", getColumnAlphabet(8)+"1", "可用库存") f.MergeCell("Sheet1", getColumnAlphabet(8)+"1", getColumnAlphabet(11)+"1") // 合并单元格 f.SetCellValue("Sheet1", getColumnAlphabet(12)+"1", "入库") f.MergeCell("Sheet1", getColumnAlphabet(12)+"1", getColumnAlphabet(15)+"1") // 合并单元格 f.SetCellValue("Sheet1", getColumnAlphabet(16)+"1", "出库") f.MergeCell("Sheet1", getColumnAlphabet(16)+"1", getColumnAlphabet(19)+"1") // 合并单元格 unitData := []string{"件", "匹", "米", "重量"} for i, header := range unitData { f.SetCellValue("Sheet1", getColumnAlphabet(i+4)+"2", header) f.SetCellValue("Sheet1", getColumnAlphabet(i+8)+"2", header) f.SetCellValue("Sheet1", getColumnAlphabet(i+12)+"2", header) f.SetCellValue("Sheet1", getColumnAlphabet(i+16)+"2", header) } // 设置表头样式 style := &excelize.Style{ Border: nil, Fill: excelize.Fill{ Type: "pattern", Pattern: 1, Shading: 0, }, Font: &excelize.Font{ Bold: true, }, Alignment: &excelize.Alignment{ Horizontal: "center", }, } titleStyle, err := f.NewStyle(style) if err != nil { return } lastColumn := getColumnAlphabet(19) f.SetCellStyle("Sheet1", "A1", lastColumn+"2", titleStyle) // 设置列宽 f.SetColWidth("Sheet1", "A", "D", 30) f.SetColWidth("Sheet1", "E", lastColumn, 15) for i, v := range dataList { column := strconv.Itoa(i + 3) f.SetCellValue("Sheet1", getColumnAlphabet(0)+column, v.ProductName) f.SetCellValue("Sheet1", getColumnAlphabet(1)+column, v.ProductType) f.SetCellValue("Sheet1", getColumnAlphabet(2)+column, v.Cost) f.SetCellValue("Sheet1", getColumnAlphabet(3)+column, v.Value) f.SetCellValue("Sheet1", getColumnAlphabet(4)+column, v.Amount) FillMoreUnitToExcel(v.Amount, v.AmountMoreUnits, 5, i+3, f) f.SetCellValue("Sheet1", getColumnAlphabet(8)+column, v.AvailableNumber) FillMoreUnitToExcel(v.AvailableNumber, v.AvailableNumberMoreUnits, 9, i+3, f) f.SetCellValue("Sheet1", getColumnAlphabet(12)+column, v.In) FillMoreUnitToExcel(v.In, v.AmountMoreUnits, 13, i+3, f) f.SetCellValue("Sheet1", getColumnAlphabet(16)+column, v.Out) FillMoreUnitToExcel(v.Out, v.AmountMoreUnits, 17, i+3, f) } 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]) } return getColumnAlphabet((index)/26) + getColumnAlphabet((index)%26+1) }