mh_goadmin_server/app/admin/models/commodity.go

2549 lines
88 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package models
import (
"errors"
"fmt"
"github.com/gin-gonic/gin"
"github.com/xuri/excelize/v2"
orm "go-admin/common/global"
"go-admin/logger"
"go-admin/tools"
"go-admin/tools/config"
"golang.org/x/sync/errgroup"
"gorm.io/gorm"
"math/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 // 销售锁定中
SystemOut = 6 // 系统出库
CheckOut = 7 // 盘点出库
)
// 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"` // 首次入库时间
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"` // 备注
//ErpOrderCommodityId uint32 `json:"erp_order_commodity_id"` // 零售订单商品表的主键ID后端使用前端忽略
//Commodity ErpCommodity `json:"commodity" gorm:"-"`
}
// ErpCommodity 商品表
type ErpCommodity struct {
Model
SerialNumber string `json:"serial_number"` // 商品编号
Number uint32 `json:"number"` // 商品数量
Name string `json:"name"` // 商品名称
ErpCategoryId uint32 `json:"erp_category_id" gorm:"index"` // 商品分类id
ErpCategoryName string `json:"erp_category_name"` // 商品分类名称
ErpBarcode string `json:"erp_barcode"` // 商品条码
IsIMEI uint32 `json:"is_imei" gorm:"-"` // 是否串码1-串码类 2-非串码
IMEIType uint32 `json:"imei_type"` // 1-无串码 2-串码(系统生成) 3-串码(手动添加)
IMEI string `json:"imei"` // 串码
ErpSupplierId uint32 `json:"erp_supplier_id" gorm:"index"` // 主供应商id
ErpSupplierName string `json:"erp_supplier_name"` // 主供应商名称
RetailPrice float64 `json:"retail_price"` // 指导零售价
MinRetailPrice float64 `json:"min_retail_price"` // 最低零售价
StaffCostPrice float64 `json:"staff_cost_price"` // 员工成本价加价加价50不是加价后的价格
WholesalePrice float64 `json:"wholesale_price"` // 指导采购价
Brokerage1 float64 `json:"brokerage_1"` // 销售毛利提成
Brokerage2 float64 `json:"brokerage_2"` // 员工毛利提成
MemberDiscount float64 `json:"member_discount"` // 会员优惠
Origin string `json:"origin"` // 产地
Remark string `json:"remark" gorm:"type:varchar(512)"` // 备注
StockCount uint32 `json:"stock_count" gorm:"-"` // 库存数量
ErpCategory *ErpCategory `json:"erp_category" gorm:"-"`
}
// ErpCategory 商品分类
type ErpCategory struct {
Model
Name string `json:"name"` // 名称
Priority string `json:"priority"` // 分类
Number string `json:"number"`
FullNum uint32 `json:"full_num"`
State uint32 `json:"state"` // 1-未使用 2-使用 3-隐藏
Level uint32 `json:"level"` // 分类层级
Pid uint32 `json:"pid" gorm:"index"`
Sort uint32 `json:"sort"`
SubCats []ErpCategory `json:"sub_cats" gorm:"-"` // 子列表
// erp_category
}
// ErpSupplier 供应商
type ErpSupplier struct {
Model
Number string `json:"number" gorm:"index"`
Name string `json:"name"`
Contact string `json:"contact"`
Tel string `json:"tel"`
Address string `json:"address"`
OpeningBank string `json:"opening_bank"`
BankAccount string `json:"bank_account"`
PaymentCycle uint32 `json:"payment_cycle"`
TaxNumber string `json:"tax_number"`
StoreIds string `json:"store_ids"`
Landline string `json:"landline"`
Email string `json:"email"`
CompanyWebsite string `json:"company_website"`
// erp_supplier
}
type ErpInventoryStock struct {
Model
StoreId uint32 `json:"store_id" gorm:"index"`
StoreName string `json:"store_name"`
ErpCommodityId uint32 `json:"erp_commodity_id" gorm:"index"`
ErpCommodityName string `json:"erp_commodity_name"`
ErpCategoryId uint32 `json:"erp_category_id" gorm:"index"`
ErpCategoryName string `json:"erp_category_name"`
CommoditySerialNumber string `json:"commodity_serial_number" gorm:"index"`
IMEIType uint32 `json:"imei_type"` // 1-无串码 2-串码(系统生成) 3-串码(手动添加)
RetailPrice float64 `json:"retail_price"`
MinRetailPrice float64 `json:"min_retail_price"`
Count uint32 `json:"count"`
Sn string `json:"sn" gorm:"index"`
//ErpSupplierId uint32 `json:"erp_supplier_id" gorm:"index"`
//ErpSupplierName string `json:"erp_supplier_name"`
//IMEI string `json:"imei"`
//StockTime time.Time `json:"stock_time"`
//StaffCostPrice uint32 `json:"staff_cost_price"`
//WholesalePrice uint32 `json:"wholesale_price"`
// erp_inventory_stock
}
type ErpInventoryStockCommodity struct {
Model
ErpInventoryStockId uint32 `json:"erp_inventory_stock_id" gorm:"index"`
ErpCommodityId uint32 `json:"erp_commodity_id" gorm:"index"`
ErpCommodityName string `json:"erp_commodity_name"`
CommoditySerialNumber string `json:"commodity_serial_number" gorm:"index"`
IMEIType uint32 `json:"imei_type"` // 1-无串码 2-串码(系统生成) 3-串码(手动添加)
IMEI string `json:"imei"`
ErpSupplierId uint32 `json:"erp_supplier_id" gorm:"index"`
ErpSupplierName string `json:"erp_supplier_name"`
StockTime time.Time `json:"stock_time"`
RetailPrice float64 `json:"retail_price"`
MinRetailPrice float64 `json:"min_retail_price"`
StaffCostPrice float64 `json:"staff_cost_price"`
WholesalePrice float64 `json:"wholesale_price"`
Count uint32 `json:"count"`
StorageType uint32 `json:"storage_type"`
Sn string `json:"sn" gorm:"index"`
//StoreId uint32 `json:"store_id" gorm:"index"`
//StoreName string `json:"store_name"`
//ErpCategoryId uint32 `json:"erp_category_id" gorm:"index"`
//ErpCategoryName string `json:"erp_category_name"`
// erp_inventory_stock_commodity
}
// QueryCodeReq 查询商品串码或条码入参
type QueryCodeReq struct {
ScanCode string `json:"scan_code" binding:"required"` // 扫码枪扫码数据:串码/条码
PageIndex int `json:"pageIndex"` // 页码
PageSize int `json:"pageSize"` // 页面条数
}
type QueryCodeResp struct {
List []string `json:"list"` // 串码/条码
Total int `json:"count"` // 数据总条数
PageIndex int `json:"pageIndex"` // 页码
PageSize int `json:"pageSize"` // 每页展示条数
}
// IdInit 新增/编辑商品时获取分类和供应商信息
func (c *ErpCommodity) IdInit() error {
if c.ErpCategoryId != 0 {
if c.ErpCategory == nil {
category, err := GetErpCategory(c.ErpCategoryId)
if err != nil || category.ID == 0 {
//logger.Error("get erp category err:", err)
return errors.New("商品分类信息不存在")
}
c.ErpCategory = category
}
c.ErpCategoryName = c.ErpCategory.Name
}
if c.ErpSupplierId != 0 {
supplier, err := GetErpSupplier(c.ErpSupplierId)
if err != nil || supplier.ID == 0 {
//logger.Error("get erp category err:", err)
return errors.New("供应商信息不存在")
} else {
c.ErpSupplierName = supplier.Name
}
}
return nil
}
// GetErpCategory 根据id查询分类信息
func GetErpCategory(id uint32) (*ErpCategory, error) {
category := new(ErpCategory)
err := orm.Eloquent.Table("erp_category").Where("id=?", id).Find(category).Error
if err != nil {
//logger.Error("category err:", err)
return category, err
}
return category, nil
}
// GetErpSupplier 根据id查询供应商
func GetErpSupplier(id uint32) (*ErpSupplier, error) {
supplier := new(ErpSupplier)
err := orm.Eloquent.Table("erp_supplier").Where("id=?", id).Find(supplier).Error
if err != nil {
//logger.Error("category err:", err)
return supplier, err
}
return supplier, err
}
// SetErpCategory 新增/编辑商品时设置分类信息
func (c *ErpCommodity) SetErpCategory() error {
if c.ErpCategoryId != 0 {
category, err := GetErpCategory(c.ErpCategoryId)
if err != nil {
logger.Errorf("get erp category err:", err)
return err
}
c.ErpCategory = category
return nil
}
return errors.New("erp category id null")
}
type ErpCommodityListReq struct {
SerialNumber string `json:"serial_number"` // 商品编号
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 {
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 // 串码
}
// 查询库存数量
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"`
StaffCostPriceString string `json:"staff_cost_price_string"` // 员工成本价加价
WholesalePriceString string `json:"wholesale_price_string"` // 指导采购价
CountString string `json:"count_string"` // 数量
//StoreName string `json:"store_name"`
//ErpCommodityName string `json:"erp_commodity_name"`
//ErpCategoryName string `json:"erp_category_name"`
//SerialNumber string `json:"serial_number"`
//IMEIType uint32 `json:"imei_type"` // 1-无串码 2-串码
//IMEI string `json:"imei"`
//ErpSupplierName string `json:"erp_supplier_name"`
ErpStockCommodity
}
func (e *ErpStockFileExcel) Processing() {
e.Count = IntStringToUin32(e.CountString)
if e.IsIMEI == "是" {
e.IMEIType = 2
e.Count = 1
} else if e.IsIMEI == "否" {
e.IMEIType = 1
}
//parseTime, err := time.Parse(DateTimeFormat, e.StockTimeString)
//fmt.Println("StockTimeString:", e.StockTimeString)
//parseTime, err := time.Parse("01-02-06", e.StockTimeString)
format := "2006/01/02"
parseTime, err := time.Parse(format, e.StockTimeString)
if err != nil {
//logger.Error("parse err:", err)
parseTime = time.Now()
}
e.StockTime = parseTime
e.RetailPrice = PercentFloatStringToFloat64(e.RetailPriceString)
e.MinRetailPrice = PercentFloatStringToFloat64(e.MinRetailPriceString)
e.StaffCostPrice = PercentFloatStringToFloat64(e.StaffCostPriceString)
e.WholesalePrice = PercentFloatStringToFloat64(e.WholesalePriceString)
}
// ImportStockData 库存导入
// 更新库存表和商品表
// 库存表:插入对应的库存数据
// 库存商品表:插入对应库存商品的数据;供应商只需判断是否在供应商列表即可 todo 库存商品表未插入库存id
func (m *StockImporter) ImportStockData(colsMap []map[string]interface{}) error {
list, err := transStockData(colsMap)
if err != nil {
return err
}
var erpStockCommodity []ErpStockCommodity
storeNameMap := make(map[string]uint32, 0)
erpCommodityMap := make(map[string]*ErpCommodity, 0)
erpSupplierNameMap := make(map[string]uint32, 0)
storeNames := make([]string, 0, len(list))
erpCommodityNames := make([]string, 0, len(list))
erpSupplierNames := make([]string, 0, len(list))
for i, _ := range list {
_, ok1 := storeNameMap[list[i].StoreName]
if !ok1 {
storeNames = append(storeNames, list[i].StoreName)
}
_, ok2 := erpCommodityMap[list[i].Name]
if !ok2 {
if list[i].Name == "" {
//如果商品名称为空,则需要补全
commodityName, err := getCommodityNameBySerialNum(list[i].SerialNum)
if err != nil {
logger.Errorf("getCommodityNameBySerialNum err:", logger.Field("err", err))
return err
}
list[i].Name = commodityName
}
erpCommodityNames = append(erpCommodityNames, list[i].Name)
}
_, ok3 := erpSupplierNameMap[list[i].SupplierName]
if !ok3 {
erpSupplierNames = append(erpSupplierNames, list[i].SupplierName)
}
storeNameMap[list[i].StoreName] = uint32(0)
erpCommodityMap[list[i].Name] = nil
erpSupplierNameMap[list[i].SupplierName] = uint32(0)
}
var stores []Store
err = orm.Eloquent.Table("store").Where("name IN (?)", storeNames).Find(&stores).Error
if err != nil {
//logger.Error("stores err:", err)
return err
}
var erpCommodities []ErpCommodity
err = orm.Eloquent.Table("erp_commodity").Where("name IN (?)", erpCommodityNames).Find(&erpCommodities).Error
if err != nil {
//logger.Error("stores err:", err)
return err
}
var erpSuppliers []ErpSupplier
err = orm.Eloquent.Table("erp_supplier").Debug().Where("name IN (?)", erpSupplierNames).Find(&erpSuppliers).Error
if err != nil && !errors.Is(err, RecordNotFound) {
//logger.Error("stores err:", err)
return err
}
for i, _ := range stores {
storeNameMap[stores[i].Name] = stores[i].ID
m.StoreMap[stores[i].ID] = stores[i].Name
}
for i, _ := range erpCommodities {
erpCommodityMap[erpCommodities[i].Name] = &erpCommodities[i]
m.CommodityMap[erpCommodities[i].ID] = &erpCommodities[i]
}
for i, _ := range erpSuppliers {
erpSupplierNameMap[erpSuppliers[i].Name] = erpSuppliers[i].ID
}
nowTime := time.Now()
for i, _ := range list {
v1, ok1 := storeNameMap[list[i].StoreName]
if !ok1 {
logger.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.ParseFloat(list[i].StaffCostPrice, 64)
if err != nil {
return fmt.Errorf("员工成本价转换有误:[%v]", err)
}
nWholesalePrice, err := strconv.ParseFloat(list[i].WholesalePrice, 64) // 指导采购价
if err != nil {
return fmt.Errorf("指导采购价转换有误:[%v]", err)
}
if nStaffCostPrice < nWholesalePrice {
return fmt.Errorf("导入价格有误,员工成本价低于采购价")
}
nCount, err := strconv.ParseUint(list[i].Count, 10, 32)
if err != nil {
return fmt.Errorf("数量转换有误:[%v]", err)
}
for j := 0; j < int(nCount); j++ { // 商品库存表都是单笔数据,如果非串码商品有多个,需要插入多条数据
stockCommodity := ErpStockCommodity{
StoreId: v1,
StoreName: list[i].StoreName,
ErpCommodityId: v2.ID,
ErpCommodityName: v2.Name,
CommoditySerialNumber: v2.SerialNumber,
ErpCategoryId: v2.ErpCategoryId,
ErpCategoryName: v2.ErpCategoryName,
ErpSupplierId: v3,
ErpSupplierName: list[i].SupplierName,
StaffCostPrice: tools.RoundToTwoDecimalPlaces(nStaffCostPrice - nWholesalePrice),
WholesalePrice: tools.RoundToTwoDecimalPlaces(nWholesalePrice),
State: InStock,
StorageType: SystemInventory,
FirstStockTime: nowTime,
StockTime: nowTime,
Count: 1,
ErpBarcode: v2.ErpBarcode, // 240106新增商品条码
IMEIType: v2.IMEIType,
IMEI: "",
Remark: "",
MemberDiscount: v2.MemberDiscount,
MinRetailPrice: v2.MinRetailPrice,
RetailPrice: v2.RetailPrice,
} // todo 首次入库订单编号、订单编号还未赋值
if list[i].StockTime != "" { //导入时间不为空
local, _ := time.LoadLocation("Local")
parsedTime, _ := time.ParseInLocation("2006/1/2", list[i].StockTime, local)
//parsedTime, _ := time.Parse("2006/1/2", list[i].StockTime)
stockCommodity.FirstStockTime = parsedTime
stockCommodity.StockTime = parsedTime
}
//if list[i].SysGenerate != "" && v2.IMEIType == 3 { //导入串码不为空则默认为3手动添加
// stockCommodity.IMEIType = 3
// stockCommodity.IMEI = list[i].SysGenerate
//} else if v2.IMEIType == 2 { // 如果该商品串码类型是2-串码(系统生成),则系统自动生成
// stockCommodity.IMEIType = 2
// serialCode, err := generateSerialCode(v2.ErpCategoryId)
// if err != nil {
// return err
// }
// stockCommodity.IMEI = serialCode
//}
if list[i].SysGenerate != "" && v2.IMEIType != 1 { // 商品为串码商品,且导入串码不为空
stockCommodity.IMEIType = v2.IMEIType
stockCommodity.IMEI = list[i].SysGenerate
}
erpStockCommodity = append(erpStockCommodity, stockCommodity)
_, ok4 := m.CensusMap[stockCommodity.StoreId]
if ok4 {
m.CensusMap[stockCommodity.StoreId][stockCommodity.ErpCommodityId] += stockCommodity.Count
} else {
m.CensusMap[stockCommodity.StoreId] = map[uint32]uint32{stockCommodity.ErpCommodityId: stockCommodity.Count}
}
}
}
if err = m.processErpStocks(erpStockCommodity); err != nil {
return err
}
return nil
}
// 获取商品名称
func getCommodityNameBySerialNum(serialNumber string) (string, error) {
var commodity ErpCommodity
err := orm.Eloquent.Table("erp_commodity").Where("serial_number=?", serialNumber).Find(&commodity).Error
if err != nil {
return "", err
}
return commodity.Name, nil
}
// GenerateSerialCode 生成商品串码
func GenerateSerialCode(categoryID uint32) (string, error) {
max := 1
for {
if max > 5 {
logger.Error("generate SerialCode err")
return "", errors.New("generate SerialCode err")
}
// 生成年月日6位数
dateStr := time.Now().Format("060102")
// 生成四位随机数
rand.Seed(time.Now().UnixNano())
randomNumber := rand.Intn(10000)
randomStr := fmt.Sprintf("%04d", randomNumber)
// 获取商品分类编号前3位
categoryStr, err := getCategoryCode(categoryID)
if err != nil {
logger.Errorf("getCategoryCode err:", logger.Field("err", err))
return "", err
}
// 拼接串码
serialCode := categoryStr + dateStr + randomStr
// 检查生成的串码是否已存在 todo 是否需要判断状态,采购退货的可以重复?
exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_stock_commodity WHERE FIND_IN_SET(%s, imei) > 0 ", serialCode))
if err != nil {
logger.Error("exist sn err")
}
if !exist {
return serialCode, nil
}
}
}
// getCategoryCode 根据商品分类ID查询分类编号前3位
func getCategoryCode(categoryID uint32) (string, error) {
// 这里模拟查询数据库,实际中你需要连接数据库进行查询
// 假设这里的数据是从数据库中查询得到的
categoryInfo, err := GetCategoryById(categoryID)
if err != nil {
logger.Errorf("GetCategoryById err:", logger.Field("err", err))
return "", err
}
// 取前3位字符串
return categoryInfo.Number[:3], nil
}
func (m *StockImporter) processErpStocks(erpStocks []ErpStockCommodity) error {
begin := orm.Eloquent.Begin()
total := len(erpStocks)
size := 200
page := (total + size - 1) / size
errGroup := errgroup.Group{}
for i := 0; i < page; i++ {
start := i * size
end := (i + 1) * size
if end > total {
end = total
}
stockList := erpStocks[start:end]
errGroup.Go(func() error {
return createStockList(begin, stockList) //插入库存商品详情
})
}
err := m.ErpStockCountUpdate(begin) //更新or插入库存表
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
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
}
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 := 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
}
// InventoryDetailListExport 导出库存商品列表
func InventoryDetailListExport(list []ErpStockCommodity) (string, error) {
file := excelize.NewFile()
streamWriter, err := file.NewStreamWriter("Sheet1")
if err != nil {
fmt.Println(err)
}
url := ExportUrl
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].OriginalSn,
list[rowId].StockTime,
list[rowId].WholesalePrice,
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 := 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-导出
}
type ErpStockListResp struct {
List []ErpStock `json:"list"`
Total int `json:"total"` // 数据总条数
PageIndex int `json:"pageIndex"` // 页码
PageSize int `json:"pageSize"` // 每页展示条数
ExportUrl string `json:"export_url"`
}
func (m *ErpStockListReq) List() (*ErpStockListResp, error) {
resp := &ErpStockListResp{
PageIndex: m.PageIndex,
PageSize: m.PageSize,
}
page := m.PageIndex - 1
if page < 0 {
page = 0
}
if m.PageSize == 0 {
m.PageSize = 10
}
qs := orm.Eloquent.Table("erp_stock").
Joins("JOIN erp_category c ON erp_stock.erp_category_id = c.id")
if m.SerialNumber != "" {
qs = qs.Where("commodity_serial_number=?", m.SerialNumber)
}
if m.CommodityName != "" {
qs = qs.Where("erp_commodity_name LIKE ?", "%"+m.CommodityName+"%")
}
if m.ErpCategoryId != 0 {
qs = qs.Where("erp_category_id=?", m.ErpCategoryId)
}
if m.StoreId != 0 {
qs = qs.Where("store_id=?", m.StoreId)
}
switch m.StockType {
case 2:
qs = qs.Where("count > 0")
case 3:
qs = qs.Where("count = 0")
}
// SQL Order By Clause
qs = qs.Order("CASE WHEN c.pid = 0 THEN CAST(c.number AS SIGNED) ELSE " +
"CAST(SUBSTRING(c.number, 1, 3) AS SIGNED) END").
Order("CAST(c.pid AS SIGNED)").
Order("CAST(SUBSTRING(erp_stock.commodity_serial_number, -4) AS SIGNED)")
var count int64
if err := qs.Count(&count).Error; err != nil {
//logger.Error("count err:", err)
return resp, err
}
var commodities []ErpStock
err := qs.Offset(page * m.PageSize).Limit(m.PageSize).Find(&commodities).Error
if err != nil && !errors.Is(err, RecordNotFound) {
//logger.Error("erp commodity list err:", err)
return resp, err
}
resp.List = commodities
//跟之前保持一致
resp.Total = int(count)
resp.PageIndex = page + 1
resp.PageSize = m.PageSize
return resp, nil
}
// StockList
// 1、如果筛选条件有库存情况筛选则以库存表为准查库存表即可
// 1没门店则查所有库存情况并排序
// 2有门店则查门店对应的库存情况并排序
// 2、如果筛选条件没有库存情况
// 1先查询商品资料并排序支持筛选条件商品编号、商品分类、商品名称
// 2然后查询每个商品资料的库存情况没传门店id则查所有库存否则查当前门店的库存情况
func (m *ErpStockListReq) StockList(c *gin.Context) (*ErpStockListResp, error) {
switch m.StockType {
case 2: // 有库存
return m.stockNoEmptyList(c)
case 3: // 无库存,连表查询商品明细和库存表
return m.stockIsEmptyList(c)
default: // 0和1以及其他值表示无库存情况筛选
return m.allCommodityList(c)
}
}
// stockIsEmptyList 库存列表-无库存查询
// 无库存,要连表查询(商品明细、库存表)
func (m *ErpStockListReq) stockIsEmptyList(c *gin.Context) (*ErpStockListResp, error) {
resp := &ErpStockListResp{
PageIndex: m.PageIndex,
PageSize: m.PageSize,
}
page := m.PageIndex - 1
if page < 0 {
page = 0
}
if m.PageSize == 0 {
m.PageSize = 10
}
//以下代码组合了2个连表查询
//1查询无库存的商品资料
/***
SELECT erp_commodity.*,
COALESCE(erp_stock.count, 0) AS count
FROM erp_commodity
LEFT JOIN (
SELECT erp_commodity_id, SUM(count) AS count
FROM erp_stock
GROUP BY erp_commodity_id
) erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id
WHERE erp_stock.count IS NULL OR erp_stock.count = 0;
***/
//2按商品编号排序
/***
SELECT e.*
FROM erp_stock e
JOIN erp_category c ON e.erp_category_id = c.id
ORDER BY
CASE WHEN c.pid = 0 THEN CAST(c.number AS SIGNED) ELSE CAST(SUBSTRING(c.number, 1, 3) AS SIGNED) END,
CAST(c.pid AS SIGNED),
CAST(SUBSTRING(e.commodity_serial_number, -4) AS SIGNED);
***/
qs := orm.Eloquent.Debug().Table("erp_commodity")
es := orm.Eloquent.Debug().Table("erp_commodity")
// 非管理员才判断所属门店
var storeList []uint32
if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") {
sysUser, err := GetSysUserByCtx(c)
if err != nil {
return nil, err
}
// 返回sysUser未过期的门店id列表
storeList = GetValidStoreIDs(sysUser.StoreData)
if m.StoreId != 0 {
if !Contains(storeList, m.StoreId) {
return nil, errors.New("您没有该门店权限")
}
} else {
if len(storeList) == 0 {
return nil, errors.New("用户未绑定门店")
}
}
}
if m.StoreId != 0 { // 传门店id
qs = qs.Select("erp_commodity.*, COALESCE(erp_stock.count, 0) AS total_count").
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之和
if len(storeList) == 0 {
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)")
} else {
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 WHERE store_id IN (?) GROUP BY erp_commodity_id) "+
"erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id", storeList).
Joins("JOIN erp_category c ON erp_commodity.erp_category_id = c.id").
Where("erp_stock.count IS NULL OR erp_stock.count = 0").
Order("CASE WHEN c.pid = 0 THEN CAST(c.number AS SIGNED) " +
"ELSE CAST(SUBSTRING(c.number, 1, 3) AS SIGNED) END, " +
"CAST(c.pid AS SIGNED), CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)")
es = es.Select("erp_commodity.*, COALESCE(erp_stock.count, 0) AS total_count").
Joins("LEFT JOIN (SELECT erp_commodity_id, SUM(count) AS count FROM erp_stock WHERE store_id IN (?) GROUP BY erp_commodity_id) "+
"erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id", storeList).
Joins("JOIN erp_category c ON erp_commodity.erp_category_id = c.id").
Where("erp_stock.count IS NULL OR erp_stock.count = 0").
Order("CASE WHEN c.pid = 0 THEN CAST(c.number AS SIGNED) " +
"ELSE CAST(SUBSTRING(c.number, 1, 3) AS SIGNED) END, " +
"CAST(c.pid AS SIGNED), CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)")
}
}
if m.SerialNumber != "" {
qs = qs.Where("erp_commodity.serial_number=?", m.SerialNumber)
es = es.Where("erp_commodity.serial_number=?", m.SerialNumber)
}
if m.CommodityName != "" {
qs = qs.Where("erp_commodity.name Like '%" + m.CommodityName + "%'")
es = es.Where("erp_commodity.name Like '%" + m.CommodityName + "%'")
//qs = qs.Where("name LIKE ?", m.Name)
}
if m.ErpCategoryId != 0 {
qs = qs.Where("erp_commodity.erp_category_id=?", m.ErpCategoryId)
es = es.Where("erp_commodity.erp_category_id=?", m.ErpCategoryId)
}
var count int64
err := es.Count(&count).Error
if err != nil {
logger.Error("查询无库存列表数量失败", logger.Field("err", err))
return nil, err
}
var commodities []struct {
ErpCommodity
TotalCount int
}
//err = qs.Offset(page * m.PageSize).Limit(m.PageSize).Find(&commodities).Error
err = qs.Find(&commodities).Error
if err != nil && err != RecordNotFound {
logger.Error("查询无库存列表失败", logger.Field("err", err))
return nil, err
}
//遍历商品资料,转换为库存列表数据
var stockList []ErpStock
for _, commodity := range commodities {
if commodity.TotalCount == 0 {
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)
}
}
// Paginate results
startIndex := page * m.PageSize
endIndex := startIndex + m.PageSize
if endIndex > len(stockList) {
endIndex = len(stockList)
}
// Slice the users based on pagination
pagedList := stockList[startIndex:endIndex]
//跟之前保持一致
resp.Total = len(stockList)
resp.PageIndex = page + 1
resp.PageSize = m.PageSize
resp.List = pagedList
return resp, nil
}
func (m *ErpStockListReq) stockNoEmptyList(c *gin.Context) (*ErpStockListResp, error) {
resp := &ErpStockListResp{
PageIndex: m.PageIndex,
PageSize: m.PageSize,
}
page := m.PageIndex - 1
if page < 0 {
page = 0
}
if m.PageSize == 0 {
m.PageSize = 10
}
/***
//组合查询
SELECT
ec.*,
COALESCE(SUM(es.count), 0) AS total_count
FROM
erp_commodity ec
LEFT JOIN
erp_stock es ON ec.id = es.erp_commodity_id
GROUP BY
ec.id
ORDER BY
CASE
WHEN ec.erp_category_id IN (SELECT id FROM erp_category WHERE pid = 0) THEN CAST((SELECT number FROM erp_category WHERE id = ec.erp_category_id) AS SIGNED)
ELSE CAST(SUBSTRING((SELECT number FROM erp_category WHERE id = (SELECT pid FROM erp_category WHERE id = ec.erp_category_id)), 1, 3) AS SIGNED)
END,
CAST((SELECT pid FROM erp_category WHERE id = ec.erp_category_id) AS SIGNED),
CAST(SUBSTRING(ec.serial_number, -4) AS SIGNED);
***/
qs := orm.Eloquent.Debug().Table("erp_commodity")
es := orm.Eloquent.Debug().Table("erp_commodity")
// 非管理员才判断所属门店
if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") {
sysUser, err := GetSysUserByCtx(c)
if err != nil {
return nil, err
}
// 返回sysUser未过期的门店id列表
storeList := GetValidStoreIDs(sysUser.StoreData)
if m.StoreId != 0 {
if !Contains(storeList, m.StoreId) {
return nil, errors.New("您没有该门店权限")
}
} else {
if len(storeList) > 0 {
qs = qs.Where("store_id IN (?)", storeList)
es = es.Where("store_id IN (?)", storeList)
} else {
return nil, errors.New("用户未绑定门店")
}
}
}
if m.StoreId == 0 { // 没指定门店,连表查询并计算总数量
qs = qs.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_count, erp_stock.id AS erp_stock_id").
Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id AND erp_stock.count != 0").
Group("erp_commodity.id").
Order("CASE WHEN erp_commodity.erp_category_id IN (SELECT id FROM erp_category WHERE pid = 0) " +
"THEN CAST((SELECT number FROM erp_category WHERE id = erp_commodity.erp_category_id) AS SIGNED) " +
"ELSE CAST(SUBSTRING((SELECT number FROM erp_category WHERE id = (SELECT pid FROM erp_category WHERE id = erp_commodity.erp_category_id)), 1, 3) AS SIGNED) " +
"END, CAST((SELECT pid FROM erp_category WHERE id = erp_commodity.erp_category_id) AS SIGNED), " +
"CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)")
es = es.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_count, 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")
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)
es = es.Where("erp_commodity.serial_number=?", m.SerialNumber)
}
if m.CommodityName != "" {
qs = qs.Where("erp_commodity.name Like '%" + m.CommodityName + "%'")
es = es.Where("erp_commodity.name Like '%" + m.CommodityName + "%'")
}
if m.ErpCategoryId != 0 {
qs = qs.Where("erp_commodity.erp_category_id=?", m.ErpCategoryId)
es = es.Where("erp_commodity.erp_category_id=?", m.ErpCategoryId)
}
var commodities []struct {
ErpCommodity
TotalCount int
ErpStockId int
}
var count int64
err := es.Count(&count).Error
if err != nil {
logger.Error("commodityList count err", logger.Field("err", err))
return resp, err
}
//err = qs.Offset(page * m.PageSize).Limit(m.PageSize).Find(&commodities).Error
err = qs.Find(&commodities).Error
if err != nil && err != RecordNotFound {
logger.Error("commodityList err", logger.Field("err", err))
return resp, err
}
//遍历商品资料,转换为库存列表数据
var stockList []ErpStock
for _, commodity := range commodities {
if commodity.TotalCount != 0 {
var stock ErpStock
stock.ID = uint32(commodity.ErpStockId)
stock.ErpCommodityId = commodity.ID
stock.ErpCommodityName = commodity.Name
stock.ErpCategoryId = commodity.ErpCategoryId
stock.ErpCategoryName = commodity.ErpCategoryName
stock.CommoditySerialNumber = commodity.SerialNumber
stock.IMEIType = commodity.IMEIType
stock.RetailPrice = commodity.RetailPrice
stock.MinRetailPrice = commodity.MinRetailPrice
stock.Count = uint32(commodity.TotalCount)
stock.DispatchCount = 0
stockList = append(stockList, stock)
}
}
// Paginate results
startIndex := page * m.PageSize
endIndex := startIndex + m.PageSize
if endIndex > len(stockList) {
endIndex = len(stockList)
}
// Slice the users based on pagination
pagedList := stockList[startIndex:endIndex]
//跟之前保持一致
resp.Total = len(stockList)
resp.PageIndex = page + 1
resp.PageSize = m.PageSize
resp.List = pagedList
return resp, nil
}
// 筛选条件无:库存情况筛选
// 1先查询商品资料并排序支持筛选条件商品编号、商品分类、商品名称
// 2然后查询每个商品资料的库存情况没传门店id则查所有库存否则查当前门店的库存情况
func (m *ErpStockListReq) allCommodityList(c *gin.Context) (*ErpStockListResp, error) {
resp := &ErpStockListResp{
PageIndex: m.PageIndex,
PageSize: m.PageSize,
}
page := m.PageIndex - 1
if page < 0 {
page = 0
}
if m.PageSize == 0 {
m.PageSize = 10
}
/***
//组合查询
SELECT
ec.*,
COALESCE(SUM(es.count), 0) AS total_count
FROM
erp_commodity ec
LEFT JOIN
erp_stock es ON ec.id = es.erp_commodity_id
GROUP BY
ec.id
ORDER BY
CASE
WHEN ec.erp_category_id IN (SELECT id FROM erp_category WHERE pid = 0) THEN CAST((SELECT number FROM erp_category WHERE id = ec.erp_category_id) AS SIGNED)
ELSE CAST(SUBSTRING((SELECT number FROM erp_category WHERE id = (SELECT pid FROM erp_category WHERE id = ec.erp_category_id)), 1, 3) AS SIGNED)
END,
CAST((SELECT pid FROM erp_category WHERE id = ec.erp_category_id) AS SIGNED),
CAST(SUBSTRING(ec.serial_number, -4) AS SIGNED);
***/
qs := orm.Eloquent.Debug().Table("erp_commodity")
es := orm.Eloquent.Debug().Table("erp_commodity")
if m.SerialNumber != "" {
qs = qs.Where("erp_commodity.serial_number=?", m.SerialNumber)
es = es.Where("erp_commodity.serial_number=?", m.SerialNumber)
}
if m.CommodityName != "" {
qs = qs.Where("erp_commodity.name Like '%" + m.CommodityName + "%'")
es = es.Where("erp_commodity.name Like '%" + m.CommodityName + "%'")
}
if m.ErpCategoryId != 0 {
qs = qs.Where("erp_commodity.erp_category_id=?", m.ErpCategoryId)
es = es.Where("erp_commodity.erp_category_id=?", m.ErpCategoryId)
}
// 非管理员才判断所属门店
if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") {
sysUser, err := GetSysUserByCtx(c)
if err != nil {
return nil, err
}
// 返回sysUser未过期的门店id列表
storeList := GetValidStoreIDs(sysUser.StoreData)
if m.StoreId != 0 {
if !Contains(storeList, m.StoreId) {
return nil, errors.New("您没有该门店权限")
}
} else {
if len(storeList) > 0 {
qs = qs.Where("store_id IN (?)", storeList)
es = es.Where("store_id IN (?)", storeList)
} else {
return nil, errors.New("用户未绑定门店")
}
}
}
if m.StoreId == 0 { // 没指定门店,连表查询并计算总数量
qs = qs.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_count, erp_stock.id AS erp_stock_id").
Joins("LEFT JOIN erp_stock ON erp_commodity.id = erp_stock.erp_commodity_id").
Group("erp_commodity.id").
Order("CASE WHEN erp_commodity.erp_category_id IN (SELECT id FROM erp_category WHERE pid = 0) " +
"THEN CAST((SELECT number FROM erp_category WHERE id = erp_commodity.erp_category_id) AS SIGNED) " +
"ELSE CAST(SUBSTRING((SELECT number FROM erp_category WHERE id = (SELECT pid FROM erp_category WHERE id = erp_commodity.erp_category_id)), 1, 3) AS SIGNED) " +
"END, CAST((SELECT pid FROM erp_category WHERE id = erp_commodity.erp_category_id) AS SIGNED), " +
"CAST(SUBSTRING(erp_commodity.serial_number, -4) AS SIGNED)")
es = es.Select("erp_commodity.*, COALESCE(SUM(erp_stock.count), 0) AS total_count, 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")
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
err := es.Count(&count).Error
if err != nil {
logger.Error("commodityList count err", logger.Field("err", err))
return resp, err
}
err = qs.Offset(page * m.PageSize).Limit(m.PageSize).Find(&commodities).Error
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
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
OriginalSn string `json:"original_sn"` // 首次入库订单编号
StorageType uint32 `json:"storage_type"` // 首次入库方式1-系统入库 2-采购入库
StockTimeStart string `json:"stock_time_start"` // 最近入库开始时间
StockTimeEnd string `json:"stock_time_end"` // 最近入库结束时间
Age uint32 `json:"age"` // 最近库龄
AllAge uint32 `json:"all_age"` // 总库龄
PageIndex int `json:"pageIndex"` // 页码
PageSize int `json:"pageSize"` // 每页展示数据条数
IsExport uint32 `json:"is_export"` // 是否导出excel1-导出
//Sn string `json:"sn"` // 首次入库订单编号
}
// ErpStockCommodityListResp 库存详情接口响应参数
type ErpStockCommodityListResp struct {
List []ErpStockCommodity `json:"list"`
Total int `json:"total"` // 数据总条数
PageIndex int `json:"pageIndex"` // 页码
PageSize int `json:"pageSize"` // 每页展示条数
TotalWholesalePrice int `json:"total_wholesale_price"` // 入库采购价之和
TotalStaffPrice int `json:"total_staff_price"` // 入库员工成本价之和
ExportUrl string `json:"export_url"`
}
// GetDetailList 查看库存详情
func (m *ErpStockCommodityListReq) GetDetailList(c *gin.Context) (*ErpStockCommodityListResp, error) {
resp := &ErpStockCommodityListResp{
PageIndex: m.PageIndex,
PageSize: m.PageSize,
}
page := m.PageIndex - 1
if page < 0 {
page = 0
}
if m.PageSize == 0 {
m.PageSize = 10
}
if m.ErpStockId != 0 {
updateCommodityStock(m.ErpStockId) // 同步详情表的库存id
}
// 出库数据不查询
qs := orm.Eloquent.Table("erp_stock_commodity")
// 非管理员才判断所属门店
if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") {
sysUser, err := GetSysUserByCtx(c)
if err != nil {
return nil, err
}
// 返回sysUser未过期的门店id列表
storeList := GetValidStoreIDs(sysUser.StoreData)
if m.StoreId != 0 {
if !Contains(storeList, m.StoreId) {
return nil, errors.New("您没有该门店权限")
}
} else {
if len(storeList) > 0 {
qs = qs.Where("store_id IN (?)", storeList)
} else {
return nil, errors.New("用户未绑定门店")
}
}
}
// 构建查询条件
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
}
ErpStockCommodityListSetAge(commodities)
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+"%")
qs = qs.Where("erp_commodity_name = ?", 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.OriginalSn != "" { //首次入库订单编号
qs = qs.Where("original_sn=?", m.OriginalSn)
}
if m.StorageType != 0 { //首次入库方式
qs = qs.Where("storage_type=?", m.StorageType)
}
if m.StockTimeStart != "" { //最近入库开始时间
startTime, err := time.Parse(QueryTimeFormat, m.StockTimeStart)
if err == nil {
qs = qs.Where("stock_time>?", startTime)
} else {
//logger.Error("stock time start parse err:", err)
}
}
if m.StockTimeEnd != "" { //最近入库结束时间
endTime, err := time.Parse(QueryTimeFormat, m.StockTimeEnd)
if err == nil {
qs = qs.Where("stock_time<?", endTime)
} else {
//logger.Error("stock time end parse err:", err)
}
}
if m.Age != 0 { //最近库龄
qs = qs.Where("stock_time<?", time.Now().AddDate(0, 0, int(m.Age)*(-1)))
}
if m.AllAge != 0 { //总库龄
qs = qs.Where("first_stock_time<?", time.Now().AddDate(0, 0, int(m.AllAge)*(-1)))
}
}
}
// SetStockCommodityState 更新库存状态
func SetStockCommodityState(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
}
// 更新商品库存详情表状态为6系统出库
if err := begin.Model(&ErpStockCommodity{}).Where("id=?", id).Updates(map[string]interface{}{
"state": SystemOut}).Error; err != nil {
return fmt.Errorf("[update err]%v", err)
}
if record.Count > 0 {
// 扣减商品库存数量
err = begin.Model(&record).Update("count", record.Count-1).Error
if err != nil {
logger.Error("扣减库存数量失败", logger.Field("err", err))
return err
}
}
err = begin.Commit().Error
if err != nil {
begin.Rollback()
logger.Error("出库事务失败", logger.Field("err", err))
return err
}
return nil
}
type BatchPrintInfo struct {
ErpCommodityName string `json:"erp_commodity_name" binding:"required"` // 商品名称
RetailPrice uint32 `json:"retail_price" binding:"required"` // 指导零售价
IMEI string `json:"imei" binding:"required"` // 商品串码
}
type BatchPrintInfoReq struct {
PrintListInfo []*BatchPrintInfo `json:"print_list_info" binding:"required"`
}
// BatchPrint 批量打印标签
func BatchPrint(req *BatchPrintInfoReq) error {
return nil
}
func StringToFloat(req string) (float64, error) {
if req == "" {
return 0, nil
}
return strconv.ParseFloat(req, 64)
}
func PercentFloatStringToFloat64(s string) float64 {
u := float64(0)
if s != "" {
s = strings.ReplaceAll(s, "%", "")
}
f, err := strconv.ParseFloat(s, 64)
if err != nil {
//logger.Error("parse float err:", err)
return u
}
u = f * 100
return tools.RoundToTwoDecimalPlaces(u)
}
func IntStringToUin32(s string) uint32 {
u := uint32(0)
if s != "" {
s = strings.ReplaceAll(s, "%", "")
}
i, err := strconv.Atoi(s)
if err != nil {
//logger.Error("parse float err:", err)
return u
}
u = uint32(i)
return u
}
func NewStockImporter() *StockImporter {
return &StockImporter{
CensusMap: make(map[uint32]map[uint32]uint32, 0),
CommodityMap: make(map[uint32]*ErpCommodity),
StoreMap: make(map[uint32]string, 0),
Inventories: make([]*ErpInventoryStock, 0),
}
}
// UpdateStockCommodityRemark 更新库存商品备注信息
func UpdateStockCommodityRemark(id int, remark string) error {
if remark == "" {
return nil
}
if err := orm.Eloquent.Model(&ErpStockCommodity{}).Where("id=?", id).Updates(map[string]interface{}{
"remark": remark}).Error; err != nil {
return fmt.Errorf("[update err]%v", err)
}
return nil
}
func GetCommodity(id uint32) (*ErpCommodity, error) {
var erpCommodity ErpCommodity
err := orm.Eloquent.Table("erp_commodity").Where("id=?", id).Find(&erpCommodity).Error
if err != nil {
logger.Errorf("err:", logger.Field("err", err))
return &erpCommodity, err
}
return &erpCommodity, nil
}
// CheckAndConvertBarcode 校验输入的条码数据是否正确
func CheckAndConvertBarcode(input string) (string, error) {
// 1. 检查总长度不超过100字符
if utf8.RuneCountInString(input) > 100 {
return "", errors.New("error: Total length exceeds 100 characters")
}
// 2. 转换中文逗号为英文逗号
input = strings.ReplaceAll(input, "", ",")
// 3. 两个逗号之前需要有数据,不能是空格或无数据
parts := strings.Split(input, ",")
for _, part := range parts {
trimmedPart := strings.TrimSpace(part)
if trimmedPart == "" {
return "", errors.New("error: Two commas should have data in between")
}
}
// 4.判断逗号隔开的数据数据库是否已存在
for _, part := range parts {
// 检查生成的条码是否已存在
exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_commodity WHERE FIND_IN_SET(%s, erp_barcode) > 0", part))
if err != nil {
logger.Error("exist sn err")
}
if exist {
return "", errors.New("[" + part + "]" + "该条码已存在,请检查")
}
}
// 如果通过所有检查,返回处理后的字符串
return input, nil
}
// CheckBarcodeById 校验输入的条码数据是否正确
func CheckBarcodeById(input string, commodityId uint32) (string, error) {
// 1. 检查总长度不超过100字符
if utf8.RuneCountInString(input) > 100 {
return "", errors.New("error: Total length exceeds 100 characters")
}
// 2. 转换中文逗号为英文逗号
input = strings.ReplaceAll(input, "", ",")
// 3. 两个逗号之前需要有数据,不能是空格或无数据
parts := strings.Split(input, ",")
for _, part := range parts {
trimmedPart := strings.TrimSpace(part)
if trimmedPart == "" {
return "", errors.New("error: Two commas should have data in between")
}
}
// 4.判断逗号隔开的数据数据库是否已存在
for _, part := range parts {
// 检查生成的条码是否已存在
exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_commodity WHERE FIND_IN_SET(%s, erp_barcode) > 0", part))
if err != nil {
logger.Error("exist sn err")
}
if exist {
var commodityInfo ErpCommodity
err = orm.Eloquent.Table("erp_commodity").Where("id=?", commodityId).Find(&commodityInfo).Error
if err != nil {
logger.Errorf("get erp_commodity err:", err)
return "", err
}
if commodityInfo.ErpBarcode == input {
return input, nil
} else {
return "", errors.New("[" + part + "]" + "该条码已存在,请检查")
}
}
}
// 如果通过所有检查,返回处理后的字符串
return input, nil
}
// GenerateBarcode 系统生成条码
func GenerateBarcode(categoryCode uint32) (string, error) {
category := new(Category)
err := orm.Eloquent.Table("erp_category").Where("id=?", categoryCode).Find(category).Error
if err != nil {
logger.Errorf("get erp category err:", err)
return "", err
}
max := 1
for {
if max > 5 {
logger.Error("create Barcode err")
return "", errors.New("create Barcode err")
}
// 生成当前日期的六位数
dateCode := time.Now().Format("060102")
// 生成三位随机数
randomNumber := fmt.Sprintf("%03d", rand.Intn(1000))
// 组合生成的条码
barcode := fmt.Sprintf("%03s%s%s", category.Number[:3], dateCode, randomNumber)
// 检查生成的条码是否已存在
exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_commodity WHERE FIND_IN_SET(%s, erp_barcode) > 0", barcode))
if err != nil {
logger.Error("exist sn err")
}
if !exist {
return barcode, nil
}
}
}
// GetCodeList 查询输入的条码或串码数据
func GetCodeList(req *QueryCodeReq) (*QueryCodeResp, error) {
resp := &QueryCodeResp{
PageIndex: req.PageIndex,
PageSize: req.PageSize,
}
page := req.PageIndex - 1
if page < 0 {
page = 0
}
if req.PageSize == 0 {
req.PageSize = 10
}
var commodities []ErpStockCommodity
orderStr := fmt.Sprintf("CASE WHEN imei LIKE '%%%s%%' THEN 1 ELSE 2 END", req.ScanCode)
qs := orm.Eloquent.Debug().Table("erp_stock_commodity").
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
}
type CommodityCreateRequest struct {
Name string `json:"name" binding:"required"` // 商品名称
ErpCategoryId uint32 `json:"erp_category_id" binding:"required"` // 商品分类id
IsIMEI uint32 `json:"is_imei" binding:"required"` // 是否串码1-串码类 2-非串码
ErpBarcode string `json:"erp_barcode"` // 商品条码
IMEIType uint32 `json:"imei_type"` // 系统生成串码2-是(系统生成) 3-否(手动添加)
ErpSupplierId uint32 `json:"erp_supplier_id" binding:"required"` // 主供应商
RetailPrice float64 `json:"retail_price"` // 指导零售价
MinRetailPrice float64 `json:"min_retail_price"` // 最低零售价
StaffCostPrice float64 `json:"staff_cost_price"` // 员工成本价加价
WholesalePrice float64 `json:"wholesale_price"` // 指导采购价
Brokerage1 float64 `json:"brokerage_1"` // 销售毛利提成
Brokerage2 float64 `json:"brokerage_2"` // 员工毛利提成
MemberDiscount float64 `json:"member_discount"` // 会员优惠
Origin string `json:"origin"` // 产地
Remark string `json:"remark" gorm:"type:varchar(512)"` // 备注
}
type CommodityEditRequest struct {
Id uint32 `json:"id" binding:"required"` // 商品id
Name string `json:"name" binding:"required"` // 商品名称
ErpCategoryId uint32 `json:"erp_category_id" binding:"required"` // 商品分类id
ErpBarcode string `json:"erp_barcode"` // 商品条码
IsIMEI uint32 `json:"is_imei" binding:"required"` // 是否串码1-串码类 2-非串码
IMEIType uint32 `json:"imei_type" binding:"required"` // 1-无串码 2-串码(系统生成) 3-串码(手动添加)
ErpSupplierId uint32 `json:"erp_supplier_id" binding:"required"` // 主供应商id
RetailPrice float64 `json:"retail_price"` // 指导零售价
MinRetailPrice float64 `json:"min_retail_price"` // 最低零售价
StaffCostPrice float64 `json:"staff_cost_price"` // 员工成本价加价
WholesalePrice float64 `json:"wholesale_price"` // 指导采购价
Brokerage1 float64 `json:"brokerage_1"` // 销售毛利提成
Brokerage2 float64 `json:"brokerage_2"` // 员工毛利提成
MemberDiscount float64 `json:"member_discount"` // 会员优惠
Origin string `json:"origin"` // 产地
Remark string `json:"remark" gorm:"type:varchar(512)"` // 备注
}
type CommodityDetailRequest struct {
ErpCommodityId uint32 `json:"erp_commodity_id"` // 商品id
SerialNumber string `json:"serial_number"` // 商品编号
}
type CommodityDelRequest struct {
ErpCommodityId uint32 `json:"erp_commodity_id" binding:"required"` // 商品id
}