617 lines
30 KiB
Go
617 lines
30 KiB
Go
package model
|
||
|
||
import (
|
||
"fmt"
|
||
"github.com/codinl/go-logger"
|
||
"github.com/jinzhu/gorm"
|
||
"math/rand"
|
||
"time"
|
||
)
|
||
|
||
const (
|
||
ErpOrderStateAudited = "audited" // 已审核
|
||
HavePaid = 2 // 已支付
|
||
NoPrint = 1 // 未打印
|
||
RetailTypeSale = "sale" // 零售销售订单
|
||
SysUserIdByAdmin = 1 // 系统管理员id
|
||
SysUserNameByAdmin = "系统管理员" // 系统管理员名称
|
||
SoldOut = 2 // 已售
|
||
)
|
||
|
||
// ErpStock 库存列表
|
||
type ErpStock struct {
|
||
Model
|
||
|
||
StoreId uint32 `json:"store_id" gorm:"index"` // 门店编号
|
||
StoreName string `json:"store_name"` // 门店名称
|
||
ErpCommodityId uint32 `json:"erp_commodity_id" gorm:"index"` // 商品id
|
||
ErpCommodityName string `json:"erp_commodity_name"` // 商品名称
|
||
ErpCategoryId uint32 `json:"erp_category_id" gorm:"index"` // 分类id
|
||
ErpCategoryName string `json:"erp_category_name"` // 分类名称
|
||
CommoditySerialNumber string `json:"commodity_serial_number" gorm:"index"` // 商品编码/串码
|
||
IMEIType uint32 `json:"imei_type"` // 1-无串码 2-串码(系统生成) 3-串码(手动添加)
|
||
RetailPrice float64 `json:"retail_price"` // 指导零售价
|
||
MinRetailPrice float64 `json:"min_retail_price"` // 最低零售价
|
||
Count uint32 `json:"count"` // 数量
|
||
DispatchCount uint32 `json:"dispatch_count"` // 调拨中数量(调拨中调入)
|
||
DecisionStoreId []uint32 `json:"decision_store_id" gorm:"-"` // 门店编号列表(查询进销存的时候使用)
|
||
Commodities []ErpStockCommodity `json:"commodities" gorm:"-"` //
|
||
}
|
||
|
||
// ErpStockCommodity 库存详情
|
||
type ErpStockCommodity struct {
|
||
Model
|
||
|
||
ErpStockId uint32 `json:"erp_stock_id" gorm:"index"` // 库存id
|
||
StoreId uint32 `json:"store_id" gorm:"index"` // 门店id
|
||
StoreName string `json:"store_name"` // 门店名称
|
||
ErpCommodityId uint32 `json:"erp_commodity_id" gorm:"index"` // 商品id
|
||
ErpCommodityName string `json:"erp_commodity_name"` // 商品名称
|
||
CommoditySerialNumber string `json:"commodity_serial_number" gorm:"index"` // 商品编号
|
||
ErpCategoryId uint32 `json:"erp_category_id" gorm:"index"` // 分类id
|
||
ErpCategoryName string `json:"erp_category_name"` // 分类名称
|
||
ErpBarcode string `json:"erp_barcode"` // 商品条码
|
||
IMEIType uint32 `json:"imei_type"` // 是否串码:1-无串码 2-串码(系统生成) 3-串码(手动添加)
|
||
IMEI string `json:"imei"` // 商品串码
|
||
ErpSupplierId uint32 `json:"erp_supplier_id" gorm:"index"` // 供应商id
|
||
ErpSupplierName string `json:"erp_supplier_name"` // 供应商名称
|
||
StockTime time.Time `json:"stock_time"` // 最近入库时间
|
||
RetailPrice float64 `json:"retail_price"` // 指导零售价
|
||
MinRetailPrice float64 `json:"min_retail_price"` // 最低零售价
|
||
StaffCostPrice float64 `json:"staff_cost_price"` // 员工成本价加价(如:加价50,不是加价后的价格)
|
||
WholesalePrice float64 `json:"wholesale_price"` // 指导采购价
|
||
MemberDiscount float64 `json:"member_discount"` // 会员优惠
|
||
State uint32 `json:"state"` // 状态:1-在库 2-已售 3-采购退货 4-调拨中 5-销售锁定中 6-产品出库 7-盘点出库
|
||
Count uint32 `json:"count"` // 数量
|
||
StorageType uint32 `json:"storage_type"` // 入库方式:1-系统入库 2-采购入库 3-产品入库 4-盘点入库
|
||
FirstStockTime time.Time `json:"first_stock_time"` // 首次入库时间
|
||
StockSn string `json:"stock_sn"` // 库存订单编号(跟采购入库的入库编号关联)
|
||
OriginalSn string `json:"original_sn" gorm:"index"` // 首次入库订单编号(单据编号)
|
||
StockStartTime time.Time `json:"stock_start_time" gorm:"-"` // 最近入库开始时间
|
||
StockEndTime time.Time `json:"stock_end_time" gorm:"-"` // 最近入库结束时间
|
||
Age uint32 `json:"age" gorm:"-"` // 最近库龄
|
||
AllAge uint32 `json:"all_age" gorm:"-"` // 总库龄
|
||
Remark string `json:"remark"` // 备注
|
||
CategoryNumber string `json:"category_number" gorm:"-"` // 商品分类编号
|
||
}
|
||
|
||
// ErpCommodity 商品表
|
||
type ErpCommodity struct {
|
||
Model
|
||
|
||
SerialNumber string `json:"serial_number"` // 商品编号
|
||
Number uint32 `json:"number"` // 商品数量
|
||
Name string `json:"name"` // 商品名称
|
||
ErpCategoryId uint32 `json:"erp_category_id" gorm:"index"` // 商品分类id
|
||
ErpCategoryName string `json:"erp_category_name"` // 商品分类名称
|
||
ErpBarcode string `json:"erp_barcode"` // 商品条码
|
||
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"` // 员工成本价加价(如:加价50,不是加价后的价格)
|
||
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)"` // 备注
|
||
Img string `json:"img"` // 图片
|
||
StopPurchase uint32 `json:"stop_purchase"` // 0-未勾选,正常采购;1-勾选,停止采购
|
||
ManufacturerCode string `json:"manufacturer_code"` // 厂家编码
|
||
IsSyncedToMall uint32 `json:"is_synced_to_mall"` // 是否同步到小程序商城:0-否;1-是
|
||
GoodsId uint32 `json:"goods_id"` // 小程序商城-商品ID
|
||
SaleStatus uint32 `json:"sale_status"` // 小程序商城-在售状态 1-在售 2-下架
|
||
|
||
ErpCategory *ErpCategory `json:"erp_category" gorm:"-"`
|
||
}
|
||
|
||
// ErpCategory 商品分类
|
||
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:"-"` // 子列表
|
||
}
|
||
|
||
// ErpOrder 零售订单表
|
||
type ErpOrder struct {
|
||
Model
|
||
|
||
BillSn string `json:"bill_sn" gorm:"index"` // 单据编号
|
||
RetailType string `json:"retail_type"` // 销售类型:sale 零售销售; rejected 零售退货
|
||
GoodsOrderId uint32 `json:"goods_order_id" gorm:"index"` // 小程序商城订单id
|
||
Uid int `json:"uid"` // 用户id
|
||
UserType int `json:"user_type"` // 会员类别:1-普通会员 2-黄金会员 4-白金会员 5-黑金会员 6-尊享会员 7-黄金&尊享 8-白金&尊享 9-黑金&尊享
|
||
Tel string `json:"tel" gorm:"index"` // 客户手机号
|
||
StoreId uint32 `json:"store_id" gorm:"index"` // 门店id
|
||
StoreName string `json:"store_name"` // 门店名称
|
||
MakerId uint32 `json:"maker_id" gorm:"index"` // 制单人id
|
||
MakerName string `json:"maker_name"` // 制单人名称
|
||
MakerTime time.Time `json:"maker_time"` // 制单时间
|
||
AuditorId uint32 `json:"auditor_id" gorm:"index"` // 审核人id
|
||
AuditorName string `json:"auditor_name"` // 审核人姓名
|
||
AuditTime *time.Time `json:"audit_time"` // 审核时间
|
||
CashierList string `json:"cashier_list" gorm:"type:text"` // 付款方式,存储json数据
|
||
SalesmanList string `json:"salesman_list" gorm:"type:text"` // 销售员信息,存储json数据
|
||
MemberType string `json:"member_type"` // 会员类型:general 普通; member 会员
|
||
State string `json:"state" gorm:"index"` // 订单状态:un_audit 待审核; audited 已审核
|
||
TotalRetailPrice float64 `json:"total_retail_price"` // 订单总指导零售价
|
||
TotalAmount float64 `json:"total_amount"` // 订单实收金额
|
||
TotalCount int32 `json:"total_count"` // 订单商品数量
|
||
TotalSalesProfit float64 `json:"total_sales_profit"` // 订单总销售毛利
|
||
TotalStaffProfit float64 `json:"total_staff_profit"` // 订单总员工毛利
|
||
VmCount uint32 `json:"vm_count"` // 使用会员积分
|
||
SaleOrderId uint32 `json:"sale_order_id"` // 销售订单id
|
||
PayStatus uint32 `json:"pay_status"` // 支付状态 0-未创建 ;1-待支付; 2-已支付
|
||
IsPrint uint32 `json:"is_print"` // 是否打印小票 1-未打印 2-已打印
|
||
PrintCount uint32 `json:"print_count"` // 小票打印次数
|
||
InvoiceCode string `json:"invoice_code"` // 发票代码
|
||
InvoiceNumber string `json:"invoice_number"` // 发票编码
|
||
RejectedTotalAmount float64 `json:"rejected_total_amount" gorm:"-"` // 订单总退货金额
|
||
RejectedTotalCount uint32 `json:"rejected_total_count" gorm:"-"` // 订单总退货数量
|
||
StorePer float64 `json:"store_per"` // 门店提成:订单总员工毛利X该门店设置好的提成比例,保留到小数后两位多余舍去
|
||
TotalDiscount float64 `json:"total_discount"` // 订单总优惠:订单所有商品零售优惠+会员优惠+会员积分抵扣之和
|
||
BankTrxNo string `json:"bank_trx_no" gorm:"-"` // 银行流水号
|
||
Commodities []ErpOrderCommodity `json:"commodities" gorm:"-"` // 零售订单商品信息
|
||
Cashiers []ErpOrderCashier `json:"cashiers" gorm:"-"` // 收付款方式
|
||
Salesman []ErpOrderSales `json:"salesman" gorm:"-"` // 销售员信息
|
||
}
|
||
|
||
// ErpOrderSales 销售员信息
|
||
type ErpOrderSales struct {
|
||
Model
|
||
|
||
ErpOrderId uint32 `json:"erp_order_id" gorm:"index"` // 零售订单id(后端生成)
|
||
Uid uint32 `json:"userId" binding:"required"` // 销售员用户ID(20240322:更换为系统用户表主键id)
|
||
Name string `json:"name"` // 销售员用户姓名
|
||
SalesProfitPer float64 `json:"sales_profit_per"` // 销售毛利提成:每个商品销售毛利X其对应的提成比例后求和;如果是两个销售员参与则分别除以2 ,保留到小数后两位多余舍去
|
||
StaffProfitPer float64 `json:"staff_profit_per"` // 员工毛利提成:每个商品员工毛利X其对应的提成比例后求和;如果是两个销售员参与则分别除以2 ,保留到小数后两位多余舍去
|
||
SalesmanPer float64 `json:"salesman_per"` // 销售员提成:订单总员工毛利X该销售员设置好的提成比例;如果是两个销售员参与,那么两个人算出的提成均除以2,保留到小数后两位多余舍去
|
||
}
|
||
|
||
// ErpOrderCashier 订单收款方式
|
||
type ErpOrderCashier struct {
|
||
CashierId uint32 `json:"cashier_id"` // 收付款方式id
|
||
Name string `json:"name"` // 收付款方式名称
|
||
Amount float64 `json:"amount"` // 金额
|
||
}
|
||
|
||
// ErpOrderCommodity 零售订单商品表
|
||
type ErpOrderCommodity struct {
|
||
Model
|
||
|
||
ErpOrderId uint32 `json:"erp_order_id" gorm:"index"` // 零售订单id(后端生成)
|
||
ErpCategoryId uint32 `json:"erp_category_id" gorm:"index"` // 分类id
|
||
ErpCategoryName string `json:"erp_category_name"` // 分类名称
|
||
ErpCommodityId uint32 `json:"erp_commodity_id" gorm:"index"` // 商品id
|
||
ErpCommodityName string `json:"erp_commodity_name"` // 商品名称
|
||
ErpSupplierId uint32 `json:"erp_supplier_id" gorm:"index"` // 主供应商id
|
||
ErpSupplierName string `json:"erp_supplier_name"` // 主供应商名称
|
||
IMEIType uint32 `json:"imei_type"` // 1-无串码 2-串码
|
||
IMEI string `json:"imei" gorm:"index"` // 串码
|
||
PresentType uint32 `json:"present_type"` // 赠送类型:1-非赠送 2-赠送
|
||
RetailPrice float64 `json:"retail_price"` // 指导零售价
|
||
SalePrice float64 `json:"sale_price"` // 零售价
|
||
Count int32 `json:"count"` // 销售数量
|
||
SaleDiscount float64 `json:"sale_discount"` // 零售优惠
|
||
MemberDiscount float64 `json:"member_discount"` // 会员优惠
|
||
VmDiscount float64 `json:"vm_discount"` // 会员积分抵扣
|
||
CouponID uint32 `json:"coupon_id"` // 优惠券ID
|
||
CouponDiscount float64 `json:"coupon_discount"` // 优惠券抵扣
|
||
CouponCode string `json:"coupon_code"` // 优惠券券码
|
||
CouponName string `json:"coupon_name"` // 优惠券名称
|
||
Amount float64 `json:"amount"` // 实际零售价
|
||
ReceivedAmount float64 `json:"received_amount"` // 商品实收金额
|
||
Remark string `json:"remark"` // 销售备注
|
||
RejectedRemark string `json:"rejected_remark"` // 退货备注
|
||
RejectedPrice float64 `json:"rejected_price"` // 退货单价
|
||
RejectedCount uint32 `json:"rejected_count"` // 退货数量
|
||
RejectedAmount float64 `json:"rejected_amount"` // 退货金额
|
||
RejectedOrderCommodityId uint32 `json:"rejected_order_commodity_id"` // 退货订单商品id
|
||
StaffCostPrice float64 `json:"staff_cost_price"` // 员工成本价加价(如:加价50,不是加价后的价格)
|
||
WholesalePrice float64 `json:"wholesale_price"` // 指导采购价
|
||
SalesProfit float64 `json:"sales_profit"` // 销售毛利:实际零售价-采购单价;如果为退货订单,则为实际退货价-采购单价
|
||
StaffProfit float64 `json:"staff_profit"` // 员工毛利:实际零售价-员工成本价;如果为退货订单,则为实际退货价-员工成本价
|
||
ErpStockCommodityID string `json:"erp_stock_commodity_id"` // 库存商品表主键id
|
||
StaffPrice float64 `json:"staff_price" gorm:"-"` // 员工成本价
|
||
CommoditySerialNumber string `json:"commodity_serial_number" gorm:"-"` // 商品编号
|
||
CategoryNumber string `json:"category_number" gorm:"-"` // 商品分类编号
|
||
}
|
||
|
||
// NewErpBillSn 生成零售订单号
|
||
func NewErpBillSn() string {
|
||
nowTime := time.Now()
|
||
rand.Seed(nowTime.UnixNano())
|
||
max := 1
|
||
for {
|
||
if max > 5 {
|
||
logger.Error("create sn err")
|
||
return ""
|
||
}
|
||
random := rand.Int31n(9999) + 1000
|
||
sn := fmt.Sprintf("%s%d", nowTime.Format("060102"), random)
|
||
exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_order WHERE bill_sn='%s'", sn))
|
||
if err != nil {
|
||
logger.Error("exist sn err")
|
||
}
|
||
if !exist {
|
||
return sn
|
||
}
|
||
|
||
max++
|
||
}
|
||
}
|
||
|
||
// SelectDeliveryStock 选择发货库存
|
||
// commodityId: 商品ID
|
||
// storeId: 指定门店ID(0 表示不指定)
|
||
// return: 选中的库存记录
|
||
func SelectDeliveryStock(commodityId, storeId uint32) (*ErpStockCommodity, error) {
|
||
// 先校验门店
|
||
if storeId == 0 {
|
||
return nil, fmt.Errorf("无法确定最近门店,请先设置 NearestStoreId")
|
||
}
|
||
|
||
var stock ErpStockCommodity
|
||
|
||
// 1️⃣ 优先指定门店
|
||
err := DB.Table("erp_stock_commodity").
|
||
Where("erp_commodity_id = ? AND store_id = ? AND state = 1 AND count > 0",
|
||
commodityId, storeId).
|
||
Order("first_stock_time ASC").
|
||
First(&stock).Error
|
||
if err == nil {
|
||
return &stock, nil
|
||
}
|
||
|
||
// 2️⃣ 如果指定门店无库存,则报错(不允许跨门店)
|
||
return nil, fmt.Errorf("商品 %d 在门店 %d 无可用库存", commodityId, storeId)
|
||
}
|
||
|
||
//func SelectDeliveryStock(commodityId, storeId uint32) (*ErpStockCommodity, error) {
|
||
// var stock ErpStockCommodity
|
||
//
|
||
// // 1️⃣ 优先指定门店
|
||
// if storeId > 0 {
|
||
// err := DB.Table("erp_stock_commodity").
|
||
// Where("erp_commodity_id = ? AND store_id = ? AND state = 1 AND count > 0", commodityId, storeId).
|
||
// Order("first_stock_time ASC").
|
||
// First(&stock).Error
|
||
// if err == nil {
|
||
// return &stock, nil
|
||
// }
|
||
// }
|
||
//
|
||
// // 2️⃣ 其他门店,优先库龄最久
|
||
// var otherStocks []ErpStockCommodity
|
||
// err := DB.Table("erp_stock_commodity").
|
||
// Where("erp_commodity_id = ? AND state = 1 AND count > 0", commodityId).
|
||
// Order("first_stock_time ASC").
|
||
// Find(&otherStocks).Error
|
||
// if err != nil {
|
||
// return nil, err
|
||
// }
|
||
// if len(otherStocks) == 0 {
|
||
// return nil, fmt.Errorf("无可用库存")
|
||
// }
|
||
//
|
||
// // 3️⃣ 多个相同库龄,随机一个
|
||
// oldestTime := otherStocks[0].FirstStockTime
|
||
// var oldestStocks []ErpStockCommodity
|
||
// for _, s := range otherStocks {
|
||
// if s.FirstStockTime.Equal(oldestTime) {
|
||
// oldestStocks = append(oldestStocks, s)
|
||
// } else {
|
||
// break
|
||
// }
|
||
// }
|
||
//
|
||
// rand.Seed(time.Now().UnixNano())
|
||
// chosen := oldestStocks[rand.Intn(len(oldestStocks))]
|
||
// return &chosen, nil
|
||
//}
|
||
|
||
//// CreateErpOrder 根据小程序订单生成ERP零售订单
|
||
//func CreateErpOrder(goodsOrder *GoodsOrder, goods *Goods) error {
|
||
// logger.Infof("enter CreateErpOrder")
|
||
// logger.Infof("goods: %+v", goods)
|
||
// logger.Infof("goodsOrder: %+v", goodsOrder)
|
||
// tx := DB.Begin()
|
||
// if tx.Error != nil {
|
||
// return tx.Error
|
||
// }
|
||
//
|
||
// defer func() {
|
||
// if r := recover(); r != nil {
|
||
// logger.Errorf("事务 panic 回滚: %v", r)
|
||
// tx.Rollback()
|
||
// }
|
||
// }()
|
||
//
|
||
// // 1. 查找用户信息
|
||
// var user User
|
||
// if err := tx.Where("uid = ?", goodsOrder.Uid).First(&user).Error; err != nil {
|
||
// logger.Infof("用户 %d 不存在", goodsOrder.Uid)
|
||
// return fmt.Errorf("用户不存在: %w", err)
|
||
// }
|
||
//
|
||
// var orderCommodities []ErpOrderCommodity
|
||
// var totalAmount, totalRetailPrice, totalSalesProfit, totalStaffProfit float64
|
||
// var totalCount int32
|
||
//
|
||
// // 遍历小程序订单的商品明细
|
||
// // 选择库存:优先指定门店 → 其他门店库龄最久
|
||
// stock, err := SelectDeliveryStock(goods.ErpCommodityId, goodsOrder.NearestStoreId)
|
||
// if err != nil {
|
||
// tx.Rollback()
|
||
// logger.Infof("商品 %d 无库存: %v", goods.ErpCommodityId, err)
|
||
// return fmt.Errorf("商品 %d 无库存: %v", goods.ErpCommodityId, err)
|
||
// }
|
||
// logger.Infof("stock: %+v", stock)
|
||
//
|
||
// // 扣减库存
|
||
// err = tx.Table("erp_stock_commodity").Where("id = ?", stock.ID).
|
||
// Updates(map[string]interface{}{
|
||
// "state": SoldOut,
|
||
// "updated_at": time.Now(),
|
||
// }).Error // 状态更新为已销售
|
||
// if err != nil {
|
||
// logger.Error("update erp_stock_commodity err:", err)
|
||
// tx.Rollback()
|
||
// return fmt.Errorf("库存扣减失败: %v", err)
|
||
// }
|
||
//
|
||
// err = tx.Table("erp_stock").Where("store_id = ? and erp_commodity_id = ? AND count > 0",
|
||
// stock.StoreId, stock.ErpCommodityId).
|
||
// Updates(map[string]interface{}{
|
||
// "count": gorm.Expr("count - ?", 1),
|
||
// "updated_at": time.Now(),
|
||
// }).Error // 库存数量-1
|
||
// if err != nil {
|
||
// logger.Error("update erp_stock err:", err)
|
||
// tx.Rollback()
|
||
// return fmt.Errorf("库存不足")
|
||
// }
|
||
//
|
||
// // 金额计算
|
||
// receivedAmount := float64(goodsOrder.Quantity) * float64(goods.PriceRm/100)
|
||
// salesProfit := receivedAmount - stock.WholesalePrice*float64(goodsOrder.Quantity)
|
||
// staffProfit := receivedAmount - (stock.WholesalePrice+stock.StaffCostPrice)*float64(goodsOrder.Quantity)
|
||
//
|
||
// orderCommodities = append(orderCommodities, ErpOrderCommodity{
|
||
// ErpCategoryId: stock.ErpCategoryId,
|
||
// ErpCategoryName: stock.ErpCategoryName,
|
||
// ErpCommodityId: stock.ErpCommodityId,
|
||
// ErpCommodityName: stock.ErpCommodityName,
|
||
// ErpSupplierId: stock.ErpSupplierId,
|
||
// ErpSupplierName: stock.ErpSupplierName,
|
||
// IMEIType: stock.IMEIType,
|
||
// IMEI: "",
|
||
// PresentType: 1,
|
||
// RetailPrice: stock.RetailPrice,
|
||
// SalePrice: float64(goods.PriceRm / 100),
|
||
// Count: int32(goodsOrder.Quantity),
|
||
// Amount: receivedAmount,
|
||
// ReceivedAmount: receivedAmount,
|
||
// StaffCostPrice: stock.StaffCostPrice,
|
||
// WholesalePrice: stock.WholesalePrice,
|
||
// SalesProfit: salesProfit,
|
||
// StaffProfit: staffProfit,
|
||
// ErpStockCommodityID: fmt.Sprintf("%d", stock.ID),
|
||
// })
|
||
// logger.Infof("orderCommodities 长度: %d", len(orderCommodities))
|
||
//
|
||
// totalAmount += receivedAmount
|
||
// totalRetailPrice += stock.RetailPrice * float64(goodsOrder.Quantity)
|
||
// totalSalesProfit += salesProfit
|
||
// totalStaffProfit += staffProfit
|
||
// totalCount += int32(goodsOrder.Quantity)
|
||
//
|
||
// nowTime := time.Now()
|
||
// // ERP订单
|
||
// order := ErpOrder{
|
||
// BillSn: NewErpBillSn(),
|
||
// GoodsOrderId: goodsOrder.OrderId,
|
||
// RetailType: RetailTypeSale,
|
||
// Uid: int(goodsOrder.Uid),
|
||
// UserType: int(user.MemberLevel),
|
||
// Tel: user.Tel,
|
||
// StoreId: stock.StoreId,
|
||
// StoreName: stock.StoreName,
|
||
// MakerId: SysUserIdByAdmin,
|
||
// MakerName: SysUserNameByAdmin,
|
||
// MakerTime: nowTime,
|
||
// AuditorId: SysUserIdByAdmin,
|
||
// AuditorName: SysUserNameByAdmin,
|
||
// AuditTime: &nowTime,
|
||
// State: ErpOrderStateAudited,
|
||
// TotalRetailPrice: totalRetailPrice,
|
||
// TotalAmount: totalAmount,
|
||
// TotalCount: totalCount,
|
||
// TotalSalesProfit: totalSalesProfit,
|
||
// TotalStaffProfit: totalStaffProfit,
|
||
// PayStatus: HavePaid, // 已支付
|
||
// IsPrint: NoPrint, // 未打印
|
||
// PrintCount: 0,
|
||
// }
|
||
//
|
||
// if err = tx.Create(&order).Error; err != nil {
|
||
// logger.Infof("新建erp零售订单失败: %v", err)
|
||
// tx.Rollback()
|
||
// return fmt.Errorf("新建erp零售订单失败: %v", err)
|
||
// }
|
||
//
|
||
// // 绑定订单ID
|
||
// for i := range orderCommodities {
|
||
// orderCommodities[i].ErpOrderId = order.ID
|
||
// if err = tx.Create(&orderCommodities[i]).Error; err != nil {
|
||
// logger.Errorf("新建erp零售订单商品信息失败: %v", err)
|
||
// tx.Rollback()
|
||
// return fmt.Errorf("新建erp零售订单商品信息失败: %v", err)
|
||
// }
|
||
// }
|
||
//
|
||
// if err = tx.Commit().Error; err != nil {
|
||
// logger.Infof("提交事务失败: %v", err)
|
||
// return fmt.Errorf("提交事务失败: %v", err)
|
||
// }
|
||
//
|
||
// logger.Infof("leave CreateErpOrder")
|
||
// return nil
|
||
//}
|
||
|
||
// CreateErpOrder 根据小程序订单生成ERP零售订单(支持多商品)
|
||
func CreateErpOrder(goodsOrder *GoodsOrder, commodities []GoodsOrderCommodity) error {
|
||
logger.Infof("enter CreateErpOrder")
|
||
tx := DB.Begin()
|
||
if tx.Error != nil {
|
||
return tx.Error
|
||
}
|
||
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
logger.Errorf("事务 panic 回滚: %v", r)
|
||
tx.Rollback()
|
||
}
|
||
}()
|
||
|
||
// 1. 查找用户信息
|
||
var user User
|
||
if err := tx.Where("uid = ?", goodsOrder.Uid).First(&user).Error; err != nil {
|
||
logger.Infof("用户 %d 不存在", goodsOrder.Uid)
|
||
return fmt.Errorf("用户不存在: %w", err)
|
||
}
|
||
|
||
var orderCommodities []ErpOrderCommodity
|
||
var totalAmount, totalRetailPrice, totalSalesProfit, totalStaffProfit float64
|
||
var totalCount int32
|
||
|
||
for _, commodity := range commodities {
|
||
var goods Goods
|
||
err := NewGoodsQuerySet(tx).GoodsIdEq(commodity.GoodsId).One(&goods)
|
||
if err != nil {
|
||
logger.Errorf("商品不存在: %d, err: %v", commodity.GoodsId, err)
|
||
tx.Rollback()
|
||
return err
|
||
}
|
||
|
||
// 选择库存
|
||
stock, err := SelectDeliveryStock(goods.ErpCommodityId, goodsOrder.NearestStoreId)
|
||
if err != nil {
|
||
tx.Rollback()
|
||
logger.Infof("商品 %d 无库存: %v", goods.ErpCommodityId, err)
|
||
return fmt.Errorf("商品 %d 无库存: %v", goods.ErpCommodityId, err)
|
||
}
|
||
|
||
// 扣减库存
|
||
err = tx.Table("erp_stock_commodity").Where("id = ?", stock.ID).
|
||
Updates(map[string]interface{}{"state": SoldOut, "updated_at": time.Now()}).Error
|
||
if err != nil {
|
||
tx.Rollback()
|
||
return fmt.Errorf("erp_stock_commodity 扣减库存失败: %v", err)
|
||
}
|
||
|
||
err = tx.Table("erp_stock").Where("store_id = ? AND erp_commodity_id = ? AND count > 0",
|
||
stock.StoreId, stock.ErpCommodityId).Updates(map[string]interface{}{
|
||
"count": gorm.Expr("count - ?", commodity.Quantity),
|
||
"updated_at": time.Now(),
|
||
}).Error
|
||
if err != nil {
|
||
tx.Rollback()
|
||
return fmt.Errorf("erp_stock 扣减库存失败: %v", err)
|
||
}
|
||
|
||
// 金额计算
|
||
receivedAmount := float64(commodity.Quantity) * float64(goods.PriceRm/100)
|
||
salesProfit := receivedAmount - stock.WholesalePrice*float64(commodity.Quantity)
|
||
staffProfit := receivedAmount - (stock.WholesalePrice+stock.StaffCostPrice)*float64(commodity.Quantity)
|
||
|
||
orderCommodities = append(orderCommodities, ErpOrderCommodity{
|
||
ErpCategoryId: stock.ErpCategoryId,
|
||
ErpCategoryName: stock.ErpCategoryName,
|
||
ErpCommodityId: stock.ErpCommodityId,
|
||
ErpCommodityName: stock.ErpCommodityName,
|
||
ErpSupplierId: stock.ErpSupplierId,
|
||
ErpSupplierName: stock.ErpSupplierName,
|
||
IMEIType: stock.IMEIType,
|
||
IMEI: "",
|
||
PresentType: 1,
|
||
RetailPrice: stock.RetailPrice,
|
||
SalePrice: float64(goods.PriceRm / 100),
|
||
Count: int32(commodity.Quantity),
|
||
Amount: receivedAmount,
|
||
ReceivedAmount: receivedAmount,
|
||
StaffCostPrice: stock.StaffCostPrice,
|
||
WholesalePrice: stock.WholesalePrice,
|
||
SalesProfit: salesProfit,
|
||
StaffProfit: staffProfit,
|
||
ErpStockCommodityID: fmt.Sprintf("%d", stock.ID),
|
||
})
|
||
|
||
totalAmount += receivedAmount
|
||
totalRetailPrice += stock.RetailPrice * float64(commodity.Quantity)
|
||
totalSalesProfit += salesProfit
|
||
totalStaffProfit += staffProfit
|
||
totalCount += int32(commodity.Quantity)
|
||
}
|
||
|
||
// 创建ERP订单
|
||
nowTime := time.Now()
|
||
order := ErpOrder{
|
||
BillSn: NewErpBillSn(),
|
||
GoodsOrderId: goodsOrder.OrderId,
|
||
RetailType: RetailTypeSale,
|
||
Uid: int(goodsOrder.Uid),
|
||
UserType: int(user.MemberLevel),
|
||
Tel: user.Tel,
|
||
StoreId: goodsOrder.NearestStoreId,
|
||
StoreName: goodsOrder.NearestStoreName,
|
||
MakerId: SysUserIdByAdmin,
|
||
MakerName: SysUserNameByAdmin,
|
||
MakerTime: nowTime,
|
||
AuditorId: SysUserIdByAdmin,
|
||
AuditorName: SysUserNameByAdmin,
|
||
AuditTime: &nowTime,
|
||
State: ErpOrderStateAudited,
|
||
TotalRetailPrice: totalRetailPrice,
|
||
TotalAmount: totalAmount,
|
||
TotalCount: totalCount,
|
||
TotalSalesProfit: totalSalesProfit,
|
||
TotalStaffProfit: totalStaffProfit,
|
||
PayStatus: HavePaid,
|
||
IsPrint: NoPrint,
|
||
PrintCount: 0,
|
||
}
|
||
|
||
if err := tx.Create(&order).Error; err != nil {
|
||
tx.Rollback()
|
||
return fmt.Errorf("创建ERP订单失败: %v", err)
|
||
}
|
||
|
||
// 绑定商品明细
|
||
for i := range orderCommodities {
|
||
orderCommodities[i].ErpOrderId = order.ID
|
||
if err := tx.Create(&orderCommodities[i]).Error; err != nil {
|
||
tx.Rollback()
|
||
return fmt.Errorf("创建ERP商品明细失败: %v", err)
|
||
}
|
||
}
|
||
|
||
if err := tx.Commit().Error; err != nil {
|
||
return fmt.Errorf("提交事务失败: %v", err)
|
||
}
|
||
|
||
logger.Infof("leave CreateErpOrder")
|
||
return nil
|
||
}
|