2023-11-09 10:26:19 +00:00
|
|
|
|
package models
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
2024-03-16 08:19:12 +00:00
|
|
|
|
"database/sql"
|
2023-11-09 10:26:19 +00:00
|
|
|
|
"encoding/json"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"github.com/xuri/excelize/v2"
|
2023-11-10 09:49:44 +00:00
|
|
|
|
"go-admin/app/admin/models/tools"
|
|
|
|
|
orm "go-admin/common/global"
|
2024-02-23 10:06:21 +00:00
|
|
|
|
"go-admin/logger"
|
2024-04-17 01:37:02 +00:00
|
|
|
|
tool "go-admin/tools"
|
2023-11-10 09:49:44 +00:00
|
|
|
|
"gorm.io/gorm"
|
2023-11-09 10:26:19 +00:00
|
|
|
|
"reflect"
|
2023-11-10 09:49:44 +00:00
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
2023-11-23 12:38:11 +00:00
|
|
|
|
"time"
|
|
|
|
|
"unicode"
|
2023-11-09 10:26:19 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type CategoryExcel struct {
|
|
|
|
|
FirstCategory string `json:"first_category" binding:"required"` // 一级分类
|
|
|
|
|
SecondCategory string `json:"second_category"` // 二级分类
|
|
|
|
|
ThreeCategory string `json:"three_category"` // 三级分类
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type CommodityExcel struct {
|
|
|
|
|
Category string `json:"category" binding:"required"` // 商品所属分类
|
|
|
|
|
Name string `json:"name" binding:"required"` // 商品名称
|
2024-01-06 08:48:35 +00:00
|
|
|
|
ErpBarcode string `json:"erp_barcode"` // 商品条码
|
2023-11-09 10:26:19 +00:00
|
|
|
|
IMEIType string `json:"imei_type" binding:"required"` // 是否串码 1-无串码 2-串码(系统生成) 3-串码(手动添加)
|
|
|
|
|
SysGenerate string `json:"sys_generate" binding:"required"` // 系统生成串码
|
|
|
|
|
SupplierName string `json:"supplier_name" binding:"required"` // 供应商名称
|
|
|
|
|
SellBrokerage string `json:"sell_brokerage" binding:"required"` // 销售毛利提成
|
|
|
|
|
StaffBrokerage string `json:"staff_brokerage" binding:"required"` // 员工毛利提成
|
|
|
|
|
MemberDiscount string `json:"member_discount" binding:"required"` // 会员优惠
|
|
|
|
|
RetailPrice string `json:"retail_price" binding:"required"` // 指导零售价
|
|
|
|
|
MinRetailPrice string `json:"min_retail_price" binding:"required"` // 最低零售价
|
|
|
|
|
WholesalePrice string `json:"wholesale_price" binding:"required"` // 指导采购价
|
|
|
|
|
StaffCostPrice string `json:"staff_cost_price" binding:"required"` // 员工成本价加价
|
|
|
|
|
Origin string `json:"origin"` // 产地
|
|
|
|
|
Remark string `json:"remark"` // 备注
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-23 12:38:11 +00:00
|
|
|
|
type StockExcel struct {
|
|
|
|
|
Name string `json:"name"` // 商品名称
|
|
|
|
|
SerialNum string `json:"serial_num"` // 商品编号
|
|
|
|
|
StoreName string `json:"store_name" binding:"required"` // 所属门店
|
|
|
|
|
SysGenerate string `json:"sys_generate"` // 系统生成串码
|
|
|
|
|
WholesalePrice string `json:"wholesale_price" binding:"required"` // 指导采购价
|
|
|
|
|
StaffCostPrice string `json:"staff_cost_price" binding:"required"` // 员工成本价=员工成本价加价+指导采购价
|
|
|
|
|
SupplierName string `json:"supplier_name" binding:"required"` // 供应商名称
|
|
|
|
|
StockTime string `json:"stock_time"` // 入库时间
|
|
|
|
|
Count string `json:"count" binding:"required"` // 数量
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-09 10:26:19 +00:00
|
|
|
|
// 获取struct的tag标签,匹配excel导入数据
|
|
|
|
|
func getJSONTagNames(s interface{}) []string {
|
|
|
|
|
valueType := reflect.TypeOf(s)
|
|
|
|
|
if valueType.Kind() != reflect.Struct {
|
|
|
|
|
fmt.Println("传入的不是结构体")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var tagNames []string
|
|
|
|
|
for i := 0; i < valueType.NumField(); i++ {
|
|
|
|
|
field := valueType.Field(i)
|
|
|
|
|
tag := field.Tag.Get("json")
|
|
|
|
|
if tag != "" {
|
|
|
|
|
tagNames = append(tagNames, tag)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tagNames
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-15 10:17:35 +00:00
|
|
|
|
// FileExcelReader 预览excel数据(不做必填项校验)
|
2023-11-09 10:26:19 +00:00
|
|
|
|
func FileExcelReader(d []byte, cols []string) ([]byte, []map[string]interface{}, error) {
|
|
|
|
|
reader, err := excelize.OpenReader(bytes.NewReader(d))
|
|
|
|
|
if err != nil {
|
2023-11-10 09:49:44 +00:00
|
|
|
|
return nil, nil, fmt.Errorf("open reader error: %v", err)
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sheetList := reader.GetSheetList()
|
|
|
|
|
if len(sheetList) == 0 {
|
2023-11-10 09:49:44 +00:00
|
|
|
|
return nil, nil, errors.New("sheet list is empty")
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
2023-11-10 09:49:44 +00:00
|
|
|
|
|
2023-11-09 10:26:19 +00:00
|
|
|
|
rows, err := reader.Rows(sheetList[0])
|
|
|
|
|
if err != nil {
|
2023-11-10 09:49:44 +00:00
|
|
|
|
return nil, nil, fmt.Errorf("reader rows error: %v", err)
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
2023-11-10 09:49:44 +00:00
|
|
|
|
|
2023-11-09 10:26:19 +00:00
|
|
|
|
sheetCols, err := reader.GetCols(sheetList[0])
|
|
|
|
|
if err != nil {
|
2023-11-10 09:49:44 +00:00
|
|
|
|
return nil, nil, fmt.Errorf("reader get cols error: %v", err)
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
|
|
|
|
if len(sheetCols) == 0 {
|
2023-11-10 09:49:44 +00:00
|
|
|
|
return nil, nil, errors.New("get cols is empty")
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
2023-11-10 09:49:44 +00:00
|
|
|
|
|
|
|
|
|
var colsMap []map[string]interface{}
|
2023-11-09 10:26:19 +00:00
|
|
|
|
if len(cols) == 0 {
|
2023-11-10 09:49:44 +00:00
|
|
|
|
switch sheetList[0] {
|
|
|
|
|
case "导分类":
|
2023-11-09 10:26:19 +00:00
|
|
|
|
cols = getJSONTagNames(CategoryExcel{})
|
2023-11-10 09:49:44 +00:00
|
|
|
|
case "导商品":
|
2023-11-09 10:26:19 +00:00
|
|
|
|
cols = getJSONTagNames(CommodityExcel{})
|
2023-11-23 12:38:11 +00:00
|
|
|
|
case "导库存":
|
|
|
|
|
cols = getJSONTagNames(StockExcel{})
|
2023-11-10 09:49:44 +00:00
|
|
|
|
default:
|
2023-11-09 10:26:19 +00:00
|
|
|
|
cols = make([]string, len(sheetCols))
|
2023-11-10 09:49:44 +00:00
|
|
|
|
for i := range sheetCols {
|
2023-11-09 10:26:19 +00:00
|
|
|
|
cols[i] = fmt.Sprintf("c%02d", i)
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-10 09:49:44 +00:00
|
|
|
|
} else if len(cols) != len(sheetCols) {
|
|
|
|
|
return nil, nil, errors.New("cols length does not match the number of columns")
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
columns, err := rows.Columns()
|
|
|
|
|
if err != nil {
|
2023-11-10 09:49:44 +00:00
|
|
|
|
return nil, nil, fmt.Errorf("rows columns error: %v", err)
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-10 09:49:44 +00:00
|
|
|
|
columnMap := make(map[string]interface{}, len(cols))
|
|
|
|
|
for i, col := range cols {
|
|
|
|
|
if i < len(columns) {
|
|
|
|
|
columnMap[col] = columns[i]
|
|
|
|
|
} else {
|
|
|
|
|
columnMap[col] = ""
|
|
|
|
|
}
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
2023-11-10 09:49:44 +00:00
|
|
|
|
|
2024-02-23 10:06:21 +00:00
|
|
|
|
logger.Info("columnMap:", logger.Field("columnMap", columnMap))
|
2023-11-09 10:26:19 +00:00
|
|
|
|
colsMap = append(colsMap, columnMap)
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-23 10:06:21 +00:00
|
|
|
|
logger.Info("colsMap:", logger.Field("colsMap", colsMap))
|
2023-11-09 10:26:19 +00:00
|
|
|
|
mCols, err := json.Marshal(colsMap)
|
|
|
|
|
if err != nil {
|
2023-11-10 09:49:44 +00:00
|
|
|
|
return mCols, nil, fmt.Errorf("marshal error: %v", err)
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mCols, colsMap, nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-15 10:17:35 +00:00
|
|
|
|
// FileExcelImport 导入excel数据(校验必填项)
|
|
|
|
|
func FileExcelImport(d []byte, cols []string, nType int) ([]byte, []map[string]interface{}, error) {
|
2023-11-09 10:26:19 +00:00
|
|
|
|
reader, err := excelize.OpenReader(bytes.NewReader(d))
|
|
|
|
|
if err != nil {
|
2023-11-10 09:49:44 +00:00
|
|
|
|
return nil, nil, fmt.Errorf("open reader error: %v", err)
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sheetList := reader.GetSheetList()
|
|
|
|
|
if len(sheetList) == 0 {
|
2023-11-10 09:49:44 +00:00
|
|
|
|
return nil, nil, errors.New("sheet list is empty")
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
2023-11-10 09:49:44 +00:00
|
|
|
|
|
2023-11-24 09:09:24 +00:00
|
|
|
|
rowData, err := reader.GetRows(sheetList[0])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, fmt.Errorf("reader rows error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-09 10:26:19 +00:00
|
|
|
|
rows, err := reader.Rows(sheetList[0])
|
|
|
|
|
if err != nil {
|
2023-11-10 09:49:44 +00:00
|
|
|
|
return nil, nil, fmt.Errorf("reader rows err: %v", err)
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
2023-11-10 09:49:44 +00:00
|
|
|
|
|
2023-11-09 10:26:19 +00:00
|
|
|
|
sheetCols, err := reader.GetCols(sheetList[0])
|
|
|
|
|
if err != nil {
|
2023-11-10 09:49:44 +00:00
|
|
|
|
return nil, nil, fmt.Errorf("reader get cols err: %v", err)
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
2023-11-10 09:49:44 +00:00
|
|
|
|
|
2023-11-09 10:26:19 +00:00
|
|
|
|
if len(sheetCols) == 0 {
|
2023-11-10 09:49:44 +00:00
|
|
|
|
return nil, nil, errors.New("get cols is empty")
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
2023-11-10 09:49:44 +00:00
|
|
|
|
|
2023-11-24 09:09:24 +00:00
|
|
|
|
sheetCols = checkExcelFormat(rowData, sheetCols)
|
|
|
|
|
|
2023-11-10 09:49:44 +00:00
|
|
|
|
var colsMap []map[string]interface{}
|
2023-11-09 10:26:19 +00:00
|
|
|
|
if len(cols) == 0 {
|
2023-11-23 12:38:11 +00:00
|
|
|
|
switch nType { // 导入类型:1 商品分类 2 商品资料 3 商品库存
|
2023-11-10 09:49:44 +00:00
|
|
|
|
case 1:
|
|
|
|
|
if sheetList[0] != "导分类" {
|
|
|
|
|
return nil, nil, errors.New("格式错误,不是分类模版excel")
|
|
|
|
|
}
|
|
|
|
|
if err := checkCategoryExcel(sheetCols); err != nil {
|
2023-11-09 10:26:19 +00:00
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
cols = getJSONTagNames(CategoryExcel{})
|
2023-11-10 09:49:44 +00:00
|
|
|
|
case 2:
|
|
|
|
|
if sheetList[0] != "导商品" {
|
|
|
|
|
return nil, nil, errors.New("格式错误,不是商品模版excel")
|
|
|
|
|
}
|
|
|
|
|
if err := checkCommodityExcel(sheetCols); err != nil {
|
2023-11-09 10:26:19 +00:00
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
cols = getJSONTagNames(CommodityExcel{})
|
2023-11-23 12:38:11 +00:00
|
|
|
|
case 3:
|
|
|
|
|
if sheetList[0] != "导库存" {
|
|
|
|
|
return nil, nil, errors.New("格式错误,不是库存模版excel")
|
|
|
|
|
}
|
|
|
|
|
if err := checkStockExcel(sheetCols); err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
cols = getJSONTagNames(StockExcel{})
|
2023-11-10 09:49:44 +00:00
|
|
|
|
default:
|
|
|
|
|
return nil, nil, errors.New("格式错误,不是商品分类或资料模版excel")
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
2023-11-10 09:49:44 +00:00
|
|
|
|
} else if len(cols) != len(sheetCols) {
|
|
|
|
|
return nil, nil, errors.New("cols length does not match the number of columns")
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-24 09:09:24 +00:00
|
|
|
|
nCounter := len(rowData) //记录有数据的行数,空行则跳过循环
|
2023-11-09 10:26:19 +00:00
|
|
|
|
for rows.Next() {
|
2023-11-24 09:09:24 +00:00
|
|
|
|
if nCounter == 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
nCounter--
|
2023-11-09 10:26:19 +00:00
|
|
|
|
columns, err := rows.Columns()
|
|
|
|
|
if err != nil {
|
2023-11-10 09:49:44 +00:00
|
|
|
|
return nil, nil, fmt.Errorf("rows columns err: %v", err)
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-10 09:49:44 +00:00
|
|
|
|
columnMap := make(map[string]interface{}, len(cols))
|
|
|
|
|
for i, col := range cols {
|
|
|
|
|
if i < len(columns) {
|
|
|
|
|
columnMap[col] = columns[i]
|
|
|
|
|
} else {
|
|
|
|
|
columnMap[col] = ""
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-23 10:06:21 +00:00
|
|
|
|
logger.Info("columnMap:", logger.Field("columnMap", columnMap))
|
2023-11-09 10:26:19 +00:00
|
|
|
|
colsMap = append(colsMap, columnMap)
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-23 10:06:21 +00:00
|
|
|
|
logger.Info("colsMap:", logger.Field("colsMap", colsMap))
|
2023-11-09 10:26:19 +00:00
|
|
|
|
mCols, err := json.Marshal(colsMap)
|
|
|
|
|
if err != nil {
|
2023-11-10 09:49:44 +00:00
|
|
|
|
return mCols, nil, fmt.Errorf("marshal err: %v", err)
|
2023-11-09 10:26:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mCols, colsMap, nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-24 09:09:24 +00:00
|
|
|
|
// checkExcelFormat校验excel表格格式,剔除最后1行的空格
|
|
|
|
|
func checkExcelFormat(rowData, sheetCols [][]string) [][]string {
|
|
|
|
|
if findMaxLength(sheetCols) > len(rowData) { // 最后1行有空格,踢除
|
|
|
|
|
newSheetCols := make([][]string, len(sheetCols))
|
|
|
|
|
for i, col := range sheetCols {
|
|
|
|
|
if len(col) > len(rowData) {
|
|
|
|
|
newSheetCols[i] = col[:len(rowData)]
|
|
|
|
|
} else {
|
|
|
|
|
newSheetCols[i] = col
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return newSheetCols
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sheetCols
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-09 10:26:19 +00:00
|
|
|
|
// 校验商品分类导入规则
|
|
|
|
|
// 导入商品分类校验报错: (1)只有3级,没有2级或1级 (2)有2,3级,但没有1级
|
|
|
|
|
func checkCategoryExcel(sheetCols [][]string) error {
|
|
|
|
|
if len(sheetCols) != 3 {
|
|
|
|
|
return errors.New("模版错误,请检查文件")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(sheetCols[0]) < len(sheetCols[1]) || len(sheetCols[0]) < len(sheetCols[2]) {
|
|
|
|
|
return errors.New("格式错误,缺少一级分类")
|
|
|
|
|
} else if len(sheetCols[1]) < len(sheetCols[2]) {
|
|
|
|
|
return errors.New("格式错误,缺少二级分类")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := 1; i < len(sheetCols[2]); i++ {
|
|
|
|
|
if sheetCols[0][i] == "" && (sheetCols[1][i] != "" || sheetCols[2][i] != "") {
|
|
|
|
|
return errors.New("格式错误,缺少一级分类")
|
|
|
|
|
} else if sheetCols[2][i] != "" && sheetCols[1][i] == "" {
|
|
|
|
|
return errors.New("格式错误,缺少二级分类")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-10 09:49:44 +00:00
|
|
|
|
if duplicateName, nFlag := hasDuplicateNames(sheetCols); nFlag {
|
|
|
|
|
return fmt.Errorf("分类名称不允许重复,请检查:[%v]", duplicateName)
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-09 10:26:19 +00:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-27 10:01:38 +00:00
|
|
|
|
//// 校验名称是否相同,有则false
|
|
|
|
|
//func hasDuplicateNames(sheetCols [][]string) (string, bool) {
|
|
|
|
|
// nameMap := make(map[string]struct{})
|
|
|
|
|
//
|
|
|
|
|
// for _, col := range sheetCols {
|
|
|
|
|
// for _, name := range col {
|
|
|
|
|
// if _, exists := nameMap[name]; exists && name != "" {
|
|
|
|
|
// // 有重复的名称
|
|
|
|
|
// return name, true
|
|
|
|
|
// }
|
|
|
|
|
// nameMap[name] = struct{}{}
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// // 没有重复的名称
|
|
|
|
|
// return "", false
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
//// 校验名称是否相同,有则返回true和重复的名称,备注:只能校验不同的列中不能有重复数据,如:第一列的123,第二列不能有123
|
|
|
|
|
//func hasDuplicateNames(sheetCols [][]string) (string, bool) {
|
|
|
|
|
// globalMap := make(map[string]struct{})
|
|
|
|
|
//
|
|
|
|
|
// for _, col := range sheetCols {
|
|
|
|
|
// colMap := make(map[string]struct{})
|
|
|
|
|
// for _, name := range col {
|
|
|
|
|
// // 忽略空名称
|
|
|
|
|
// if name == "" {
|
|
|
|
|
// continue
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // 检查全局map中是否已存在
|
|
|
|
|
// if _, exists := globalMap[name]; exists {
|
|
|
|
|
// return name, true
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // 将名称添加到当前列map中
|
|
|
|
|
// colMap[name] = struct{}{}
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // 将当前列map中的元素添加到全局map中
|
|
|
|
|
// for name := range colMap {
|
|
|
|
|
// globalMap[name] = struct{}{}
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // 没有重复的名称
|
|
|
|
|
// return "", false
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
// 校验名称是否相同,有则返回true和重复的名称
|
|
|
|
|
// 第二列数据如果重复,需要看第一列同一行的数据是否相同,如果不同则报错。
|
|
|
|
|
// 第三列数据不能有重复的名称
|
2023-11-10 09:49:44 +00:00
|
|
|
|
func hasDuplicateNames(sheetCols [][]string) (string, bool) {
|
2024-03-27 10:01:38 +00:00
|
|
|
|
//firstColMap := make(map[string]bool)
|
|
|
|
|
secondColMap := make(map[string]string) // Map记录第二列数据的重复情况,以及对应第一列的数据
|
|
|
|
|
thirdColMap := make(map[string]bool)
|
2023-11-10 09:49:44 +00:00
|
|
|
|
|
2024-03-27 10:01:38 +00:00
|
|
|
|
// 确保每一列数据都有相同数量的行数
|
|
|
|
|
rowsCount := len(sheetCols[0])
|
2024-04-17 01:37:02 +00:00
|
|
|
|
//for i, col := range sheetCols {
|
|
|
|
|
// if len(col) != rowsCount && i != 2 {
|
|
|
|
|
// return "每列数据的行数不一致", true
|
|
|
|
|
// }
|
|
|
|
|
//}
|
2024-03-27 10:01:38 +00:00
|
|
|
|
|
|
|
|
|
for i := 0; i < rowsCount; i++ {
|
|
|
|
|
//// 检查第一列
|
|
|
|
|
//firstColName := sheetCols[0][i]
|
|
|
|
|
//if firstColName != "" {
|
|
|
|
|
// if _, exists := firstColMap[firstColName]; exists {
|
|
|
|
|
// return firstColName, true
|
|
|
|
|
// }
|
|
|
|
|
// firstColMap[firstColName] = true
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
// 检查第二列
|
2024-04-01 03:00:22 +00:00
|
|
|
|
if len(sheetCols[1]) < i+1 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2024-03-27 10:01:38 +00:00
|
|
|
|
secondColName := sheetCols[1][i]
|
|
|
|
|
if secondColName != "" {
|
|
|
|
|
if firstCol, exists := secondColMap[secondColName]; exists && firstCol != sheetCols[0][i] {
|
|
|
|
|
return secondColName, true
|
|
|
|
|
}
|
|
|
|
|
secondColMap[secondColName] = sheetCols[0][i]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查第三列
|
2024-04-01 03:00:22 +00:00
|
|
|
|
if len(sheetCols[2]) < i+1 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2024-03-27 10:01:38 +00:00
|
|
|
|
thirdColName := sheetCols[2][i]
|
|
|
|
|
if thirdColName != "" {
|
|
|
|
|
if _, exists := thirdColMap[thirdColName]; exists {
|
|
|
|
|
return thirdColName, true
|
2023-11-10 09:49:44 +00:00
|
|
|
|
}
|
2024-03-27 10:01:38 +00:00
|
|
|
|
thirdColMap[thirdColName] = true
|
2023-11-10 09:49:44 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-27 10:01:38 +00:00
|
|
|
|
|
2023-11-10 09:49:44 +00:00
|
|
|
|
// 没有重复的名称
|
|
|
|
|
return "", false
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-09 10:26:19 +00:00
|
|
|
|
// 校验商品资料导入规则
|
|
|
|
|
// 导入商品资料校验报错:必填项缺少数据(除了产地、备注,其他都是必填项)
|
|
|
|
|
func checkCommodityExcel(sheetCols [][]string) error {
|
2024-01-06 08:48:35 +00:00
|
|
|
|
if len(sheetCols) != 15 {
|
2023-11-09 10:26:19 +00:00
|
|
|
|
return errors.New("模版错误,请检查文件")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := 0; i < len(sheetCols)-2; i++ {
|
2024-01-06 08:48:35 +00:00
|
|
|
|
if i == 2 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2023-11-09 10:26:19 +00:00
|
|
|
|
if len(sheetCols[i]) < len(sheetCols[i+1]) {
|
|
|
|
|
return errors.New("格式错误,有必填项未录入")
|
|
|
|
|
}
|
|
|
|
|
for _, v := range sheetCols[i] {
|
|
|
|
|
if v == "" {
|
|
|
|
|
return errors.New("格式错误,有必填项未录入")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2023-11-10 09:49:44 +00:00
|
|
|
|
|
2023-11-23 12:38:11 +00:00
|
|
|
|
// 校验库存导入规则
|
|
|
|
|
// 导入规则:
|
|
|
|
|
// (1)商品名称:100 字符内,商品名称和商品编号可只填一项,同时填写时优先按照商品名称导库存;
|
|
|
|
|
// 且需为已有商品名称,否则红框展示,下方红字提示“该商品不存在,请新建商品”
|
|
|
|
|
// (2)商品编号:限纯数字,商品名称和商品编号可只填一项,同时填写时优先按照商品名称导库存;
|
|
|
|
|
// 且需为已有商品编号,否则红框展示,下方红字提示“该商品编号不存在”
|
|
|
|
|
// (3)所属门店:必填项,100 字符内;且需为已有门店,否则红框展示,下方红字提示“该门店不存在,请新建门店”
|
|
|
|
|
|
|
|
|
|
// (4)商品串码:100 字符内,限制仅可填数字或英文;串码类必填,非串码不填
|
|
|
|
|
// 如果是串码类商品没填串码或者填了中文,红框展示,下方红字提示“串码类商品,请填写正确地串码”
|
|
|
|
|
// 如果是非串码商品,填了串码,红框展示,下方红字提示“非串码商品,无需填写串码”
|
2023-12-19 10:21:44 +00:00
|
|
|
|
// 备注:库存导入时,如果是串码商品都需填写串码才能导入,不区分是否自动生成串码;自动生成是在后续采购导入时使用;2023/12/19跟产品核实
|
2023-11-23 12:38:11 +00:00
|
|
|
|
|
|
|
|
|
// (5)采购价:必填项,入库时的采购价,限>0 数字
|
|
|
|
|
// (6)员工成本价:必填项,入库时的员工成本价,限>0 数字
|
|
|
|
|
// (7)供应商:必填项,100 字符内,且需为已有供应商,否则红框展示,下方红字提示“该供应商不存在,请新建供应商”
|
|
|
|
|
// (8)入库时间:非必填项,含年月日,填写后入库时间会按照填写的日期0点展示,不填写则按照实际导入成功时间展示
|
|
|
|
|
|
|
|
|
|
// (9)数量:必填项,限制≥1正整数,
|
|
|
|
|
// 串码类商品需拆分成每个数量为1单独导入,
|
|
|
|
|
// 非串码可大于1直接导入 * 串码类商品如数量填写>1,红框展示,下方红字提示“串码类商品数量只能为1”
|
2023-12-11 08:21:21 +00:00
|
|
|
|
|
|
|
|
|
// (10)入库时间:必须早于/小于当前时间
|
2023-11-23 12:38:11 +00:00
|
|
|
|
func checkStockExcel(sheetCols [][]string) error {
|
|
|
|
|
if len(sheetCols) != 9 {
|
|
|
|
|
return errors.New("模版错误,请检查文件")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maxLength := findMaxLength(sheetCols)
|
|
|
|
|
nLow, nMax := determineLowAndMax(sheetCols)
|
|
|
|
|
|
|
|
|
|
if nMax < maxLength {
|
|
|
|
|
return errors.New("第" + strconv.Itoa(nMax+1) + "行商品名称和商品编号不能同时为空")
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-11 08:21:21 +00:00
|
|
|
|
// 判断是否有重复串码
|
|
|
|
|
if duplicateName, nFlag := hasDuplicateIMEI(sheetCols[3]); nFlag {
|
|
|
|
|
return fmt.Errorf("商品串码不允许重复,请检查:[%v]", duplicateName)
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-23 12:38:11 +00:00
|
|
|
|
//先遍历第1列
|
|
|
|
|
for i := 1; i < nLow; i++ {
|
|
|
|
|
if sheetCols[0][i] == "" && sheetCols[1][i] == "" { // 商品名称和编号不能都为空
|
|
|
|
|
return errors.New("第" + strconv.Itoa(i+1) + "行商品名称和商品编号不能同时为空")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 12:05:53 +00:00
|
|
|
|
for i := 1; i < nMax; i++ { // todo
|
|
|
|
|
if i < len(sheetCols[0]) {
|
2023-12-21 09:36:13 +00:00
|
|
|
|
if !IsExistingProduct(sheetCols[0][i]) {
|
2023-12-13 12:05:53 +00:00
|
|
|
|
return errors.New("第" + strconv.Itoa(i+1) + "行商品不存在,请新建商品")
|
|
|
|
|
}
|
2023-11-23 12:38:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 12:05:53 +00:00
|
|
|
|
if i < len(sheetCols[1]) {
|
|
|
|
|
// 商品编号必须为纯数字
|
|
|
|
|
if sheetCols[1][i] != "" {
|
|
|
|
|
if _, err := strconv.Atoi(sheetCols[1][i]); err != nil {
|
|
|
|
|
return errors.New("第" + strconv.Itoa(i+1) + "行商品编号必须为纯数字")
|
|
|
|
|
}
|
2023-11-23 12:38:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 12:05:53 +00:00
|
|
|
|
if !isExistingProductCode(sheetCols[1][i]) {
|
|
|
|
|
return errors.New("第" + strconv.Itoa(i+1) + "行商品编号不存在")
|
|
|
|
|
}
|
2023-11-23 12:38:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 所属门店不能为空
|
2023-12-13 12:05:53 +00:00
|
|
|
|
if i < len(sheetCols[2]) {
|
|
|
|
|
if sheetCols[2][i] == "" {
|
|
|
|
|
return errors.New("第" + strconv.Itoa(i+1) + "行所属门店不能为空")
|
|
|
|
|
}
|
|
|
|
|
if !isExistingStore(sheetCols[2][i]) {
|
|
|
|
|
return errors.New("第" + strconv.Itoa(i+1) + "行门店不存在,请新建门店")
|
|
|
|
|
}
|
2023-11-23 12:38:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 商品串码规则校验 当串码行数跟其他一致时正常遍历,如果小于其他行,则最后一行不遍历 todo
|
2023-12-11 08:21:21 +00:00
|
|
|
|
// 如串码在商品库存表已经存在,则报错提示
|
2023-12-13 12:05:53 +00:00
|
|
|
|
//if len(sheetCols[3]) <= nLow && i+1 < nLow {
|
|
|
|
|
if i < len(sheetCols[3]) {
|
|
|
|
|
productName := ""
|
|
|
|
|
if i < len(sheetCols[0]) {
|
|
|
|
|
productName = sheetCols[0][i]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
productCode := ""
|
|
|
|
|
if i < len(sheetCols[1]) {
|
|
|
|
|
productCode = sheetCols[1][i]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
count := ""
|
|
|
|
|
if i < len(sheetCols[8]) {
|
|
|
|
|
count = sheetCols[8][i]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := checkSerialCode(productName, productCode, sheetCols[3][i], i); err != nil {
|
2023-11-23 12:38:11 +00:00
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
// 串码类商品数量只能为1
|
2023-12-13 12:05:53 +00:00
|
|
|
|
if sheetCols[3][i] != "" && count != "1" {
|
2023-11-23 12:38:11 +00:00
|
|
|
|
return errors.New("第" + strconv.Itoa(i+1) + "行串码类商品数量只能为1")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 采购价、员工成本价必须大于0
|
2023-12-13 12:05:53 +00:00
|
|
|
|
if i < len(sheetCols[4]) {
|
2024-04-17 01:37:02 +00:00
|
|
|
|
if purchasePrice, err := strconv.ParseFloat(sheetCols[4][i], 64); err != nil || purchasePrice <= 0 {
|
2023-12-13 12:05:53 +00:00
|
|
|
|
return errors.New("第" + strconv.Itoa(i+1) + "行采购价必须是大于0的数字")
|
|
|
|
|
}
|
2023-11-23 12:38:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 12:05:53 +00:00
|
|
|
|
if i < len(sheetCols[5]) {
|
2024-04-17 01:37:02 +00:00
|
|
|
|
if employeeCost, err := strconv.ParseFloat(sheetCols[5][i], 64); err != nil || employeeCost <= 0 {
|
2023-12-13 12:05:53 +00:00
|
|
|
|
return errors.New("第" + strconv.Itoa(i+1) + "行员工成本价必须是大于0的数字")
|
|
|
|
|
}
|
2023-11-23 12:38:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 供应商不能为空
|
2023-12-13 12:05:53 +00:00
|
|
|
|
if i < len(sheetCols[6]) {
|
|
|
|
|
if sheetCols[6][i] == "" {
|
|
|
|
|
return errors.New("第" + strconv.Itoa(i+1) + "行供应商不能为空")
|
|
|
|
|
}
|
|
|
|
|
if !isExistingSupplier(sheetCols[6][i]) {
|
|
|
|
|
return errors.New("第" + strconv.Itoa(i+1) + "行供应商不存在,请新建供应商")
|
|
|
|
|
}
|
2023-11-23 12:38:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 入库时间格式校验
|
2023-12-11 08:21:21 +00:00
|
|
|
|
//if len(sheetCols[7]) < nLow && i+1 < nLow {
|
2023-12-13 12:05:53 +00:00
|
|
|
|
if i < len(sheetCols[7]) {
|
2023-11-23 12:38:11 +00:00
|
|
|
|
if sheetCols[7][i] != "" {
|
|
|
|
|
parsedTime, err := time.Parse("2006/1/2", sheetCols[7][i])
|
|
|
|
|
if err != nil {
|
2024-01-23 02:49:07 +00:00
|
|
|
|
return errors.New("第" + strconv.Itoa(i+1) + "行入库时间格式错误,请设置单元格格式为日期:YYYY/MM/DD")
|
2023-11-23 12:38:11 +00:00
|
|
|
|
}
|
|
|
|
|
// 格式化时间为指定格式
|
|
|
|
|
formattedTime := parsedTime.Format(DateTimeFormat)
|
2023-12-13 12:05:53 +00:00
|
|
|
|
//sheetCols[7][i] = formattedTime + "00-00-00"
|
2023-12-11 08:21:21 +00:00
|
|
|
|
|
|
|
|
|
// 需小于当前时间
|
|
|
|
|
nFlag, _ := isInputTimeBeforeOrEqualNow(formattedTime)
|
|
|
|
|
if !nFlag {
|
|
|
|
|
return errors.New("第" + strconv.Itoa(i+1) + "行入库时间不能晚于当前时间")
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-23 12:38:11 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 数量必须为正整数
|
2023-12-13 12:05:53 +00:00
|
|
|
|
if i < len(sheetCols[8]) {
|
|
|
|
|
if quantity, err := strconv.Atoi(sheetCols[8][i]); err != nil || quantity < 1 {
|
|
|
|
|
return errors.New("第" + strconv.Itoa(i+1) + "行数量必须是大于等于1的整数")
|
|
|
|
|
}
|
2023-11-23 12:38:11 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-11 08:21:21 +00:00
|
|
|
|
// 判断是否早于等于当前时间
|
|
|
|
|
func isInputTimeBeforeOrEqualNow(inputTimeString string) (bool, error) {
|
|
|
|
|
// 将输入时间字符串解析为 time.Time 类型
|
|
|
|
|
inputTime, _ := time.Parse(time.RFC3339, inputTimeString)
|
|
|
|
|
|
|
|
|
|
// 获取当前时间
|
|
|
|
|
currentTime := time.Now()
|
|
|
|
|
|
|
|
|
|
// 比较时间
|
|
|
|
|
isBeforeOrEqual := inputTime.After(currentTime)
|
|
|
|
|
|
|
|
|
|
return !isBeforeOrEqual, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 校验串码是否相同,有则false
|
|
|
|
|
func hasDuplicateIMEI(sheetCols []string) (string, bool) {
|
|
|
|
|
nameMap := make(map[string]struct{})
|
|
|
|
|
|
|
|
|
|
for _, name := range sheetCols {
|
|
|
|
|
if _, exists := nameMap[name]; exists && name != "" {
|
|
|
|
|
// 有重复的名称
|
|
|
|
|
return name, true
|
|
|
|
|
}
|
|
|
|
|
nameMap[name] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
// 没有重复的名称
|
|
|
|
|
return "", false
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-23 12:38:11 +00:00
|
|
|
|
func findMaxLength(sheetCols [][]string) int {
|
|
|
|
|
maxLength := 0
|
|
|
|
|
for _, col := range sheetCols {
|
|
|
|
|
if len(col) > maxLength {
|
|
|
|
|
maxLength = len(col)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return maxLength
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func determineLowAndMax(sheetCols [][]string) (int, int) {
|
|
|
|
|
nLow := len(sheetCols[0])
|
|
|
|
|
nMax := len(sheetCols[1])
|
|
|
|
|
if len(sheetCols[0]) >= len(sheetCols[1]) {
|
|
|
|
|
nLow = len(sheetCols[1])
|
|
|
|
|
nMax = len(sheetCols[0])
|
|
|
|
|
}
|
|
|
|
|
return nLow, nMax
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-21 09:36:13 +00:00
|
|
|
|
// IsExistingProduct 查询商品资料中某个名称的商品是否存在
|
|
|
|
|
func IsExistingProduct(productName string) bool {
|
2023-11-23 12:38:11 +00:00
|
|
|
|
if productName == "" {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
// 实现商品名称是否存在的逻辑
|
|
|
|
|
var count int64
|
|
|
|
|
orm.Eloquent.Debug().Model(&ErpCommodity{}).
|
|
|
|
|
Where("name = ?", productName).
|
|
|
|
|
Count(&count)
|
|
|
|
|
|
|
|
|
|
return count > 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isExistingProductCode(productCode string) bool {
|
|
|
|
|
if productCode == "" {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
// 实现商品编号是否存在的逻辑
|
|
|
|
|
var count int64
|
|
|
|
|
orm.Eloquent.Debug().Model(&ErpCommodity{}).
|
|
|
|
|
Where("serial_number = ?", productCode).
|
|
|
|
|
Count(&count)
|
|
|
|
|
|
|
|
|
|
return count > 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isExistingStore(storeName string) bool {
|
|
|
|
|
// 实现门店是否存在的逻辑
|
|
|
|
|
var count int64
|
|
|
|
|
orm.Eloquent.Debug().Model(&Store{}).
|
|
|
|
|
Where("name = ?", storeName).
|
|
|
|
|
Count(&count)
|
|
|
|
|
|
|
|
|
|
return count > 0
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-23 02:49:07 +00:00
|
|
|
|
func IsExistingStoreById(storeId int) bool {
|
|
|
|
|
// 实现门店是否存在的逻辑
|
|
|
|
|
var count int64
|
|
|
|
|
orm.Eloquent.Debug().Model(&Store{}).
|
|
|
|
|
Where("id = ?", storeId).
|
|
|
|
|
Count(&count)
|
|
|
|
|
|
|
|
|
|
return count > 0
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-23 12:38:11 +00:00
|
|
|
|
func isExistingSupplier(supplierName string) bool {
|
|
|
|
|
// 实现供应商是否存在的逻辑
|
|
|
|
|
var count int64
|
|
|
|
|
orm.Eloquent.Debug().Model(&ErpSupplier{}).
|
|
|
|
|
Where("name = ?", supplierName).
|
|
|
|
|
Count(&count)
|
|
|
|
|
|
|
|
|
|
return count > 0
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-24 09:09:24 +00:00
|
|
|
|
func checkSerialCode(productName, productCode, serialCode string, row int) error {
|
2023-11-23 12:38:11 +00:00
|
|
|
|
var existingCommodity ErpCommodity
|
2023-11-24 09:09:24 +00:00
|
|
|
|
var existingStockCommodity ErpStockCommodity
|
2023-11-23 12:38:11 +00:00
|
|
|
|
var queryCondition string
|
|
|
|
|
|
2023-11-24 09:09:24 +00:00
|
|
|
|
if productCode != "" {
|
|
|
|
|
queryCondition = "serial_number= ?"
|
|
|
|
|
} else if productName != "" {
|
2023-11-23 12:38:11 +00:00
|
|
|
|
queryCondition = "name = ?"
|
|
|
|
|
} else {
|
|
|
|
|
return errors.New("商品名称和商品编码不能都为空")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result := orm.Eloquent.Where(queryCondition, productName).Or(queryCondition, productCode).First(&existingCommodity)
|
|
|
|
|
if result.Error != nil {
|
|
|
|
|
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
|
|
|
|
return errors.New("未查询到商品数据")
|
|
|
|
|
}
|
|
|
|
|
fmt.Println("Error:", result.Error)
|
|
|
|
|
return result.Error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nIMEIType := int(existingCommodity.IMEIType)
|
|
|
|
|
|
|
|
|
|
if nIMEIType == 1 && serialCode != "" {
|
|
|
|
|
return errors.New("非串码商品,无需填写串码")
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-11 08:21:21 +00:00
|
|
|
|
//if nIMEIType == 2 && serialCode != ""{
|
|
|
|
|
// return errors.New("第" + strconv.Itoa(row+1) + "行串码无需填写,该商品串码系统自动生成")
|
|
|
|
|
//}
|
|
|
|
|
|
2023-12-19 10:21:44 +00:00
|
|
|
|
if (nIMEIType == 2 || nIMEIType == 1) && serialCode == "" {
|
|
|
|
|
return errors.New("第" + strconv.Itoa(row+1) + "行是串码类商品,请填写正确地串码")
|
2023-12-11 08:21:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-12-19 10:21:44 +00:00
|
|
|
|
if nIMEIType != 1 && !isNumericOrAlpha(serialCode) {
|
2023-11-23 12:38:11 +00:00
|
|
|
|
return errors.New("串码类商品,请填写正确的串码")
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-24 09:09:24 +00:00
|
|
|
|
result = orm.Eloquent.Where("imei = ?", serialCode).First(&existingStockCommodity)
|
|
|
|
|
if result.Error != nil {
|
|
|
|
|
if errors.Is(result.Error, gorm.ErrRecordNotFound) { //没找到对应串码
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
fmt.Println("Error:", result.Error)
|
|
|
|
|
return result.Error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return errors.New("第" + strconv.Itoa(row+1) + "行串码重复,请勿重复导入")
|
2023-11-23 12:38:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isNumericOrAlpha(s string) bool {
|
|
|
|
|
for _, char := range s {
|
|
|
|
|
if !unicode.IsDigit(char) && !unicode.IsLetter(char) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-10 09:49:44 +00:00
|
|
|
|
// 将读取的excel数据转换成CategoryExcel struct
|
|
|
|
|
func transCategoryData(colsMap []map[string]interface{}) []CategoryExcel {
|
|
|
|
|
var categories []CategoryExcel
|
|
|
|
|
|
|
|
|
|
// 遍历 colsMap 进行类型断言和转换
|
|
|
|
|
for _, col := range colsMap {
|
|
|
|
|
var category CategoryExcel
|
|
|
|
|
|
|
|
|
|
for key, value := range col {
|
|
|
|
|
switch key {
|
|
|
|
|
case "first_category":
|
|
|
|
|
if name, ok := value.(string); ok {
|
|
|
|
|
category.FirstCategory = name
|
|
|
|
|
}
|
|
|
|
|
case "second_category":
|
|
|
|
|
if name, ok := value.(string); ok {
|
|
|
|
|
category.SecondCategory = name
|
|
|
|
|
}
|
|
|
|
|
case "three_category":
|
|
|
|
|
if name, ok := value.(string); ok {
|
|
|
|
|
category.ThreeCategory = name
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 将处理后的数据追加到 categories 中
|
|
|
|
|
categories = append(categories, category)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return categories
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-15 10:17:35 +00:00
|
|
|
|
// ImportCategoryData 导入商品分类到数据库
|
2023-11-10 09:49:44 +00:00
|
|
|
|
// 规则:分类只有3级,所有分类不允许名称完全相同
|
|
|
|
|
func ImportCategoryData(colsMap []map[string]interface{}, businessId uint32) error {
|
|
|
|
|
data := transCategoryData(colsMap)
|
|
|
|
|
|
|
|
|
|
if duplicateName, nFlag := hasDuplicateInDb(data); nFlag {
|
|
|
|
|
return fmt.Errorf("数据库已有此分类名称,不允许重复,请检查:[%v]", duplicateName)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, item := range data {
|
|
|
|
|
var firstCategory, secondCategory, threeCategory Category
|
|
|
|
|
if item.FirstCategory == "" { // 一级分类名称为空则跳过,继续遍历
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 插入或获取一级分类
|
2024-04-01 03:00:22 +00:00
|
|
|
|
err := orm.Eloquent.Debug().Table("erp_category").Where("name = ? and pid = ? and display = ? "+
|
|
|
|
|
"and cooperative_business_id = ?", item.FirstCategory, 0, 1, businessId).
|
|
|
|
|
First(&firstCategory).Error
|
2023-11-10 09:49:44 +00:00
|
|
|
|
if err != nil {
|
2024-04-01 03:00:22 +00:00
|
|
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
|
|
|
firstCategory.Name = item.FirstCategory
|
|
|
|
|
firstCategory.Pid = 0
|
|
|
|
|
firstCategory.Display = 1
|
|
|
|
|
firstCategory.CooperativeBusinessId = businessId
|
|
|
|
|
firstCategory.ID = 0
|
|
|
|
|
// 如果没有符合条件的记录,则创建新记录
|
|
|
|
|
err = orm.Eloquent.Debug().Create(&firstCategory).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2023-11-10 09:49:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 插入或获取二级分类
|
|
|
|
|
if item.SecondCategory != "" {
|
|
|
|
|
err = orm.Eloquent.Debug().FirstOrCreate(&secondCategory,
|
|
|
|
|
Category{
|
|
|
|
|
Name: item.SecondCategory,
|
|
|
|
|
Pid: firstCategory.ID,
|
|
|
|
|
Display: 1,
|
|
|
|
|
CooperativeBusinessId: businessId,
|
|
|
|
|
}).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 插入三级分类
|
|
|
|
|
if item.ThreeCategory != "" {
|
|
|
|
|
err = orm.Eloquent.Debug().FirstOrCreate(&threeCategory,
|
|
|
|
|
Category{
|
|
|
|
|
Name: item.ThreeCategory,
|
|
|
|
|
Pid: secondCategory.ID,
|
|
|
|
|
Display: 1,
|
|
|
|
|
CooperativeBusinessId: businessId,
|
|
|
|
|
}).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 判断是否存在重复数据
|
|
|
|
|
func hasDuplicateInDb(data []CategoryExcel) (string, bool) {
|
|
|
|
|
for _, item := range data {
|
2024-03-27 10:01:38 +00:00
|
|
|
|
// 判断是导入几级分类
|
|
|
|
|
nType := 0
|
|
|
|
|
if item.ThreeCategory != "" && item.SecondCategory != "" && item.FirstCategory != "" {
|
|
|
|
|
nType = 3
|
|
|
|
|
} else if item.ThreeCategory == "" && item.SecondCategory != "" && item.FirstCategory != "" {
|
|
|
|
|
nType = 2
|
|
|
|
|
} else if item.ThreeCategory == "" && item.SecondCategory == "" && item.FirstCategory != "" {
|
|
|
|
|
nType = 1
|
|
|
|
|
} else {
|
|
|
|
|
return "", true
|
2023-11-10 09:49:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-27 10:01:38 +00:00
|
|
|
|
switch nType {
|
|
|
|
|
case 1: // 导入一级分类
|
|
|
|
|
if exists := isCategoryExists(item.FirstCategory); exists {
|
|
|
|
|
return item.FirstCategory, true
|
|
|
|
|
}
|
2024-04-01 03:00:22 +00:00
|
|
|
|
case 2: // 导入二级分类:查看二级分类名称是否重复;查询一级分类名称跟二/三级分类是否重复
|
|
|
|
|
if exists := isFirstCategoryDuplicate(item.FirstCategory); exists {
|
|
|
|
|
return item.FirstCategory, true
|
|
|
|
|
}
|
2024-03-27 10:01:38 +00:00
|
|
|
|
if exists := isCategoryExists(item.SecondCategory); exists {
|
|
|
|
|
return item.SecondCategory, true
|
|
|
|
|
}
|
2024-04-01 03:00:22 +00:00
|
|
|
|
case 3: // 导入三级分类:查看三级分类名称是否重复;查询一级分类名称跟二/三级分类是否重复,查询二级分类跟一,三级是否重复
|
|
|
|
|
if exists := isFirstCategoryDuplicate(item.FirstCategory); exists {
|
|
|
|
|
return item.FirstCategory, true
|
|
|
|
|
}
|
|
|
|
|
if exists := isSecondCategoryDuplicate(item.SecondCategory); exists {
|
|
|
|
|
return item.SecondCategory, true
|
|
|
|
|
}
|
2024-03-27 10:01:38 +00:00
|
|
|
|
if exists := isCategoryExists(item.ThreeCategory); exists {
|
|
|
|
|
return item.ThreeCategory, true
|
|
|
|
|
}
|
2023-11-10 09:49:44 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "", false
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-01 03:00:22 +00:00
|
|
|
|
// 查询一级分类名称跟二/三级分类是否重复
|
|
|
|
|
func isFirstCategoryDuplicate(categoryName string) bool {
|
|
|
|
|
var count int64
|
|
|
|
|
orm.Eloquent.Debug().Model(&Category{}).
|
|
|
|
|
Where("name = ? and pid != ?", categoryName, 0).
|
|
|
|
|
Count(&count)
|
|
|
|
|
|
|
|
|
|
return count > 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 查询二级分类跟一,三级是否重复
|
|
|
|
|
func isSecondCategoryDuplicate(categoryName string) bool {
|
|
|
|
|
var count int64
|
|
|
|
|
orm.Eloquent.Debug().Model(&Category{}).
|
|
|
|
|
Where("name = ? and len(number) != ?", categoryName, 6).
|
|
|
|
|
Count(&count)
|
|
|
|
|
|
|
|
|
|
return count > 0
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-10 09:49:44 +00:00
|
|
|
|
// 判断分类是否存在
|
|
|
|
|
func isCategoryExists(categoryName string) bool {
|
|
|
|
|
var count int64
|
|
|
|
|
orm.Eloquent.Debug().Model(&Category{}).
|
|
|
|
|
Where("name = ?", categoryName).
|
|
|
|
|
Count(&count)
|
|
|
|
|
|
|
|
|
|
return count > 0
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-15 10:17:35 +00:00
|
|
|
|
// ImportCommodityData 导入商品资料
|
2023-11-10 09:49:44 +00:00
|
|
|
|
func ImportCommodityData(colsMap []map[string]interface{}, businessId uint32) error {
|
|
|
|
|
data, err := transCommodityData(colsMap)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var erpCommodities []ErpCommodity
|
|
|
|
|
erpCommodities, err = convertToErpCommodities(data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = updateOrInsertCommodities(erpCommodities)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 将读取的excel数据转换成CommodityExcel struct
|
|
|
|
|
func transCommodityData(colsMap []map[string]interface{}) ([]CommodityExcel, error) {
|
|
|
|
|
var commodities []CommodityExcel
|
|
|
|
|
|
|
|
|
|
// 遍历 colsMap 进行类型断言和转换
|
|
|
|
|
for _, col := range colsMap {
|
|
|
|
|
var commodity CommodityExcel
|
|
|
|
|
|
|
|
|
|
// 将 col 转换为 JSON 字符串
|
|
|
|
|
jsonData, err := json.Marshal(col)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to marshal data to JSON: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 将 JSON 字符串反序列化为 CommodityExcel
|
|
|
|
|
if err := json.Unmarshal(jsonData, &commodity); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to unmarshal JSON to CommodityExcel: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 将处理后的数据追加到 commodities 中
|
|
|
|
|
commodities = append(commodities, commodity)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return commodities, nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-23 12:38:11 +00:00
|
|
|
|
// 将读取的excel数据转换成CommodityExcel struct
|
|
|
|
|
func transStockData(colsMap []map[string]interface{}) ([]StockExcel, error) {
|
|
|
|
|
var stockInfos []StockExcel
|
|
|
|
|
|
|
|
|
|
// 遍历 colsMap 进行类型断言和转换
|
|
|
|
|
for _, col := range colsMap {
|
|
|
|
|
var stockInfo StockExcel
|
|
|
|
|
|
|
|
|
|
// 将 col 转换为 JSON 字符串
|
|
|
|
|
jsonData, err := json.Marshal(col)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to marshal data to JSON: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 将 JSON 字符串反序列化为 CommodityExcel
|
|
|
|
|
if err := json.Unmarshal(jsonData, &stockInfo); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to unmarshal JSON to StockExcel: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 将处理后的数据追加到 commodities 中
|
|
|
|
|
stockInfos = append(stockInfos, stockInfo)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return stockInfos, nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-10 09:49:44 +00:00
|
|
|
|
func convertToErpCommodities(data []CommodityExcel) ([]ErpCommodity, error) {
|
|
|
|
|
var erpCommodities []ErpCommodity
|
|
|
|
|
|
|
|
|
|
productCounter := tools.NewProductCounter()
|
|
|
|
|
for _, item := range data {
|
|
|
|
|
category, err := getCategoryByName(item.Category)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list, err := GetSupplier(GetSupplierRequest{Name: item.SupplierName})
|
|
|
|
|
if len(list) == 0 || errors.Is(err, gorm.ErrRecordNotFound) {
|
2023-11-24 09:09:24 +00:00
|
|
|
|
return nil, fmt.Errorf("[%v]该供应商不存在,请新建供应商", item.SupplierName)
|
2023-11-10 09:49:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-06 08:48:35 +00:00
|
|
|
|
barCode := ""
|
|
|
|
|
if item.ErpBarcode != "" { // 条码不为空则校验
|
|
|
|
|
barCode, err = CheckAndConvertBarcode(item.ErpBarcode)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
} else { // 条码为空则自动生成
|
|
|
|
|
barCode, err = GenerateBarcode(category.ID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-10 09:49:44 +00:00
|
|
|
|
serialNumber := fmt.Sprintf("%s%04d", category.Number,
|
|
|
|
|
getNumberOnCategory(category.ID)+int64(productCounter.GetNextProductNumber(category.Number)))
|
|
|
|
|
fmt.Println("商品编号:", serialNumber)
|
|
|
|
|
|
2024-01-06 08:48:35 +00:00
|
|
|
|
erpCommodity, err := convertToErpCommodity(item, category, list[0], serialNumber, barCode)
|
2023-11-10 09:49:44 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
erpCommodities = append(erpCommodities, erpCommodity)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return erpCommodities, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 查询商品分类
|
|
|
|
|
func getCategoryByName(name string) (*Category, error) {
|
|
|
|
|
var category Category
|
|
|
|
|
err := orm.Eloquent.Debug().Model(&Category{}).
|
|
|
|
|
Where("name = ?", name).First(&category).Error
|
|
|
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
2023-11-24 09:09:24 +00:00
|
|
|
|
return nil, fmt.Errorf("[%v]该分类不存在,请先新建分类", name)
|
2023-11-10 09:49:44 +00:00
|
|
|
|
}
|
|
|
|
|
return &category, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 转换格式,匹配Commodity的model
|
2024-01-06 08:48:35 +00:00
|
|
|
|
func convertToErpCommodity(item CommodityExcel, category *Category, supplier *Supplier, serialNumber, barCode string) (ErpCommodity, error) {
|
2023-11-10 09:49:44 +00:00
|
|
|
|
brokerage1Float, err := strconv.ParseFloat(strings.TrimRight(item.SellBrokerage, "%"), 64) // 销售毛利
|
|
|
|
|
if err != nil {
|
|
|
|
|
return ErpCommodity{}, fmt.Errorf("销售毛利转换有误:[%v]", err)
|
|
|
|
|
}
|
|
|
|
|
brokerage2Float, err := strconv.ParseFloat(strings.TrimRight(item.StaffBrokerage, "%"), 64) // 员工毛利提成
|
|
|
|
|
if err != nil {
|
|
|
|
|
return ErpCommodity{}, fmt.Errorf("员工毛利提成转换有误:[%v]", err)
|
|
|
|
|
}
|
|
|
|
|
memberDiscountFloat, err := strconv.ParseFloat(item.MemberDiscount, 64) // 会员优惠
|
|
|
|
|
if err != nil {
|
|
|
|
|
return ErpCommodity{}, fmt.Errorf("会员优惠转换有误:[%v]", err)
|
|
|
|
|
}
|
2024-04-17 01:37:02 +00:00
|
|
|
|
nRetailPrice, err := strconv.ParseFloat(item.RetailPrice, 64) // 指导零售价
|
2023-11-10 09:49:44 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return ErpCommodity{}, fmt.Errorf("指导零售价转换有误:[%v]", err)
|
|
|
|
|
}
|
2024-04-17 01:37:02 +00:00
|
|
|
|
nMinRetailPrice, err := strconv.ParseFloat(item.MinRetailPrice, 64) // 最低零售价
|
2023-11-10 09:49:44 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return ErpCommodity{}, fmt.Errorf("最低零售价转换有误:[%v]", err)
|
|
|
|
|
}
|
2024-04-17 01:37:02 +00:00
|
|
|
|
nStaffCostPrice, err := strconv.ParseFloat(item.StaffCostPrice, 64) // 员工成本价加价
|
2023-11-10 09:49:44 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return ErpCommodity{}, fmt.Errorf("员工成本价加价转换有误:[%v]", err)
|
|
|
|
|
}
|
2024-04-17 01:37:02 +00:00
|
|
|
|
nWholesalePrice, err := strconv.ParseFloat(item.WholesalePrice, 64) // 指导采购价
|
2023-11-10 09:49:44 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return ErpCommodity{}, fmt.Errorf("指导采购价转换有误:[%v]", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//将是否串码转换为数字 1-无串码 2-串码(系统生成) 3-串码(手动添加)
|
|
|
|
|
var nIMEIType uint32
|
|
|
|
|
switch item.IMEIType {
|
|
|
|
|
case "串码类":
|
|
|
|
|
if item.SysGenerate == "是" {
|
|
|
|
|
nIMEIType = 2
|
|
|
|
|
} else if item.SysGenerate == "否" {
|
|
|
|
|
nIMEIType = 3
|
|
|
|
|
} else {
|
|
|
|
|
return ErpCommodity{}, errors.New("[系统生成串码]列有非法字段")
|
|
|
|
|
}
|
|
|
|
|
case "非串码":
|
|
|
|
|
nIMEIType = 1
|
|
|
|
|
default:
|
|
|
|
|
return ErpCommodity{}, errors.New("[是否串码]列有非法字段")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//serialNumber := fmt.Sprintf("%s%04d", category.Number, getNumberOnCategory(category.ID)+1)
|
|
|
|
|
//fmt.Println("商品编号:", serialNumber)
|
|
|
|
|
|
|
|
|
|
return ErpCommodity{
|
|
|
|
|
SerialNumber: serialNumber,
|
|
|
|
|
Name: item.Name,
|
|
|
|
|
ErpCategoryId: category.ID,
|
|
|
|
|
ErpCategoryName: item.Category,
|
2024-01-06 08:48:35 +00:00
|
|
|
|
ErpBarcode: barCode,
|
2023-11-10 09:49:44 +00:00
|
|
|
|
IMEIType: nIMEIType,
|
|
|
|
|
ErpSupplierId: supplier.ID,
|
|
|
|
|
ErpSupplierName: item.SupplierName,
|
2024-04-17 01:37:02 +00:00
|
|
|
|
RetailPrice: tool.RoundToTwoDecimalPlaces(nRetailPrice),
|
|
|
|
|
MinRetailPrice: tool.RoundToTwoDecimalPlaces(nMinRetailPrice),
|
|
|
|
|
StaffCostPrice: tool.RoundToTwoDecimalPlaces(nStaffCostPrice),
|
|
|
|
|
WholesalePrice: tool.RoundToTwoDecimalPlaces(nWholesalePrice),
|
|
|
|
|
Brokerage1: tool.RoundToTwoDecimalPlaces(brokerage1Float),
|
|
|
|
|
Brokerage2: tool.RoundToTwoDecimalPlaces(brokerage2Float),
|
|
|
|
|
MemberDiscount: tool.RoundToTwoDecimalPlaces(memberDiscountFloat),
|
2023-11-10 09:49:44 +00:00
|
|
|
|
Origin: item.Origin,
|
|
|
|
|
Remark: item.Remark,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 操作数据库,通过商品名称查找,没有则插入,有则更新
|
|
|
|
|
func updateOrInsertCommodities(erpCommodities []ErpCommodity) error {
|
|
|
|
|
for _, erpCommodity := range erpCommodities {
|
|
|
|
|
var existingCommodity ErpCommodity
|
|
|
|
|
result := orm.Eloquent.Where("name = ?", erpCommodity.Name).First(&existingCommodity)
|
|
|
|
|
|
|
|
|
|
if result.Error != nil {
|
|
|
|
|
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
|
|
|
|
orm.Eloquent.Create(&erpCommodity)
|
|
|
|
|
fmt.Println("Inserted:", erpCommodity.Name)
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Println("Error:", result.Error)
|
|
|
|
|
return result.Error
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
orm.Eloquent.Model(&existingCommodity).Updates(&erpCommodity)
|
|
|
|
|
fmt.Println("Updated:", erpCommodity.Name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 查询商品列表中同一类商品分类下商品的个数
|
|
|
|
|
func getNumberOnCategory(categoryId uint32) int64 {
|
|
|
|
|
var count int64
|
|
|
|
|
orm.Eloquent.Debug().Model(&ErpCommodity{}).
|
|
|
|
|
Where("erp_category_id = ?", categoryId).
|
|
|
|
|
Count(&count)
|
|
|
|
|
|
|
|
|
|
return count
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-16 08:19:12 +00:00
|
|
|
|
//// GenerateSerialNumber 生成商品编号(会有重复编码)
|
|
|
|
|
//func GenerateSerialNumber(categoryId uint32) (string, error) {
|
|
|
|
|
// var category Category
|
|
|
|
|
// err := orm.Eloquent.Debug().Model(&Category{}).
|
|
|
|
|
// Where("id = ?", categoryId).First(&category).Error
|
|
|
|
|
// if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
|
|
|
// return "", errors.New("未查到商品分类")
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// var count int64
|
|
|
|
|
// orm.Eloquent.Debug().Model(&ErpCommodity{}).
|
|
|
|
|
// Where("erp_category_id = ?", category.ID).
|
|
|
|
|
// Count(&count)
|
|
|
|
|
//
|
|
|
|
|
// serialNumber := fmt.Sprintf("%s%04d", category.Number, count+1)
|
|
|
|
|
// fmt.Println("商品编号:", serialNumber)
|
|
|
|
|
// return serialNumber, nil
|
|
|
|
|
//}
|
|
|
|
|
|
2023-11-15 10:17:35 +00:00
|
|
|
|
// GenerateSerialNumber 生成商品编号
|
2023-11-10 09:49:44 +00:00
|
|
|
|
func GenerateSerialNumber(categoryId uint32) (string, error) {
|
2024-03-16 08:19:12 +00:00
|
|
|
|
// 查询商品分类
|
|
|
|
|
var category ErpCategory
|
|
|
|
|
if err := orm.Eloquent.Debug().Model(&ErpCategory{}).
|
|
|
|
|
Where("id = ?", categoryId).First(&category).Error; err != nil {
|
|
|
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
|
|
|
return "", errors.New("未查到商品分类")
|
|
|
|
|
}
|
|
|
|
|
return "", err
|
2023-11-10 09:49:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-16 08:19:12 +00:00
|
|
|
|
// 查询该商品分类下最大的商品编号
|
|
|
|
|
var maxSerialNumber sql.NullString
|
|
|
|
|
if err := orm.Eloquent.Debug().Model(&ErpCommodity{}).
|
|
|
|
|
Where("erp_category_id = ?", categoryId).
|
|
|
|
|
Select("MAX(serial_number)").
|
|
|
|
|
Row().Scan(&maxSerialNumber); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理 NULL 值
|
|
|
|
|
var lastNumber uint64
|
|
|
|
|
if maxSerialNumber.Valid {
|
|
|
|
|
lastSerial := maxSerialNumber.String
|
|
|
|
|
lastNumberStr := lastSerial[len(category.Number):]
|
|
|
|
|
lastNumber, _ = strconv.ParseUint(lastNumberStr, 10, 32)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 递增生成新的商品编号
|
|
|
|
|
newNumber := lastNumber + 1
|
2023-11-10 09:49:44 +00:00
|
|
|
|
|
2024-03-16 08:19:12 +00:00
|
|
|
|
serialNumber := fmt.Sprintf("%s%04d", category.Number, newNumber)
|
2023-11-10 09:49:44 +00:00
|
|
|
|
fmt.Println("商品编号:", serialNumber)
|
|
|
|
|
return serialNumber, nil
|
|
|
|
|
}
|
2024-02-05 06:11:48 +00:00
|
|
|
|
|
|
|
|
|
// UpdateErpStockAmountInfo 更新库存和库存商品表的金额:指导零售价、最低零售价
|
2024-04-17 01:37:02 +00:00
|
|
|
|
func UpdateErpStockAmountInfo(begin *gorm.DB, commodityId uint32, retailPrice, minRetailPrice float64, barCode string,
|
2024-03-27 10:01:38 +00:00
|
|
|
|
category *ErpCategory) error {
|
|
|
|
|
if category != nil && category.ID != 0 { // 分类信息有值
|
|
|
|
|
// 更新库存表
|
|
|
|
|
err := begin.Table("erp_stock").Where("erp_commodity_id=?", commodityId).
|
|
|
|
|
Updates(map[string]interface{}{
|
|
|
|
|
"retail_price": retailPrice,
|
|
|
|
|
"min_retail_price": minRetailPrice,
|
|
|
|
|
"erp_category_id": category.ID,
|
|
|
|
|
"erp_category_name": category.Name,
|
|
|
|
|
}).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2024-02-05 06:11:48 +00:00
|
|
|
|
|
2024-03-27 10:01:38 +00:00
|
|
|
|
// 更新库存商品表
|
|
|
|
|
err = begin.Table("erp_stock_commodity").Where("erp_commodity_id=? and state not in (2,5)", commodityId).
|
|
|
|
|
Updates(map[string]interface{}{
|
|
|
|
|
"retail_price": retailPrice,
|
|
|
|
|
"min_retail_price": minRetailPrice,
|
|
|
|
|
//"staff_cost_price": staffCostPrice,
|
|
|
|
|
"erp_barcode": barCode,
|
|
|
|
|
"erp_category_id": category.ID,
|
|
|
|
|
"erp_category_name": category.Name,
|
|
|
|
|
}).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 更新库存表
|
|
|
|
|
err := begin.Table("erp_stock").Where("erp_commodity_id=?", commodityId).
|
|
|
|
|
Updates(map[string]interface{}{
|
|
|
|
|
"retail_price": retailPrice,
|
|
|
|
|
"min_retail_price": minRetailPrice,
|
|
|
|
|
}).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2024-02-05 06:11:48 +00:00
|
|
|
|
|
2024-03-27 10:01:38 +00:00
|
|
|
|
// 更新库存商品表
|
|
|
|
|
err = begin.Table("erp_stock_commodity").Where("erp_commodity_id=? and state not in (2,5)", commodityId).
|
|
|
|
|
Updates(map[string]interface{}{
|
|
|
|
|
"retail_price": retailPrice,
|
|
|
|
|
"min_retail_price": minRetailPrice,
|
|
|
|
|
//"staff_cost_price": staffCostPrice,
|
|
|
|
|
"erp_barcode": barCode,
|
|
|
|
|
}).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-05 06:11:48 +00:00
|
|
|
|
return nil
|
|
|
|
|
}
|
2024-02-23 10:06:21 +00:00
|
|
|
|
|
|
|
|
|
// CheckCommodityIsHavaStock 检查某个商品是否还有库存
|
|
|
|
|
func CheckCommodityIsHavaStock(commodityId uint32) bool {
|
|
|
|
|
var count int64
|
|
|
|
|
err := orm.Eloquent.Table("erp_stock").Where("erp_commodity_id = ? and count > 0", commodityId).
|
|
|
|
|
Count(&count).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("CheckCommodityIsHavaStock err:", logger.Field("err", err))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return count > 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsExistingCommodity 检查是否存在某个商品
|
|
|
|
|
func IsExistingCommodity(commodityId uint32) bool {
|
|
|
|
|
var count int64
|
|
|
|
|
err := orm.Eloquent.Table("erp_commodity").Where("id = ?", commodityId).
|
|
|
|
|
Count(&count).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("IsExistingCommodity err:", logger.Field("err", err))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return count > 0
|
|
|
|
|
}
|