1、优化零售批量导入商品,解决超时问题;
This commit is contained in:
parent
19e1647154
commit
429c5a203a
|
@ -122,7 +122,7 @@ func GetInventoryDetail(c *gin.Context) {
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body models.NewErpStockCommodityListReq true "查询库存详情模型"
|
// @Param request body models.NewErpStockCommodityListReq true "查询库存详情模型"
|
||||||
// @Success 200 {object} models.ErpStockCommodityListResp
|
// @Success 200 {object} models.ErpStockCommodityListResp
|
||||||
// @Router /api/v1/inventory/detail [post]
|
// @Router /api/v1/inventory/detail_new [post]
|
||||||
func GetInventoryDetailNew(c *gin.Context) {
|
func GetInventoryDetailNew(c *gin.Context) {
|
||||||
req := &models.NewErpStockCommodityListReq{}
|
req := &models.NewErpStockCommodityListReq{}
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
|
|
@ -310,7 +310,7 @@ type ErpCommodityListReq struct {
|
||||||
IMEI string `json:"imei"` // 串码
|
IMEI string `json:"imei"` // 串码
|
||||||
ErpBarcode string `json:"erp_barcode"` // 商品条码
|
ErpBarcode string `json:"erp_barcode"` // 商品条码
|
||||||
ErpSupplierId uint32 `json:"erp_supplier_id"` // 供应商id
|
ErpSupplierId uint32 `json:"erp_supplier_id"` // 供应商id
|
||||||
PurchaseType uint32 `json:"purchase_type"` // 0-全部,1-正常采购,2-停止采购
|
PurchaseType uint32 `json:"purchase_type"` // 1-正常采购,2-停止采购
|
||||||
PageIndex int `json:"pageIndex"` // 页码
|
PageIndex int `json:"pageIndex"` // 页码
|
||||||
PageSize int `json:"pageSize"` // 每页展示数据条数
|
PageSize int `json:"pageSize"` // 每页展示数据条数
|
||||||
IsExport uint32 `json:"is_export"` // 1-导出
|
IsExport uint32 `json:"is_export"` // 1-导出
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
@ -612,6 +613,38 @@ func isInputTimeBeforeOrEqualNow(inputTimeString string) (bool, error) {
|
||||||
return !isBeforeOrEqual, nil
|
return !isBeforeOrEqual, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 校验第5列门店名称是否相同
|
||||||
|
func hasDuplicateStore(sheetCols [][]string) (string, bool) {
|
||||||
|
storeMap := make(map[string]struct{})
|
||||||
|
var storeName string
|
||||||
|
|
||||||
|
for row := 1; row < len(sheetCols); row++ {
|
||||||
|
// 获取当前行的门店名称
|
||||||
|
currentStore := sheetCols[4][row] // 第5列门店名称
|
||||||
|
|
||||||
|
if currentStore == "" {
|
||||||
|
continue // 如果门店名称为空,跳过该行
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果这是第一次遇到门店名称,记录下来
|
||||||
|
if storeName == "" {
|
||||||
|
storeName = currentStore
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查当前门店名称是否与之前的名称一致
|
||||||
|
if currentStore != storeName {
|
||||||
|
// 找到不一致的门店名称,返回不一致的门店名称和标记为true
|
||||||
|
return currentStore, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 存储门店名称
|
||||||
|
storeMap[currentStore] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有发现不一致的门店名称,返回空字符串和false
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
// 校验串码是否相同,有则false
|
// 校验串码是否相同,有则false
|
||||||
func hasDuplicateIMEI(sheetCols []string) (string, bool) {
|
func hasDuplicateIMEI(sheetCols []string) (string, bool) {
|
||||||
nameMap := make(map[string]struct{})
|
nameMap := make(map[string]struct{})
|
||||||
|
@ -1360,139 +1393,346 @@ func IsExistingCommodity(commodityId uint32) bool {
|
||||||
// 商品名称和串码是否对应
|
// 商品名称和串码是否对应
|
||||||
// 串码商品数量只能为1
|
// 串码商品数量只能为1
|
||||||
// 是否有库存
|
// 是否有库存
|
||||||
|
//func checkOrderExcel(sheetCols [][]string) error {
|
||||||
|
// if len(sheetCols) != 8 {
|
||||||
|
// return errors.New("模版错误,请检查文件")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// maxLength := findMaxLength(sheetCols)
|
||||||
|
// nLow, nMax := determineLowAndMax(sheetCols)
|
||||||
|
//
|
||||||
|
// if nMax < maxLength {
|
||||||
|
// return errors.New("第" + strconv.Itoa(nMax+1) + "行商品名称和商品编号不能同时为空")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 判断是否有重复串码
|
||||||
|
// if duplicateName, nFlag := hasDuplicateIMEI(sheetCols[2]); nFlag {
|
||||||
|
// return fmt.Errorf("商品串码不允许重复,请检查:[%v]", duplicateName)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //先遍历第1列
|
||||||
|
// for i := 1; i < nLow; i++ {
|
||||||
|
// if sheetCols[0][i] == "" && sheetCols[1][i] == "" { // 商品名称和编号不能都为空
|
||||||
|
// return errors.New("第" + strconv.Itoa(i+1) + "行商品名称和商品编号不能同时为空")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for i := 1; i < nMax; i++ {
|
||||||
|
// if i < len(sheetCols[1]) {
|
||||||
|
// if !IsExistingProduct(sheetCols[1][i]) {
|
||||||
|
// return errors.New("第" + strconv.Itoa(i+1) + "行商品不存在")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 商品编号必须为纯数字
|
||||||
|
// if i < len(sheetCols[0]) {
|
||||||
|
// if sheetCols[0][i] != "" {
|
||||||
|
// if _, err := strconv.Atoi(sheetCols[0][i]); err != nil {
|
||||||
|
// return errors.New("第" + strconv.Itoa(i+1) + "行商品编号必须为纯数字")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if !isExistingProductCode(sheetCols[0][i]) {
|
||||||
|
// return errors.New("第" + strconv.Itoa(i+1) + "行商品编号不存在")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 所属门店不能为空
|
||||||
|
// if i < len(sheetCols[4]) {
|
||||||
|
// if sheetCols[4][i] == "" {
|
||||||
|
// return errors.New("第" + strconv.Itoa(i+1) + "行所属门店不能为空")
|
||||||
|
// }
|
||||||
|
// if !isExistingStore(sheetCols[4][i]) {
|
||||||
|
// return errors.New("第" + strconv.Itoa(i+1) + "行门店不存在")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 数量必须为正整数
|
||||||
|
// if i < len(sheetCols[3]) {
|
||||||
|
// if quantity, err := strconv.Atoi(sheetCols[3][i]); err != nil || quantity < 1 {
|
||||||
|
// return errors.New("第" + strconv.Itoa(i+1) + "行数量必须是大于等于1的整数")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 串码商品数量只能为1
|
||||||
|
// var nCount int
|
||||||
|
// if i < len(sheetCols[3]) {
|
||||||
|
// if i < len(sheetCols[3]) {
|
||||||
|
// nCount, _ = strconv.Atoi(sheetCols[3][i])
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 串码类商品数量只能为1
|
||||||
|
// if sheetCols[2][i] != "" && nCount != 1 {
|
||||||
|
// return errors.New("第" + strconv.Itoa(i+1) + "行串码类商品数量只能为1")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 判断商品是否赠送
|
||||||
|
// if i < len(sheetCols[6]) {
|
||||||
|
// if sheetCols[6][i] != "是" && sheetCols[6][i] != "" {
|
||||||
|
// return errors.New("第" + strconv.Itoa(i+1) + "行商品是否赠送填写有误")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 查询商品是否有库存
|
||||||
|
// if sheetCols[2][i] != "" { // 串码商品
|
||||||
|
// var imeiStockCommodity ErpStockCommodity
|
||||||
|
// err := orm.Eloquent.Table("erp_stock_commodity").Where("imei = ? and state = ?",
|
||||||
|
// sheetCols[2][i], InStock).
|
||||||
|
// Find(&imeiStockCommodity).Error
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// if imeiStockCommodity.ID == 0 {
|
||||||
|
// return errors.New("第" + strconv.Itoa(i+1) + "行商品不存在")
|
||||||
|
// }
|
||||||
|
// // 门店对比
|
||||||
|
// if imeiStockCommodity.StoreName != sheetCols[4][i] {
|
||||||
|
// return errors.New("第" + strconv.Itoa(i+1) + "行商品" +
|
||||||
|
// "[" + imeiStockCommodity.ErpCommodityName + "]非所选门店库存,请检查")
|
||||||
|
// }
|
||||||
|
// // 零售价对比
|
||||||
|
// var floatVal float64
|
||||||
|
// if i < len(sheetCols[5]) {
|
||||||
|
// if sheetCols[5][i] != "" {
|
||||||
|
// floatVal, err = strconv.ParseFloat(sheetCols[5][i], 64)
|
||||||
|
// if err != nil {
|
||||||
|
// return errors.New("第" + strconv.Itoa(i+1) + "行零售价有误")
|
||||||
|
// }
|
||||||
|
// strMin := strconv.FormatFloat(imeiStockCommodity.MinRetailPrice, 'f', 2, 64)
|
||||||
|
// if floatVal < imeiStockCommodity.MinRetailPrice {
|
||||||
|
// return errors.New("第" + strconv.Itoa(i+1) + "行零售价有误,不能低于最低零售价[" + strMin + "]")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else { // 非串码商品
|
||||||
|
// var count int64
|
||||||
|
// var err error
|
||||||
|
// if sheetCols[0][i] != "" { // 商品编号不为空
|
||||||
|
// err = orm.Eloquent.Table("erp_stock_commodity").
|
||||||
|
// Where("commodity_serial_number = ? and store_name = ? and state = ? and imei_type = ?",
|
||||||
|
// sheetCols[0][i], sheetCols[4][i], InStock, NoIMEICommodity).
|
||||||
|
// Count(&count).Error
|
||||||
|
// } else {
|
||||||
|
// err = orm.Eloquent.Table("erp_stock_commodity").
|
||||||
|
// Where("erp_commodity_name = ? and store_name = ? and state = ? and imei_type = ?",
|
||||||
|
// sheetCols[1][i], sheetCols[4][i], InStock, NoIMEICommodity).
|
||||||
|
// Count(&count).Error
|
||||||
|
// }
|
||||||
|
// if err != nil {
|
||||||
|
// return errors.New("第" + strconv.Itoa(i+1) + "行商品不存在")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if count < int64(nCount) {
|
||||||
|
// // 获取商品名称
|
||||||
|
// return errors.New("第" + strconv.Itoa(i+1) + "行商品" + "[" + sheetCols[1][i] + "]库存不足")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return nil
|
||||||
|
//}
|
||||||
|
|
||||||
func checkOrderExcel(sheetCols [][]string) error {
|
func checkOrderExcel(sheetCols [][]string) error {
|
||||||
|
// 校验列数是否正确
|
||||||
if len(sheetCols) != 8 {
|
if len(sheetCols) != 8 {
|
||||||
return errors.New("模版错误,请检查文件")
|
return errors.New("模版错误,请检查文件")
|
||||||
}
|
}
|
||||||
|
|
||||||
maxLength := findMaxLength(sheetCols)
|
// 获取最大行数
|
||||||
nLow, nMax := determineLowAndMax(sheetCols)
|
nMax := findMaxLength(sheetCols)
|
||||||
|
if nMax == 0 {
|
||||||
if nMax < maxLength {
|
return errors.New("模版内容为空")
|
||||||
return errors.New("第" + strconv.Itoa(nMax+1) + "行商品名称和商品编号不能同时为空")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断是否有重复串码
|
// 校验商品串码是否重复
|
||||||
if duplicateName, nFlag := hasDuplicateIMEI(sheetCols[2]); nFlag {
|
if duplicateName, nFlag := hasDuplicateIMEI(sheetCols[2]); nFlag {
|
||||||
return fmt.Errorf("商品串码不允许重复,请检查:[%v]", duplicateName)
|
return fmt.Errorf("商品串码不允许重复,请检查:[%v]", duplicateName)
|
||||||
}
|
}
|
||||||
|
|
||||||
//先遍历第1列
|
// 判断门店名称是否有不同
|
||||||
for i := 1; i < nLow; i++ {
|
if duplicateName, nFlag := hasDuplicateStore(sheetCols); nFlag {
|
||||||
if sheetCols[0][i] == "" && sheetCols[1][i] == "" { // 商品名称和编号不能都为空
|
return fmt.Errorf("门店名称不同,请检查:[%v]", duplicateName)
|
||||||
return errors.New("第" + strconv.Itoa(i+1) + "行商品名称和商品编号不能同时为空")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 缓存商品编号、商品名称和门店查询结果
|
||||||
|
productCodeCache := make(map[string]bool)
|
||||||
|
productNameCache := make(map[string]bool)
|
||||||
|
storeCache := make(map[string]bool)
|
||||||
|
|
||||||
|
// 预加载所有商品编号、商品名称和门店
|
||||||
|
loadAllProductsIntoCache(productCodeCache, productNameCache)
|
||||||
|
loadAllStoresIntoCache(storeCache)
|
||||||
|
|
||||||
|
// 通道用于并发校验
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
errCh := make(chan error, 10)
|
||||||
|
|
||||||
|
// 限制最大并发数量
|
||||||
|
maxConcurrency := 10
|
||||||
|
sem := make(chan struct{}, maxConcurrency)
|
||||||
|
|
||||||
for i := 1; i < nMax; i++ {
|
for i := 1; i < nMax; i++ {
|
||||||
if i < len(sheetCols[1]) {
|
// 校验行是否越界
|
||||||
if !IsExistingProduct(sheetCols[1][i]) {
|
if !isRowValid(sheetCols, i) {
|
||||||
return errors.New("第" + strconv.Itoa(i+1) + "行商品不存在")
|
continue
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 商品编号必须为纯数字
|
// 等待一个空位
|
||||||
if i < len(sheetCols[0]) {
|
sem <- struct{}{}
|
||||||
if sheetCols[0][i] != "" {
|
wg.Add(1)
|
||||||
if _, err := strconv.Atoi(sheetCols[0][i]); err != nil {
|
go func(row int) {
|
||||||
return errors.New("第" + strconv.Itoa(i+1) + "行商品编号必须为纯数字")
|
defer wg.Done()
|
||||||
|
defer func() { <-sem }() // 完成任务后释放空位
|
||||||
|
|
||||||
|
if err := validateRow(sheetCols, row, productCodeCache, productNameCache, storeCache); err != nil {
|
||||||
|
errCh <- fmt.Errorf("第 %d 行: %v", row+1, err)
|
||||||
}
|
}
|
||||||
|
}(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isExistingProductCode(sheetCols[0][i]) {
|
wg.Wait()
|
||||||
return errors.New("第" + strconv.Itoa(i+1) + "行商品编号不存在")
|
close(errCh)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 所属门店不能为空
|
// 返回第一个错误
|
||||||
if i < len(sheetCols[4]) {
|
for err := range errCh {
|
||||||
if sheetCols[4][i] == "" {
|
|
||||||
return errors.New("第" + strconv.Itoa(i+1) + "行所属门店不能为空")
|
|
||||||
}
|
|
||||||
if !isExistingStore(sheetCols[4][i]) {
|
|
||||||
return errors.New("第" + strconv.Itoa(i+1) + "行门店不存在")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 数量必须为正整数
|
|
||||||
if i < len(sheetCols[3]) {
|
|
||||||
if quantity, err := strconv.Atoi(sheetCols[3][i]); err != nil || quantity < 1 {
|
|
||||||
return errors.New("第" + strconv.Itoa(i+1) + "行数量必须是大于等于1的整数")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 串码商品数量只能为1
|
|
||||||
var nCount int
|
|
||||||
if i < len(sheetCols[3]) {
|
|
||||||
if i < len(sheetCols[3]) {
|
|
||||||
nCount, _ = strconv.Atoi(sheetCols[3][i])
|
|
||||||
}
|
|
||||||
|
|
||||||
// 串码类商品数量只能为1
|
|
||||||
if sheetCols[2][i] != "" && nCount != 1 {
|
|
||||||
return errors.New("第" + strconv.Itoa(i+1) + "行串码类商品数量只能为1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断商品是否赠送
|
|
||||||
if i < len(sheetCols[6]) {
|
|
||||||
if sheetCols[6][i] != "是" && sheetCols[6][i] != "" {
|
|
||||||
return errors.New("第" + strconv.Itoa(i+1) + "行商品是否赠送填写有误")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询商品是否有库存
|
|
||||||
if sheetCols[2][i] != "" { // 串码商品
|
|
||||||
var imeiStockCommodity ErpStockCommodity
|
|
||||||
err := orm.Eloquent.Table("erp_stock_commodity").Where("imei = ? and state = ?",
|
|
||||||
sheetCols[2][i], InStock).
|
|
||||||
Find(&imeiStockCommodity).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if imeiStockCommodity.ID == 0 {
|
|
||||||
return errors.New("第" + strconv.Itoa(i+1) + "行商品不存在")
|
validateStock(sheetCols)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查第 row 行是否所有列都有有效数据
|
||||||
|
func isRowValid(sheetCols [][]string, row int) bool {
|
||||||
|
for _, col := range sheetCols {
|
||||||
|
if row >= len(col) { // 行数超出当前列的长度
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
// 门店对比
|
|
||||||
if imeiStockCommodity.StoreName != sheetCols[4][i] {
|
|
||||||
return errors.New("第" + strconv.Itoa(i+1) + "行商品" +
|
|
||||||
"[" + imeiStockCommodity.ErpCommodityName + "]非所选门店库存,请检查")
|
|
||||||
}
|
}
|
||||||
// 零售价对比
|
return true
|
||||||
var floatVal float64
|
}
|
||||||
if i < len(sheetCols[5]) {
|
|
||||||
if sheetCols[5][i] != "" {
|
// 批量加载商品信息到缓存
|
||||||
floatVal, err = strconv.ParseFloat(sheetCols[5][i], 64)
|
func loadAllProductsIntoCache(productCodeCache, productNameCache map[string]bool) {
|
||||||
|
var products []ErpCommodity
|
||||||
|
orm.Eloquent.Debug().Find(&products)
|
||||||
|
for _, product := range products {
|
||||||
|
productCodeCache[product.SerialNumber] = true
|
||||||
|
productNameCache[product.Name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量加载门店信息到缓存
|
||||||
|
func loadAllStoresIntoCache(storeCache map[string]bool) {
|
||||||
|
var stores []Store
|
||||||
|
orm.Eloquent.Debug().Find(&stores)
|
||||||
|
for _, store := range stores {
|
||||||
|
storeCache[store.Name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验单行数据
|
||||||
|
func validateRow(sheetCols [][]string, row int, productCodeCache, productNameCache, storeCache map[string]bool) error {
|
||||||
|
fmt.Println("row is:", row)
|
||||||
|
// 校验商品名称和商品编号
|
||||||
|
if sheetCols[0][row] == "" && sheetCols[1][row] == "" {
|
||||||
|
return errors.New("商品名称和商品编号不能同时为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验商品编号是否存在
|
||||||
|
if sheetCols[0][row] != "" {
|
||||||
|
if _, err := strconv.Atoi(sheetCols[0][row]); err != nil {
|
||||||
|
return errors.New("商品编号必须为纯数字")
|
||||||
|
}
|
||||||
|
if !productCodeCache[sheetCols[0][row]] {
|
||||||
|
return errors.New("商品编号不存在")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验商品名称是否存在
|
||||||
|
if sheetCols[1][row] != "" && !productNameCache[sheetCols[1][row]] {
|
||||||
|
return errors.New("商品名称不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验所属门店是否存在
|
||||||
|
if sheetCols[4][row] == "" || !storeCache[sheetCols[4][row]] {
|
||||||
|
return errors.New("所属门店不能为空或不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验数量是否为正整数
|
||||||
|
if quantity, err := strconv.Atoi(sheetCols[3][row]); err != nil || quantity < 1 {
|
||||||
|
return errors.New("数量必须是大于等于1的整数")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验串码商品数量只能为1
|
||||||
|
if sheetCols[2][row] != "" && sheetCols[3][row] != "1" {
|
||||||
|
return errors.New("串码类商品数量只能为1")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验商品是否赠送
|
||||||
|
if sheetCols[6][row] != "" && sheetCols[6][row] != "是" {
|
||||||
|
return errors.New("商品是否赠送填写有误")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateStock 校验库存信息
|
||||||
|
func validateStock(sheetCols [][]string) error {
|
||||||
|
// 预加载所有库存数据,按门店 store_id 过滤
|
||||||
|
var imeiStockCommodities []ErpStockCommodity
|
||||||
|
err := orm.Eloquent.Table("erp_stock_commodity").
|
||||||
|
Where("state = ? AND store_name = ?", InStock, sheetCols[4][1]).
|
||||||
|
Find(&imeiStockCommodities).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("第" + strconv.Itoa(i+1) + "行零售价有误")
|
return fmt.Errorf("加载库存数据失败: %v", err)
|
||||||
}
|
}
|
||||||
strMin := strconv.FormatFloat(imeiStockCommodity.MinRetailPrice, 'f', 2, 64)
|
|
||||||
if floatVal < imeiStockCommodity.MinRetailPrice {
|
// 构建库存缓存:按 imei 和非串码商品分组
|
||||||
return errors.New("第" + strconv.Itoa(i+1) + "行零售价有误,不能低于最低零售价[" + strMin + "]")
|
imeiStockMap := make(map[string]ErpStockCommodity) // 串码商品缓存
|
||||||
|
nonIMEIStockMap := make(map[string]map[string]int64) // 非串码商品库存,按 store -> commodity 分组
|
||||||
|
for _, stock := range imeiStockCommodities {
|
||||||
|
if stock.IMEI != "" {
|
||||||
|
imeiStockMap[stock.IMEI] = stock
|
||||||
|
} else {
|
||||||
|
if _, exists := nonIMEIStockMap[stock.StoreName]; !exists {
|
||||||
|
nonIMEIStockMap[stock.StoreName] = make(map[string]int64)
|
||||||
}
|
}
|
||||||
|
key := stock.CommoditySerialNumber
|
||||||
|
if key == "" {
|
||||||
|
key = stock.ErpCommodityName
|
||||||
|
}
|
||||||
|
nonIMEIStockMap[stock.StoreName][key]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验库存信息
|
||||||
|
for row := 1; row < len(sheetCols[0]); row++ {
|
||||||
|
if sheetCols[2][row] != "" { // 串码商品
|
||||||
|
stock, exists := imeiStockMap[sheetCols[2][row]]
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("第 %d 行: 串码商品不存在或无库存", row+1)
|
||||||
|
}
|
||||||
|
if stock.StoreName != sheetCols[4][row] {
|
||||||
|
return fmt.Errorf("第 %d 行: 商品[%s]非所选门店库存,请检查", row+1, stock.ErpCommodityName)
|
||||||
|
}
|
||||||
|
if sheetCols[5][row] != "" {
|
||||||
|
price, err := strconv.ParseFloat(sheetCols[5][row], 64)
|
||||||
|
if err != nil || price < stock.MinRetailPrice {
|
||||||
|
return fmt.Errorf("第 %d 行: 零售价有误,不能低于最低零售价[%.2f]", row+1, stock.MinRetailPrice)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // 非串码商品
|
} else { // 非串码商品
|
||||||
var count int64
|
storeName := sheetCols[4][row]
|
||||||
var err error
|
commodityKey := sheetCols[0][row]
|
||||||
if sheetCols[0][i] != "" { // 商品编号不为空
|
if commodityKey == "" {
|
||||||
err = orm.Eloquent.Table("erp_stock_commodity").
|
commodityKey = sheetCols[1][row]
|
||||||
Where("commodity_serial_number = ? and store_name = ? and state = ? and imei_type = ?",
|
|
||||||
sheetCols[0][i], sheetCols[4][i], InStock, NoIMEICommodity).
|
|
||||||
Count(&count).Error
|
|
||||||
} else {
|
|
||||||
err = orm.Eloquent.Table("erp_stock_commodity").
|
|
||||||
Where("erp_commodity_name = ? and store_name = ? and state = ? and imei_type = ?",
|
|
||||||
sheetCols[1][i], sheetCols[4][i], InStock, NoIMEICommodity).
|
|
||||||
Count(&count).Error
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
storeStock, storeExists := nonIMEIStockMap[storeName]
|
||||||
return errors.New("第" + strconv.Itoa(i+1) + "行商品不存在")
|
if !storeExists || storeStock[commodityKey] < 1 {
|
||||||
}
|
return fmt.Errorf("第 %d 行: 商品[%s]库存不足", row+1, sheetCols[1][row])
|
||||||
|
|
||||||
if count < int64(nCount) {
|
|
||||||
// 获取商品名称
|
|
||||||
return errors.New("第" + strconv.Itoa(i+1) + "行商品" + "[" + sheetCols[1][i] + "]库存不足")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1500,6 +1740,37 @@ func checkOrderExcel(sheetCols [][]string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//// 校验库存信息
|
||||||
|
//func validateStock(sheetCols [][]string, row int) error {
|
||||||
|
// if sheetCols[2][row] != "" { // 串码商品
|
||||||
|
// var imeiStockCommodity ErpStockCommodity
|
||||||
|
// err := orm.Eloquent.Table("erp_stock_commodity").Where("imei = ? AND state = ?",
|
||||||
|
// sheetCols[2][row], InStock).Find(&imeiStockCommodity).Error
|
||||||
|
// if err != nil || imeiStockCommodity.ID == 0 {
|
||||||
|
// return errors.New("串码商品不存在或无库存")
|
||||||
|
// }
|
||||||
|
// if imeiStockCommodity.StoreName != sheetCols[4][row] {
|
||||||
|
// return fmt.Errorf("商品[%s]非所选门店库存,请检查", imeiStockCommodity.ErpCommodityName)
|
||||||
|
// }
|
||||||
|
// if sheetCols[5][row] != "" {
|
||||||
|
// if price, err := strconv.ParseFloat(sheetCols[5][row], 64); err != nil || price < imeiStockCommodity.MinRetailPrice {
|
||||||
|
// return fmt.Errorf("零售价有误,不能低于最低零售价[%.2f]", imeiStockCommodity.MinRetailPrice)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else { // 非串码商品
|
||||||
|
// var count int64
|
||||||
|
// err := orm.Eloquent.Table("erp_stock_commodity").
|
||||||
|
// Where("(commodity_serial_number = ? OR erp_commodity_name = ?) AND store_name = ? AND state = ? AND imei_type = ?",
|
||||||
|
// sheetCols[0][row], sheetCols[1][row], sheetCols[4][row], InStock, NoIMEICommodity).
|
||||||
|
// Count(&count).Error
|
||||||
|
// if err != nil || count < 1 {
|
||||||
|
// return fmt.Errorf("商品[%s]库存不足", sheetCols[1][row])
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return nil
|
||||||
|
//}
|
||||||
|
|
||||||
// 将读取的excel数据转换成OrderExcel struct
|
// 将读取的excel数据转换成OrderExcel struct
|
||||||
func transOrderData(colsMap []map[string]interface{}) ([]OrderExcel, error) {
|
func transOrderData(colsMap []map[string]interface{}) ([]OrderExcel, error) {
|
||||||
var stockInfos []OrderExcel
|
var stockInfos []OrderExcel
|
||||||
|
@ -1593,7 +1864,7 @@ func ImportOrderData(colsMap []map[string]interface{}) ([]ErpOrderCommodity, err
|
||||||
orderCommodity.RetailPrice = imeiStockCommodity.RetailPrice
|
orderCommodity.RetailPrice = imeiStockCommodity.RetailPrice
|
||||||
orderCommodity.SalePrice = floatVal
|
orderCommodity.SalePrice = floatVal
|
||||||
orderCommodity.ReceivedAmount = floatVal
|
orderCommodity.ReceivedAmount = floatVal
|
||||||
orderCommodity.SaleDiscount = floatVal - imeiStockCommodity.RetailPrice
|
orderCommodity.SaleDiscount = imeiStockCommodity.RetailPrice - floatVal
|
||||||
orderCommodity.WholesalePrice = imeiStockCommodity.WholesalePrice
|
orderCommodity.WholesalePrice = imeiStockCommodity.WholesalePrice
|
||||||
orderCommodity.StaffCostPrice = imeiStockCommodity.StaffCostPrice
|
orderCommodity.StaffCostPrice = imeiStockCommodity.StaffCostPrice
|
||||||
orderCommodity.MemberDiscount = imeiStockCommodity.MemberDiscount
|
orderCommodity.MemberDiscount = imeiStockCommodity.MemberDiscount
|
||||||
|
@ -1671,7 +1942,7 @@ func ImportOrderData(colsMap []map[string]interface{}) ([]ErpOrderCommodity, err
|
||||||
orderCommodity.RetailPrice = imeiStockCommodities[0].RetailPrice
|
orderCommodity.RetailPrice = imeiStockCommodities[0].RetailPrice
|
||||||
orderCommodity.SalePrice = floatVal
|
orderCommodity.SalePrice = floatVal
|
||||||
orderCommodity.ReceivedAmount = floatVal
|
orderCommodity.ReceivedAmount = floatVal
|
||||||
orderCommodity.SaleDiscount = floatVal - imeiStockCommodities[0].RetailPrice
|
orderCommodity.SaleDiscount = imeiStockCommodities[0].RetailPrice - floatVal
|
||||||
orderCommodity.WholesalePrice = imeiStockCommodities[0].WholesalePrice
|
orderCommodity.WholesalePrice = imeiStockCommodities[0].WholesalePrice
|
||||||
orderCommodity.StaffCostPrice = imeiStockCommodities[0].StaffCostPrice
|
orderCommodity.StaffCostPrice = imeiStockCommodities[0].StaffCostPrice
|
||||||
orderCommodity.MemberDiscount = imeiStockCommodities[0].MemberDiscount
|
orderCommodity.MemberDiscount = imeiStockCommodities[0].MemberDiscount
|
||||||
|
|
Loading…
Reference in New Issue
Block a user