mh_goadmin_server/app/admin/models/commodity.go

2390 lines
81 KiB
Go
Raw Normal View History

package models
import (
"errors"
"fmt"
"github.com/xuri/excelize/v2"
orm "go-admin/common/global"
"go-admin/logger"
"go-admin/tools"
2023-11-23 12:38:11 +00:00
"go-admin/tools/config"
"golang.org/x/sync/errgroup"
"gorm.io/gorm"
"math/rand"
"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 // 调拨中(调入门店)
OnSale = 5 // 销售锁定中
ProductReturn = 6 // 产品出库
CheckOut = 7 // 盘点出库
)
2023-11-23 12:38:11 +00:00
// 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:"-"`
}
// 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"` // 首次入库时间
2024-03-27 10:01:38 +00:00
StockSn string `json:"stock_sn"` // 库存订单编号(跟采购入库的入库编号关联)
2024-03-18 11:53:42 +00:00
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"` // 备注
2024-03-27 10:01:38 +00:00
//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)"` // 备注
2024-03-27 10:01:38 +00:00
StockCount uint32 `json:"stock_count" gorm:"-"` // 库存数量
ErpCategory *ErpCategory `json:"erp_category" gorm:"-"`
}
2023-11-23 12:38:11 +00:00
// 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
}
2023-11-23 12:38:11 +00:00
// 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"` // 页面条数
}
type QueryCodeResp struct {
List []string `json:"list"` // 串码/条码
Total int `json:"count"` // 数据总条数
PageIndex int `json:"pageIndex"` // 页码
PageSize int `json:"pageSize"` // 每页展示条数
}
2023-11-23 12:38:11 +00:00
// 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
}
2023-11-23 12:38:11 +00:00
// 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
}
2023-11-23 12:38:11 +00:00
// 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
}
2023-11-23 12:38:11 +00:00
// 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"` // 商品编号
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:"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 {
2024-03-27 10:01:38 +00:00
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)
}
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 // 串码
}
2024-03-27 10:01:38 +00:00
// 查询库存数量
var stockCount int64
err = orm.Eloquent.Table("erp_stock_commodity").Where("erp_commodity_id = ? and state = ?",
resp.List[i].ID, InStock).Count(&stockCount).Error
if err != nil {
return nil, err
}
resp.List[i].StockCount = uint32(stockCount)
}
//跟之前保持一致
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"`
2023-11-23 12:38:11 +00:00
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 {
2023-11-23 12:38:11 +00:00
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)
}
2023-11-23 12:38:11 +00:00
_, 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
}
2023-11-23 12:38:11 +00:00
erpCommodityNames = append(erpCommodityNames, list[i].Name)
}
2023-11-23 12:38:11 +00:00
_, ok3 := erpSupplierNameMap[list[i].SupplierName]
if !ok3 {
2023-11-23 12:38:11 +00:00
erpSupplierNames = append(erpSupplierNames, list[i].SupplierName)
}
storeNameMap[list[i].StoreName] = uint32(0)
2023-11-23 12:38:11 +00:00
erpCommodityMap[list[i].Name] = nil
erpSupplierNameMap[list[i].SupplierName] = uint32(0)
}
var stores []Store
2023-11-23 12:38:11 +00:00
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
2023-11-23 12:38:11 +00:00
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")
}
2023-11-23 12:38:11 +00:00
v2, ok2 := erpCommodityMap[list[i].Name]
if !ok2 || v2 == nil {
logger.Error("erp commodity name err")
return errors.New("erp commodity name err")
}
2023-11-23 12:38:11 +00:00
v3, ok3 := erpSupplierNameMap[list[i].SupplierName]
if !ok3 {
logger.Error("erp supplier name err")
return errors.New("erp supplier name err")
}
2023-11-23 12:38:11 +00:00
// 注意:表格导入是员工成本价,数据库存储是员工成本价加价=员工成本价-指导采购价
nStaffCostPrice, err := strconv.ParseFloat(list[i].StaffCostPrice, 64)
2023-11-23 12:38:11 +00:00
if err != nil {
return fmt.Errorf("员工成本价转换有误:[%v]", err)
}
nWholesalePrice, err := strconv.ParseFloat(list[i].WholesalePrice, 64) // 指导采购价
2023-11-23 12:38:11 +00:00
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}
}
2023-11-23 12:38:11 +00:00
}
}
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
2024-03-27 10:01:38 +00:00
// 检查生成的串码是否已存在 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
}
}
}
// 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
}
2023-11-23 12:38:11 +00:00
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
}
2023-11-23 12:38:11 +00:00
stockList := erpStocks[start:end]
errGroup.Go(func() error {
return createStockList(begin, stockList) //插入库存商品详情
2023-11-23 12:38:11 +00:00
})
}
err := m.ErpStockCountUpdate(begin) //更新or插入库存表
2023-11-23 12:38:11 +00:00
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.Debug().Create(&stockList).Error
2023-11-23 12:38:11 +00:00
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
}
// 查询库存详情时同步库存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
}
2023-11-23 12:38:11 +00:00
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
}
2023-11-23 12:38:11 +00:00
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
}
2023-11-23 12:38:11 +00:00
// ErpCommodityListExport 导出商品列表
func ErpCommodityListExport(list []ErpCommodity) (string, error) {
file := excelize.NewFile()
streamWriter, err := file.NewStreamWriter("Sheet1")
if err != nil {
fmt.Println(err)
}
url := ExportUrl
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
}
2023-11-23 12:38:11 +00:00
// InventoryDetailListExport 导出库存商品列表
func InventoryDetailListExport(list []ErpStockCommodity) (string, error) {
file := excelize.NewFile()
streamWriter, err := file.NewStreamWriter("Sheet1")
if err != nil {
fmt.Println(err)
}
url := ExportUrl
2023-11-23 12:38:11 +00:00
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,
2024-03-27 10:01:38 +00:00
list[rowId].OriginalSn,
2023-11-23 12:38:11 +00:00
list[rowId].StockTime,
list[rowId].WholesalePrice,
2024-03-27 10:01:38 +00:00
list[rowId].WholesalePrice + list[rowId].StaffCostPrice,
2023-11-23 12:38:11 +00:00
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 := ExportUrl
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-导出
}
2023-11-23 12:38:11 +00:00
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() (*ErpStockListResp, error) {
switch m.StockType {
case 2: // 有库存
return m.stockNoEmptyList()
case 3: // 无库存,连表查询商品明细和库存表
return m.stockIsEmptyList()
default: // 0和1以及其他值表示无库存情况筛选
return m.allCommodityList()
}
}
// stockIsEmptyList 库存列表-无库存查询
// 无库存,要连表查询(商品明细、库存表)
func (m *ErpStockListReq) stockIsEmptyList() (*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.Debug().Table("erp_commodity")
2024-04-01 03:00:22 +00:00
es := orm.Eloquent.Debug().Table("erp_commodity")
if m.StoreId != 0 { // 传门店id
2024-04-01 03:00:22 +00:00
qs = qs.Select("erp_commodity.*, COALESCE(erp_stock.count, 0) AS total_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").
//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").
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").
//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之和
2024-04-01 03:00:22 +00:00
qs = qs.Select("erp_commodity.*, COALESCE(erp_stock.count, 0) AS total_count").
Joins("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").
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").
Joins("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").
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)
2024-04-01 03:00:22 +00:00
es = es.Where("erp_commodity.serial_number=?", m.SerialNumber)
}
if m.CommodityName != "" {
qs = qs.Where("erp_commodity.name Like '%" + m.CommodityName + "%'")
2024-04-01 03:00:22 +00:00
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)
2024-04-01 03:00:22 +00:00
es = es.Where("erp_commodity.erp_category_id=?", m.ErpCategoryId)
}
var count int64
2024-04-01 03:00:22 +00:00
err := es.Count(&count).Error
if err != nil {
logger.Error("查询无库存列表数量失败", logger.Field("err", err))
return nil, err
}
2024-04-01 03:00:22 +00:00
var commodities []struct {
ErpCommodity
TotalCount 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 {
2024-04-01 03:00:22 +00:00
if commodity.TotalCount == 0 {
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)
}
}
2024-04-01 03:00:22 +00:00
// Paginate results
startIndex := page * m.PageSize
endIndex := startIndex + m.PageSize
if endIndex > len(stockList) {
endIndex = len(stockList)
}
2024-04-01 03:00:22 +00:00
// Slice the users based on pagination
pagedList := stockList[startIndex:endIndex]
//跟之前保持一致
2024-04-01 03:00:22 +00:00
resp.Total = len(stockList)
resp.PageIndex = page + 1
resp.PageSize = m.PageSize
2024-04-01 03:00:22 +00:00
resp.List = pagedList
return resp, nil
}
func (m *ErpStockListReq) stockNoEmptyList() (*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.Debug().Table("erp_commodity")
2024-04-01 03:00:22 +00:00
es := orm.Eloquent.Debug().Table("erp_commodity")
if m.StoreId == 0 { // 没指定门店,连表查询并计算总数量
qs = qs.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_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)")
2024-04-01 03:00:22 +00:00
es = es.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_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, 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")
2024-04-01 03:00:22 +00:00
es = es.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_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)
2024-04-01 03:00:22 +00:00
es = es.Where("erp_commodity.serial_number=?", m.SerialNumber)
}
if m.CommodityName != "" {
qs = qs.Where("erp_commodity.name Like '%" + m.CommodityName + "%'")
2024-04-01 03:00:22 +00:00
es = es.Where("erp_commodity.name Like '%" + m.CommodityName + "%'")
}
if m.ErpCategoryId != 0 {
qs = qs.Where("erp_commodity.erp_category_id=?", m.ErpCategoryId)
2024-04-01 03:00:22 +00:00
es = es.Where("erp_commodity.erp_category_id=?", m.ErpCategoryId)
}
var commodities []struct {
ErpCommodity
TotalCount int
ErpStockId int
}
var count int64
2024-04-01 03:00:22 +00:00
err := es.Count(&count).Error
if err != nil {
logger.Error("commodityList count err", logger.Field("err", err))
return resp, err
}
2024-04-01 03:00:22 +00:00
//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
2024-03-27 10:01:38 +00:00
stock.IMEIType = commodity.IMEIType
stock.RetailPrice = commodity.RetailPrice
stock.MinRetailPrice = commodity.MinRetailPrice
stock.Count = uint32(commodity.TotalCount)
stock.DispatchCount = 0
stockList = append(stockList, stock)
}
2024-04-01 03:00:22 +00:00
}
2024-04-01 03:00:22 +00:00
// Paginate results
startIndex := page * m.PageSize
endIndex := startIndex + m.PageSize
if endIndex > len(stockList) {
endIndex = len(stockList)
}
2024-04-01 03:00:22 +00:00
// Slice the users based on pagination
pagedList := stockList[startIndex:endIndex]
//跟之前保持一致
resp.Total = len(stockList)
resp.PageIndex = page + 1
resp.PageSize = m.PageSize
2024-04-01 03:00:22 +00:00
resp.List = pagedList
return resp, nil
}
// 筛选条件无:库存情况筛选
// 1先查询商品资料并排序支持筛选条件商品编号、商品分类、商品名称
// 2然后查询每个商品资料的库存情况没传门店id则查所有库存否则查当前门店的库存情况
func (m *ErpStockListReq) allCommodityList() (*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.Debug().Table("erp_commodity")
2024-04-01 03:00:22 +00:00
es := orm.Eloquent.Debug().Table("erp_commodity")
if m.SerialNumber != "" {
qs = qs.Where("erp_commodity.serial_number=?", m.SerialNumber)
2024-04-01 03:00:22 +00:00
es = es.Where("erp_commodity.serial_number=?", m.SerialNumber)
}
if m.CommodityName != "" {
qs = qs.Where("erp_commodity.name Like '%" + m.CommodityName + "%'")
2024-04-01 03:00:22 +00:00
es = es.Where("erp_commodity.name Like '%" + m.CommodityName + "%'")
}
if m.ErpCategoryId != 0 {
qs = qs.Where("erp_commodity.erp_category_id=?", m.ErpCategoryId)
2024-04-01 03:00:22 +00:00
es = es.Where("erp_commodity.erp_category_id=?", m.ErpCategoryId)
}
if m.StoreId == 0 { // 没指定门店,连表查询并计算总数量
qs = qs.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_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)")
2024-04-01 03:00:22 +00:00
es = es.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_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, 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")
2024-04-01 03:00:22 +00:00
es = es.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_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
}
var count int64
2024-04-01 03:00:22 +00:00
err := es.Count(&count).Error
if err != nil {
logger.Error("commodityList count err", logger.Field("err", err))
return resp, err
}
2024-04-01 03:00:22 +00:00
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 {
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
2024-03-27 10:01:38 +00:00
stock.IMEIType = commodity.IMEIType
stock.RetailPrice = commodity.RetailPrice
stock.MinRetailPrice = commodity.MinRetailPrice
stock.Count = uint32(commodity.TotalCount)
stock.DispatchCount = 0
stockList = append(stockList, stock)
}
//跟之前保持一致
resp.Total = int(count)
resp.PageIndex = page + 1
resp.PageSize = m.PageSize
resp.List = stockList
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 {
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-出库前端只看14
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"` // 是否导出excel1-导出
}
// ErpStockCommodityListResp 库存详情接口响应参数
type ErpStockCommodityListResp struct {
List []ErpStockCommodity `json:"list"`
Total int `json:"total"` // 数据总条数
PageIndex int `json:"pageIndex"` // 页码
PageSize int `json:"pageSize"` // 每页展示条数
TotalWholesalePrice int `json:"total_wholesale_price"` // 入库采购价之和
TotalStaffPrice int `json:"total_staff_price"` // 入库员工成本价之和
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
}
if m.ErpStockId != 0 {
updateCommodityStock(m.ErpStockId) // 同步详情表的库存id
}
// 出库数据不查询
qs := orm.Eloquent.Table("erp_stock_commodity")
// 构建查询条件
m.buildQueryConditions(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("id DESC").Error
if err != nil && !errors.Is(err, RecordNotFound) {
//logger.Error("dailys err:", err)
return resp, err
}
2024-03-27 10:01:38 +00:00
ErpStockCommodityListSetAge(commodities)
2023-11-23 12:38:11 +00:00
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).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)
resp.List = commodities
}
//跟之前保持一致
resp.Total = int(count)
resp.PageIndex = page + 1
resp.PageSize = m.PageSize
resp.TotalWholesalePrice = int(nTotalCount.TotalWholesalePrice)
resp.TotalStaffPrice = int(nTotalCount.TotalStaffCostPrice + nTotalCount.TotalWholesalePrice)
return resp, nil
}
func CheckScanCodeResp(scanCode string, req *ErpStockCommodityListResp) error {
if req.Total == 1 { // 串码商品只有1个条码商品刚好也只有1个
if req.List[0].IMEI == scanCode { // 串码商品只有1个
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)
}
}
}
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) {
if m.ScanCode != "" {
qs = qs.Where("erp_barcode = ? or imei = ?", m.ScanCode, m.ScanCode)
qs = qs.Where("state = ?", 1)
} else {
qs = qs.Where("state != 5")
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 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("erp_supplier_id=?", m.SupplierId)
}
defaultStates := []int{1, 4}
if m.State != 0 && contains(defaultStates, int(m.State)) {
qs = qs.Where("state=?", m.State)
} else {
qs = qs.Where("state IN (?)", defaultStates)
}
if m.Sn != "" { //首次入库订单编号
2024-03-27 10:01:38 +00:00
qs = qs.Where("original_sn=?", m.Sn)
}
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(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
}
// 查询库存数据
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
}
// 更新商品库存详情表状态为5出库
if err := begin.Model(&ErpStockCommodity{}).Where("id=?", id).Updates(map[string]interface{}{
"state": 5}).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)
}
2023-11-23 12:38:11 +00:00
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
2023-11-23 12:38:11 +00:00
}
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").
Where("imei LIKE ? OR erp_barcode LIKE ?", "%"+req.ScanCode+"%", "%"+req.ScanCode+"%")
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) { // 串码不为空,但条码包含扫码数据
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
}