package models import ( "errors" "fmt" "github.com/xuri/excelize/v2" orm "go-admin/common/global" "go-admin/logger" "go-admin/tools/config" "golang.org/x/sync/errgroup" "gorm.io/gorm" "strconv" "strings" "sync" "time" ) // 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 uint32 `json:"retail_price"` // 指导零售价 MinRetailPrice uint32 `json:"min_retail_price"` // 最低零售价 Count uint32 `json:"count"` // 数量 DispatchCount uint32 `json:"dispatch_count"` // 调拨中数量 Commodities []ErpStockCommodity `json:"commodities" 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"` // 分类名称 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 uint32 `json:"retail_price"` // 指导零售价 MinRetailPrice uint32 `json:"min_retail_price"` // 最低零售价 StaffCostPrice uint32 `json:"staff_cost_price"` // 员工成本价加价 WholesalePrice uint32 `json:"wholesale_price"` // 指导采购价 State uint32 `json:"state"` // 状态:1-在库 2-已售 3-采购退货 4-调拨中 Count uint32 `json:"count"` // 数量 StorageType uint32 `json:"storage_type"` // 入库方式:1-系统入库 2-采购入库 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"` // 备注 //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"` // 商品分类名称 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 uint32 `json:"retail_price"` // 指导零售价 MinRetailPrice uint32 `json:"min_retail_price"` // 最低零售价 StaffCostPrice uint32 `json:"staff_cost_price"` // 员工成本价加价 WholesalePrice uint32 `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)"` // 备注 ErpCategory *ErpCategory `json:"erp_category" gorm:"-"` } // ErpCategory 商品分类 type ErpCategory struct { Model Name string `json:"name"` // 名称 Priority string `json:"priority"` // 分类 Number uint32 `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 uint32 `json:"retail_price"` MinRetailPrice uint32 `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 uint32 `json:"retail_price"` MinRetailPrice uint32 `json:"min_retail_price"` StaffCostPrice uint32 `json:"staff_cost_price"` WholesalePrice uint32 `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 } // IdInit 新增/编辑商品时获取分类和供应商信息 func (c *ErpCommodity) IdInit() { if c.ErpCategoryId != 0 { if c.ErpCategory == nil { category, err := GetErpCategory(c.ErpCategoryId) if err != nil { //logger.Error("get erp category err:", err) return } c.ErpCategory = category } c.ErpCategoryName = c.ErpCategory.Name } if c.ErpSupplierId != 0 { supplier, err := GetErpSupplier(c.ErpSupplierId) if err != nil { //logger.Error("get erp category err:", err) } else { c.ErpSupplierName = supplier.Name } } } // 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.Error("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"` // 商品编号 ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 ErpCategoryId uint32 `json:"erp_category_id"` // 商品分类id IMEI string `json:"imei"` // 串码 ErpSupplierId uint32 `json:"erp_supplier_id"` // 供应商id PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 每页展示数据条数 IsExport uint32 `json:"is_export"` // 1-导出 } type ErpCommodityListResp struct { List []ErpCommodity `json:"list"` Total int `json:"count"` // 数据总条数 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 每页展示条数 ExportUrl string `json:"export_url"` // 1-导出 } 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 { qs = qs.Where("erp_category_id=?", m.ErpCategoryId) } if m.ErpSupplierId != 0 { qs = qs.Where("erp_supplier_id=?", m.ErpSupplierId) } var count int64 err := qs.Count(&count).Error if err != nil { //logger.Error("count err:", err) return resp, err } //resp.Total = int(count)/m.PageSize + 1 var commodities []ErpCommodity if m.IsExport == 1 { err = qs.Order("id DESC").Find(&commodities).Error if err != nil && err != RecordNotFound { //logger.Error("dailys err:", err) return resp, err } listExport, err := ErpCommodityListExport(commodities) if err != nil { //logger.Error("list export err:", err) } resp.ExportUrl = listExport } else { err = qs.Order("id DESC").Offset(page * m.PageSize).Limit(m.PageSize).Find(&commodities).Error if err != nil && err != RecordNotFound { //logger.Error("erp commodity list err:", err) return resp, err } resp.List = commodities } for i, v := range resp.List { if v.IMEIType == 1 { //无串码 resp.List[i].IsIMEI = 2 // 非串码 } else { resp.List[i].IsIMEI = 1 // 串码 } } //跟之前保持一致 resp.Total = int(count) resp.PageIndex = page + 1 resp.PageSize = m.PageSize return resp, nil } 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.Count(&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 = PercentFloatStringToUin32(e.RetailPriceString) e.MinRetailPrice = PercentFloatStringToUin32(e.MinRetailPriceString) e.StaffCostPrice = PercentFloatStringToUin32(e.StaffCostPriceString) e.WholesalePrice = PercentFloatStringToUin32(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 { 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.Error("store name err") return errors.New("store name err") } v2, ok2 := erpCommodityMap[list[i].Name] if !ok2 || v2 == nil { logger.Error("erp commodity name err") return errors.New("erp commodity name err") } v3, ok3 := erpSupplierNameMap[list[i].SupplierName] if !ok3 { logger.Error("erp supplier name err") return errors.New("erp supplier name err") } // 注意:表格导入是员工成本价,数据库存储是员工成本价加价=员工成本价-指导采购价 nStaffCostPrice, err := strconv.ParseUint(list[i].StaffCostPrice, 10, 32) if err != nil { return fmt.Errorf("员工成本价转换有误:[%v]", err) } nWholesalePrice, err := strconv.ParseUint(list[i].WholesalePrice, 10, 32) // 指导采购价 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: list[i].SerialNum, ErpCategoryId: v2.ErpCategoryId, ErpCategoryName: v2.ErpCategoryName, ErpSupplierId: v3, ErpSupplierName: list[i].SupplierName, StaffCostPrice: uint32(nStaffCostPrice - nWholesalePrice), WholesalePrice: uint32(nWholesalePrice), State: 1, StorageType: 1, FirstStockTime: nowTime, StockTime: nowTime, Count: 1, IMEIType: v2.IMEIType, IMEI: v2.IMEI, Remark: "", } if list[i].SysGenerate != "" { //导入串码不为空,则默认为3手动添加 stockCommodity.IMEIType = 3 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 (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 := m.ErpStockCountUpdate(begin) if err != nil { begin.Rollback() return err } err = errGroup.Wait() 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.Create(&stockList).Error if err != nil { begin.Rollback() return err } return nil } func (m *StockImporter) ErpStockCountUpdate(gdb *gorm.DB) error { for k1, v1 := range m.CensusMap { 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 } 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() streamWriter, err := file.NewStreamWriter("Sheet1") if err != nil { fmt.Println(err) } url := "http://39.108.188.218:8000/img/export/" fileName := time.Now().Format(TimeFormat) + "商品" + ".xlsx" //title := []interface{}{"供应商编号", "供应商名称", "联系人", "手机号", "地址", "开户银行", "银行账号", "付款周期/天"} title := []interface{}{"商品编号", "商品名称", "商品分类", "是否串码", "主供应商", "零售价", "最低零售价", "员工成本价", "采购价", "提成等级1", "提成等级2", "产地", "备注", "会员折扣(零售价的百分比)"} cell, _ := excelize.CoordinatesToCellName(1, 1) if err = streamWriter.SetRow(cell, title); err != nil { fmt.Println(err) } var row []interface{} for rowId := 0; rowId < len(list); rowId++ { isIMEI := "否" if list[rowId].IMEIType == 2 { isIMEI = "是" } row = []interface{}{list[rowId].SerialNumber, list[rowId].Name, list[rowId].ErpCategoryName, isIMEI, list[rowId].ErpSupplierName, list[rowId].RetailPrice, list[rowId].MinRetailPrice, list[rowId].StaffCostPrice, list[rowId].WholesalePrice, list[rowId].Brokerage1, list[rowId].Brokerage2, list[rowId].Origin, list[rowId].Remark, list[rowId].MemberDiscount} cell, _ := excelize.CoordinatesToCellName(1, rowId+2) if err := streamWriter.SetRow(cell, row); err != nil { fmt.Println(err) } } 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 } // InventoryDetailListExport 导出库存商品列表 func InventoryDetailListExport(list []ErpStockCommodity) (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" fmt.Println("url fileName:", url+fileName) title := []interface{}{"商品编号", "商品名称", "商品分类", "是否串码", "商品串码", "所属门店", "供应商", "首次入库时间", "首次入库方式", "首次入库订单编号", "最近入库时间", "入库采购价", "入库员工成本价", "最近库龄", "总库龄", "当前状态", "备注"} cell, _ := excelize.CoordinatesToCellName(1, 1) if err = streamWriter.SetRow(cell, title); err != nil { fmt.Println(err) } var row []interface{} 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 = "调拨中" } row = []interface{}{ list[rowId].CommoditySerialNumber, list[rowId].ErpCommodityName, list[rowId].ErpCategoryName, isIMEIType, list[rowId].IMEI, list[rowId].StoreName, list[rowId].ErpSupplierName, list[rowId].FirstStockTime, storageType, list[rowId].StockSn, list[rowId].StockTime, list[rowId].WholesalePrice, list[rowId].StaffCostPrice, list[rowId].Age, list[rowId].AllAge, state, list[rowId].Remark} cell, _ := excelize.CoordinatesToCellName(1, rowId+2) if err := streamWriter.SetRow(cell, row); err != nil { fmt.Println(err) } } if err := streamWriter.Flush(); err != nil { fmt.Println(err) } 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 } // ErpCategoryListExport 导出商品分类 func ErpCategoryListExport(list []ErpCategory) (string, error) { file := excelize.NewFile() streamWriter, err := file.NewStreamWriter("Sheet1") if err != nil { fmt.Println(err) } url := "http://39.108.188.218:8000/img/export/" 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"` // 商品分类 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:"count"` // 数据总条数 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") 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") } var count int64 if err := qs.Count(&count).Error; err != nil { //logger.Error("count err:", err) return resp, err } var commodities []ErpStock err := qs.Order("id DESC").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 } 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 } } // ErpStockCommodityListReq 库存详情接口请求参数 type ErpStockCommodityListReq struct { 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 IMEI string `json:"imei"` // 串码 StoreId uint32 `json:"store_id"` // 门店编号 SupplierId uint32 `json:"supplier_id"` // 供应商id State uint32 `json:"state"` // 库存状态:1-在库 2-已售 3-采购退货 4-调拨中 Sn string `json:"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-导出 } // ErpStockCommodityListResp 库存详情接口响应参数 type ErpStockCommodityListResp struct { List []ErpStockCommodity `json:"list"` Total int `json:"count"` // 数据总条数 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 每页展示条数 ExportUrl string `json:"export_url"` } // GetDetailList 查看库存详情 func (m *ErpStockCommodityListReq) GetDetailList() (*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 } qs := orm.Eloquent.Table("erp_stock_commodity").Order("id DESC") // 构建查询条件 m.buildQueryConditions(qs) var count int64 if err := qs.Count(&count).Error; err != nil { //logger.Error("count err:", err) return resp, err } //获取库存商品列表 var commodities []ErpStockCommodity if m.IsExport == 1 { err := qs.Find(&commodities).Error if err != nil && !errors.Is(err, RecordNotFound) { //logger.Error("dailys err:", err) return resp, err } listExport, err := InventoryDetailListExport(commodities) if err != nil { //logger.Error("list export err:", err) } resp.ExportUrl = listExport } else { 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 } ErpStockCommodityListSetAge(commodities) resp.List = commodities } ////正常返回 //resp.Total = int(count)/m.PageSize + 1 //resp.PageNum = page + 1 //resp.PageSize = len(resp.List) //跟之前保持一致 resp.Total = int(count) resp.PageIndex = page + 1 resp.PageSize = m.PageSize return resp, nil } // buildQueryConditions 根据请求参数构建查询条件 func (m *ErpStockCommodityListReq) buildQueryConditions(qs *gorm.DB) { 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.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 { //商品分类id qs = qs.Where("erp_category_id=?", m.ErpCategoryId) } 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("supplier_id=?", m.SupplierId) } if m.State != 0 { //库存状态 qs = qs.Where("state=?", m.State) } if m.Sn != "" { //首次入库订单编号 qs = qs.Where("stock_sn=?", m.Sn) } if m.StorageType != 0 { //首次入库方式 qs = qs.Where("storage_type=?", m.StorageType) } if m.StockTimeStart != "" { //最近入库开始时间 startTime, err := time.Parse(DateTimeFormat, m.StockTimeStart) if err == nil { qs = qs.Where("first_stock_time>?", startTime) } else { //logger.Error("stock time start parse err:", err) } } if m.StockTimeEnd != "" { //最近入库结束时间 endTime, err := time.Parse(DateTimeFormat, m.StockTimeEnd) if err == nil { qs = qs.Where("first_stock_time