mh_goadmin_server/app/admin/models/commodity.go

1392 lines
45 KiB
Go
Raw Normal View History

package models
import (
"errors"
"fmt"
"github.com/xuri/excelize/v2"
orm "go-admin/common/global"
"go-admin/logger"
"golang.org/x/sync/errgroup"
"gorm.io/gorm"
"strconv"
"strings"
"sync"
"time"
)
// ErpStock 库存表
//
//go:generate goqueryset -in commodity.go
type ErpStock struct {
Model
StoreId uint32 `json:"store_id" gorm:"index"` // 门店编号
StoreName string `json:"store_name"` // 门店名称
ErpCommodityId uint32 `json:"erp_commodity_id" gorm:"index"` // 商品id
ErpCommodityName string `json:"erp_commodity_name"` // 商品名称
ErpCategoryId uint32 `json:"erp_category_id" gorm:"index"` // 分类id
ErpCategoryName string `json:"erp_category_name"` // 分类名称
CommoditySerialNumber string `json:"commodity_serial_number" gorm:"index"` // 商品编码/串码
IMEIType uint32 `json:"imei_type"` // 1-无串码 2-串码(系统生成) 3-串码(手动添加)
RetailPrice uint32 `json:"retail_price"` // 指导零售价
MinRetailPrice uint32 `json:"min_retail_price"` // 最低零售价
Count uint32 `json:"count"` // 数量
DispatchCount uint32 `json:"dispatch_count"` // 调拨中数量
Commodities []ErpStockCommodity `json:"commodities" gorm:"-"`
}
// ErpStockCommodity 库存详情
type ErpStockCommodity struct {
Model
ErpStockId uint32 `json:"erp_stock_id" gorm:"index"` // 库存id
StoreId uint32 `json:"store_id" gorm:"index"` // 门店id
StoreName string `json:"store_name"` // 门店名称
ErpCommodityId uint32 `json:"erp_commodity_id" gorm:"index"` // 商品id
ErpCommodityName string `json:"erp_commodity_name"` // 商品名称
CommoditySerialNumber string `json:"commodity_serial_number" gorm:"index"` // 商品编号
ErpCategoryId uint32 `json:"erp_category_id" gorm:"index"` // 分类id
ErpCategoryName string `json:"erp_category_name"` // 分类名称
IMEIType uint32 `json:"imei_type"` // 是否串码1-无串码 2-串码(系统生成) 3-串码(手动添加)
IMEI string `json:"imei"` // 商品串码
ErpSupplierId uint32 `json:"erp_supplier_id" gorm:"index"` // 供应商id
ErpSupplierName string `json:"erp_supplier_name"` // 供应商名称
StockTime time.Time `json:"stock_time"` // 最近入库时间
RetailPrice uint32 `json:"retail_price"` // 指导零售价
MinRetailPrice uint32 `json:"min_retail_price"` // 最低零售价
StaffCostPrice uint32 `json:"staff_cost_price"` // 员工成本价加价
WholesalePrice uint32 `json:"wholesale_price"` // 指导采购价
State uint32 `json:"state"` // 状态:1-在库 2-已售 3-采购退货 4-调拨中
Count uint32 `json:"count"` // 数量
StorageType uint32 `json:"storage_type"` // 入库方式1-系统入库 2-采购入库
FirstStockTime time.Time `json:"first_stock_time"` // 首次入库时间
StockSn string `json:"stock_sn"` // 库存订单编号
OriginalSn string `json:"original_sn" gorm:"index"` // 首次入库订单编号
StockStartTime time.Time `json:"stock_start_time" gorm:"-"` // 最近入库开始时间
StockEndTime time.Time `json:"stock_end_time" gorm:"-"` // 最近入库结束时间
Age uint32 `json:"age" gorm:"-"` // 最近库龄
AllAge uint32 `json:"all_age" gorm:"-"` // 总库龄
//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"` // 商品分类名称
IMEIType uint32 `json:"imei_type"` // 1-无串码 2-串码(系统生成) 3-串码(手动添加)
IMEI string `json:"imei"` // 串码
ErpSupplierId uint32 `json:"erp_supplier_id" gorm:"index"` // 主供应商id
ErpSupplierName string `json:"erp_supplier_name"` // 主供应商名称
RetailPrice uint32 `json:"retail_price"` // 指导零售价
MinRetailPrice uint32 `json:"min_retail_price"` // 最低零售价
StaffCostPrice uint32 `json:"staff_cost_price"` // 员工成本价加价
WholesalePrice uint32 `json:"wholesale_price"` // 指导采购价
Brokerage1 float64 `json:"brokerage_1"` // 销售毛利提成
Brokerage2 float64 `json:"brokerage_2"` // 员工毛利提成
MemberDiscount float64 `json:"member_discount"` // 会员优惠
Origin string `json:"origin"` // 产地
Remark string `json:"remark" gorm:"type:varchar(512)"` // 备注
ErpCategory *ErpCategory `json:"erp_category" gorm:"-"`
}
// gen:qs
type ErpCategory struct {
Model
Name string `json:"name"` // 名称
Priority string `json:"priority"` // 分类
Number uint32 `json:"number"`
FullNum uint32 `json:"full_num"`
State uint32 `json:"state"` // 1-未使用 2-使用 3-隐藏
Level uint32 `json:"level"` // 分类层级
Pid uint32 `json:"pid" gorm:"index"`
Sort uint32 `json:"sort"`
SubCats []ErpCategory `json:"sub_cats" gorm:"-"` // 子列表
// erp_category
}
// gen:qs
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
}
// gen:qs
type ErpInventoryStock struct {
Model
StoreId uint32 `json:"store_id" gorm:"index"`
StoreName string `json:"store_name"`
ErpCommodityId uint32 `json:"erp_commodity_id" gorm:"index"`
ErpCommodityName string `json:"erp_commodity_name"`
ErpCategoryId uint32 `json:"erp_category_id" gorm:"index"`
ErpCategoryName string `json:"erp_category_name"`
CommoditySerialNumber string `json:"commodity_serial_number" gorm:"index"`
IMEIType uint32 `json:"imei_type"` // 1-无串码 2-串码(系统生成) 3-串码(手动添加)
RetailPrice uint32 `json:"retail_price"`
MinRetailPrice uint32 `json:"min_retail_price"`
Count uint32 `json:"count"`
Sn string `json:"sn" gorm:"index"`
//ErpSupplierId uint32 `json:"erp_supplier_id" gorm:"index"`
//ErpSupplierName string `json:"erp_supplier_name"`
//IMEI string `json:"imei"`
//StockTime time.Time `json:"stock_time"`
//StaffCostPrice uint32 `json:"staff_cost_price"`
//WholesalePrice uint32 `json:"wholesale_price"`
// erp_inventory_stock
}
// gen:qs
type ErpInventoryStockCommodity struct {
Model
ErpInventoryStockId uint32 `json:"erp_inventory_stock_id" gorm:"index"`
ErpCommodityId uint32 `json:"erp_commodity_id" gorm:"index"`
ErpCommodityName string `json:"erp_commodity_name"`
CommoditySerialNumber string `json:"commodity_serial_number" gorm:"index"`
IMEIType uint32 `json:"imei_type"` // 1-无串码 2-串码(系统生成) 3-串码(手动添加)
IMEI string `json:"imei"`
ErpSupplierId uint32 `json:"erp_supplier_id" gorm:"index"`
ErpSupplierName string `json:"erp_supplier_name"`
StockTime time.Time `json:"stock_time"`
RetailPrice uint32 `json:"retail_price"`
MinRetailPrice uint32 `json:"min_retail_price"`
StaffCostPrice uint32 `json:"staff_cost_price"`
WholesalePrice uint32 `json:"wholesale_price"`
Count uint32 `json:"count"`
StorageType uint32 `json:"storage_type"`
Sn string `json:"sn" gorm:"index"`
//StoreId uint32 `json:"store_id" gorm:"index"`
//StoreName string `json:"store_name"`
//ErpCategoryId uint32 `json:"erp_category_id" gorm:"index"`
//ErpCategoryName string `json:"erp_category_name"`
// erp_inventory_stock_commodity
}
func (c *ErpCommodity) IdInit() {
if c.ErpCategoryId != 0 {
if c.ErpCategory == nil {
category, err := GetErpCategory(c.ErpCategoryId)
if err != nil {
//logger.Error("get erp category err:", err)
return
}
c.ErpCategory = category
}
c.ErpCategoryName = c.ErpCategory.Name
}
if c.ErpSupplierId != 0 {
supplier, err := GetErpSupplier(c.ErpSupplierId)
if err != nil {
//logger.Error("get erp category err:", err)
} else {
c.ErpSupplierName = supplier.Name
}
}
}
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
}
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
}
func (c *ErpCommodity) SetErpCategory() error {
if c.ErpCategoryId != 0 {
category, err := GetErpCategory(c.ErpCategoryId)
if err != nil {
//logger.Error("get erp category err:", err)
return err
}
c.ErpCategory = category
return nil
}
return errors.New("erp category id null")
}
type ErpCommodityListReq struct {
SerialNumber string `json:"serial_number"` // 商品编号
ErpCommodityName string `json:"erp_commodity_name"` // 商品名称
ErpCategoryId uint32 `json:"erp_category_id"` // 商品分类id
IMEI string `json:"imei"` // 串码
ErpSupplierId uint32 `json:"erp_supplier_id"` // 供应商id
PageNum int `json:"page_num"` // 页码
PageSize int `json:"page_size"` // 每页展示数据条数
IsExport uint32 `json:"is_export"` // 1-导出
}
type ErpCommodityListResp struct {
List []ErpCommodity `json:"list"`
Total int `json:"total"` // 总页数
PageNum int `json:"page_num"` // 页码
PageSize int `json:"page_size"` // 每页展示数据条数
ExportUrl string `json:"export_url"` // 1-导出
}
func (m *ErpCommodityListReq) List() (*ErpCommodityListResp, error) {
resp := &ErpCommodityListResp{
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.Debug().Table("erp_commodity")
if m.SerialNumber != "" {
qs = qs.Where("serial_number=?", m.SerialNumber)
}
if m.ErpCommodityName != "" {
qs = qs.Where("name Like '%" + m.ErpCommodityName + "%'")
//qs = qs.Where("name LIKE ?", m.Name)
}
if m.IMEI != "" {
qs = qs.Where("imei=?", m.IMEI)
}
if m.ErpCategoryId != 0 {
qs = qs.Where("erp_category_id=?", m.ErpCategoryId)
}
if m.ErpSupplierId != 0 {
qs = qs.Where("erp_supplier_id=?", m.ErpSupplierId)
}
var count int64
err := qs.Count(&count).Error
if err != nil {
//logger.Error("count err:", err)
return resp, err
}
resp.Total = int(count)/m.PageSize + 1
var commodities []ErpCommodity
if m.IsExport == 1 {
err = qs.Order("id DESC").Find(&commodities).Error
if err != nil && err != RecordNotFound {
//logger.Error("dailys err:", err)
return resp, err
}
listExport, err := ErpCommodityListExport(commodities)
if err != nil {
//logger.Error("list export err:", err)
}
resp.ExportUrl = listExport
} else {
err = qs.Order("id DESC").Offset(page * m.PageSize).Limit(m.PageSize).Find(&commodities).Error
if err != nil && err != RecordNotFound {
//logger.Error("erp commodity list err:", err)
return resp, err
}
resp.List = commodities
}
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
}
var ErpCommodityFileExcelCols = []string{"category_1", "category_2", "name", "is_imei", "erp_supplier_name",
"brokerage_1_string", "brokerage_2_string", "retail_price_string", "min_retail_price_string",
"staff_cost_price_string", "wholesale_price_string", "origin", "remark"}
type ErpCommodityFileExcel struct {
Category1 string `json:"category_1"`
Category2 string `json:"category_2"`
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"`
Brokerage1String string `json:"brokerage_1_string"`
Brokerage2String string `json:"brokerage_2_string"`
Code string `json:"code"`
ErpCommodity
}
func (e *ErpCommodityFileExcel) Processing() {
if e.IsIMEI == "是" {
e.IMEIType = 2
} else if e.IsIMEI == "否" {
e.IMEIType = 1
}
e.RetailPrice = PercentFloatStringToUin32(e.RetailPriceString)
e.MinRetailPrice = PercentFloatStringToUin32(e.MinRetailPriceString)
e.StaffCostPrice = PercentFloatStringToUin32(e.StaffCostPriceString)
e.WholesalePrice = PercentFloatStringToUin32(e.WholesalePriceString)
e.Brokerage1 = PercentFloatStringToFloat(e.Brokerage1String)
e.Brokerage2 = PercentFloatStringToFloat(e.Brokerage2String)
}
func PercentFloatStringToUin32(s string) uint32 {
u := uint32(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 = uint32(f * 100)
return u
}
func PercentFloatStringToFloat(s string) float64 {
//u := uint32(0)
if s != "" {
s = strings.ReplaceAll(s, "%", "")
}
f, err := strconv.ParseFloat(s, 64)
if err != nil {
//logger.Error("parse float err:", err)
return f
}
//u = uint32(f * 100)
return f
}
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
}
type ErpCategorySub struct {
Id uint32 `json:"id"`
Name string `json:"name"`
FullNum uint32 `json:"full_num"`
SubMap map[string]ErpCategory
//UnCodeMap map[string]uint32
}
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
}
func GetErpCategorySubMap() (map[string]*ErpCategorySub, error) {
var erpCategories []ErpCategory
err := orm.Eloquent.Table("erp_category").Find(&erpCategories).Error
if err != nil {
//logger.Error("erp categories err:", err)
return nil, err
}
categoriesMaps := make(map[string]*ErpCategorySub, 0)
pidMap := make(map[uint32]string, 0)
for i, _ := range erpCategories {
if erpCategories[i].Level == 1 {
if erpCategories[i].Pid != 0 {
logger.Error("erp categories level 1 pid gt 0")
return nil, errors.New("erp categories level 1 pid gt 0")
}
if _, ok := categoriesMaps[erpCategories[i].Name]; !ok {
categoriesMaps[erpCategories[i].Name] = &ErpCategorySub{
Id: erpCategories[i].ID,
Name: erpCategories[i].Name,
FullNum: erpCategories[i].FullNum,
SubMap: make(map[string]ErpCategory, 0),
}
pidMap[erpCategories[i].ID] = erpCategories[i].Name
}
}
}
for i, _ := range erpCategories {
if erpCategories[i].Level == 2 {
if erpCategories[i].Pid == 0 {
logger.Error("erp categories level 2 pid is 0")
return nil, errors.New("erp categories level 2 pid is 0")
}
name, ok := pidMap[erpCategories[i].Pid]
if ok {
categoriesMaps[name].SubMap[erpCategories[i].Name] = erpCategories[i]
}
}
}
return categoriesMaps, nil
}
func ErpCategoryCreate(name string, pid uint32) (*ErpCategory, error) {
category := &ErpCategory{
Number: 0,
State: 2,
Level: 1,
Pid: pid,
}
var (
pCategory ErpCategory
sCategory ErpCategory
)
qs := orm.Eloquent.Table("erp_category")
if category.Pid != 0 {
category.Level = 2
qs = qs.Where("id=?", pid)
err := orm.Eloquent.Table("erp_category").Where("pid=?", pid).Order("id DESC").Limit(1).Find(&sCategory).Error
if err != nil {
//logger.Error("s category err:", err)
return category, err
}
} else {
qs = qs.Where("level=1")
}
err := qs.Order("id DESC").Limit(1).Find(&pCategory).Error
if err != nil && err != RecordNotFound {
//logger.Error("cat erp commodity err:", err)
return category, err
}
if category.Level == 1 {
category.Number = pCategory.Number + 1
category.FullNum = pCategory.FullNum + 1000
category.Name = fmt.Sprintf("%03d%s", category.Number, name)
} else {
category.Number = sCategory.Number + 1
category.Name = fmt.Sprintf("%03d-%03d%s", pCategory.Number, category.Number, name)
category.FullNum = pCategory.FullNum + category.Number
}
err = orm.Eloquent.Create(category).Error
if err != nil {
//logger.Error("create commodity err:", err)
return category, err
}
return category, nil
}
type ErpSupplierListResp struct {
List []ErpSupplier `json:"list"`
Total int `json:"total"`
PageNum int `json:"page_num"`
PageSize int `json:"page_size"`
ExportUrl string `json:"export_url"`
}
var ErpStockFileExcelCols = []string{"store_name", "erp_commodity_name", "erp_category_name", "serial_number",
"is_imei", "imei", "erp_supplier_name", "stock_time_string", "retail_price_string",
"min_retail_price_string", "staff_cost_price_string", "wholesale_price_string", "count_string",
}
var EsfLock sync.Mutex
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 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),
}
}
func (e *ErpStockFileExcel) Processing() {
e.Count = IntStringToUin32(e.CountString)
if e.IsIMEI == "是" {
e.IMEIType = 2
e.Count = 1
} else if e.IsIMEI == "否" {
e.IMEIType = 1
}
//parseTime, err := time.Parse(DateTimeFormat, e.StockTimeString)
//fmt.Println("StockTimeString:", e.StockTimeString)
//parseTime, err := time.Parse("01-02-06", e.StockTimeString)
format := "2006/01/02"
parseTime, err := time.Parse(format, e.StockTimeString)
if err != nil {
//logger.Error("parse err:", err)
parseTime = time.Now()
}
e.StockTime = parseTime
e.RetailPrice = PercentFloatStringToUin32(e.RetailPriceString)
e.MinRetailPrice = PercentFloatStringToUin32(e.MinRetailPriceString)
e.StaffCostPrice = PercentFloatStringToUin32(e.StaffCostPriceString)
e.WholesalePrice = PercentFloatStringToUin32(e.WholesalePriceString)
}
func (m *StockImporter) ErpStockFileExcelListProcessing(list []ErpStockFileExcel) error {
//allCategoriesMap, err := GetErpCategorySubMap()
//if err != nil {
// logger.Error("all categories map err:", err)
// return
//}
// 加锁
//m.CensusMap = make(map[uint32]map[uint32]uint32, 0)
storeNameMap := make(map[string]uint32, 0)
erpCommodityMap := make(map[string]*ErpCommodity, 0)
erpCategoryNameMap := make(map[string]uint32, 0)
erpSupplierNameMap := make(map[string]uint32, 0)
storeNames := make([]string, 0, len(list))
erpCommodityNames := make([]string, 0, len(list))
erpCategoryNames := make([]string, 0, len(list))
erpSupplierNames := make([]string, 0, len(list))
for i, _ := range list {
list[i].Processing()
_, ok1 := storeNameMap[list[i].StoreName]
if !ok1 {
storeNames = append(storeNames, list[i].StoreName)
}
_, ok2 := erpCommodityMap[list[i].ErpCommodityName]
if !ok2 {
erpCommodityNames = append(erpCommodityNames, list[i].ErpCommodityName)
}
_, ok3 := erpCategoryNameMap[list[i].ErpCategoryName]
if !ok3 {
erpCategoryNames = append(erpCategoryNames, list[i].ErpCategoryName)
}
_, ok4 := erpSupplierNameMap[list[i].ErpSupplierName]
if !ok4 {
erpSupplierNames = append(erpSupplierNames, list[i].ErpSupplierName)
}
storeNameMap[list[i].StoreName] = uint32(0)
erpCommodityMap[list[i].ErpCommodityName] = nil
erpCategoryNameMap[list[i].ErpCategoryName] = uint32(0)
erpSupplierNameMap[list[i].ErpSupplierName] = 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 erpCategories []ErpCategory
err = orm.Eloquent.Table("erp_category").Where("name IN (?)", erpCategoryNames).Find(&erpCategories).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 && 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 erpCategories {
erpCategoryNameMap[erpCategories[i].Name] = erpCategories[i].ID
}
for i, _ := range erpSuppliers {
erpSupplierNameMap[erpSuppliers[i].Name] = erpSuppliers[i].ID
}
//categoryCommodityNumMap := make(map[uint32]uint32, 0)
//for i, _ := range erpCommodities {
// var commodity ErpCommodity
// err = orm.Eloquent.Table("erp_commodity").Where("erp_category_id=?", erpCommodities[i].ErpCategoryId).
// Order("id DESC").Limit(1).Find(&commodity).Error
// if err != nil {
// logger.Error("commodity err:", err)
// }
// categoryCommodityNumMap[erpCommodities[i].ErpCategoryId] = commodity.Number
//}
nowTime := time.Now()
for i, _ := range list {
v1, ok1 := storeNameMap[list[i].StoreName]
v2, ok2 := erpCommodityMap[list[i].ErpCommodityName]
v3, ok3 := erpCategoryNameMap[list[i].ErpCategoryName]
v4, ok4 := erpSupplierNameMap[list[i].ErpSupplierName]
if !ok1 {
logger.Error("store name err")
return errors.New("store name err")
}
if !ok2 && v2 == nil {
logger.Error("erp commodity name err")
return errors.New("erp commodity name err")
}
if !ok3 {
logger.Error("erp category name err")
return errors.New("erp category name err")
}
if !ok4 {
logger.Error("erp supplier name err")
return errors.New("erp supplier name err")
}
list[i].StoreId = v1
list[i].ErpCommodityId = v2.ID
list[i].CommoditySerialNumber = v2.SerialNumber
list[i].ErpCategoryId = v3
list[i].ErpSupplierId = v4
list[i].State = 1
list[i].StorageType = 1
list[i].FirstStockTime = nowTime
//v6, ok6 := m.CommodityMap[v2]
_, ok5 := m.CensusMap[list[i].StoreId]
if ok5 {
m.CensusMap[list[i].StoreId][list[i].ErpCommodityId] += list[i].Count
} else {
m.CensusMap[list[i].StoreId] = map[uint32]uint32{list[i].ErpCommodityId: list[i].Count}
}
//v5, ok5 := categoryCommodityNumMap[list[i].ErpCategoryId]
//if !ok5 {
// logger.Error("category commodity num err")
// return errors.New("category commodity num err")
//}
}
return nil
}
func (m *StockImporter) ErpStockCountUpdate(gdb *gorm.DB) error {
for k1, v1 := range m.CensusMap {
for k2, v2 := range v1 {
exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_stock WHERE store_id=%d AND erp_commodity_id=%d", k1, k2))
if err != nil {
//logger.Error("exist err:", err)
return err
}
v, ok := m.CommodityMap[k2]
fmt.Println("CommodityMap", m.CommodityMap)
fmt.Println("ok", ok)
fmt.Println("v", v)
if exist {
err = gdb.Exec(fmt.Sprintf(
"UPDATE erp_stock SET count=count+%d WHERE store_id=%d AND erp_commodity_id=%d;", v2, k1, k2)).Error
if err != nil {
//logger.Error("update stock err:", err)
return err
}
} else {
if ok && v != nil {
stock := &ErpStock{
StoreId: k1,
StoreName: m.StoreMap[k1],
ErpCommodityId: v.ID,
ErpCommodityName: v.Name,
ErpCategoryId: v.ErpCategoryId,
ErpCategoryName: v.ErpCategoryName,
CommoditySerialNumber: v.SerialNumber,
IMEIType: v.IMEIType,
RetailPrice: v.RetailPrice,
MinRetailPrice: v.MinRetailPrice,
Count: v2,
DispatchCount: 0,
}
err = gdb.Create(stock).Error
if err != nil {
//logger.Error("create stock err:", err)
return err
}
}
}
if ok && v != nil {
inventoryStock := &ErpInventoryStock{
StoreId: k1,
StoreName: m.StoreMap[k1],
ErpCommodityId: v.ID,
ErpCommodityName: v.Name,
ErpCategoryId: v.ErpCategoryId,
ErpCategoryName: v.ErpCategoryName,
CommoditySerialNumber: v.SerialNumber,
IMEIType: v.IMEIType,
RetailPrice: v.RetailPrice,
MinRetailPrice: v.MinRetailPrice,
Count: v2,
}
fmt.Println("inventoryStock", inventoryStock.Count)
m.Inventories = append(m.Inventories, inventoryStock)
}
}
}
return nil
}
func (m *StockImporter) ErpInventoryStockCreate(gdb *gorm.DB, list []ErpStockFileExcel) 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(inventory).Error
err := orm.Eloquent.Create(inventory).Error
if err != nil {
//logger.Error("create erp inventory 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
}
//go func() {
// //inventoryStockIdMap := make(map[string]uint32, 0)
// //for _, inventory := range m.Inventories {
// // //err := gdb.Create(inventory).Error
// // err := orm.Eloquent.Create(inventory).Error
// // if err != nil {
// // logger.Error("create erp inventory stock err:", err)
// // return
// // }
// // inventoryStockIdMap[fmt.Sprintf("%d_%d", inventory.StoreId, inventory.ErpCommodityId)] = inventory.ID
// //}
//
// //stock := ErpStockFileExcel{}
// //ErpInventoryStock
// 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,
// SerialNumber: list[i].SerialNumber,
// 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
// err := orm.Eloquent.Create(inventoryCommodity).Error
// if err != nil {
// logger.Error("create erp inventory stock commodity err:", err)
// return
// }
// }
//
// }
//}()
return nil
}
func ErpStockCommodityToInventory(inventoryStockIdMap map[string]uint32, list []ErpStockFileExcel) []*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 inventory stock commodity err:", err)
// return inventoryList
//}
}
}
return inventoryList
}
func ErpCommodityListExport(list []ErpCommodity) (string, error) {
file := excelize.NewFile()
streamWriter, err := file.NewStreamWriter("Sheet1")
if err != nil {
fmt.Println(err)
}
url := "http://39.108.188.218:8000/img/export/"
fileName := time.Now().Format(TimeFormat) + "商品" + ".xlsx"
//title := []interface{}{"供应商编号", "供应商名称", "联系人", "手机号", "地址", "开户银行", "银行账号", "付款周期/天"}
title := []interface{}{"商品编号", "商品名称", "商品分类", "是否串码", "主供应商", "零售价", "最低零售价", "员工成本价",
"采购价", "提成等级1", "提成等级2", "产地", "备注", "会员折扣(零售价的百分比)"}
cell, _ := excelize.CoordinatesToCellName(1, 1)
if err = streamWriter.SetRow(cell, title); err != nil {
fmt.Println(err)
}
var row []interface{}
for rowId := 0; rowId < len(list); rowId++ {
isIMEI := "否"
if list[rowId].IMEIType == 2 {
isIMEI = "是"
}
row = []interface{}{list[rowId].SerialNumber, list[rowId].Name, list[rowId].ErpCategoryName,
isIMEI, list[rowId].ErpSupplierName, list[rowId].RetailPrice,
list[rowId].MinRetailPrice, list[rowId].StaffCostPrice, list[rowId].WholesalePrice, list[rowId].Brokerage1,
list[rowId].Brokerage2, list[rowId].Origin, list[rowId].Remark, list[rowId].MemberDiscount}
cell, _ := excelize.CoordinatesToCellName(1, rowId+2)
if err := streamWriter.SetRow(cell, row); err != nil {
fmt.Println(err)
}
}
if err := streamWriter.Flush(); err != nil {
fmt.Println(err)
}
if err := file.SaveAs("/www/server/images/export/" + fileName); err != nil {
//if err := file.SaveAs("./" + fileName); err != nil {
fmt.Println(err)
}
return url + fileName, nil
}
func ErpCategoryListExport(list []ErpCategory) (string, error) {
file := excelize.NewFile()
streamWriter, err := file.NewStreamWriter("Sheet1")
if err != nil {
fmt.Println(err)
}
url := "http://39.108.188.218:8000/img/export/"
fileName := time.Now().Format(TimeFormat) + "分类列表数据" + ".xlsx"
//title := []interface{}{"门店", "用户ID", "订单编号", "下单时间", "卡带", "说明", "回收价", "闲麦价", "审核时间", "审核人", "操作", "复核时间", "复核状态"}
title := []interface{}{"一级分类", "二级分类"}
cell, _ := excelize.CoordinatesToCellName(1, 1)
if err = streamWriter.SetRow(cell, title); err != nil {
fmt.Println(err)
}
rowIdx := 2
var row []interface{}
for rowId := 0; rowId < len(list); rowId++ {
cats := list[rowId].SubCats
for i := 0; i < len(cats); i++ {
rowName := list[rowId].Name
if i != 0 {
rowName = ""
}
row = []interface{}{rowName, cats[i].Name}
cell, _ := excelize.CoordinatesToCellName(1, rowIdx)
if err := streamWriter.SetRow(cell, row); err != nil {
fmt.Println(err)
}
rowIdx++
}
}
if err := streamWriter.Flush(); err != nil {
fmt.Println(err)
}
if err := file.SaveAs("/www/server/images/export/" + fileName); err != nil {
//if err := file.SaveAs("./" + fileName); err != nil {
fmt.Println(err)
}
return url + fileName, nil
}
type ErpStockListReq struct {
SerialNumber string `json:"serial_number"` // 商品编号
CommodityName string `json:"commodity_name"` // 商品名称
ErpCategoryId uint32 `json:"erp_category_id"` // 商品分类
StockType uint32 `json:"stock_type"` // 库存情况:1-全部 2-有库存 3-无库存
StoreId uint32 `json:"store_id"` // 门店编号
PageNum int `json:"page_num"` // 页面条数
PageSize int `json:"page_size"` // 页码
//IsExport uint32 `json:"is_export"` // 1-导出
}
type ErpStockListResp struct {
List []ErpStock `json:"list"`
Total int `json:"total"`
PageNum int `json:"page_num"`
PageSize int `json:"page_size"`
ExportUrl string `json:"export_url"`
}
func (m *ErpStockListReq) List() (*ErpStockListResp, error) {
resp := &ErpStockListResp{
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_stock")
if m.SerialNumber != "" {
qs = qs.Where("commodity_serial_number=?", m.SerialNumber)
}
if m.CommodityName != "" {
qs = qs.Where("name Like %" + m.CommodityName + "%")
}
if m.ErpCategoryId != 0 {
qs = qs.Where("erp_category_id=?", m.ErpCategoryId)
}
if m.StoreId != 0 {
qs = qs.Where("store_id=?", m.StoreId)
}
switch m.StockType {
case 2:
qs = qs.Where("count > 0")
case 3:
qs = qs.Where("count = 0")
}
var count int64
if err := qs.Count(&count).Error; err != nil {
//logger.Error("count err:", err)
return resp, err
}
resp.Total = int(count)/m.PageSize + 1
var commodities []ErpStock
err := qs.Order("id DESC").Offset(page * m.PageSize).Limit(m.PageSize).Find(&commodities).Error
if err != nil && !errors.Is(err, RecordNotFound) {
//logger.Error("erp commodity list err:", err)
return resp, err
}
resp.List = commodities
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
}
}
func ErpStockCommodityListSetCommodity(commodities []ErpStockCommodity) {
nowTime := time.Now()
for i, _ := range commodities {
commodities[i].Age = uint32(nowTime.Sub(commodities[i].StockTime).Hours()) / 24
commodities[i].AllAge = uint32(nowTime.Sub(commodities[i].FirstStockTime).Hours()) / 24
}
}
// ErpStockCommodityListReq 库存详情接口请求参数
type ErpStockCommodityListReq struct {
ErpStockId uint32 `json:"erp_stock_id"` // 库存id
ErpCommodityId uint32 `json:"erp_commodity_id"` // 商品id
SerialNumber string `json:"serial_number"` // 商品编号
CommodityName string `json:"commodity_name"` // 商品名称
ErpCategoryId uint32 `json:"erp_category_id"` // 商品分类Id
IMEI string `json:"imei"` // 串码
StoreId uint32 `json:"store_id"` // 门店编号
SupplierId uint32 `json:"supplier_id"` // 供应商id
State uint32 `json:"state"` // 库存状态:1-在库 2-已售 3-采购退货 4-调拨中
Sn string `json:"sn"` // 首次入库订单编号
StorageType uint32 `json:"storage_type"` // 首次入库方式1-系统入库 2-采购入库
StockTimeStart string `json:"stock_time_start"` // 最近入库开始时间
StockTimeEnd string `json:"stock_time_end"` // 最近入库结束时间
Age uint32 `json:"age"` // 最近库龄
AllAge uint32 `json:"all_age"` // 总库龄
PageNum int `json:"page_num"` // 页面条数
PageSize int `json:"page_size"` // 页码
IsExport uint32 `json:"is_export"` // 是否导出excel1-导出
}
type ErpStockCommodityListResp struct {
List []ErpStockCommodity `json:"list"`
Total int `json:"total"`
PageNum int `json:"page_num"`
PageSize int `json:"page_size"`
ExportUrl string `json:"export_url"`
}
func (m *ErpStockCommodityListReq) GetDetailList() (*ErpStockCommodityListResp, error) {
resp := &ErpStockCommodityListResp{
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_stock_commodity").Order("id DESC")
// 构建查询条件
m.buildQueryConditions(qs)
var count int64
if err := qs.Count(&count).Error; err != nil {
//logger.Error("count err:", err)
return resp, err
}
resp.Total = int(count)/m.PageSize + 1
//获取库存商品列表
var commodities []ErpStockCommodity
if m.IsExport == 1 {
err := qs.Find(&commodities).Error
if err != nil && !errors.Is(err, RecordNotFound) {
//logger.Error("dailys err:", err)
return resp, err
}
//listExport, err := ErpCommodityListExport(commodities)
//if err != nil {
// logger.Error("list export err:", err)
//}
//resp.ExportUrl = listExport
} else {
err := qs.Offset(page * m.PageSize).Limit(m.PageSize).Find(&commodities).Error
if err != nil && !errors.Is(err, RecordNotFound) {
//logger.Error("erp commodity list err:", err)
return resp, err
}
ErpStockCommodityListSetAge(commodities)
resp.List = commodities
}
return resp, nil
}
// buildQueryConditions 根据请求参数构建查询条件
func (m *ErpStockCommodityListReq) buildQueryConditions(qs *gorm.DB) {
if m.ErpStockId != 0 { //库存id
qs = qs.Where("erp_stock_id=?", m.ErpStockId)
}
if m.ErpCommodityId != 0 { //商品id
qs = qs.Where("erp_commodity_id=?", m.ErpCommodityId)
}
if m.SerialNumber != "" { //商品编号
qs = qs.Where("commodity_serial_number=?", m.SerialNumber)
}
if m.CommodityName != "" { //商品名称
qs = qs.Where("erp_commodity_name LIKE ?", "%"+m.CommodityName+"%")
}
if m.ErpCategoryId != 0 { //商品分类id
qs = qs.Where("erp_category_id=?", m.ErpCategoryId)
}
if m.IMEI != "" { //商品串码
qs = qs.Where("imei=?", m.IMEI)
}
if m.StoreId != 0 { //门店编号
qs = qs.Where("store_id=?", m.StoreId)
}
if m.State != 0 { //库存状态
qs = qs.Where("state=?", m.State)
}
if m.Sn != "" { //首次入库订单编号
qs = qs.Where("stock_sn=?", m.Sn)
}
if m.StorageType != 0 { //首次入库方式
qs = qs.Where("storage_type=?", m.StorageType)
}
if m.StockTimeStart != "" { //最近入库开始时间
startTime, err := time.Parse(DateTimeFormat, m.StockTimeStart)
if err == nil {
qs = qs.Where("first_stock_time>?", startTime)
} else {
//logger.Error("stock time start parse err:", err)
}
}
if m.StockTimeEnd != "" { //最近入库结束时间
endTime, err := time.Parse(DateTimeFormat, m.StockTimeEnd)
if err == nil {
qs = qs.Where("first_stock_time<?", 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)))
}
}