package models

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"
	"golang.org/x/sync/errgroup"
	"gorm.io/gorm"
	"math"
	"math/rand"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"
	"unicode/utf8"
)

const (
	NoIMEICommodity = 1 // 非串码商品类型

	SystemInventory   = 1 // 系统入库
	PurchaseInventory = 2 // 采购入库
	ProductInventory  = 3 // 产品入库
	CheckInventory    = 4 // 盘点入库

	InStock        = 1 // 在库
	SoldOut        = 2 // 已售
	PurchaseReturn = 3 // 采购退货
	InAllot        = 4 // 调拨中（调出门店）
	SystemOut      = 5 // 系统出库
	CheckOut       = 6 // 盘点出库
	OnSale         = 7 // 销售锁定中
	PurchaseCancel = 8 // 采购订单反审核
	PurchaseChange = 9 // 采购换机
)

// ErpStock 库存列表
type ErpStock struct {
	Model

	StoreId               uint32              `json:"store_id" gorm:"index"`                // 门店编号
	StoreName             string              `json:"store_name"`                           // 门店名称
	ErpCommodityId        uint32              `json:"erp_commodity_id" gorm:"index"`        // 商品id
	ErpCommodityName      string              `json:"erp_commodity_name"`                   // 商品名称
	ErpCategoryId         uint32              `json:"erp_category_id" gorm:"index"`         // 分类id
	ErpCategoryName       string              `json:"erp_category_name"`                    // 分类名称
	CommoditySerialNumber string              `json:"commodity_serial_number" gorm:"index"` // 商品编码/串码
	IMEIType              uint32              `json:"imei_type"`                            // 1-无串码 2-串码（系统生成） 3-串码（手动添加）
	RetailPrice           float64             `json:"retail_price"`                         // 指导零售价
	MinRetailPrice        float64             `json:"min_retail_price"`                     // 最低零售价
	Count                 uint32              `json:"count"`                                // 数量
	DispatchCount         uint32              `json:"dispatch_count"`                       // 调拨中数量（调拨中调入）
	Commodities           []ErpStockCommodity `json:"commodities" gorm:"-"`                 //
	DecisionStoreId       []uint32            `json:"decision_store_id" gorm:"-"`           // 门店编号列表（查询进销存的时候使用）
}

// ErpStockCommodity 库存详情
type ErpStockCommodity struct {
	Model

	ErpStockId            uint32    `json:"erp_stock_id" gorm:"index"`            // 库存id
	StoreId               uint32    `json:"store_id" gorm:"index"`                // 门店id
	StoreName             string    `json:"store_name"`                           // 门店名称
	ErpCommodityId        uint32    `json:"erp_commodity_id" gorm:"index"`        // 商品id
	ErpCommodityName      string    `json:"erp_commodity_name"`                   // 商品名称
	CommoditySerialNumber string    `json:"commodity_serial_number" gorm:"index"` // 商品编号
	ErpCategoryId         uint32    `json:"erp_category_id" gorm:"index"`         // 分类id
	ErpCategoryName       string    `json:"erp_category_name"`                    // 分类名称
	ErpBarcode            string    `json:"erp_barcode"`                          // 商品条码
	IMEIType              uint32    `json:"imei_type"`                            // 是否串码：1-无串码 2-串码（系统生成） 3-串码（手动添加）
	IMEI                  string    `json:"imei"`                                 // 商品串码
	ErpSupplierId         uint32    `json:"erp_supplier_id" gorm:"index"`         // 供应商id
	ErpSupplierName       string    `json:"erp_supplier_name"`                    // 供应商名称
	StockTime             time.Time `json:"stock_time"`                           // 最近入库时间
	RetailPrice           float64   `json:"retail_price"`                         // 指导零售价
	MinRetailPrice        float64   `json:"min_retail_price"`                     // 最低零售价
	StaffCostPrice        float64   `json:"staff_cost_price"`                     // 员工成本价加价（如：加价50，不是加价后的价格）
	WholesalePrice        float64   `json:"wholesale_price"`                      // 指导采购价
	MemberDiscount        float64   `json:"member_discount"`                      // 会员优惠
	State                 uint32    `json:"state"`                                // 状态:1-在库 2-已售 3-采购退货 4-调拨中 5-销售锁定中 6-产品出库 7-盘点出库
	Count                 uint32    `json:"count"`                                // 数量
	StorageType           uint32    `json:"storage_type"`                         // 入库方式：1-系统入库 2-采购入库 3-产品入库 4-盘点入库
	FirstStockTime        time.Time `json:"first_stock_time"`                     // 首次入库时间
	StockSn               string    `json:"stock_sn"`                             // 库存订单编号（跟采购入库的入库编号关联）
	OriginalSn            string    `json:"original_sn" gorm:"index"`             // 首次入库订单编号(单据编号）
	StockStartTime        time.Time `json:"stock_start_time" gorm:"-"`            // 最近入库开始时间
	StockEndTime          time.Time `json:"stock_end_time" gorm:"-"`              // 最近入库结束时间
	Age                   uint32    `json:"age" gorm:"-"`                         // 最近库龄
	AllAge                uint32    `json:"all_age" gorm:"-"`                     // 总库龄
	Remark                string    `json:"remark"`                               // 备注
	CategoryNumber        string    `json:"category_number" gorm:"-"`             // 商品分类编号
	//ErpOrderCommodityId   uint32    `json:"erp_order_commodity_id"`               // 零售订单商品表的主键ID（后端使用，前端忽略）
	//Commodity             ErpCommodity `json:"commodity" gorm:"-"`
}

// ErpCommodity 商品表
type ErpCommodity struct {
	Model

	SerialNumber     string  `json:"serial_number"`                   // 商品编号
	Number           uint32  `json:"number"`                          // 商品数量
	Name             string  `json:"name"`                            // 商品名称
	ErpCategoryId    uint32  `json:"erp_category_id" gorm:"index"`    // 商品分类id
	ErpCategoryName  string  `json:"erp_category_name"`               // 商品分类名称
	ErpBarcode       string  `json:"erp_barcode"`                     // 商品条码
	IsIMEI           uint32  `json:"is_imei"  gorm:"-"`               // 是否串码：1-串码类 2-非串码
	IMEIType         uint32  `json:"imei_type"`                       // 1-无串码 2-串码（系统生成） 3-串码（手动添加）
	IMEI             string  `json:"imei"`                            // 串码
	ErpSupplierId    uint32  `json:"erp_supplier_id" gorm:"index"`    // 主供应商id
	ErpSupplierName  string  `json:"erp_supplier_name"`               // 主供应商名称
	RetailPrice      float64 `json:"retail_price"`                    // 指导零售价
	MinRetailPrice   float64 `json:"min_retail_price"`                // 最低零售价
	StaffCostPrice   float64 `json:"staff_cost_price"`                // 员工成本价加价（如：加价50，不是加价后的价格）
	WholesalePrice   float64 `json:"wholesale_price"`                 // 指导采购价
	Brokerage1       float64 `json:"brokerage_1"`                     // 销售毛利提成
	Brokerage2       float64 `json:"brokerage_2"`                     // 员工毛利提成
	MemberDiscount   float64 `json:"member_discount"`                 // 会员优惠
	Origin           string  `json:"origin"`                          // 产地
	Remark           string  `json:"remark" gorm:"type:varchar(512)"` // 备注
	StockCount       uint32  `json:"stock_count" gorm:"-"`            // 库存数量
	Img              string  `json:"img"`                             // 图片
	StopPurchase     uint32  `json:"stop_purchase"`                   // 0-未勾选，正常采购；1-勾选，停止采购
	ManufacturerCode string  `json:"manufacturer_code"`               // 厂家编码

	ErpCategory *ErpCategory `json:"erp_category" gorm:"-"`
}

// ErpCategory 商品分类
type ErpCategory struct {
	Model

	Name     string        `json:"name"`     // 名称
	Priority string        `json:"priority"` // 分类
	Number   string        `json:"number"`
	FullNum  uint32        `json:"full_num"`
	State    uint32        `json:"state"` // 1-未使用 2-使用 3-隐藏
	Level    uint32        `json:"level"` // 分类层级
	Pid      uint32        `json:"pid" gorm:"index"`
	Sort     uint32        `json:"sort"`
	SubCats  []ErpCategory `json:"sub_cats" gorm:"-"` // 子列表
	// erp_category
}

// ErpSupplier 供应商
type ErpSupplier struct {
	Model

	Number         string `json:"number" gorm:"index"`
	Name           string `json:"name"`
	Contact        string `json:"contact"`
	Tel            string `json:"tel"`
	Address        string `json:"address"`
	OpeningBank    string `json:"opening_bank"`
	BankAccount    string `json:"bank_account"`
	PaymentCycle   uint32 `json:"payment_cycle"`
	TaxNumber      string `json:"tax_number"`
	StoreIds       string `json:"store_ids"`
	Landline       string `json:"landline"`
	Email          string `json:"email"`
	CompanyWebsite string `json:"company_website"`
	// erp_supplier
}

type ErpInventoryStock struct {
	Model

	StoreId               uint32  `json:"store_id" gorm:"index"`
	StoreName             string  `json:"store_name"`
	ErpCommodityId        uint32  `json:"erp_commodity_id" gorm:"index"`
	ErpCommodityName      string  `json:"erp_commodity_name"`
	ErpCategoryId         uint32  `json:"erp_category_id" gorm:"index"`
	ErpCategoryName       string  `json:"erp_category_name"`
	CommoditySerialNumber string  `json:"commodity_serial_number" gorm:"index"`
	IMEIType              uint32  `json:"imei_type"` // 1-无串码 2-串码（系统生成） 3-串码（手动添加）
	RetailPrice           float64 `json:"retail_price"`
	MinRetailPrice        float64 `json:"min_retail_price"`
	Count                 uint32  `json:"count"`
	Sn                    string  `json:"sn" gorm:"index"`
	//ErpSupplierId    uint32    `json:"erp_supplier_id" gorm:"index"`
	//ErpSupplierName  string    `json:"erp_supplier_name"`
	//IMEI             string    `json:"imei"`
	//StockTime        time.Time `json:"stock_time"`
	//StaffCostPrice   uint32    `json:"staff_cost_price"`
	//WholesalePrice   uint32    `json:"wholesale_price"`
	// erp_inventory_stock
}

type ErpInventoryStockCommodity struct {
	Model

	ErpInventoryStockId   uint32    `json:"erp_inventory_stock_id" gorm:"index"`
	ErpCommodityId        uint32    `json:"erp_commodity_id" gorm:"index"`
	ErpCommodityName      string    `json:"erp_commodity_name"`
	CommoditySerialNumber string    `json:"commodity_serial_number" gorm:"index"`
	IMEIType              uint32    `json:"imei_type"` // 1-无串码 2-串码（系统生成） 3-串码（手动添加）
	IMEI                  string    `json:"imei"`
	ErpSupplierId         uint32    `json:"erp_supplier_id" gorm:"index"`
	ErpSupplierName       string    `json:"erp_supplier_name"`
	StockTime             time.Time `json:"stock_time"`
	RetailPrice           float64   `json:"retail_price"`
	MinRetailPrice        float64   `json:"min_retail_price"`
	StaffCostPrice        float64   `json:"staff_cost_price"`
	WholesalePrice        float64   `json:"wholesale_price"`
	Count                 uint32    `json:"count"`
	StorageType           uint32    `json:"storage_type"`
	Sn                    string    `json:"sn" gorm:"index"`
	//StoreId          uint32    `json:"store_id" gorm:"index"`
	//StoreName        string    `json:"store_name"`
	//ErpCategoryId    uint32    `json:"erp_category_id" gorm:"index"`
	//ErpCategoryName  string    `json:"erp_category_name"`
	// erp_inventory_stock_commodity
}

// QueryCodeReq 查询商品串码或条码入参
type QueryCodeReq struct {
	ScanCode  string `json:"scan_code"  binding:"required"` // 扫码枪扫码数据：串码/条码
	PageIndex int    `json:"pageIndex"`                     // 页码
	PageSize  int    `json:"pageSize"`                      // 页面条数
	State     uint32 `json:"state"`                         // 库存状态:1-在库 2-已售 3-采购退货 4-调拨中 5-出库（前端只看1，4）
	StoreId   uint32 `json:"store_id"`                      // 门店id
}

type QueryCodeResp struct {
	List      []string `json:"list"`      // 串码/条码
	Total     int      `json:"count"`     // 数据总条数
	PageIndex int      `json:"pageIndex"` // 页码
	PageSize  int      `json:"pageSize"`  // 每页展示条数
}

// QueryNameReq 通过名称模糊查询商品库存详情入参
type QueryNameReq struct {
	CommodityName string `json:"commodity_name"   binding:"required"` // 商品名称
	PageIndex     int    `json:"pageIndex"`                           // 页码
	PageSize      int    `json:"pageSize"`                            // 每页展示数据条数
	State         uint32 `json:"state"`                               // 库存状态:1-在库 2-已售 3-采购退货 4-调拨中 5-出库（前端只看1，4）
	StoreId       uint32 `json:"store_id"`                            // 门店id
}

// IdInit 新增/编辑商品时获取分类和供应商信息
func (c *ErpCommodity) IdInit() error {
	if c.ErpCategoryId != 0 {
		if c.ErpCategory == nil {
			category, err := GetErpCategory(c.ErpCategoryId)
			if err != nil || category.ID == 0 {
				//logger.Error("get erp category err:", err)
				return errors.New("商品分类信息不存在")
			}
			c.ErpCategory = category
		}
		c.ErpCategoryName = c.ErpCategory.Name
	}

	if c.ErpSupplierId != 0 {
		supplier, err := GetErpSupplier(c.ErpSupplierId)
		if err != nil || supplier.ID == 0 {
			//logger.Error("get erp category err:", err)
			return errors.New("供应商信息不存在")
		} else {
			c.ErpSupplierName = supplier.Name
		}
	}

	return nil
}

// GetErpCategory 根据id查询分类信息
func GetErpCategory(id uint32) (*ErpCategory, error) {
	category := new(ErpCategory)
	err := orm.Eloquent.Table("erp_category").Where("id=?", id).Find(category).Error
	if err != nil {
		//logger.Error("category err:", err)
		return category, err
	}
	return category, nil
}

// GetErpSupplier 根据id查询供应商
func GetErpSupplier(id uint32) (*ErpSupplier, error) {
	supplier := new(ErpSupplier)
	err := orm.Eloquent.Table("erp_supplier").Where("id=?", id).Find(supplier).Error
	if err != nil {
		//logger.Error("category err:", err)
		return supplier, err
	}
	return supplier, err
}

// SetErpCategory 新增/编辑商品时设置分类信息
func (c *ErpCommodity) SetErpCategory() error {
	if c.ErpCategoryId != 0 {
		category, err := GetErpCategory(c.ErpCategoryId)
		if err != nil {
			logger.Errorf("get erp category err:", err)
			return err
		}
		c.ErpCategory = category
		return nil
	}
	return errors.New("erp category id null")
}

type ErpCommodityListReq struct {
	SerialNumber     string `json:"serial_number"`      // 商品编号
	ManufacturerCode string `json:"manufacturer_code"`  // 厂家编码
	ErpCommodityName string `json:"erp_commodity_name"` // 商品名称
	ErpCategoryId    uint32 `json:"erp_category_id"`    // 商品分类id
	IMEI             string `json:"imei"`               // 串码
	ErpBarcode       string `json:"erp_barcode"`        // 商品条码
	ErpSupplierId    uint32 `json:"erp_supplier_id"`    // 供应商id
	PurchaseType     uint32 `json:"purchase_type"`      // 1-正常采购，2-停止采购
	PageIndex        int    `json:"pageIndex"`          // 页码
	PageSize         int    `json:"pageSize"`           // 每页展示数据条数
	IsExport         uint32 `json:"is_export"`          // 1-导出
}
type ErpCommodityListResp struct {
	List      []ErpCommodity `json:"list"`
	Total     int            `json:"total"`      // 数据总条数
	PageIndex int            `json:"pageIndex"`  // 页码
	PageSize  int            `json:"pageSize"`   // 每页展示条数
	ExportUrl string         `json:"export_url"` // 文件路径
}

//func (m *ErpCommodityListReq) List() (*ErpCommodityListResp, error) {
//	resp := &ErpCommodityListResp{
//		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_commodity")
//	if m.SerialNumber != "" {
//		qs = qs.Where("serial_number=?", m.SerialNumber)
//	}
//	if m.ErpCommodityName != "" {
//		qs = qs.Where("name Like '%" + m.ErpCommodityName + "%'")
//		//qs = qs.Where("name LIKE ?", m.Name)
//	}
//	if m.IMEI != "" {
//		qs = qs.Where("imei=?", m.IMEI)
//	}
//	if m.ErpCategoryId != 0 {
//		categoryInfo, err := GetErpCategory(m.ErpCategoryId)
//		if err != nil {
//			return nil, err
//		}
//		qs = qs.Where("serial_number like ?", categoryInfo.Number+"%")
//	}
//	if m.ErpSupplierId != 0 {
//		qs = qs.Where("erp_supplier_id=?", m.ErpSupplierId)
//	}
//	if m.ErpBarcode != "" {
//		qs = qs.Where("erp_barcode LIKE ?", "%"+m.ErpBarcode+"%")
//	}
//
//	var count int64
//	err := qs.Count(&count).Error
//	if err != nil {
//		//logger.Error("count err:", err)
//		return resp, err
//	}
//
//	var commodities []ErpCommodity
//	err = qs.Order("id DESC").Find(&commodities).Error
//	if err != nil && err != RecordNotFound {
//		//logger.Error("dailys err:", err)
//		return resp, err
//	}
//
//	// 按商品编号进行排序
//	SortCommoditiesAsc(commodities)
//
//	if m.IsExport == 1 {
//		listExport, err := ErpCommodityListExport(commodities)
//		if err != nil {
//			//logger.Error("list export err:", err)
//		}
//		resp.ExportUrl = listExport
//	} else {
//		for i, v := range commodities {
//			if v.IMEIType == 1 { //无串码
//				commodities[i].IsIMEI = 2 // 非串码
//			} else {
//				commodities[i].IsIMEI = 1 // 串码
//			}
//
//			// 查询库存数量
//			var stockCount int64
//			err = orm.Eloquent.Table("erp_stock_commodity").Where("erp_commodity_id = ? and state = ?",
//				v.ID, InStock).Count(&stockCount).Error
//			if err != nil {
//				return nil, err
//			}
//			commodities[i].StockCount = uint32(stockCount)
//		}
//
//		// 计算分页所需的切片索引
//		startIndex := page * m.PageSize
//		endIndex := (page + 1) * m.PageSize
//		if endIndex > len(commodities) {
//			endIndex = len(commodities)
//		}
//		resp.List = commodities[startIndex:endIndex]
//
//		//跟之前保持一致
//		resp.Total = int(count)
//		resp.PageIndex = page + 1
//		resp.PageSize = m.PageSize
//	}
//
//	return resp, nil
//}

func (m *ErpCommodityListReq) List() (*ErpCommodityListResp, error) {
	resp := &ErpCommodityListResp{
		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.Table("erp_commodity")
	if m.SerialNumber != "" {
		qs = qs.Where("serial_number = ?", m.SerialNumber)
	}
	if m.ManufacturerCode != "" {
		qs = qs.Where("manufacturer_code = ?", m.ManufacturerCode)
	}
	if m.ErpCommodityName != "" {
		qs = qs.Where("name LIKE ?", "%"+m.ErpCommodityName+"%")
	}
	if m.IMEI != "" {
		qs = qs.Where("imei = ?", m.IMEI)
	}
	if m.ErpCategoryId != 0 {
		categoryInfo, err := GetErpCategory(m.ErpCategoryId)
		if err != nil {
			return nil, err
		}
		qs = qs.Where("serial_number LIKE ?", categoryInfo.Number+"%")
	}
	if m.ErpSupplierId != 0 {
		qs = qs.Where("erp_supplier_id = ?", m.ErpSupplierId)
	}
	if m.ErpBarcode != "" {
		qs = qs.Where("erp_barcode LIKE ?", "%"+m.ErpBarcode+"%")
	}
	if m.PurchaseType != 0 {
		switch m.PurchaseType {
		case 1: // 正常采购
			qs = qs.Where("stop_purchase = ?", 0)
		case 2: // 停止采购
			qs = qs.Where("stop_purchase = ?", 1)
		}
	}

	var count int64
	err := qs.Count(&count).Error
	if err != nil {
		return resp, err
	}

	var commodities []ErpCommodity
	err = qs.Order("id DESC").Find(&commodities).Error
	if err != nil && err != RecordNotFound {
		return resp, err
	}

	// 按商品编号进行排序
	SortCommoditiesAsc(commodities)

	if m.IsExport == 1 {
		listExport, err := ErpCommodityListExport(commodities)
		if err != nil {
			return resp, err
		}
		resp.ExportUrl = listExport
	} else {
		commodityIDs := make([]uint32, len(commodities))
		for i, v := range commodities {
			commodityIDs[i] = v.ID
			if v.IMEIType == 1 {
				commodities[i].IsIMEI = 2
			} else {
				commodities[i].IsIMEI = 1
			}
		}

		// 批量查询库存数量
		var stockCounts []struct {
			ErpCommodityID uint32 `gorm:"column:erp_commodity_id"`
			StockCount     int64  `gorm:"column:stock_count"`
		}
		err = orm.Eloquent.Table("erp_stock_commodity").
			Where("erp_commodity_id IN (?) AND state = ?", commodityIDs, InStock).
			Select("erp_commodity_id, COUNT(*) as stock_count").
			Group("erp_commodity_id").
			Scan(&stockCounts).Error

		if err != nil {
			return nil, err
		}

		// 将库存数量填充回commodities
		stockCountMap := make(map[uint32]int64)
		for _, sc := range stockCounts {
			stockCountMap[sc.ErpCommodityID] = sc.StockCount
		}
		for i, v := range commodities {
			commodities[i].StockCount = uint32(stockCountMap[v.ID])
		}

		// 计算分页所需的切片索引
		startIndex := page * m.PageSize
		if (len(commodities)/m.PageSize+1)*m.PageSize < startIndex {
			startIndex = 0
			page = 0
		}
		if startIndex > len(commodities) {
			startIndex = 0
			page = 0
		}
		endIndex := (page + 1) * m.PageSize
		if endIndex > len(commodities) {
			endIndex = len(commodities)
		}
		resp.List = commodities[startIndex:endIndex]

		resp.Total = int(count)
		resp.PageIndex = page + 1
		resp.PageSize = m.PageSize
	}

	return resp, nil
}

// SortByErpSupplierId 对商品数组进行排序，先按供应商ID排序，如果相同则按商品编号排序（升序）
func SortByErpSupplierId(commodities []ErpCommodity, supplierIDDesc bool) {
	// 定义排序函数
	less := func(i, j int) bool {
		// 按照供应商ID排序
		if commodities[i].ErpSupplierId != commodities[j].ErpSupplierId {
			if supplierIDDesc { // 降序 DESC
				return commodities[i].ErpSupplierId > commodities[j].ErpSupplierId
			}
			return commodities[i].ErpSupplierId < commodities[j].ErpSupplierId // 升序 ASC
		}

		// 解析商品编号，提取分类编号和商品编号的数字部分
		catNumI, subCatNumI, threeSubCatNumI, itemNumI := parseSerialNumber(commodities[i].SerialNumber)
		catNumJ, subCatNumJ, threeSubCatNumJ, itemNumJ := parseSerialNumber(commodities[j].SerialNumber)

		// 按照分类编号从小到大排序
		if catNumI != catNumJ {
			return catNumI < catNumJ
		}

		// 如果分类编号相同，按照具体分类下的商品编号递增排序
		if subCatNumI != subCatNumJ {
			return subCatNumI < subCatNumJ
		}

		if threeSubCatNumI != threeSubCatNumJ {
			return threeSubCatNumI < threeSubCatNumJ
		}

		// 如果具体分类编号也相同，按照商品编号递增排序
		return itemNumI < itemNumJ
	}

	// 调用排序函数进行排序
	sort.SliceStable(commodities, less)
}

// SortStockCommodities 对库存商品数组进行排序
func SortStockCommodities(commodities []ErpStock) {
	// 定义排序函数
	less := func(i, j int) bool {
		// 解析商品编号，提取分类编号和商品编号的数字部分
		catNumI, subCatNumI, threeSubCatNumI, itemNumI := parseSerialNumber(commodities[i].CommoditySerialNumber)
		catNumJ, subCatNumJ, threeSubCatNumJ, itemNumJ := parseSerialNumber(commodities[j].CommoditySerialNumber)

		// 按照分类编号从小到大排序
		if catNumI != catNumJ {
			return catNumI < catNumJ
		}

		// 如果分类编号相同，按照具体分类下的商品编号递增排序
		if subCatNumI != subCatNumJ {
			return subCatNumI < subCatNumJ
		}

		if threeSubCatNumI != threeSubCatNumJ {
			return threeSubCatNumI < threeSubCatNumJ
		}

		// 如果具体分类编号也相同，按照商品编号递增排序
		return itemNumI < itemNumJ
	}

	// 调用排序函数进行排序
	sort.SliceStable(commodities, less)
}

// SortCommoditiesAsc 对商品数组进行排序（升序）
func SortCommoditiesAsc(commodities []ErpCommodity) {
	// 定义排序函数
	less := func(i, j int) bool {
		// 解析商品编号，提取分类编号和商品编号的数字部分
		catNumI, subCatNumI, threeSubCatNumI, itemNumI := parseSerialNumber(commodities[i].SerialNumber)
		catNumJ, subCatNumJ, threeSubCatNumJ, itemNumJ := parseSerialNumber(commodities[j].SerialNumber)

		// 按照分类编号从小到大排序
		if catNumI != catNumJ {
			return catNumI < catNumJ
		}

		// 如果分类编号相同，按照具体分类下的商品编号递增排序
		if subCatNumI != subCatNumJ {
			return subCatNumI < subCatNumJ
		}

		if threeSubCatNumI != threeSubCatNumJ {
			return threeSubCatNumI < threeSubCatNumJ
		}

		// 如果具体分类编号也相同，按照商品编号递增排序
		return itemNumI < itemNumJ
	}

	// 调用排序函数进行排序
	sort.SliceStable(commodities, less)
}

// SortCommoditiesDesc 对商品数组进行排序（降序）
func SortCommoditiesDesc(commodities []ErpCommodity) {
	// 定义排序函数
	less := func(i, j int) bool {
		// 解析商品编号，提取分类编号和商品编号的数字部分
		catNumI, subCatNumI, threeSubCatNumI, itemNumI := parseSerialNumber(commodities[i].SerialNumber)
		catNumJ, subCatNumJ, threeSubCatNumJ, itemNumJ := parseSerialNumber(commodities[j].SerialNumber)

		// 按照分类编号从大到小排序
		if catNumI != catNumJ {
			return catNumI > catNumJ
		}

		// 如果分类编号相同，按照具体分类下的商品编号递增排序
		if subCatNumI != subCatNumJ {
			return subCatNumI > subCatNumJ
		}

		if threeSubCatNumI != threeSubCatNumJ {
			return threeSubCatNumI > threeSubCatNumJ
		}

		// 如果具体分类编号也相同，按照商品编号递增排序
		return itemNumI > itemNumJ
	}

	// 调用排序函数进行排序
	sort.SliceStable(commodities, less)
}

// SortReportByAllotDataCommodities 对商品数组进行排序
func SortReportByAllotDataCommodities(commodities []ReportByAllotData) {
	// 定义排序函数
	less := func(i, j int) bool {
		// 解析商品编号，提取分类编号和商品编号的数字部分
		catNumI, subCatNumI, threeSubCatNumI, itemNumI := parseSerialNumber(commodities[i].CommoditySerialNumber)
		catNumJ, subCatNumJ, threeSubCatNumJ, itemNumJ := parseSerialNumber(commodities[j].CommoditySerialNumber)

		// 按照分类编号从小到大排序
		if catNumI != catNumJ {
			return catNumI < catNumJ
		}

		// 如果分类编号相同，按照具体分类下的商品编号递增排序
		if subCatNumI != subCatNumJ {
			return subCatNumI < subCatNumJ
		}

		if threeSubCatNumI != threeSubCatNumJ {
			return threeSubCatNumI < threeSubCatNumJ
		}

		// 如果具体分类编号也相同，按照商品编号递增排序
		return itemNumI < itemNumJ
	}

	// 调用排序函数进行排序
	sort.SliceStable(commodities, less)
}

// SortReportByDecisionCommodities 对商品数组进行排序
func SortReportByDecisionCommodities(commodities []DecisionReportData) {
	// 定义排序函数
	less := func(i, j int) bool {
		// 解析商品编号，提取分类编号和商品编号的数字部分
		catNumI, subCatNumI, threeSubCatNumI, itemNumI := parseSerialNumber(commodities[i].CommoditySerialNumber)
		catNumJ, subCatNumJ, threeSubCatNumJ, itemNumJ := parseSerialNumber(commodities[j].CommoditySerialNumber)

		// 按照分类编号从小到大排序
		if catNumI != catNumJ {
			return catNumI < catNumJ
		}

		// 如果分类编号相同，按照具体分类下的商品编号递增排序
		if subCatNumI != subCatNumJ {
			return subCatNumI < subCatNumJ
		}

		if threeSubCatNumI != threeSubCatNumJ {
			return threeSubCatNumI < threeSubCatNumJ
		}

		// 如果具体分类编号也相同，按照商品编号递增排序
		return itemNumI < itemNumJ
	}

	// 调用排序函数进行排序
	sort.SliceStable(commodities, less)
}

// SortReportByOtherDataCommodities 对商品数组进行排序
func SortReportByOtherDataCommodities(commodities []ReportByOtherData) {
	// 定义排序函数
	less := func(i, j int) bool {
		// 解析商品编号，提取分类编号和商品编号的数字部分
		catNumI, subCatNumI, threeSubCatNumI, itemNumI := parseSerialNumber(commodities[i].CommoditySerialNumber)
		catNumJ, subCatNumJ, threeSubCatNumJ, itemNumJ := parseSerialNumber(commodities[j].CommoditySerialNumber)

		// 按照分类编号从小到大排序
		if catNumI != catNumJ {
			return catNumI < catNumJ
		}

		// 如果分类编号相同，按照具体分类下的商品编号递增排序
		if subCatNumI != subCatNumJ {
			return subCatNumI < subCatNumJ
		}

		if threeSubCatNumI != threeSubCatNumJ {
			return threeSubCatNumI < threeSubCatNumJ
		}

		// 如果具体分类编号也相同，按照商品编号递增排序
		return itemNumI < itemNumJ
	}

	// 调用排序函数进行排序
	sort.SliceStable(commodities, less)
}

// parseSerialNumber 解析商品编号，提取分类编号、具体分类编号和商品编号的数字部分
func parseSerialNumber(serialNumber string) (catNum, subCatNum, threeSubCatNum, itemNum int) {
	if len(serialNumber) < 3 {
		return 0, 0, 0, 0 // 如果商品编号长度不足3位，则返回默认值
	}
	a := serialNumber[:3]
	fmt.Println(a)
	catNum, _ = strconv.Atoi(serialNumber[:3])

	switch len(serialNumber) {
	case 7: // 一级分类
		subCatNum, _ = strconv.Atoi(serialNumber[3:7])
	case 10: // 二级分类
		b := serialNumber[3:6]
		fmt.Println(b)
		subCatNum, _ = strconv.Atoi(serialNumber[3:6])
		threeSubCatNum = 0
		c := serialNumber[6:]
		fmt.Println(c)
		itemNum, _ = strconv.Atoi(serialNumber[6:])
	case 13: // 三级分类
		subCatNum, _ = strconv.Atoi(serialNumber[3:6])
		d := serialNumber[6:9]
		fmt.Println(d)
		threeSubCatNum, _ = strconv.Atoi(serialNumber[6:9])
		e := serialNumber[9:]
		fmt.Println(e)
		itemNum, _ = strconv.Atoi(serialNumber[9:])
	}

	return catNum, subCatNum, threeSubCatNum, itemNum
}

type ErpCategoryListReq struct {
	ErpCategoryId uint32 `json:"erp_category_id"`
	Pid           uint32 `json:"pid"`
	Level         uint32 `json:"level"` // 分类层级
	State         uint32 `json:"state"` // 1-未使用 2-使用 3-隐藏
	PageNum       int    `json:"page_num"`
	PageSize      int    `json:"page_size"`
	IsExport      uint32 `json:"is_export"` // 1-导出
}
type ErpCategoryListResp struct {
	List      []ErpCategory `json:"list"`
	Total     int           `json:"total"`
	PageNum   int           `json:"page_num"`
	PageSize  int           `json:"page_size"`
	ExportUrl string        `json:"export_url"`
}

func (m *ErpCategoryListReq) List() (*ErpCategoryListResp, error) {
	resp := &ErpCategoryListResp{
		PageNum:  m.PageNum,
		PageSize: m.PageSize,
	}
	//page := m.PageNum - 1
	//if page < 0 {
	//	page = 0
	//}
	//if m.PageSize == 0 {
	//	m.PageSize = 10
	//}
	//qs := orm.Eloquent.Table("erp_category")
	//if m.Level != 0 {
	//	qs = qs.Where("level=?", m.Level)
	//}
	////if m.ErpCategoryId != 0 {
	////	qs = qs.IDEq(m.ErpCategoryId)
	////}
	//if m.Pid != 0 {
	//	qs = qs.Where("pid", m.Pid)
	//}
	//var count int64
	//err := qs.Total(&count).Error
	//if err != nil {
	//	logger.Error("count err:", err)
	//	return resp, err
	//}
	//resp.Total = int(count)/m.PageSize + 1
	//var categories []ErpCategory
	//err = qs.Order("id DESC").Offset(page * m.PageSize).Limit(m.PageSize).Find(&categories).Error
	//if err != nil && err != RecordNotFound {
	//	logger.Error("erp commodity list err:", err)
	//	return resp, err
	//}

	var categories []ErpCategory
	qs := orm.Eloquent.Table("erp_category").Where("level=?", 1)
	if m.Pid != 0 {
		qs = qs.Where("id=?", m.Pid)
	}
	if m.State != 0 {
		qs.Where("state=?", m.State)
	}

	err := qs.Order("sort DESC").Find(&categories).Error
	if err != nil {
		//logger.Error("erp commodity list err:", err)
		return resp, err
	}
	pids := make([]uint32, 0)
	for i, _ := range categories {
		pids = append(pids, categories[i].ID)
	}

	var subCat []ErpCategory
	subQs := orm.Eloquent.Table("erp_category").Where("pid in (?)", pids)
	if m.State != 0 {
		subQs.Where("state=?", m.State)
	}
	err = subQs.Order("sort DESC").Find(&subCat).Error
	if err != nil {
		logger.Errorf("pCat err:%#v", err)
		return resp, err
	}

	pCatMap := make(map[uint32][]ErpCategory, 0)
	for i, _ := range subCat {
		pCatMap[subCat[i].Pid] = append(pCatMap[subCat[i].Pid], subCat[i])
	}

	for i, _ := range categories {
		v, ok := pCatMap[categories[i].ID]

		if ok {
			categories[i].SubCats = v
		}
	}

	if m.IsExport == 1 {
		listExport, err := ErpCategoryListExport(categories)
		if err != nil {
			//logger.Error("list export err:", err)
		}
		resp.ExportUrl = listExport
	}

	resp.List = categories
	return resp, nil
}

type CommodityNumberCount struct {
	NumberMap map[uint32]uint32 `json:"number_map"`
}

func (m *CommodityNumberCount) GetErpCommodityNumberByCategoryId(categoryId uint32) (uint32, error) {
	v, ok := m.NumberMap[categoryId]
	if ok {
		m.NumberMap[categoryId] = v + 1
		return v + 1, nil
	}
	var commodity ErpCommodity
	err := orm.Eloquent.Raw(fmt.Sprintf(
		"SELECT number FROM erp_commodity WHERE erp_category_id=%d ORDER BY id DESC LIMIT 0,1;", categoryId)).Scan(&commodity).Error
	if err != nil {
		//logger.Error("all categories map err:", err)
		return 0, err
	}
	m.NumberMap[categoryId] = commodity.Number + 1
	return commodity.Number + 1, nil
}

var EsStockLock sync.Mutex

type StockImporter struct {
	CensusMap    map[uint32]map[uint32]uint32
	CommodityMap map[uint32]*ErpCommodity
	StoreMap     map[uint32]string
	Inventories  []*ErpInventoryStock
}

type ErpStockFileExcel struct {
	StockTimeString string `json:"stock_time_string"`
	IsIMEI          string `json:"is_imei"`

	RetailPriceString    string `json:"retail_price_string"`
	MinRetailPriceString string `json:"min_retail_price_string"`
	StaffCostPriceString string `json:"staff_cost_price_string"` // 员工成本价加价
	WholesalePriceString string `json:"wholesale_price_string"`  // 指导采购价
	CountString          string `json:"count_string"`            // 数量

	//StoreName        string `json:"store_name"`
	//ErpCommodityName string `json:"erp_commodity_name"`
	//ErpCategoryName  string `json:"erp_category_name"`
	//SerialNumber     string `json:"serial_number"`
	//IMEIType         uint32 `json:"imei_type"` // 1-无串码 2-串码
	//IMEI             string `json:"imei"`
	//ErpSupplierName  string `json:"erp_supplier_name"`

	ErpStockCommodity
}

func (e *ErpStockFileExcel) Processing() {
	e.Count = IntStringToUin32(e.CountString)
	if e.IsIMEI == "是" {
		e.IMEIType = 2
		e.Count = 1
	} else if e.IsIMEI == "否" {
		e.IMEIType = 1
	}
	//parseTime, err := time.Parse(DateTimeFormat, e.StockTimeString)
	//fmt.Println("StockTimeString:", e.StockTimeString)
	//parseTime, err := time.Parse("01-02-06", e.StockTimeString)
	format := "2006/01/02"
	parseTime, err := time.Parse(format, e.StockTimeString)
	if err != nil {
		//logger.Error("parse err:", err)
		parseTime = time.Now()
	}
	e.StockTime = parseTime

	e.RetailPrice = PercentFloatStringToFloat64(e.RetailPriceString)
	e.MinRetailPrice = PercentFloatStringToFloat64(e.MinRetailPriceString)
	e.StaffCostPrice = PercentFloatStringToFloat64(e.StaffCostPriceString)
	e.WholesalePrice = PercentFloatStringToFloat64(e.WholesalePriceString)

}

// ImportStockData 库存导入
// 更新库存表和商品表
// 库存表：插入对应的库存数据
// 库存商品表：插入对应库存商品的数据；供应商只需判断是否在供应商列表即可 todo 库存商品表未插入库存id
func (m *StockImporter) ImportStockData(colsMap []map[string]interface{}) error {
	list, err := transStockData(colsMap)
	if err != nil {
		return err
	}

	var erpStockCommodity []ErpStockCommodity
	storeNameMap := make(map[string]uint32, 0)
	erpCommodityMap := make(map[string]*ErpCommodity, 0)
	erpSupplierNameMap := make(map[string]uint32, 0)

	storeNames := make([]string, 0, len(list))
	erpCommodityNames := make([]string, 0, len(list))
	erpSupplierNames := make([]string, 0, len(list))

	for i, _ := range list {
		_, ok1 := storeNameMap[list[i].StoreName]
		if !ok1 {
			storeNames = append(storeNames, list[i].StoreName)
		}
		_, ok2 := erpCommodityMap[list[i].Name]
		if !ok2 {
			if list[i].Name == "" {
				//如果商品名称为空，则需要补全
				commodityName, err := getCommodityNameBySerialNum(list[i].SerialNum)
				if err != nil {
					logger.Errorf("getCommodityNameBySerialNum err:", logger.Field("err", err))
					return err
				}

				list[i].Name = commodityName
			}
			erpCommodityNames = append(erpCommodityNames, list[i].Name)

		}
		_, ok3 := erpSupplierNameMap[list[i].SupplierName]
		if !ok3 {
			erpSupplierNames = append(erpSupplierNames, list[i].SupplierName)
		}
		storeNameMap[list[i].StoreName] = uint32(0)
		erpCommodityMap[list[i].Name] = nil
		erpSupplierNameMap[list[i].SupplierName] = uint32(0)
	}

	var stores []Store
	err = orm.Eloquent.Table("store").Where("name IN (?)", storeNames).Find(&stores).Error
	if err != nil {
		//logger.Error("stores err:", err)
		return err
	}
	var erpCommodities []ErpCommodity
	err = orm.Eloquent.Table("erp_commodity").Where("name IN (?)", erpCommodityNames).Find(&erpCommodities).Error
	if err != nil {
		//logger.Error("stores err:", err)
		return err
	}
	var erpSuppliers []ErpSupplier
	err = orm.Eloquent.Table("erp_supplier").Debug().Where("name IN (?)", erpSupplierNames).Find(&erpSuppliers).Error
	if err != nil && !errors.Is(err, RecordNotFound) {
		//logger.Error("stores err:", err)
		return err
	}

	for i, _ := range stores {
		storeNameMap[stores[i].Name] = stores[i].ID
		m.StoreMap[stores[i].ID] = stores[i].Name
	}
	for i, _ := range erpCommodities {
		erpCommodityMap[erpCommodities[i].Name] = &erpCommodities[i]
		m.CommodityMap[erpCommodities[i].ID] = &erpCommodities[i]
	}
	for i, _ := range erpSuppliers {
		erpSupplierNameMap[erpSuppliers[i].Name] = erpSuppliers[i].ID
	}

	nowTime := time.Now()
	for i, _ := range list {
		v1, ok1 := storeNameMap[list[i].StoreName]
		if !ok1 {
			logger.Errorf("erp store name err:%s", list[i].StoreName)
			fmt.Printf("erp store name err:%s", list[i].StoreName)
			return errors.New(fmt.Sprintf("导入门店名称[%s]跟系统不符，请检查", list[i].StoreName))
		}
		v2, ok2 := erpCommodityMap[list[i].Name]
		if !ok2 || v2 == nil {
			logger.Errorf("erp commodity name err:%s", list[i].Name)
			fmt.Printf("erp commodity name err:%s", list[i].Name)
			return errors.New(fmt.Sprintf("导入商品名称[%s]跟系统不符，请检查", list[i].Name))
		}
		v3, ok3 := erpSupplierNameMap[list[i].SupplierName]
		if !ok3 {
			logger.Errorf("erp supplier name err:%s", list[i].SupplierName)
			fmt.Printf("erp supplier name err:%s", list[i].SupplierName)
			return errors.New(fmt.Sprintf("导入供应商名称[%s]跟系统不符，请检查", list[i].SupplierName))
		}

		// 注意：表格导入是员工成本价，数据库存储是员工成本价加价=员工成本价-指导采购价
		nStaffCostPrice, err := strconv.ParseFloat(list[i].StaffCostPrice, 64)
		if err != nil {
			return fmt.Errorf("员工成本价转换有误:[%v]", err)
		}
		nWholesalePrice, err := strconv.ParseFloat(list[i].WholesalePrice, 64) // 指导采购价
		if err != nil {
			return fmt.Errorf("指导采购价转换有误:[%v]", err)
		}
		if nStaffCostPrice < nWholesalePrice {
			return fmt.Errorf("导入价格有误，员工成本价低于采购价")
		}

		nCount, err := strconv.ParseUint(list[i].Count, 10, 32)
		if err != nil {
			return fmt.Errorf("数量转换有误:[%v]", err)
		}

		for j := 0; j < int(nCount); j++ { // 商品库存表都是单笔数据，如果非串码商品有多个，需要插入多条数据
			stockCommodity := ErpStockCommodity{
				StoreId:               v1,
				StoreName:             list[i].StoreName,
				ErpCommodityId:        v2.ID,
				ErpCommodityName:      v2.Name,
				CommoditySerialNumber: v2.SerialNumber,
				ErpCategoryId:         v2.ErpCategoryId,
				ErpCategoryName:       v2.ErpCategoryName,
				ErpSupplierId:         v3,
				ErpSupplierName:       list[i].SupplierName,
				StaffCostPrice:        tools.RoundToTwoDecimalPlaces(nStaffCostPrice - nWholesalePrice),
				WholesalePrice:        tools.RoundToTwoDecimalPlaces(nWholesalePrice),
				State:                 InStock,
				StorageType:           SystemInventory,
				FirstStockTime:        nowTime,
				StockTime:             nowTime,
				Count:                 1,
				ErpBarcode:            v2.ErpBarcode, // 240106新增商品条码
				IMEIType:              v2.IMEIType,
				IMEI:                  "",
				Remark:                "",
				MemberDiscount:        v2.MemberDiscount,
				MinRetailPrice:        v2.MinRetailPrice,
				RetailPrice:           v2.RetailPrice,
			} // todo 首次入库订单编号、订单编号还未赋值

			if list[i].StockTime != "" { //导入时间不为空
				local, _ := time.LoadLocation("Local")
				parsedTime, _ := time.ParseInLocation("2006/1/2", list[i].StockTime, local)
				//parsedTime, _ := time.Parse("2006/1/2", list[i].StockTime)
				stockCommodity.FirstStockTime = parsedTime
				stockCommodity.StockTime = parsedTime
			}

			//if list[i].SysGenerate != "" && v2.IMEIType == 3 { //导入串码不为空，则默认为3手动添加
			//	stockCommodity.IMEIType = 3
			//	stockCommodity.IMEI = list[i].SysGenerate
			//} else if v2.IMEIType == 2 { // 如果该商品串码类型是：2-串码（系统生成），则系统自动生成
			//	stockCommodity.IMEIType = 2
			//	serialCode, err := generateSerialCode(v2.ErpCategoryId)
			//	if err != nil {
			//		return err
			//	}
			//	stockCommodity.IMEI = serialCode
			//}

			if list[i].SysGenerate != "" && v2.IMEIType != 1 { // 商品为串码商品，且导入串码不为空
				stockCommodity.IMEIType = v2.IMEIType
				stockCommodity.IMEI = list[i].SysGenerate
			}

			erpStockCommodity = append(erpStockCommodity, stockCommodity)

			_, ok4 := m.CensusMap[stockCommodity.StoreId]
			if ok4 {
				m.CensusMap[stockCommodity.StoreId][stockCommodity.ErpCommodityId] += stockCommodity.Count
			} else {
				m.CensusMap[stockCommodity.StoreId] = map[uint32]uint32{stockCommodity.ErpCommodityId: stockCommodity.Count}
			}
		}
	}

	if err = m.processErpStocks(erpStockCommodity); err != nil {
		return err
	}

	return nil
}

// 获取商品名称
func getCommodityNameBySerialNum(serialNumber string) (string, error) {
	var commodity ErpCommodity
	err := orm.Eloquent.Table("erp_commodity").Where("serial_number=?", serialNumber).Find(&commodity).Error
	if err != nil {
		return "", err
	}

	return commodity.Name, nil
}

//// GenerateSerialCode 生成商品串码
//func GenerateSerialCode(categoryID uint32) (string, error) {
//	max := 1
//	for {
//		if max > 5 {
//			logger.Error("generate SerialCode err")
//			return "", errors.New("generate SerialCode err")
//		}
//
//		// 生成年月日6位数
//		dateStr := time.Now().Format("060102")
//
//		// 生成四位随机数
//		rand.Seed(time.Now().UnixNano())
//		randomNumber := rand.Intn(10000)
//		randomStr := fmt.Sprintf("%04d", randomNumber)
//
//		// 获取商品分类编号前3位
//		categoryStr, err := getCategoryCode(categoryID)
//		if err != nil {
//			logger.Errorf("getCategoryCode err:", logger.Field("err", err))
//			return "", err
//		}
//
//		// 拼接串码
//		serialCode := categoryStr + dateStr + randomStr
//
//		// 检查生成的串码是否已存在 todo 是否需要判断状态，采购退货的可以重复？
//		exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_stock_commodity WHERE FIND_IN_SET('%s', imei) > 0 ", serialCode))
//		if err != nil {
//			logger.Error("exist sn err")
//		}
//		if !exist {
//			return serialCode, nil
//		}
//	}
//
//}

// GenerateSerialCode 生成商品串码
func GenerateSerialCode(categoryID uint32, generatedIMEIs map[string]struct{}) (string, error) {
	max := 1
	for {
		if max > 5 {
			logger.Error("generate SerialCode err")
			return "", errors.New("generate SerialCode err")
		}

		// 生成年月日6位数
		dateStr := time.Now().Format("060102")

		// 生成四位随机数
		rand.Seed(time.Now().UnixNano())
		randomNumber := rand.Intn(10000)
		randomStr := fmt.Sprintf("%04d", randomNumber)

		// 获取商品分类编号前3位
		categoryStr, err := getCategoryCode(categoryID)
		if err != nil {
			logger.Errorf("getCategoryCode err:", logger.Field("err", err))
			return "", err
		}

		// 拼接串码
		serialCode := categoryStr + dateStr + randomStr

		// 检查生成的串码是否已存在
		exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_stock_commodity WHERE FIND_IN_SET('%s', imei) > 0 ", serialCode))
		if err != nil {
			logger.Error("exist sn err")
		}

		// 如果已存在数据库中或在传入的 generatedIMEIs 集合中，则继续生成
		if !exist {
			// 检查是否已在 generatedIMEIs 集合中
			if _, found := generatedIMEIs[serialCode]; !found {
				// 将新生成的串码加入集合，以便后续校验
				return serialCode, nil
			}
		}

		max++
	}
}

// getCategoryCode 根据商品分类ID查询分类编号前3位
func getCategoryCode(categoryID uint32) (string, error) {
	// 这里模拟查询数据库，实际中你需要连接数据库进行查询
	// 假设这里的数据是从数据库中查询得到的
	categoryInfo, err := GetCategoryById(categoryID)
	if err != nil {
		logger.Errorf("GetCategoryById err:", logger.Field("err", err))
		return "", err
	}

	// 取前3位字符串
	return categoryInfo.Number[:3], nil
}

func (m *StockImporter) processErpStocks(erpStocks []ErpStockCommodity) error {
	begin := orm.Eloquent.Begin()
	total := len(erpStocks)
	size := 200
	page := (total + size - 1) / size

	errGroup := errgroup.Group{}

	for i := 0; i < page; i++ {
		start := i * size
		end := (i + 1) * size
		if end > total {
			end = total
		}

		stockList := erpStocks[start:end]
		errGroup.Go(func() error {
			return createStockList(begin, stockList) //插入库存商品详情
		})
	}

	err := errGroup.Wait()
	if err != nil {
		begin.Rollback()
		return err
	}

	err = m.ErpStockCountUpdate(begin) //更新or插入库存表
	if err != nil {
		begin.Rollback()
		return err
	}

	err = begin.Commit().Error
	if err != nil {
		begin.Rollback()
		return err
	}

	return nil
}

func createStockList(begin *gorm.DB, stockList []ErpStockCommodity) error {
	err := begin.Debug().Create(&stockList).Error
	if err != nil {
		begin.Rollback()
		return err
	}
	return nil
}

// 合并导入数据的辅助函数
func mergeCensusMap(censusMap map[uint32]map[uint32]uint32) map[uint32]map[uint32]uint32 {
	mergedMap := make(map[uint32]map[uint32]uint32)
	for storeId, commodities := range censusMap {
		if mergedMap[storeId] == nil {
			mergedMap[storeId] = make(map[uint32]uint32)
		}
		for commodityId, count := range commodities {
			mergedMap[storeId][commodityId] += count
		}
	}
	return mergedMap
}

func (m *StockImporter) ErpStockCountUpdate(gdb *gorm.DB) error {
	// 合并导入数据，避免同一商品多次重复处理
	mergedCensusMap := mergeCensusMap(m.CensusMap)

	//for k1, v1 := range m.CensusMap {
	for k1, v1 := range mergedCensusMap {
		for k2, v2 := range v1 {
			exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_stock WHERE store_id=%d AND erp_commodity_id=%d", k1, k2))
			if err != nil {
				//logger.Error("exist err:", err)
				return err
			}
			v, ok := m.CommodityMap[k2]
			fmt.Println("CommodityMap", m.CommodityMap)
			fmt.Println("ok", ok)
			fmt.Println("v", v)
			if exist {
				err = gdb.Exec(fmt.Sprintf(
					"UPDATE erp_stock SET count=count+%d WHERE  store_id=%d AND erp_commodity_id=%d;", v2, k1, k2)).Error
				if err != nil {
					//logger.Error("update stock err:", err)
					return err
				}
			} else {
				if ok && v != nil {
					stock := &ErpStock{
						StoreId:               k1,
						StoreName:             m.StoreMap[k1],
						ErpCommodityId:        v.ID,
						ErpCommodityName:      v.Name,
						ErpCategoryId:         v.ErpCategoryId,
						ErpCategoryName:       v.ErpCategoryName,
						CommoditySerialNumber: v.SerialNumber,
						IMEIType:              v.IMEIType,
						RetailPrice:           v.RetailPrice,
						MinRetailPrice:        v.MinRetailPrice,
						Count:                 v2,
						DispatchCount:         0,
					}
					err = gdb.Create(stock).Error
					if err != nil {
						//logger.Error("create stock err:", err)
						return err
					}
				}

			}
			if ok && v != nil {
				inventoryStock := &ErpInventoryStock{
					StoreId:               k1,
					StoreName:             m.StoreMap[k1],
					ErpCommodityId:        v.ID,
					ErpCommodityName:      v.Name,
					ErpCategoryId:         v.ErpCategoryId,
					ErpCategoryName:       v.ErpCategoryName,
					CommoditySerialNumber: v.SerialNumber,
					IMEIType:              v.IMEIType,
					RetailPrice:           v.RetailPrice,
					MinRetailPrice:        v.MinRetailPrice,
					Count:                 v2,
				}
				fmt.Println("inventoryStock", inventoryStock.Count)
				m.Inventories = append(m.Inventories, inventoryStock)
			}
		}
	}

	return nil
}

// 查询库存详情时同步库存id
func updateCommodityStock(id uint32) error {
	var stock ErpStock
	err := orm.Eloquent.Table("erp_stock").Raw("SELECT * FROM erp_stock WHERE id = ?", id).Scan(&stock).Error
	if err != nil {
		return err
	}

	if stock.ID != 0 {
		err = orm.Eloquent.Debug().Exec(fmt.Sprintf(
			"UPDATE erp_stock_commodity SET erp_stock_id = %d WHERE erp_commodity_name = '%s' AND erp_stock_id = 0;",
			stock.ID, stock.ErpCommodityName)).Error
		if err != nil {
			return err
		}
	}

	return nil
}

func (m *StockImporter) ErpInventoryStockCreate(gdb *gorm.DB, list []ErpStockCommodity) error {
	defer func() {
		if err := recover(); err != nil {
			//logger.Error("err:", err)
			return
		}
	}()

	inventoryStockIdMap := make(map[string]uint32, 0)
	for _, inventory := range m.Inventories {
		//err := gdb.Create(inventorymanage).Error
		err := orm.Eloquent.Create(inventory).Error
		if err != nil {
			//logger.Error("create erp inventorymanage stock err:", err)
			return err
		}
		inventoryStockIdMap[fmt.Sprintf("%d_%d", inventory.StoreId, inventory.ErpCommodityId)] = inventory.ID
	}

	begin := orm.Eloquent.Begin()
	total := len(list)
	size := 500
	page := total / size
	if total%size != 0 {
		page += 1
	}

	errGroup := errgroup.Group{}
	for i := 0; i < page; i++ {
		if i == page-1 {
			stockList := ErpStockCommodityToInventory(inventoryStockIdMap, list[i*size:])
			err := begin.Create(&stockList).Error
			if err != nil {
				begin.Rollback()
				//logger.Error("create commodity  err:", err)
				return err
			}
		} else {
			errGroup.Go(func() error {
				//stockList := list[i*size : (i+1)*size]
				stockList := ErpStockCommodityToInventory(inventoryStockIdMap, list[i*size:(i+1)*size])
				err := begin.Create(&stockList).Error
				if err != nil {
					begin.Rollback()
					//logger.Error("create commodity  err:", err)
					return err
				}
				return nil
			})
		}
	}
	err := errGroup.Wait()
	if err != nil {
		//logger.Error("wait err:", err)
		return err
	}
	err = begin.Commit().Error
	if err != nil {
		//logger.Error("commit err:", err)
		return err
	}

	return nil
}

func ErpStockCommodityToInventory(inventoryStockIdMap map[string]uint32, list []ErpStockCommodity) []*ErpInventoryStockCommodity {
	inventoryList := make([]*ErpInventoryStockCommodity, 0, len(list))
	for i, _ := range list {
		v, ok := inventoryStockIdMap[fmt.Sprintf("%d_%d", list[i].StoreId, list[i].ErpCommodityId)]
		if ok {
			inventoryCommodity := &ErpInventoryStockCommodity{
				ErpInventoryStockId:   v,
				ErpCommodityId:        list[i].ErpCommodityId,
				ErpCommodityName:      list[i].ErpCommodityName,
				CommoditySerialNumber: list[i].CommoditySerialNumber,
				IMEIType:              list[i].IMEIType,
				IMEI:                  list[i].IMEI,
				ErpSupplierId:         list[i].ErpSupplierId,
				ErpSupplierName:       list[i].ErpSupplierName,
				StockTime:             list[i].StockTime,
				RetailPrice:           list[i].RetailPrice,
				MinRetailPrice:        list[i].MinRetailPrice,
				StaffCostPrice:        list[i].StaffCostPrice,
				WholesalePrice:        list[i].WholesalePrice,
				Count:                 list[i].Count,
			}
			//err := gdb.Create(inventoryCommodity).Error
			inventoryList = append(inventoryList, inventoryCommodity)
			//err := orm.Eloquent.Create(inventoryCommodity).Error
			//if err != nil {
			//	logger.Error("create erp inventorymanage stock commodity err:", err)
			//	return inventoryList
			//}
		}
	}
	return inventoryList
}

// ErpCommodityListExport 导出商品列表
func ErpCommodityListExport(list []ErpCommodity) (string, error) {
	file := excelize.NewFile()
	fSheet := "Sheet1"

	url := config.ExportConfig.Url
	fileName := time.Now().Format(TimeFormat) + "商品资料" + ".xlsx"

	title := []interface{}{"商品编号", "商品名称", "商品分类", "商品条码", "是否串码", "系统生成串码", "主供应商", "指导零售价",
		"最低零售价", "员工成本价加价", "指导采购价", "销售毛利提成", "员工毛利提成", "会员优惠", "产地", "备注"}
	for i, _ := range title {
		cell, _ := excelize.CoordinatesToCellName(1+i, 1)
		err := file.SetCellValue(fSheet, cell, title[i])
		if err != nil {
			logger.Errorf("file set value err:", err)
		}
	}

	var row []interface{}
	nExcelStartRow := 0
	for rowId := 0; rowId < len(list); rowId++ {
		isIMEI := "否"
		systemIMEI := "否"
		if list[rowId].IMEIType != 1 { // 非串码
			isIMEI = "是"
			if list[rowId].IMEIType == 2 { //2-串码（系统生成）
				systemIMEI = "是"
			}
		}

		row = []interface{}{
			list[rowId].SerialNumber,
			list[rowId].Name,
			list[rowId].ErpCategoryName,
			list[rowId].ErpBarcode,
			isIMEI,
			systemIMEI,
			list[rowId].ErpSupplierName,
			list[rowId].RetailPrice,
			list[rowId].MinRetailPrice,
			list[rowId].StaffCostPrice,
			list[rowId].WholesalePrice,
			list[rowId].Brokerage1,
			list[rowId].Brokerage2,
			list[rowId].MemberDiscount,
			list[rowId].Origin,
			list[rowId].Remark}

		for j, _ := range row {
			cell, _ := excelize.CoordinatesToCellName(1+j, nExcelStartRow+2)
			err := file.SetCellValue(fSheet, cell, row[j])
			if err != nil {
				logger.Error("file set value err:", logger.Field("err", err))
			}
		}
		nExcelStartRow++
	}
	// 设置所有单元格的样式: 居中、加边框
	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", 15)
	file.SetColWidth("Sheet1", "B", "B", 30)
	file.SetColWidth("Sheet1", "D", "D", 18)

	endRow := fmt.Sprintf("P"+"%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
}

// 判断是否有入库采购价、入库员工成本价的权限
func checkRoleMenu(c *gin.Context, menuName string) (bool, error) {
	if tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员" {
		return true, nil
	}

	var sysMenu Menu
	err := orm.Eloquent.Table("sys_menu").Raw("select * from sys_menu where permission  = ?",
		menuName).Scan(&sysMenu).Error
	if err != nil {
		logger.Errorf("get sys_menu err:", err)
		return false, err
	}
	roleId := tools.GetRoleId(c)
	exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM sys_role_menu WHERE `menu_id`='%d' "+
		"and `role_id`='%d'", sysMenu.MenuId, roleId))
	if err != nil {
		logger.Errorf("checkRoleMenu QueryRecordExist err:", err)
		return false, err
	}

	return exist, nil
}

// InventoryDetailListExport 导出库存详情
func InventoryDetailListExport(list []ErpStockCommodity, c *gin.Context) (string, error) {
	file := excelize.NewFile()
	streamWriter, err := file.NewStreamWriter("Sheet1")
	if err != nil {
		fmt.Println(err)
		return "", err
	}

	url := config.ExportConfig.Url
	fileName := time.Now().Format(TimeFormat) + "库存详情" + ".xlsx"
	fmt.Println("url fileName:", url+fileName)

	// 判断是否有入库采购价、入库员工成本价的权限
	exportPurchasePrice, _ := checkRoleMenu(c, PurchasePrice)
	exportStaffCostPrice, _ := checkRoleMenu(c, EmployeeCostPrice)
	fmt.Println("exportPurchasePrice is:", exportPurchasePrice)
	fmt.Println("exportStaffCostPrice is:", exportStaffCostPrice)
	logger.Info("exportPurchasePrice is:", logger.Field("exportPurchasePrice", exportPurchasePrice))
	logger.Info("exportStaffCostPrice is:", logger.Field("exportStaffCostPrice", exportStaffCostPrice))

	// 设置标题行
	title := []interface{}{"商品编号", "商品名称", "商品分类", "是否串码", "商品串码", "所属门店", "供应商", "库存数量",
		"首次入库时间", "首次入库方式", "首次入库订单编号", "最近入库时间"}

	if exportPurchasePrice {
		title = append(title, "入库采购价")
	}
	if exportStaffCostPrice {
		title = append(title, "入库员工成本价")
	}

	title = append(title, "最近库龄", "总库龄", "当前状态", "备注")

	cell, _ := excelize.CoordinatesToCellName(1, 1)
	if err = streamWriter.SetRow(cell, title); err != nil {
		fmt.Println(err)
		return "", err
	}

	for rowId := 0; rowId < len(list); rowId++ {
		isIMEIType := "是"
		if list[rowId].IMEIType == 1 {
			isIMEIType = "否"
		}

		storageType := "系统入库"
		if list[rowId].StorageType == 2 {
			storageType = "采购入库"
		}

		state := "在库"
		switch list[rowId].State {
		case 1:
			state = "在库"
		case 2:
			state = "已售"
		case 3:
			state = "采购退货"
		case 4:
			state = "调出中"
		case 5:
			state = "系统出库"
		case 6:
			state = "盘点出库"
		}

		row := []interface{}{
			list[rowId].CommoditySerialNumber,
			list[rowId].ErpCommodityName,
			list[rowId].ErpCategoryName,
			isIMEIType,
			list[rowId].IMEI,
			list[rowId].StoreName,
			list[rowId].ErpSupplierName,
			1,
			list[rowId].FirstStockTime,
			storageType,
			list[rowId].OriginalSn,
			list[rowId].StockTime,
		}

		// 控制是否导出入库采购价和入库员工成本价
		if exportPurchasePrice {
			row = append(row, list[rowId].WholesalePrice)
		}
		if exportStaffCostPrice {
			row = append(row, list[rowId].WholesalePrice+list[rowId].StaffCostPrice)
		}

		row = append(row, list[rowId].Age)
		row = append(row, list[rowId].AllAge)
		row = append(row, state)
		row = append(row, list[rowId].Remark)

		cell, _ = excelize.CoordinatesToCellName(1, rowId+2)
		if err = streamWriter.SetRow(cell, row); err != nil {
			fmt.Println(err)
			return "", err
		}
	}

	if err = streamWriter.Flush(); err != nil {
		fmt.Println(err)
		return "", err
	}
	fmt.Println("save fileName:", config.ExportConfig.Path+fileName)
	if err = file.SaveAs(config.ExportConfig.Path + fileName); err != nil {
		fmt.Println(err)
		return "", err
	}
	return url + fileName, nil
}

// ErpCategoryListExport 导出商品分类
func ErpCategoryListExport(list []ErpCategory) (string, error) {
	file := excelize.NewFile()
	streamWriter, err := file.NewStreamWriter("Sheet1")
	if err != nil {
		fmt.Println(err)
	}

	url := config.ExportConfig.Url
	fileName := time.Now().Format(TimeFormat) + "分类列表数据" + ".xlsx"

	//title := []interface{}{"门店", "用户ID", "订单编号", "下单时间", "卡带", "说明", "回收价", "闲麦价", "审核时间", "审核人", "操作", "复核时间", "复核状态"}
	title := []interface{}{"一级分类", "二级分类"}
	cell, _ := excelize.CoordinatesToCellName(1, 1)
	if err = streamWriter.SetRow(cell, title); err != nil {
		fmt.Println(err)
	}
	rowIdx := 2
	var row []interface{}
	for rowId := 0; rowId < len(list); rowId++ {
		cats := list[rowId].SubCats
		for i := 0; i < len(cats); i++ {
			rowName := list[rowId].Name
			if i != 0 {
				rowName = ""
			}
			row = []interface{}{rowName, cats[i].Name}
			cell, _ := excelize.CoordinatesToCellName(1, rowIdx)
			if err := streamWriter.SetRow(cell, row); err != nil {
				fmt.Println(err)
			}
			rowIdx++
		}
	}
	if err := streamWriter.Flush(); err != nil {
		fmt.Println(err)
	}
	if err := file.SaveAs("/www/server/images/export/" + fileName); err != nil {
		//if err := file.SaveAs("./" + fileName); err != nil {
		fmt.Println(err)
	}
	return url + fileName, nil
}

type ErpStockListReq struct {
	SerialNumber  string `json:"serial_number"`   // 商品编号
	CommodityName string `json:"commodity_name"`  // 商品名称
	ErpCategoryId uint32 `json:"erp_category_id"` // 商品分类
	IsIMEI        uint32 `json:"is_imei"`         // 是否串码：0-查全部 1-查串码类 2-查非串码
	StockType     uint32 `json:"stock_type"`      // 库存情况:1-全部 2-有库存 3-无库存
	StoreId       uint32 `json:"store_id"`        // 门店编号
	PageIndex     int    `json:"pageIndex"`       // 页码
	PageSize      int    `json:"pageSize"`        // 页面条数
	IsExport      uint32 `json:"is_export"`       // 1-导出
}

type ErpStockListResp struct {
	List      []ErpStock `json:"list"`
	Total     int        `json:"total"`     // 数据总条数
	PageIndex int        `json:"pageIndex"` // 页码
	PageSize  int        `json:"pageSize"`  // 每页展示条数
	ExportUrl string     `json:"export_url"`
}

func (m *ErpStockListReq) List() (*ErpStockListResp, error) {
	resp := &ErpStockListResp{
		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.Table("erp_stock").
		Joins("JOIN erp_category c ON erp_stock.erp_category_id = c.id")

	if m.SerialNumber != "" {
		qs = qs.Where("commodity_serial_number=?", m.SerialNumber)
	}
	if m.CommodityName != "" {
		qs = qs.Where("erp_commodity_name LIKE ?", "%"+m.CommodityName+"%")
	}
	if m.ErpCategoryId != 0 {
		qs = qs.Where("erp_category_id=?", m.ErpCategoryId)
	}
	if m.StoreId != 0 {
		qs = qs.Where("store_id=?", m.StoreId)
	}
	switch m.StockType {
	case 2:
		qs = qs.Where("count > 0")
	case 3:
		qs = qs.Where("count = 0")
	}

	// SQL Order By Clause
	qs = qs.Order("CASE WHEN c.pid = 0 THEN CAST(c.number AS SIGNED) ELSE " +
		"CAST(SUBSTRING(c.number, 1, 3) AS SIGNED) END").
		Order("CAST(c.pid AS SIGNED)").
		Order("CAST(SUBSTRING(erp_stock.commodity_serial_number, -4) AS SIGNED)")

	var count int64
	if err := qs.Count(&count).Error; err != nil {
		//logger.Error("count err:", err)
		return resp, err
	}

	var commodities []ErpStock
	err := qs.Offset(page * m.PageSize).Limit(m.PageSize).Find(&commodities).Error
	if err != nil && !errors.Is(err, RecordNotFound) {
		//logger.Error("erp commodity list err:", err)
		return resp, err
	}
	resp.List = commodities

	//跟之前保持一致
	resp.Total = int(count)
	resp.PageIndex = page + 1
	resp.PageSize = m.PageSize

	return resp, nil
}

// StockList
// 1、如果筛选条件有：库存情况筛选；则以库存表为准，查库存表即可
// （1）没门店，则查所有库存情况并排序
// （2）有门店，则查门店对应的库存情况并排序
// 2、如果筛选条件没有库存情况，
// （1）先查询商品资料，并排序；支持筛选条件：商品编号、商品分类、商品名称
// （2）然后查询每个商品资料的库存情况，没传门店id，则查所有库存；否则查当前门店的库存情况
func (m *ErpStockListReq) StockList(c *gin.Context) (*ErpStockListResp, error) {
	switch m.StockType {
	case 2: // 有库存
		return m.stockNoEmptyList(c)
	case 3: // 无库存，连表查询商品明细和库存表
		return m.stockIsEmptyList(c)
	default: // 0和1，以及其他值，表示无库存情况筛选
		return m.allCommodityList(c)
	}
}

// stockIsEmptyList 库存列表-无库存查询
// 无库存，要连表查询（商品明细、库存表）
func (m *ErpStockListReq) stockIsEmptyList(c *gin.Context) (*ErpStockListResp, error) {
	resp := &ErpStockListResp{
		PageIndex: m.PageIndex,
		PageSize:  m.PageSize,
	}
	page := m.PageIndex - 1
	if page < 0 {
		page = 0
	}
	if m.PageSize == 0 {
		m.PageSize = 10
	}

	//以下代码组合了2个连表查询
	//（1）查询无库存的商品资料
	/***
	SELECT erp_commodity.*,
		COALESCE(erp_stock.count, 0) AS count
	FROM erp_commodity
	LEFT JOIN (
		SELECT erp_commodity_id, SUM(count) AS count
	FROM erp_stock
	GROUP BY erp_commodity_id
	) erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id
	WHERE erp_stock.count IS NULL OR erp_stock.count = 0;
	***/

	//（2）按商品编号排序
	/***
	SELECT e.*
	FROM erp_stock e
	JOIN erp_category c ON e.erp_category_id = c.id
	ORDER BY
	  CASE WHEN c.pid = 0 THEN CAST(c.number AS SIGNED) ELSE CAST(SUBSTRING(c.number, 1, 3) AS SIGNED) END,
	  CAST(c.pid AS SIGNED),
	  CAST(SUBSTRING(e.commodity_serial_number, -4) AS SIGNED);
	***/

	qs := orm.Eloquent.Table("erp_commodity")
	es := orm.Eloquent.Table("erp_commodity")

	// 非管理员才判断所属门店
	var storeList []uint32
	if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") {
		sysUser, err := GetSysUserByCtx(c)
		if err != nil {
			return nil, err
		}

		// 返回sysUser未过期的门店id列表
		storeList = GetValidStoreIDs(sysUser.StoreData)
		if m.StoreId != 0 {
			if !Contains(storeList, m.StoreId) {
				return nil, errors.New("您没有该门店权限")
			}
		} else {
			if len(storeList) == 0 {
				return nil, errors.New("用户未绑定门店")
			}
		}
	}

	if m.StoreId != 0 { // 传门店id
		qs = qs.Select("erp_commodity.*, COALESCE(erp_stock.count, 0) AS total_count,"+
			"COALESCE(SUM(erp_stock.dispatch_count), 0) AS total_dispatch_count").
			Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id AND erp_stock.store_id = ?", m.StoreId).
			//Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id").
			Joins("LEFT JOIN erp_category c ON erp_commodity.erp_category_id = c.id").
			Group("erp_commodity.id").
			//Where("erp_stock.count = 0 AND erp_stock.store_id = ?", m.StoreId).
			Order("CASE WHEN c.pid = 0 THEN CAST(c.number AS SIGNED) " +
				"ELSE CAST(SUBSTRING(c.number, 1, 3) AS SIGNED) END, " +
				"CAST(c.pid AS SIGNED), CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)")
		es = es.Select("erp_commodity.*, COALESCE(erp_stock.count, 0) AS total_count, "+
			"COALESCE(SUM(erp_stock.dispatch_count), 0) AS total_dispatch_count").
			Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id AND erp_stock.store_id = ?", m.StoreId).
			//Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id").
			Joins("LEFT JOIN erp_category c ON erp_commodity.erp_category_id = c.id").
			Group("erp_commodity.id").
			//Where("erp_stock.count = 0 AND erp_stock.store_id = ?", m.StoreId).
			Order("CASE WHEN c.pid = 0 THEN CAST(c.number AS SIGNED) " +
				"ELSE CAST(SUBSTRING(c.number, 1, 3) AS SIGNED) END, " +
				"CAST(c.pid AS SIGNED), CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)")
	} else {
		qs = qs.Select("erp_commodity.*, COALESCE(erp_stock.count, 0) AS total_count," +
			"COALESCE(SUM(erp_stock.dispatch_count), 0) AS total_dispatch_count").
			Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id").
			//Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id").
			Joins("LEFT JOIN erp_category c ON erp_commodity.erp_category_id = c.id").
			Group("erp_commodity.id").
			//Where("erp_stock.count = 0 AND erp_stock.store_id = ?", m.StoreId).
			Order("CASE WHEN c.pid = 0 THEN CAST(c.number AS SIGNED) " +
				"ELSE CAST(SUBSTRING(c.number, 1, 3) AS SIGNED) END, " +
				"CAST(c.pid AS SIGNED), CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)")
		es = es.Select("erp_commodity.*, COALESCE(erp_stock.count, 0) AS total_count, " +
			"COALESCE(SUM(erp_stock.dispatch_count), 0) AS total_dispatch_count").
			Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id").
			//Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id").
			Joins("LEFT JOIN erp_category c ON erp_commodity.erp_category_id = c.id").
			Group("erp_commodity.id").
			//Where("erp_stock.count = 0 AND erp_stock.store_id = ?", m.StoreId).
			Order("CASE WHEN c.pid = 0 THEN CAST(c.number AS SIGNED) " +
				"ELSE CAST(SUBSTRING(c.number, 1, 3) AS SIGNED) END, " +
				"CAST(c.pid AS SIGNED), CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)")
	}
	//} else { // 没传门店id，则子查询先求库存表中erp_commodity_id相同的count之和
	//	if len(storeList) == 0 {
	//		qs = qs.Select("erp_commodity.*, COALESCE(erp_stock.count, 0) AS total_count, " +
	//			"COALESCE(SUM(erp_stock.dispatch_count), 0) AS total_dispatch_count").
	//			Joins("LEFT JOIN (SELECT erp_commodity_id, SUM(count) AS count, SUM(dispatch_count) AS dispatch_count  FROM erp_stock GROUP BY erp_commodity_id) " +
	//				"erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id").
	//			Joins("JOIN erp_category c ON erp_commodity.erp_category_id = c.id").
	//			Where("erp_stock.count IS NULL OR erp_stock.count = 0").
	//			Order("CASE WHEN c.pid = 0 THEN CAST(c.number AS SIGNED) " +
	//				"ELSE CAST(SUBSTRING(c.number, 1, 3) AS SIGNED) END, " +
	//				"CAST(c.pid AS SIGNED), CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)")
	//		es = es.Select("erp_commodity.*, COALESCE(erp_stock.count, 0) AS total_count, " +
	//			"COALESCE(SUM(erp_stock.dispatch_count), 0) AS total_dispatch_count").
	//			Joins("LEFT JOIN (SELECT erp_commodity_id, SUM(count) AS count, SUM(dispatch_count) AS dispatch_count FROM erp_stock GROUP BY erp_commodity_id) " +
	//				"erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id").
	//			Joins("JOIN erp_category c ON erp_commodity.erp_category_id = c.id").
	//			Where("erp_stock.count IS NULL OR erp_stock.count = 0").
	//			Order("CASE WHEN c.pid = 0 THEN CAST(c.number AS SIGNED) " +
	//				"ELSE CAST(SUBSTRING(c.number, 1, 3) AS SIGNED) END, " +
	//				"CAST(c.pid AS SIGNED), CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)")
	//	} else {
	//		qs = qs.Select("erp_commodity.*, COALESCE(erp_stock.count, 0) AS total_count, "+
	//			"COALESCE(SUM(erp_stock.dispatch_count), 0) AS total_dispatch_count").
	//			Joins("LEFT JOIN (SELECT erp_commodity_id, SUM(count) AS count, SUM(dispatch_count) AS dispatch_count  FROM erp_stock WHERE store_id IN (?) GROUP BY erp_commodity_id) "+
	//				"erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id", storeList).
	//			Joins("JOIN erp_category c ON erp_commodity.erp_category_id = c.id").
	//			Where("erp_stock.count IS NULL OR erp_stock.count = 0").
	//			Order("CASE WHEN c.pid = 0 THEN CAST(c.number AS SIGNED) " +
	//				"ELSE CAST(SUBSTRING(c.number, 1, 3) AS SIGNED) END, " +
	//				"CAST(c.pid AS SIGNED), CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)")
	//		es = es.Select("erp_commodity.*, COALESCE(erp_stock.count, 0) AS total_count, "+
	//			"COALESCE(SUM(erp_stock.dispatch_count), 0) AS total_dispatch_count").
	//			Joins("LEFT JOIN (SELECT erp_commodity_id, SUM(count) AS count, SUM(dispatch_count) AS dispatch_count  FROM erp_stock WHERE store_id IN (?) GROUP BY erp_commodity_id) "+
	//				"erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id", storeList).
	//			Joins("JOIN erp_category c ON erp_commodity.erp_category_id = c.id").
	//			Where("erp_stock.count IS NULL OR erp_stock.count = 0").
	//			Order("CASE WHEN c.pid = 0 THEN CAST(c.number AS SIGNED) " +
	//				"ELSE CAST(SUBSTRING(c.number, 1, 3) AS SIGNED) END, " +
	//				"CAST(c.pid AS SIGNED), CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)")
	//	}
	//}

	if m.SerialNumber != "" {
		qs = qs.Where("erp_commodity.serial_number=?", m.SerialNumber)
		es = es.Where("erp_commodity.serial_number=?", m.SerialNumber)
	}
	if m.CommodityName != "" {
		qs = qs.Where("erp_commodity.name Like '%" + m.CommodityName + "%'")
		es = es.Where("erp_commodity.name Like '%" + m.CommodityName + "%'")
		//qs = qs.Where("name LIKE ?", m.Name)
	}
	if m.ErpCategoryId != 0 {
		qs = qs.Where("erp_commodity.erp_category_id=?", m.ErpCategoryId)
		es = es.Where("erp_commodity.erp_category_id=?", m.ErpCategoryId)
	}

	var count int64
	err := es.Count(&count).Error
	if err != nil {
		logger.Error("查询无库存列表数量失败", logger.Field("err", err))
		return nil, err
	}

	var commodities []struct {
		ErpCommodity
		TotalCount         int
		TotalDispatchCount int
	}
	//err = qs.Offset(page * m.PageSize).Limit(m.PageSize).Find(&commodities).Error
	err = qs.Find(&commodities).Error
	if err != nil && err != RecordNotFound {
		logger.Error("查询无库存列表失败", logger.Field("err", err))
		return nil, err
	}

	//遍历商品资料，转换为库存列表数据
	var stockList []ErpStock
	for _, commodity := range commodities {
		if commodity.TotalCount == 0 {
			if commodity.StopPurchase == 1 { // 停止采购且没有库存
				continue
			}
			var stock ErpStock
			stock.ErpCommodityId = commodity.ID
			stock.ErpCommodityName = commodity.Name
			stock.ErpCategoryId = commodity.ErpCategoryId
			stock.ErpCategoryName = commodity.ErpCategoryName
			stock.CommoditySerialNumber = commodity.SerialNumber
			stock.IMEIType = commodity.IMEIType
			stock.RetailPrice = commodity.RetailPrice
			stock.MinRetailPrice = commodity.MinRetailPrice
			stock.Count = 0
			stock.DispatchCount = 0

			stockList = append(stockList, stock)
		}
	}

	SortStockCommodities(stockList)

	if m.IsExport == 1 {
		filePath, err := InventoryListExport(stockList, m.StockType)
		if err != nil {
			return nil, err
		}
		resp = &ErpStockListResp{}
		resp.ExportUrl = filePath
	} else {
		// Paginate results
		startIndex := page * m.PageSize
		endIndex := startIndex + m.PageSize
		if endIndex > len(stockList) {
			endIndex = len(stockList)
		}

		// Slice the users based on pagination
		pagedList := stockList[startIndex:endIndex]

		//跟之前保持一致
		resp.Total = len(stockList)
		resp.PageIndex = page + 1
		resp.PageSize = m.PageSize
		resp.List = pagedList
	}

	return resp, nil
}

func (m *ErpStockListReq) stockNoEmptyList(c *gin.Context) (*ErpStockListResp, error) {
	resp := &ErpStockListResp{
		PageIndex: m.PageIndex,
		PageSize:  m.PageSize,
	}
	page := m.PageIndex - 1
	if page < 0 {
		page = 0
	}
	if m.PageSize == 0 {
		m.PageSize = 10
	}

	/***
	//组合查询
	SELECT
	ec.*,
		COALESCE(SUM(es.count), 0) AS total_count
	FROM
	erp_commodity ec
	LEFT JOIN
	erp_stock es ON ec.id = es.erp_commodity_id
	GROUP BY
	ec.id
	ORDER BY
	CASE
	WHEN ec.erp_category_id IN (SELECT id FROM erp_category WHERE pid = 0) THEN CAST((SELECT number FROM erp_category WHERE id = ec.erp_category_id) AS SIGNED)
	ELSE CAST(SUBSTRING((SELECT number FROM erp_category WHERE id = (SELECT pid FROM erp_category WHERE id = ec.erp_category_id)), 1, 3) AS SIGNED)
	END,
		CAST((SELECT pid FROM erp_category WHERE id = ec.erp_category_id) AS SIGNED),
	CAST(SUBSTRING(ec.serial_number, -4) AS SIGNED);
	***/

	qs := orm.Eloquent.Table("erp_commodity")
	es := orm.Eloquent.Table("erp_commodity")

	// 非管理员才判断所属门店
	if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") {
		sysUser, err := GetSysUserByCtx(c)
		if err != nil {
			return nil, err
		}

		// 返回sysUser未过期的门店id列表
		storeList := GetValidStoreIDs(sysUser.StoreData)
		if m.StoreId != 0 {
			if !Contains(storeList, m.StoreId) {
				return nil, errors.New("您没有该门店权限")
			}
		} else {
			if len(storeList) > 0 {
				if len(storeList) == 1 {
					qs = qs.Where("erp_stock.store_id = ?", storeList[0])
					es = es.Where("erp_stock.store_id = ?", storeList[0])
				} else {
					qs = qs.Where("erp_stock.store_id IN (?)", storeList)
					es = es.Where("erp_stock.store_id IN (?)", storeList)
				}
			} else {
				return nil, errors.New("用户未绑定门店")
			}
		}
	}

	if m.StoreId == 0 { // 没指定门店，连表查询并计算总数量
		qs = qs.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_count, " +
			"COALESCE(SUM(erp_stock.dispatch_count), 0) AS total_dispatch_count, erp_stock.id AS erp_stock_id").
			Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id AND erp_stock.count != 0").
			Group("erp_commodity.id").
			Order("CASE WHEN erp_commodity.erp_category_id IN (SELECT id FROM erp_category WHERE pid = 0) " +
				"THEN CAST((SELECT number FROM erp_category WHERE id = erp_commodity.erp_category_id) AS SIGNED) " +
				"ELSE CAST(SUBSTRING((SELECT number FROM erp_category WHERE id = (SELECT pid FROM erp_category WHERE id = erp_commodity.erp_category_id)), 1, 3) AS SIGNED) " +
				"END, CAST((SELECT pid FROM erp_category WHERE id = erp_commodity.erp_category_id) AS SIGNED), " +
				"CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)")
		es = es.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_count, " +
			"COALESCE(SUM(erp_stock.dispatch_count), 0) AS total_dispatch_count, erp_stock.id AS erp_stock_id").
			Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id AND erp_stock.count != 0").
			Group("erp_commodity.id").
			Order("CASE WHEN erp_commodity.erp_category_id IN (SELECT id FROM erp_category WHERE pid = 0) " +
				"THEN CAST((SELECT number FROM erp_category WHERE id = erp_commodity.erp_category_id) AS SIGNED) " +
				"ELSE CAST(SUBSTRING((SELECT number FROM erp_category WHERE id = (SELECT pid FROM erp_category WHERE id = erp_commodity.erp_category_id)), 1, 3) AS SIGNED) " +
				"END, CAST((SELECT pid FROM erp_category WHERE id = erp_commodity.erp_category_id) AS SIGNED), " +
				"CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)")
	} else { // 指定了门店，连表查询指定store_id的count
		qs = qs.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_count, "+
			"COALESCE(SUM(erp_stock.dispatch_count), 0) AS total_dispatch_count, erp_stock.id AS erp_stock_id").
			Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id "+
				"AND erp_stock.count != 0 AND erp_stock.store_id = ?", m.StoreId).
			Group("erp_commodity.id").
			Order("CASE WHEN erp_category.pid = 0 THEN CAST(erp_category.number AS SIGNED) " +
				"ELSE CAST(SUBSTRING(erp_category.number, 1, 3) AS SIGNED) END, " +
				"CAST(erp_category.pid AS SIGNED), " +
				"CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)").
			Joins("JOIN erp_category ON erp_commodity.erp_category_id = erp_category.id")
		es = es.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_count, "+
			"COALESCE(SUM(erp_stock.dispatch_count), 0) AS total_dispatch_count, erp_stock.id AS erp_stock_id").
			Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id "+
				"AND erp_stock.count != 0 AND erp_stock.store_id = ?", m.StoreId).
			Group("erp_commodity.id").
			Order("CASE WHEN erp_category.pid = 0 THEN CAST(erp_category.number AS SIGNED) " +
				"ELSE CAST(SUBSTRING(erp_category.number, 1, 3) AS SIGNED) END, " +
				"CAST(erp_category.pid AS SIGNED), " +
				"CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)").
			Joins("JOIN erp_category ON erp_commodity.erp_category_id = erp_category.id")
	}

	if m.SerialNumber != "" {
		qs = qs.Where("erp_commodity.serial_number=?", m.SerialNumber)
		es = es.Where("erp_commodity.serial_number=?", m.SerialNumber)
	}
	if m.CommodityName != "" {
		qs = qs.Where("erp_commodity.name Like '%" + m.CommodityName + "%'")
		es = es.Where("erp_commodity.name Like '%" + m.CommodityName + "%'")
	}
	if m.ErpCategoryId != 0 {
		qs = qs.Where("erp_commodity.erp_category_id=?", m.ErpCategoryId)
		es = es.Where("erp_commodity.erp_category_id=?", m.ErpCategoryId)
	}

	if m.IsIMEI != 0 {
		if m.IsIMEI == 1 { // 查串码数据
			qs = qs.Where("erp_commodity.imei_type != ?", NoIMEICommodity)
		} else if m.IsIMEI == 2 { // 查非串码数据
			qs = qs.Where("erp_commodity.imei_type = ?", NoIMEICommodity)
		}
	}

	var commodities []struct {
		ErpCommodity
		TotalCount         int
		ErpStockId         int
		TotalDispatchCount int
	}

	var count int64
	err := es.Count(&count).Error
	if err != nil {
		logger.Error("commodityList count err", logger.Field("err", err))
		return resp, err
	}

	//err = qs.Offset(page * m.PageSize).Limit(m.PageSize).Find(&commodities).Error
	err = qs.Find(&commodities).Error
	if err != nil && err != RecordNotFound {
		logger.Error("commodityList err", logger.Field("err", err))
		return resp, err
	}

	//遍历商品资料，转换为库存列表数据
	var stockList []ErpStock
	for _, commodity := range commodities {
		if commodity.TotalCount != 0 {
			var stock ErpStock
			stock.ID = uint32(commodity.ErpStockId)
			stock.ErpCommodityId = commodity.ID
			stock.ErpCommodityName = commodity.Name
			stock.ErpCategoryId = commodity.ErpCategoryId
			stock.ErpCategoryName = commodity.ErpCategoryName
			stock.CommoditySerialNumber = commodity.SerialNumber
			stock.IMEIType = commodity.IMEIType
			stock.RetailPrice = commodity.RetailPrice
			stock.MinRetailPrice = commodity.MinRetailPrice
			stock.Count = uint32(commodity.TotalCount)
			stock.DispatchCount = uint32(commodity.TotalDispatchCount)

			stockList = append(stockList, stock)
		}
	}

	SortStockCommodities(stockList)

	if m.IsExport == 1 {
		filePath, err := InventoryListExport(stockList, m.StockType)
		if err != nil {
			return nil, err
		}
		resp = &ErpStockListResp{}
		resp.ExportUrl = filePath
	} else {
		// Paginate results
		startIndex := page * m.PageSize
		endIndex := startIndex + m.PageSize
		if endIndex > len(stockList) {
			endIndex = len(stockList)
		}

		// Slice the users based on pagination
		pagedList := stockList[startIndex:endIndex]

		//跟之前保持一致
		resp.Total = len(stockList)
		resp.PageIndex = page + 1
		resp.PageSize = m.PageSize
		resp.List = pagedList
	}

	return resp, nil
}

// 筛选条件无：库存情况筛选
// （1）先查询商品资料，并排序；支持筛选条件：商品编号、商品分类、商品名称
// （2）然后查询每个商品资料的库存情况，没传门店id，则查所有库存；否则查当前门店的库存情况
func (m *ErpStockListReq) allCommodityList(c *gin.Context) (*ErpStockListResp, error) {
	resp := &ErpStockListResp{
		PageIndex: m.PageIndex,
		PageSize:  m.PageSize,
	}
	page := m.PageIndex - 1
	if page < 0 {
		page = 0
	}
	if m.PageSize == 0 {
		m.PageSize = 10
	}

	/***
	//组合查询
	SELECT
	ec.*,
		COALESCE(SUM(es.count), 0) AS total_count
	FROM
	erp_commodity ec
	LEFT JOIN
	erp_stock es ON ec.id = es.erp_commodity_id
	GROUP BY
	ec.id
	ORDER BY
	CASE
	WHEN ec.erp_category_id IN (SELECT id FROM erp_category WHERE pid = 0) THEN CAST((SELECT number FROM erp_category WHERE id = ec.erp_category_id) AS SIGNED)
	ELSE CAST(SUBSTRING((SELECT number FROM erp_category WHERE id = (SELECT pid FROM erp_category WHERE id = ec.erp_category_id)), 1, 3) AS SIGNED)
	END,
		CAST((SELECT pid FROM erp_category WHERE id = ec.erp_category_id) AS SIGNED),
	CAST(SUBSTRING(ec.serial_number, -4) AS SIGNED);
	***/

	qs := orm.Eloquent.Table("erp_commodity")
	es := orm.Eloquent.Table("erp_commodity")

	if m.SerialNumber != "" {
		qs = qs.Where("erp_commodity.serial_number=?", m.SerialNumber)
		es = es.Where("erp_commodity.serial_number=?", m.SerialNumber)
	}
	if m.CommodityName != "" {
		qs = qs.Where("erp_commodity.name Like '%" + m.CommodityName + "%'")
		es = es.Where("erp_commodity.name Like '%" + m.CommodityName + "%'")
	}
	if m.ErpCategoryId != 0 {
		qs = qs.Where("erp_commodity.erp_category_id=?", m.ErpCategoryId)
		es = es.Where("erp_commodity.erp_category_id=?", m.ErpCategoryId)
	}

	// 非管理员才判断所属门店
	if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") {
		sysUser, err := GetSysUserByCtx(c)
		if err != nil {
			return nil, err
		}

		// 返回sysUser未过期的门店id列表
		storeList := GetValidStoreIDs(sysUser.StoreData)
		if m.StoreId != 0 {
			if !Contains(storeList, m.StoreId) {
				return nil, errors.New("您没有该门店权限")
			}
		} else {
			if len(storeList) > 0 {
				if len(storeList) == 1 {
					qs = qs.Where("store_id = ?", storeList[0])
					es = es.Where("store_id = ?", storeList[0])
				} else {
					qs = qs.Where("store_id IN (?)", storeList)
					es = es.Where("store_id IN (?)", storeList)
				}
			} else {
				return nil, errors.New("用户未绑定门店")
			}
		}
	}

	if m.StoreId == 0 { // 没指定门店，连表查询并计算总数量
		qs = qs.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_count, " +
			"COALESCE(SUM(erp_stock.dispatch_count), 0) AS total_dispatch_count, erp_stock.id AS erp_stock_id").
			Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id").
			Group("erp_commodity.id").
			Order("CASE WHEN erp_commodity.erp_category_id IN (SELECT id FROM erp_category WHERE pid = 0) " +
				"THEN CAST((SELECT number FROM erp_category WHERE id = erp_commodity.erp_category_id) AS SIGNED) " +
				"ELSE CAST(SUBSTRING((SELECT number FROM erp_category WHERE id = (SELECT pid FROM erp_category WHERE id = erp_commodity.erp_category_id)), 1, 3) AS SIGNED) " +
				"END, CAST((SELECT pid FROM erp_category WHERE id = erp_commodity.erp_category_id) AS SIGNED), " +
				"CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)")
		es = es.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_count, " +
			"COALESCE(SUM(erp_stock.dispatch_count), 0) AS total_dispatch_count, erp_stock.id AS erp_stock_id").
			Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id").
			Group("erp_commodity.id").
			Order("CASE WHEN erp_commodity.erp_category_id IN (SELECT id FROM erp_category WHERE pid = 0) " +
				"THEN CAST((SELECT number FROM erp_category WHERE id = erp_commodity.erp_category_id) AS SIGNED) " +
				"ELSE CAST(SUBSTRING((SELECT number FROM erp_category WHERE id = (SELECT pid FROM erp_category WHERE id = erp_commodity.erp_category_id)), 1, 3) AS SIGNED) " +
				"END, CAST((SELECT pid FROM erp_category WHERE id = erp_commodity.erp_category_id) AS SIGNED), " +
				"CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)")
	} else { // 指定了门店，连表查询指定store_id的count
		qs = qs.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_count, "+
			"COALESCE(SUM(erp_stock.dispatch_count), 0) AS total_dispatch_count, erp_stock.id AS erp_stock_id").
			Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id "+
				"AND erp_stock.store_id = ?", m.StoreId).
			Group("erp_commodity.id").
			Order("CASE WHEN erp_category.pid = 0 THEN CAST(erp_category.number AS SIGNED) " +
				"ELSE CAST(SUBSTRING(erp_category.number, 1, 3) AS SIGNED) END, " +
				"CAST(erp_category.pid AS SIGNED), " +
				"CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)").
			Joins("JOIN erp_category ON erp_commodity.erp_category_id = erp_category.id")
		es = es.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_count, "+
			"COALESCE(SUM(erp_stock.dispatch_count), 0) AS total_dispatch_count, erp_stock.id AS erp_stock_id").
			Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id "+
				"AND erp_stock.store_id = ?", m.StoreId).
			Group("erp_commodity.id").
			Order("CASE WHEN erp_category.pid = 0 THEN CAST(erp_category.number AS SIGNED) " +
				"ELSE CAST(SUBSTRING(erp_category.number, 1, 3) AS SIGNED) END, " +
				"CAST(erp_category.pid AS SIGNED), " +
				"CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)").
			Joins("JOIN erp_category ON erp_commodity.erp_category_id = erp_category.id")
	}

	var commodities []struct {
		ErpCommodity
		TotalCount         int
		ErpStockId         int
		TotalDispatchCount int
	}

	var count int64
	err := orm.Eloquent.Debug().Table("(?) AS subquery", es).Count(&count).Error
	//err := es.Total(&count).Error
	if err != nil {
		logger.Error("commodityList count err", logger.Field("err", err))
		return resp, err
	}

	if m.IsExport == 1 {
		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("commodityList err", logger.Field("err", err))
		return resp, err
	}

	//遍历商品资料，转换为库存列表数据
	var stockList []ErpStock
	for _, commodity := range commodities {
		if commodity.StopPurchase == 1 && commodity.TotalCount == 0 { // 停止采购且没有库存
			continue
		}

		var stock ErpStock
		stock.ID = uint32(commodity.ErpStockId)
		stock.ErpCommodityId = commodity.ID
		stock.ErpCommodityName = commodity.Name
		stock.ErpCategoryId = commodity.ErpCategoryId
		stock.ErpCategoryName = commodity.ErpCategoryName
		stock.CommoditySerialNumber = commodity.SerialNumber
		stock.IMEIType = commodity.IMEIType
		stock.RetailPrice = commodity.RetailPrice
		stock.MinRetailPrice = commodity.MinRetailPrice
		stock.Count = uint32(commodity.TotalCount)
		stock.DispatchCount = uint32(commodity.TotalDispatchCount)

		stockList = append(stockList, stock)
	}

	SortStockCommodities(stockList)

	if m.IsExport == 1 {
		filePath, err := InventoryListExport(stockList, m.StockType)
		if err != nil {
			return nil, err
		}
		resp = &ErpStockListResp{}
		resp.ExportUrl = filePath
	} else {
		//跟之前保持一致
		resp.Total = int(count)
		resp.PageIndex = page + 1
		resp.PageSize = m.PageSize
		resp.List = stockList
	}

	return resp, nil
}

// InventoryListExport 导出库存列表
func InventoryListExport(list []ErpStock, nType uint32) (string, error) {
	file := excelize.NewFile()
	fSheet := "Sheet1"

	var fileName string
	switch nType {
	case 2: // 有库存
		fileName = time.Now().Format(TimeFormat) + "库存列表（有库存）" + ".xlsx"
	case 3: // 无库存
		fileName = time.Now().Format(TimeFormat) + "库存列表（无库存）" + ".xlsx"
	default: // 全部
		fileName = time.Now().Format(TimeFormat) + "库存列表（全部）" + ".xlsx"
	}

	url := config.ExportConfig.Url
	fmt.Println("url fileName:", url+fileName)

	// 设置标题行
	title1 := []interface{}{"基础资料", "", "", "", "", "", "库存情况", ""}
	title2 := []interface{}{"商品编号", "商品名称", "商品分类", "是否串码", "指导零售价", "最低零售价", "有效库存数量", "调出中数量"}

	for i, _ := range title1 {
		cell, _ := excelize.CoordinatesToCellName(1+i, 1)
		err := file.SetCellValue(fSheet, cell, title1[i])
		if err != nil {
			logger.Errorf("file set value err:", err)
		}
	}

	for i, _ := range title2 {
		cell, _ := excelize.CoordinatesToCellName(1+i, 2)
		err := file.SetCellValue(fSheet, cell, title2[i])
		if err != nil {
			logger.Errorf("file set value err:", err)
		}
	}

	nExcelStartRow := 0
	for rowId := 0; rowId < len(list); rowId++ {
		isIMEIType := "是"
		if list[rowId].IMEIType == 1 {
			isIMEIType = "否"
		}

		row := []interface{}{
			list[rowId].CommoditySerialNumber,
			list[rowId].ErpCommodityName,
			list[rowId].ErpCategoryName,
			isIMEIType,
			list[rowId].RetailPrice,
			list[rowId].MinRetailPrice,
			list[rowId].Count,
			list[rowId].DispatchCount,
		}

		for j, _ := range row {
			cell, _ := excelize.CoordinatesToCellName(1+j, nExcelStartRow+3)
			err := file.SetCellValue(fSheet, cell, row[j])
			if err != nil {
				logger.Error("file set value err:", logger.Field("err", err))
			}
		}
		nExcelStartRow++
	}

	// 设置所有单元格的样式: 居中、加边框
	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.MergeCell(fSheet, "A1", "F1")
	_ = file.MergeCell(fSheet, "G1", "H1")

	//设置单元格高度
	file.SetRowHeight("Sheet1", 1, 20)

	// 设置单元格大小
	file.SetColWidth("Sheet1", "A", "A", 13)
	file.SetColWidth("Sheet1", "B", "B", 40)
	file.SetColWidth("Sheet1", "C", "C", 13)
	file.SetColWidth("Sheet1", "E", "E", 13)
	file.SetColWidth("Sheet1", "F", "F", 13)
	file.SetColWidth("Sheet1", "G", "G", 13)
	file.SetColWidth("Sheet1", "H", "H", 13)

	endRow := fmt.Sprintf("H"+"%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
}

func ErpStockCommodityListSetAge(commodities []ErpStockCommodity) {
	nowTime := time.Now()
	for i, _ := range commodities {
		commodities[i].Age = uint32(nowTime.Sub(commodities[i].StockTime).Hours()) / 24
		commodities[i].AllAge = uint32(nowTime.Sub(commodities[i].FirstStockTime).Hours()) / 24
	}
}

func ErpStockCommodityListSetCategoryNumber(commodities []ErpStockCommodity) {
	for i, _ := range commodities {
		categoryInfo, _ := GetErpCategory(commodities[i].ErpCategoryId)
		if categoryInfo != nil {
			commodities[i].CategoryNumber = categoryInfo.Number
		}
	}
}

// ErpStockCommodityListReq 库存详情接口请求参数
type ErpStockCommodityListReq struct {
	ScanCode       string   `json:"scan_code"`        // 扫码枪扫码数据：串码/条码
	ErpStockId     uint32   `json:"erp_stock_id"`     // 库存id
	ErpCommodityId uint32   `json:"erp_commodity_id"` // 商品id
	SerialNumber   string   `json:"serial_number"`    // 商品编号
	CommodityName  []string `json:"commodity_name"`   // 商品名称
	ErpCategoryId  uint32   `json:"erp_category_id"`  // 商品分类Id
	IsIMEI         uint32   `json:"is_imei"`          // 是否串码：0-查全部 1-查串码类 2-查非串码
	IMEI           string   `json:"imei"`             // 串码
	StoreId        uint32   `json:"store_id"`         // 门店编号
	SupplierId     uint32   `json:"supplier_id"`      // 供应商id
	State          []uint32 `json:"state"`            // 库存状态:1-在库 2-已售 3-采购退货 4-调拨中 5-出库（前端只看1，4）
	OriginalSn     string   `json:"original_sn"`      // 首次入库订单编号
	StorageType    uint32   `json:"storage_type"`     // 首次入库方式：1-系统入库 2-采购入库
	StockTimeStart string   `json:"stock_time_start"` // 最近入库开始时间
	StockTimeEnd   string   `json:"stock_time_end"`   // 最近入库结束时间
	Age            uint32   `json:"age"`              // 最近库龄
	AllAge         uint32   `json:"all_age"`          // 总库龄
	PageIndex      int      `json:"pageIndex"`        // 页码
	PageSize       int      `json:"pageSize"`         // 每页展示数据条数
	IsExport       uint32   `json:"is_export"`        // 是否导出excel：1-导出
	StockSortFlag  uint32   `json:"stock_sort_flag"`  // 排序方式：0-按入库时间降序排列（默认），1-按入库时间升序排列
	//Sn             string `json:"sn"`               // 首次入库订单编号
}

// NewErpStockCommodityListReq 库存详情接口请求参数
type NewErpStockCommodityListReq struct {
	ScanCode       string   `json:"scan_code"`        // 扫码枪扫码数据：串码/条码
	ErpStockId     uint32   `json:"erp_stock_id"`     // 库存id
	ErpCommodityId uint32   `json:"erp_commodity_id"` // 商品id
	SerialNumber   string   `json:"serial_number"`    // 商品编号
	CommodityName  []string `json:"commodity_name"`   // 商品名称
	ErpCategoryId  uint32   `json:"erp_category_id"`  // 商品分类Id
	IsIMEI         uint32   `json:"is_imei"`          // 是否串码：0-查全部 1-查串码类 2-查非串码
	IMEI           string   `json:"imei"`             // 串码
	StoreId        []uint32 `json:"store_id"`         // 门店编号
	SupplierId     uint32   `json:"supplier_id"`      // 供应商id
	State          []uint32 `json:"state"`            // 库存状态:1-在库 2-已售 3-采购退货 4-调拨中 5-出库（前端只看1，4）
	OriginalSn     string   `json:"original_sn"`      // 首次入库订单编号
	StorageType    uint32   `json:"storage_type"`     // 首次入库方式：1-系统入库 2-采购入库
	StockTimeStart string   `json:"stock_time_start"` // 最近入库开始时间
	StockTimeEnd   string   `json:"stock_time_end"`   // 最近入库结束时间
	Age            uint32   `json:"age"`              // 最近库龄
	AllAge         uint32   `json:"all_age"`          // 总库龄
	PageIndex      int      `json:"pageIndex"`        // 页码
	PageSize       int      `json:"pageSize"`         // 每页展示数据条数
	IsExport       uint32   `json:"is_export"`        // 是否导出excel：1-导出
	StockSortFlag  uint32   `json:"stock_sort_flag"`  // 排序方式：0-按入库时间降序排列（默认），1-按入库时间升序排列
	//Sn             string `json:"sn"`               // 首次入库订单编号
}

// ErpStockCommodityListResp 库存详情接口响应参数
type ErpStockCommodityListResp struct {
	List                []ErpStockCommodity `json:"list"`
	Total               int                 `json:"total"`                 // 数据总条数
	PageIndex           int                 `json:"pageIndex"`             // 页码
	PageSize            int                 `json:"pageSize"`              // 每页展示条数
	TotalWholesalePrice float64             `json:"total_wholesale_price"` // 入库采购价之和
	TotalStaffPrice     float64             `json:"total_staff_price"`     // 入库员工成本价之和
	ExportUrl           string              `json:"export_url"`
}

// GetDetailList 查看库存详情
func (m *ErpStockCommodityListReq) GetDetailList(c *gin.Context, nType uint32) (*ErpStockCommodityListResp, error) {
	resp := &ErpStockCommodityListResp{
		PageIndex: m.PageIndex,
		PageSize:  m.PageSize,
	}
	page := m.PageIndex - 1
	if page < 0 {
		page = 0
	}
	if m.PageSize == 0 {
		m.PageSize = 10
	}

	if m.ErpStockId != 0 {
		updateCommodityStock(m.ErpStockId) // 同步详情表的库存id
	}

	// 出库数据不查询
	qs := orm.Eloquent.Table("erp_stock_commodity")

	// 非管理员才判断所属门店
	if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") {
		sysUser, err := GetSysUserByCtx(c)
		if err != nil {
			return nil, err
		}

		// 返回sysUser未过期的门店id列表
		storeList := GetValidStoreIDs(sysUser.StoreData)
		if m.StoreId != 0 {
			if !Contains(storeList, m.StoreId) {
				return nil, errors.New("您没有该门店权限")
			}
		} else {
			if len(storeList) > 0 {
				if len(storeList) == 1 {
					qs = qs.Where("store_id = ?", storeList[0])
				} else {
					qs = qs.Where("store_id IN (?)", storeList)
				}
			} else {
				return nil, errors.New("用户未绑定门店")
			}
		}
	}

	// 构建查询条件
	m.buildQueryConditions(qs, nType)
	es := qs

	var count int64
	if err := qs.Count(&count).Error; err != nil {
		logger.Error("count err:", logger.Field("err", err))
		return resp, err
	}

	nTotalCount := &struct {
		TotalWholesalePrice float64 `json:"total_wholesale_price"`
		TotalStaffCostPrice float64 `json:"total_staff_cost_price"`
	}{}
	err := es.Debug().Select("SUM(wholesale_price) as total_wholesale_price, SUM(staff_cost_price) as total_staff_cost_price").
		Scan(&nTotalCount).Error
	if err != nil {
		logger.Error("count err:", logger.Field("err", err))
		return resp, err
	}

	//获取库存商品列表
	var commodities []ErpStockCommodity
	if m.IsExport == 1 {
		err := qs.Find(&commodities).Order("stock_time DESC,id DESC").Error
		if err != nil && !errors.Is(err, RecordNotFound) {
			logger.Error("find err:", logger.Field("err", err))
			return resp, err
		}
		ErpStockCommodityListSetAge(commodities)

		listExport, err := InventoryDetailListExport(commodities, c)
		if err != nil {
			//logger.Error("list export err:", err)
		}
		resp.ExportUrl = listExport
	} else {
		if m.StockSortFlag == 1 {
			err = qs.Offset(page * m.PageSize).Limit(m.PageSize).Order("stock_time ASC,id ASC").Find(&commodities).Error
		} else {
			err = qs.Offset(page * m.PageSize).Limit(m.PageSize).Order("stock_time DESC,id DESC").Find(&commodities).Error
		}
		if err != nil && !errors.Is(err, RecordNotFound) {
			//logger.Error("erp commodity list err:", err)
			return resp, err
		}
		ErpStockCommodityListSetAge(commodities)
		ErpStockCommodityListSetCategoryNumber(commodities)
		resp.List = commodities
	}

	if len(commodities) == 1 && commodities[0].State == InAllot && m.ScanCode != "" {
		return nil, errors.New("该商品在调拨中")
	}

	//跟之前保持一致
	resp.Total = int(count)
	resp.PageIndex = page + 1
	resp.PageSize = m.PageSize
	resp.TotalWholesalePrice = math.Round(nTotalCount.TotalWholesalePrice*100) / 100
	resp.TotalStaffPrice = math.Round((nTotalCount.TotalStaffCostPrice+nTotalCount.TotalWholesalePrice)*100) / 100

	return resp, nil
}

// GetDetailListNew 查看库存详情
func (m *NewErpStockCommodityListReq) GetDetailListNew(c *gin.Context) (*ErpStockCommodityListResp, error) {
	resp := &ErpStockCommodityListResp{
		PageIndex: m.PageIndex,
		PageSize:  m.PageSize,
	}
	page := m.PageIndex - 1
	if page < 0 {
		page = 0
	}
	if m.PageSize == 0 {
		m.PageSize = 10
	}

	if m.ErpStockId != 0 {
		updateCommodityStock(m.ErpStockId) // 同步详情表的库存id
	}

	// 出库数据不查询
	qs := orm.Eloquent.Table("erp_stock_commodity")

	// 非管理员才判断所属门店
	if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") {
		sysUser, err := GetSysUserByCtx(c)
		if err != nil {
			return nil, err
		}

		// 返回sysUser未过期的门店id列表
		storeList := GetValidStoreIDs(sysUser.StoreData)
		if len(m.StoreId) != 0 {
			if len(CompareLists(storeList, m.StoreId)) == 0 {
				return nil, errors.New("您没有该门店权限")
			}
		} else {
			if len(storeList) > 0 {
				if len(storeList) == 1 {
					qs = qs.Where("store_id = ?", storeList[0])
				} else {
					qs = qs.Where("store_id IN (?)", storeList)
				}
			} else {
				return nil, errors.New("用户未绑定门店")
			}
		}
	}

	// 构建查询条件
	m.buildQueryConditionsNew(qs)
	es := qs

	var count int64
	if err := qs.Count(&count).Error; err != nil {
		logger.Error("count err:", logger.Field("err", err))
		return resp, err
	}

	nTotalCount := &struct {
		TotalWholesalePrice float64 `json:"total_wholesale_price"`
		TotalStaffCostPrice float64 `json:"total_staff_cost_price"`
	}{}
	err := es.Debug().Select("SUM(wholesale_price) as total_wholesale_price, SUM(staff_cost_price) as total_staff_cost_price").
		Scan(&nTotalCount).Error
	if err != nil {
		logger.Error("count err:", logger.Field("err", err))
		return resp, err
	}

	//获取库存商品列表
	var commodities []ErpStockCommodity
	if m.IsExport == 1 {
		err := qs.Find(&commodities).Order("stock_time DESC,id DESC").Error
		if err != nil && !errors.Is(err, RecordNotFound) {
			logger.Error("find err:", logger.Field("err", err))
			return resp, err
		}
		ErpStockCommodityListSetAge(commodities)

		listExport, err := InventoryDetailListExport(commodities, c)
		if err != nil {
			//logger.Error("list export err:", err)
		}
		resp.ExportUrl = listExport
	} else {
		if m.StockSortFlag == 1 {
			err = qs.Offset(page * m.PageSize).Limit(m.PageSize).Order("stock_time ASC,id ASC").Find(&commodities).Error
		} else {
			err = qs.Offset(page * m.PageSize).Limit(m.PageSize).Order("stock_time DESC,id DESC").Find(&commodities).Error
		}
		if err != nil && !errors.Is(err, RecordNotFound) {
			//logger.Error("erp commodity list err:", err)
			return resp, err
		}
		ErpStockCommodityListSetAge(commodities)
		ErpStockCommodityListSetCategoryNumber(commodities)
		resp.List = commodities
	}

	if len(commodities) == 1 && commodities[0].State == InAllot && m.ScanCode != "" {
		return nil, errors.New("该商品在调拨中")
	}

	//跟之前保持一致
	resp.Total = int(count)
	resp.PageIndex = page + 1
	resp.PageSize = m.PageSize
	resp.TotalWholesalePrice = math.Round(nTotalCount.TotalWholesalePrice*100) / 100
	resp.TotalStaffPrice = math.Round((nTotalCount.TotalStaffCostPrice+nTotalCount.TotalWholesalePrice)*100) / 100

	return resp, nil
}

// buildQueryConditions 根据请求参数构建查询条件
func (m *NewErpStockCommodityListReq) buildQueryConditionsNew(qs *gorm.DB) {
	if m.ScanCode != "" {
		qs = qs.Where("erp_barcode = ? or imei = ?", m.ScanCode, m.ScanCode)
		qs = qs.Where("state in (?)", []uint32{InStock, InAllot})
	} else {
		qs = qs.Where("state in (?)", []uint32{InStock, InAllot})
		if m.ErpStockId != 0 { //库存id
			qs = qs.Where("erp_stock_id=?", m.ErpStockId)
		}

		if m.ErpCommodityId != 0 { //商品id
			qs = qs.Where("erp_commodity_id=?", m.ErpCommodityId)
		}

		if m.IsIMEI != 0 { // 是否串码
			if m.IsIMEI == 1 { // 查串码数据
				qs = qs.Where("imei_type != ?", NoIMEICommodity)
			} else if m.IsIMEI == 2 { // 查非串码数据
				qs = qs.Where("imei_type = ?", NoIMEICommodity)
			}
		}

		if m.SerialNumber != "" { //商品编号
			qs = qs.Where("commodity_serial_number=?", m.SerialNumber)
		}

		if len(m.CommodityName) != 0 { //商品名称
			qs = qs.Where("erp_commodity_name IN (?)", m.CommodityName)
		}

		if m.ErpCategoryId != 0 { //商品分类id
			categoryList, _ := GetSubcategoryIds(m.ErpCategoryId)
			if categoryList == nil {
				categoryList = append(categoryList, m.ErpCategoryId)
			}

			qs = qs.Where("erp_category_id in ?", categoryList)
		}

		if m.IMEI != "" { //商品串码
			qs = qs.Where("imei=?", m.IMEI)
		}

		if len(m.StoreId) != 0 { //门店编号
			qs = qs.Where("store_id in ?", m.StoreId)
		}

		if m.SupplierId != 0 { //供应商id
			qs = qs.Where("erp_supplier_id=?", m.SupplierId)
		}

		defaultStates := []int{1, 4}
		if len(m.State) != 0 {
			if len(m.State) == 1 {
				qs = qs.Where("state = ?", m.State[0])
			} else {
				qs = qs.Where("state IN (?)", m.State)
			}
		} else {
			qs = qs.Where("state IN (?)", defaultStates)
		}

		if m.OriginalSn != "" { //首次入库订单编号
			qs = qs.Where("original_sn=?", m.OriginalSn)
		}

		if m.StorageType != 0 { //首次入库方式
			qs = qs.Where("storage_type=?", m.StorageType)
		}

		if m.StockTimeStart != "" { //最近入库开始时间
			startTime, err := time.Parse(QueryTimeFormat, m.StockTimeStart)
			if err == nil {
				qs = qs.Where("stock_time>?", startTime)
			} else {
				//logger.Error("stock time start parse err:", err)
			}
		}

		if m.StockTimeEnd != "" { //最近入库结束时间
			endTime, err := time.Parse(QueryTimeFormat, m.StockTimeEnd)
			if err == nil {
				qs = qs.Where("stock_time<?", endTime)
			} else {
				//logger.Error("stock time end parse err:", err)
			}
		}

		if m.Age != 0 { //最近库龄
			qs = qs.Where("stock_time<?", time.Now().AddDate(0, 0, int(m.Age)*(-1)))
		}

		if m.AllAge != 0 { //总库龄
			qs = qs.Where("first_stock_time<?", time.Now().AddDate(0, 0, int(m.AllAge)*(-1)))
		}
	}
}

// RemoveDuplicates 去除重复数据，只保留 FirstStockTime 最久的记录
func RemoveDuplicates(resp *ErpStockCommodityListResp) (*ErpStockCommodityListResp, error) {
	uniqueItems := make(map[string]ErpStockCommodity)
	for _, item := range resp.List {
		if existing, found := uniqueItems[item.ErpCommodityName]; found {
			if item.FirstStockTime.Before(existing.FirstStockTime) {
				uniqueItems[item.ErpCommodityName] = item
			}
		} else {
			uniqueItems[item.ErpCommodityName] = item
		}
	}

	// 转换回列表
	var newList []ErpStockCommodity
	for _, item := range uniqueItems {
		//if len(uniqueItems) == 1 && item.IMEIType != 1{
		//	return nil, errors.New("串码商品请扫串码")
		//}
		if item.IMEIType != 1 {
			continue
		}
		newList = append(newList, item)
	}

	// 更新响应结构体
	resp.List = newList
	resp.Total = len(newList)
	resp.TotalWholesalePrice = 0
	resp.TotalStaffPrice = 0

	return resp, nil
}

func CheckScanCodeResp(scanCode string, storeId uint32, req *ErpStockCommodityListResp) error {
	if req.Total == 1 { // 串码商品，只有1个；条码商品，刚好也只有1个
		if req.List[0].IMEI == scanCode { // 串码商品，只有1个
			if req.List[0].StoreId != storeId && storeId != 0 {
				return errors.New(req.List[0].ErpCommodityName + "商品非所选门店库存，请检查")
			}
			return nil
		}

		if req.List[0].ErpBarcode == scanCode && req.List[0].IMEI != "" {
			return errors.New("串码类商品，请直接输入串码")
		}
	} else { // 条码商品，数量多
		var barCodeList []ErpStockCommodity
		var imeiLst []ErpStockCommodity
		for _, item := range req.List {
			if item.ErpBarcode == scanCode { // 扫码跟商品条码匹配
				flag := false
				for _, barCode := range barCodeList {
					if barCode.ErpBarcode == item.ErpBarcode {
						flag = true
						break
					}
				}

				if !flag { // 遍历后不存在则添加
					barCodeList = append(barCodeList, item)
				}
			}

			if item.IMEI == scanCode { // 扫码跟商品串码匹配
				flag := false
				for _, barCode := range imeiLst {
					if barCode.IMEI == item.IMEI {
						flag = true
						break
					}
				}

				if !flag { // 遍历后不存在则添加
					imeiLst = append(imeiLst, item)
				}
			}
		}

		// 查询的数据是条码，但是条码对应的商品是串码商品
		if len(barCodeList) == 1 && len(imeiLst) == 0 {
			if barCodeList[0].ErpBarcode == scanCode && barCodeList[0].IMEI != "" {
				return errors.New("串码类商品，请直接输入串码")
			}
		}

		req.List = nil
		if len(barCodeList) > 0 {
			req.List = append(req.List, barCodeList[0])
		}

		if len(imeiLst) > 0 {
			req.List = append(req.List, imeiLst[0])
		}

		req.Total = len(req.List)
		req.TotalStaffPrice = 0
		req.TotalWholesalePrice = 0
	}

	return nil
}

// contains 函数用于检查一个整数切片中是否包含某个值
func contains(s []int, e int) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}

// buildQueryConditions 根据请求参数构建查询条件
func (m *ErpStockCommodityListReq) buildQueryConditions(qs *gorm.DB, nType uint32) {
	if m.ScanCode != "" {
		qs = qs.Where("erp_barcode = ? or imei = ?", m.ScanCode, m.ScanCode)
		qs = qs.Where("state in (?)", []uint32{InStock, InAllot})
	} else {
		qs = qs.Where("state in (?)", []uint32{InStock, InAllot})
		if m.ErpStockId != 0 { //库存id
			qs = qs.Where("erp_stock_id=?", m.ErpStockId)
		}

		if m.ErpCommodityId != 0 { //商品id
			qs = qs.Where("erp_commodity_id=?", m.ErpCommodityId)
		}

		if m.IsIMEI != 0 { // 是否串码
			if m.IsIMEI == 1 { // 查串码数据
				qs = qs.Where("imei_type != ?", NoIMEICommodity)
			} else if m.IsIMEI == 2 { // 查非串码数据
				qs = qs.Where("imei_type = ?", NoIMEICommodity)
			}
		}

		if m.SerialNumber != "" { //商品编号
			qs = qs.Where("commodity_serial_number=?", m.SerialNumber)
		}

		if len(m.CommodityName) != 0 { //商品名称
			if nType == 1 {
				qs = qs.Where("erp_commodity_name IN (?)", m.CommodityName)
			} else {
				qs = qs.Where("erp_commodity_name like ?", "%"+m.CommodityName[0]+"%")
			}
		}

		if m.ErpCategoryId != 0 { //商品分类id
			categoryList, _ := GetSubcategoryIds(m.ErpCategoryId)
			if categoryList == nil {
				categoryList = append(categoryList, m.ErpCategoryId)
			}

			qs = qs.Where("erp_category_id in ?", categoryList)
		}

		if m.IMEI != "" { //商品串码
			qs = qs.Where("imei=?", m.IMEI)
		}

		if m.StoreId != 0 { //门店编号
			qs = qs.Where("store_id=?", m.StoreId)
		}

		if m.SupplierId != 0 { //供应商id
			qs = qs.Where("erp_supplier_id=?", m.SupplierId)
		}

		defaultStates := []int{1, 4}
		if len(m.State) != 0 {
			if len(m.State) == 1 {
				qs = qs.Where("state = ?", m.State[0])
			} else {
				qs = qs.Where("state IN (?)", m.State)
			}
		} else {
			qs = qs.Where("state IN (?)", defaultStates)
		}

		if m.OriginalSn != "" { //首次入库订单编号
			qs = qs.Where("original_sn=?", m.OriginalSn)
		}

		if m.StorageType != 0 { //首次入库方式
			qs = qs.Where("storage_type=?", m.StorageType)
		}

		if m.StockTimeStart != "" { //最近入库开始时间
			startTime, err := time.Parse(QueryTimeFormat, m.StockTimeStart)
			if err == nil {
				qs = qs.Where("stock_time>?", startTime)
			} else {
				//logger.Error("stock time start parse err:", err)
			}
		}

		if m.StockTimeEnd != "" { //最近入库结束时间
			endTime, err := time.Parse(QueryTimeFormat, m.StockTimeEnd)
			if err == nil {
				qs = qs.Where("stock_time<?", endTime)
			} else {
				//logger.Error("stock time end parse err:", err)
			}
		}

		if m.Age != 0 { //最近库龄
			qs = qs.Where("stock_time<?", time.Now().AddDate(0, 0, int(m.Age)*(-1)))
		}

		if m.AllAge != 0 { //总库龄
			qs = qs.Where("first_stock_time<?", time.Now().AddDate(0, 0, int(m.AllAge)*(-1)))
		}
	}
}

// SetStockCommodityState 更新库存状态
func SetStockCommodityState(c *gin.Context, id uint32) error { //更新库存状态为5，并同步扣减库存数量
	begin := orm.Eloquent.Begin()

	// 查询库存详情
	var commodityInfo ErpStockCommodity
	err := orm.Eloquent.Table("erp_stock_commodity").
		Raw("SELECT * FROM erp_stock_commodity WHERE id = ?", id).
		Scan(&commodityInfo).Error
	if err != nil {
		logger.Error("查询库存商品信息失败", logger.Field("err", err))
		return err
	}

	// 校验是否有入参门店权限
	err = AuthUserStore(c, commodityInfo.StoreId)
	if err != nil {
		return err
	}

	// 查询库存数据
	var record ErpStock
	err = orm.Eloquent.Table("erp_stock").
		Where("erp_commodity_name = ? and store_id = ?",
			commodityInfo.ErpCommodityName, commodityInfo.StoreId).First(&record).Error
	if err != nil {
		logger.Error("查询库存数量失败", logger.Field("err", err))
		return err
	}

	// 更新商品库存详情表状态为：系统出库
	if err := begin.Model(&ErpStockCommodity{}).Where("id=?", id).Updates(map[string]interface{}{
		"state":      SystemOut,
		"updated_at": time.Now(),
	}).Error; err != nil {
		return fmt.Errorf("[update err]：%v", err)
	}

	if record.Count > 0 {
		// 扣减商品库存数量
		err = begin.Model(&record).Update("count", record.Count-1).Error
		if err != nil {
			logger.Error("扣减库存数量失败", logger.Field("err", err))
			return err
		}
	}

	err = begin.Commit().Error
	if err != nil {
		begin.Rollback()
		logger.Error("出库事务失败", logger.Field("err", err))
		return err
	}

	return nil
}

type BatchPrintInfo struct {
	ErpCommodityName string `json:"erp_commodity_name" binding:"required"` // 商品名称
	RetailPrice      uint32 `json:"retail_price" binding:"required"`       // 指导零售价
	IMEI             string `json:"imei" binding:"required"`               // 商品串码
}

type BatchPrintInfoReq struct {
	PrintListInfo []*BatchPrintInfo `json:"print_list_info" binding:"required"`
}

// BatchPrint 批量打印标签
func BatchPrint(req *BatchPrintInfoReq) error {

	return nil
}

func StringToFloat(req string) (float64, error) {
	if req == "" {
		return 0, nil
	}

	return strconv.ParseFloat(req, 64)
}

func PercentFloatStringToFloat64(s string) float64 {
	u := float64(0)
	if s != "" {
		s = strings.ReplaceAll(s, "%", "")
	}
	f, err := strconv.ParseFloat(s, 64)
	if err != nil {
		//logger.Error("parse float err:", err)
		return u
	}

	u = f * 100
	return tools.RoundToTwoDecimalPlaces(u)
}

func IntStringToUin32(s string) uint32 {
	u := uint32(0)
	if s != "" {
		s = strings.ReplaceAll(s, "%", "")
	}
	i, err := strconv.Atoi(s)
	if err != nil {
		//logger.Error("parse float err:", err)
		return u
	}

	u = uint32(i)
	return u
}

func NewStockImporter() *StockImporter {
	return &StockImporter{
		CensusMap:    make(map[uint32]map[uint32]uint32, 0),
		CommodityMap: make(map[uint32]*ErpCommodity),
		StoreMap:     make(map[uint32]string, 0),
		Inventories:  make([]*ErpInventoryStock, 0),
	}
}

// UpdateStockCommodityRemark 更新库存商品备注信息
func UpdateStockCommodityRemark(id int, remark string) error {
	if remark == "" {
		return nil
	}
	if err := orm.Eloquent.Model(&ErpStockCommodity{}).Where("id=?", id).Updates(map[string]interface{}{
		"remark": remark}).Error; err != nil {
		return fmt.Errorf("[update err]：%v", err)
	}

	return nil
}

func GetCommodity(id uint32) (*ErpCommodity, error) {
	var erpCommodity ErpCommodity
	err := orm.Eloquent.Table("erp_commodity").Where("id=?", id).Find(&erpCommodity).Error
	if err != nil {
		logger.Errorf("err:", logger.Field("err", err))
		return &erpCommodity, err
	}
	return &erpCommodity, nil
}

// CheckAndConvertBarcode 校验输入的条码数据是否正确
func CheckAndConvertBarcode(input string) (string, error) {
	// 1. 检查总长度不超过100字符
	if utf8.RuneCountInString(input) > 100 {
		return "", errors.New("error: Total length exceeds 100 characters")
	}

	// 2. 转换中文逗号为英文逗号
	input = strings.ReplaceAll(input, "，", ",")

	// 3. 两个逗号之前需要有数据，不能是空格或无数据
	parts := strings.Split(input, ",")
	for _, part := range parts {
		trimmedPart := strings.TrimSpace(part)
		if trimmedPart == "" {
			return "", errors.New("error: Two commas should have data in between")
		}
	}

	// 4.判断逗号隔开的数据数据库是否已存在
	for _, part := range parts {
		// 检查生成的条码是否已存在
		exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_commodity WHERE FIND_IN_SET('%s', erp_barcode) > 0", part))
		if err != nil {
			logger.Error("exist sn err")
		}
		if exist {
			return "", errors.New("[" + part + "]" + "该条码已存在,请检查")
		}
	}

	// 如果通过所有检查，返回处理后的字符串
	return input, nil
}

// CheckBarcodeById 校验输入的条码数据是否正确
func CheckBarcodeById(input string, commodityId uint32) (string, error) {
	// 1. 检查总长度不超过100字符
	if utf8.RuneCountInString(input) > 100 {
		return "", errors.New("error: Total length exceeds 100 characters")
	}

	// 2. 转换中文逗号为英文逗号
	input = strings.ReplaceAll(input, "，", ",")

	// 3. 两个逗号之前需要有数据，不能是空格或无数据
	parts := strings.Split(input, ",")
	for _, part := range parts {
		trimmedPart := strings.TrimSpace(part)
		if trimmedPart == "" {
			return "", errors.New("error: Two commas should have data in between")
		}
	}

	// 4.判断逗号隔开的数据数据库是否已存在
	for _, part := range parts {
		// 检查生成的条码是否已存在
		exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_commodity WHERE FIND_IN_SET('%s', erp_barcode) > 0", part))
		if err != nil {
			logger.Error("exist sn err")
		}
		if exist {
			var commodityInfo ErpCommodity
			err = orm.Eloquent.Table("erp_commodity").Where("id=?", commodityId).Find(&commodityInfo).Error
			if err != nil {
				logger.Errorf("get erp_commodity err:", err)
				return "", err
			}
			if commodityInfo.ErpBarcode == input {
				return input, nil
			} else {
				return "", errors.New("[" + part + "]" + "该条码已存在,请检查")
			}
		}
	}

	// 如果通过所有检查，返回处理后的字符串
	return input, nil
}

// GenerateBarcode 系统生成条码
func GenerateBarcode(categoryCode uint32) (string, error) {
	category := new(Category)
	err := orm.Eloquent.Table("erp_category").Where("id=?", categoryCode).Find(category).Error
	if err != nil {
		logger.Errorf("get erp category err:", err)
		return "", err
	}

	max := 1
	for {
		if max > 5 {
			logger.Error("create Barcode err")
			return "", errors.New("create Barcode err")
		}
		// 生成当前日期的六位数
		dateCode := time.Now().Format("060102")

		// 生成三位随机数
		randomNumber := fmt.Sprintf("%03d", rand.Intn(1000))

		// 组合生成的条码
		barcode := fmt.Sprintf("%03s%s%s", category.Number[:3], dateCode, randomNumber)

		// 检查生成的条码是否已存在
		exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_commodity WHERE FIND_IN_SET('%s', erp_barcode) > 0", barcode))
		if err != nil {
			logger.Error("exist sn err")
		}
		if !exist {
			return barcode, nil
		}
	}
}

// GetCodeList 查询输入的条码或串码数据
func GetCodeList(req *QueryCodeReq) (*QueryCodeResp, error) {
	resp := &QueryCodeResp{
		PageIndex: req.PageIndex,
		PageSize:  req.PageSize,
	}
	page := req.PageIndex - 1
	if page < 0 {
		page = 0
	}
	if req.PageSize == 0 {
		req.PageSize = 10
	}

	var commodities []ErpStockCommodity

	orderStr := fmt.Sprintf("CASE WHEN imei LIKE '%%%s%%' THEN 1 ELSE 2 END", req.ScanCode)

	qs := orm.Eloquent.Debug().Table("erp_stock_commodity")
	qs = qs.Where("imei LIKE ? OR erp_barcode LIKE ?", "%"+req.ScanCode+"%", "%"+req.ScanCode+"%")

	if req.State != 0 {
		qs = qs.Where("state = ?", req.State)
	}

	if req.StoreId != 0 {
		qs = qs.Where("store_id = ?", req.StoreId)
	}

	es := qs
	err := qs.Order(orderStr).Offset(page * req.PageSize).Limit(req.PageSize).Find(&commodities).Error
	if err != nil && err != RecordNotFound {
		logger.Error("commodityList err", logger.Field("err", err))
		return resp, err
	}

	var count int64
	err = es.Count(&count).Error
	if err != nil {
		logger.Error("commodityList count err", logger.Field("err", err))
		return resp, err
	}

	var stockList []string
	var strCode string
	for _, commodity := range commodities {
		if commodity.IMEI != "" && strings.Contains(commodity.IMEI, req.ScanCode) { // 串码非空且包含查找的数据
			strCode = commodity.IMEI
			//} else if commodity.IMEI != "" && strings.Contains(commodity.ErpBarcode, req.ScanCode) { // 串码不为空，但条码包含扫码数据
		} else if commodity.IMEI != "" && commodity.ErpBarcode == req.ScanCode { // 串码不为空，但条码包含扫码数据
			return nil, errors.New("串码类商品请直接输入串码")
		} else {
			strCode = commodity.ErpBarcode
		}
		stockList = append(stockList, strCode)
	}
	//跟之前保持一致
	resp.Total = len(stockList)
	resp.PageIndex = page + 1
	resp.PageSize = req.PageSize
	resp.List = stockList

	return removeDuplicates(resp), nil
}

func removeDuplicates(resp *QueryCodeResp) *QueryCodeResp {
	// 创建一个 map 用来记录已经出现过的字符串
	uniqueMap := make(map[string]bool)

	// 新建一个切片用来保存不重复的数据
	var uniqueList []string

	// 遍历原始的 List
	for _, item := range resp.List {
		// 如果这个字符串已经在 map 中存在，说明重复，跳过
		if _, ok := uniqueMap[item]; ok {
			continue
		}
		// 否则，将其添加到新的切片中，并在 map 中标记为已经出现过
		uniqueList = append(uniqueList, item)
		uniqueMap[item] = true
	}

	// 构建新的 QueryCodeResp 结构体
	newResp := &QueryCodeResp{
		List:      uniqueList,
		Total:     len(uniqueList),
		PageIndex: resp.PageIndex,
		PageSize:  resp.PageSize,
	}

	return newResp
}

func GetErpCommodityMap(ids []uint32) (map[uint32]ErpCommodity, error) {
	commodityMap := make(map[uint32]ErpCommodity, 0)
	if len(ids) == 0 {
		return commodityMap, nil
	}
	var commodities []ErpCommodity
	err := orm.Eloquent.Table("erp_commodity").Where("id IN (?)", ids).Find(&commodities).Error
	if err != nil {
		logger.Error("commodities err:", logger.Field("err", err))
		return commodityMap, err
	}

	for i, _ := range commodities {
		commodityMap[commodities[i].ID] = commodities[i]
	}

	return commodityMap, nil
}

type CommodityCreateRequest struct {
	Name             string  `json:"name" binding:"required"`             // 商品名称
	ErpCategoryId    uint32  `json:"erp_category_id"  binding:"required"` // 商品分类id
	IsIMEI           uint32  `json:"is_imei" binding:"required"`          // 是否串码：1-串码类 2-非串码
	ErpBarcode       string  `json:"erp_barcode"`                         // 商品条码
	IMEIType         uint32  `json:"imei_type"`                           // 系统生成串码：2-是（系统生成） 3-否（手动添加）
	ErpSupplierId    uint32  `json:"erp_supplier_id" binding:"required"`  // 主供应商
	RetailPrice      float64 `json:"retail_price"`                        // 指导零售价
	MinRetailPrice   float64 `json:"min_retail_price"`                    // 最低零售价
	StaffCostPrice   float64 `json:"staff_cost_price"`                    // 员工成本价加价
	WholesalePrice   float64 `json:"wholesale_price"`                     // 指导采购价
	Brokerage1       float64 `json:"brokerage_1"`                         // 销售毛利提成
	Brokerage2       float64 `json:"brokerage_2"`                         // 员工毛利提成
	MemberDiscount   float64 `json:"member_discount"`                     // 会员优惠
	Origin           string  `json:"origin"`                              // 产地
	Remark           string  `json:"remark"`                              // 备注
	Img              string  `json:"img"`                                 // 图片
	StopPurchase     uint32  `json:"stop_purchase"`                       // 0-未勾选，正常采购；1-勾选，停止采购
	ManufacturerCode string  `json:"manufacturer_code"`                   // 厂家编码
}

type CommodityEditRequest struct {
	Id               uint32  `json:"id" binding:"required"`               // 商品id
	Name             string  `json:"name" binding:"required"`             // 商品名称
	ErpCategoryId    uint32  `json:"erp_category_id"  binding:"required"` // 商品分类id
	ErpBarcode       string  `json:"erp_barcode"`                         // 商品条码
	IsIMEI           uint32  `json:"is_imei" binding:"required"`          // 是否串码：1-串码类 2-非串码
	IMEIType         uint32  `json:"imei_type" binding:"required"`        // 1-无串码 2-串码（系统生成） 3-串码（手动添加）
	ErpSupplierId    uint32  `json:"erp_supplier_id" binding:"required"`  // 主供应商id
	RetailPrice      float64 `json:"retail_price"`                        // 指导零售价
	MinRetailPrice   float64 `json:"min_retail_price"`                    // 最低零售价
	StaffCostPrice   float64 `json:"staff_cost_price"`                    // 员工成本价加价
	WholesalePrice   float64 `json:"wholesale_price"`                     // 指导采购价
	Brokerage1       float64 `json:"brokerage_1"`                         // 销售毛利提成
	Brokerage2       float64 `json:"brokerage_2"`                         // 员工毛利提成
	MemberDiscount   float64 `json:"member_discount"`                     // 会员优惠
	Origin           string  `json:"origin"`                              // 产地
	Remark           string  `json:"remark"`                              // 备注
	Img              string  `json:"img"`                                 // 图片
	StopPurchase     uint32  `json:"stop_purchase"`                       // 0-未勾选，正常采购；1-勾选，停止采购
	ManufacturerCode string  `json:"manufacturer_code"`                   // 厂家编码
}

type CommodityDetailRequest struct {
	ErpCommodityId uint32 `json:"erp_commodity_id"` // 商品id
	SerialNumber   string `json:"serial_number"`    // 商品编号
}

type CommodityDelRequest struct {
	ErpCommodityId uint32 `json:"erp_commodity_id" binding:"required"` // 商品id
}

// ImeiTraceReq 串码追踪入参
type ImeiTraceReq struct {
	IMEI string `json:"imei"` // 商品串码
}

// ImeiTraceResp 串码追踪出参
type ImeiTraceResp struct {
	List  []ImeiTraceData `json:"list"`
	Total int             `json:"total"` // 总条数
}

type ImeiTraceData struct {
	StoreId               uint32     `json:"store_id" gorm:"index"`                // 门店id
	StoreName             string     `json:"store_name"`                           // 门店名称
	SerialType            string     `json:"serial_type"`                          // 单据类型
	MakerTime             *time.Time `json:"maker_time"`                           // 单据日期
	SerialNumber          string     `json:"serial_number" gorm:"index"`           // 单据编号
	ErpCommodityId        uint32     `json:"erp_commodity_id" gorm:"index"`        // 商品id
	ErpCommodityName      string     `json:"erp_commodity_name"`                   // 商品名称
	CommoditySerialNumber string     `json:"commodity_serial_number" gorm:"index"` // 商品编号
	Count                 int        `json:"count"`                                // 商品数量
	Remarks               string     `json:"remarks"`                              // 备注
}

// QueryImeiTraceData 串码追踪，返回多个结果
func QueryImeiTraceData(req *ImeiTraceReq) (*ImeiTraceResp, error) {
	if req == nil || req.IMEI == "" {
		return &ImeiTraceResp{}, nil
	}

	var results []ImeiTraceData

	// 1. 查询采购入库记录（关联采购订单与采购商品明细）
	var purchaseRecords []ImeiTraceData
	err := orm.Eloquent.Table("erp_purchase_inventory").
		Select(`erp_purchase_order.store_id AS store_id,
                erp_purchase_order.store_name AS store_name,
                '采购入库' AS serial_type,
                erp_purchase_order.maker_time AS maker_time,
                erp_purchase_order.serial_number AS serial_number,
                erp_purchase_inventory.erp_commodity_id AS erp_commodity_id,
                erp_purchase_inventory.erp_commodity_name AS erp_commodity_name,
                erp_purchase_inventory.commodity_serial_number AS commodity_serial_number,
                erp_purchase_inventory.count AS count,
                erp_purchase_order.remark AS remarks`).
		Joins("LEFT JOIN erp_purchase_order ON erp_purchase_order.id = erp_purchase_inventory.erp_purchase_order_id").
		Where("erp_purchase_inventory.imei = ?", req.IMEI).
		Find(&purchaseRecords).Error
	if err != nil {
		return nil, err
	}
	results = append(results, purchaseRecords...)

	// 2. 查询产品入库记录（关联产品入库订单与产品入库商品信息）
	var productRecords []ImeiTraceData
	err = orm.Eloquent.Table("erp_inventory_product_commodity").
		Select(`erp_inventory_product_order.store_id AS store_id,
                erp_inventory_product_order.store_name AS store_name,
                '产品入库' AS serial_type,
                erp_inventory_product_order.maker_time AS maker_time,
                erp_inventory_product_order.serial_number AS serial_number,
                erp_inventory_product_commodity.commodity_id AS erp_commodity_id,
                erp_inventory_product_commodity.commodity_name AS erp_commodity_name,
                '' AS commodity_serial_number,
                erp_inventory_product_commodity.count AS count,
                erp_inventory_product_commodity.remark AS remarks`).
		Joins("LEFT JOIN erp_inventory_product_order ON erp_inventory_product_order.id = erp_inventory_product_commodity.product_order_id").
		Where("erp_inventory_product_commodity.imei = ?", req.IMEI).
		Find(&productRecords).Error
	if err != nil {
		return nil, err
	}
	results = append(results, productRecords...)

	// 3. 查询库存调拨记录（以接收门店为准）
	var allotRecords []ImeiTraceData
	err = orm.Eloquent.Table("erp_inventory_allot_commodity").
		Select(`erp_inventory_allot_order.receive_store_id AS store_id,
                erp_inventory_allot_order.receive_store_name AS store_name,
                '库存调拨' AS serial_type,
                erp_inventory_allot_order.maker_time AS maker_time,
                erp_inventory_allot_order.serial_number AS serial_number,
                erp_inventory_allot_commodity.commodity_id AS erp_commodity_id,
                erp_inventory_allot_commodity.commodity_name AS erp_commodity_name,
                '' AS commodity_serial_number,
                erp_inventory_allot_commodity.count AS count,
                erp_inventory_allot_commodity.remark AS remarks`).
		Joins("LEFT JOIN erp_inventory_allot_order ON erp_inventory_allot_order.id = erp_inventory_allot_commodity.allot_order_id").
		Where("erp_inventory_allot_commodity.imei = ?", req.IMEI).
		Find(&allotRecords).Error
	if err != nil {
		return nil, err
	}
	results = append(results, allotRecords...)

	// 4. 查询库存变动记录
	var changeRecords []ImeiTraceData
	err = orm.Eloquent.Table("erp_inventory_change_commodity").
		Select(`erp_inventory_change_order.store_id AS store_id,
                erp_inventory_change_order.store_name AS store_name,
				CASE erp_inventory_change_order.change_type 
                    WHEN 'add' THEN '库存增加'
                    WHEN 'reduce' THEN '库存减少'
                    ELSE '库存变动' END AS serial_type,
                erp_inventory_change_order.maker_time AS maker_time,
                erp_inventory_change_order.serial_number AS serial_number,
                erp_inventory_change_commodity.commodity_id AS erp_commodity_id,
                erp_inventory_change_commodity.commodity_name AS erp_commodity_name,
                '' AS commodity_serial_number,
                erp_inventory_change_commodity.count AS count,
                erp_inventory_change_commodity.remark AS remarks`).
		Joins("LEFT JOIN erp_inventory_change_order ON erp_inventory_change_order.id = erp_inventory_change_commodity.change_order_id").
		Where("erp_inventory_change_commodity.imei = ?", req.IMEI).
		Find(&changeRecords).Error
	if err != nil {
		return nil, err
	}
	results = append(results, changeRecords...)

	// 5. 查询零售订单记录（关联零售订单与订单商品表）
	var retailRecords []ImeiTraceData
	err = orm.Eloquent.Table("erp_order_commodity").
		Select(`erp_order.store_id AS store_id,
                erp_order.store_name AS store_name,
                CASE erp_order.retail_type 
                    WHEN 'sale' THEN '零售销售'
                    WHEN 'rejected' THEN '零售退货'
                    ELSE '零售订单' END AS serial_type,
                erp_order.maker_time AS maker_time,
                erp_order.bill_sn AS serial_number,
                erp_order_commodity.erp_commodity_id AS erp_commodity_id,
                erp_order_commodity.erp_commodity_name AS erp_commodity_name,
                erp_order_commodity.count AS count,
                erp_order_commodity.remark AS remarks`).
		Joins("LEFT JOIN erp_order ON erp_order.id = erp_order_commodity.erp_order_id").
		Where("erp_order_commodity.imei = ?", req.IMEI).
		Find(&retailRecords).Error
	if err != nil {
		return nil, err
	}
	results = append(results, retailRecords...)

	// 6.查询采购换机订单
	var purchaseChangeRecords []ImeiTraceData
	err = orm.Eloquent.Table("erp_purchase_change_commodity").
		Select(`erp_purchase_change_order.store_id AS store_id,
                erp_purchase_change_order.store_name AS store_name,
                '采购换机' AS serial_type,
                erp_purchase_change_order.maker_time AS maker_time,
                erp_purchase_change_order.serial_number AS serial_number,
                erp_purchase_change_commodity.erp_commodity_id AS erp_commodity_id,
                erp_purchase_change_commodity.erp_commodity_name AS erp_commodity_name,
                erp_purchase_change_commodity.count AS count,
                erp_purchase_change_commodity.remark AS remarks`).
		Joins("LEFT JOIN erp_purchase_change_order ON erp_purchase_change_order.id = "+
			"erp_purchase_change_commodity.change_order_id").
		Where("erp_purchase_change_commodity.origin_imei = ? or erp_purchase_change_commodity.new_imei = ?",
			req.IMEI, req.IMEI).
		Find(&purchaseChangeRecords).Error
	if err != nil {
		return nil, err
	}
	results = append(results, purchaseChangeRecords...)

	for i, _ := range results {
		if results[i].CommoditySerialNumber == "" {
			commodityInfo, _ := GetCommodity(results[i].ErpCommodityId)
			if commodityInfo != nil {
				results[i].CommoditySerialNumber = commodityInfo.SerialNumber
			}
		}
	}

	// 可选：对结果按单据日期（maker_time）倒序排序（时间最新的在最前）
	sort.Slice(results, func(i, j int) bool {
		// 如果任一 maker_time 为空，则不做排序调整
		if results[i].MakerTime == nil || results[j].MakerTime == nil {
			return false
		}
		return results[i].MakerTime.After(*results[j].MakerTime)
	})

	// 组装返回结构
	resp := &ImeiTraceResp{
		List:  results,
		Total: len(results),
	}
	return resp, nil
}
