feat: 采购价格仅展示选定规格 (#113)
This commit is contained in:
@@ -157,8 +157,10 @@ func mappingIsValid(context repository.SybOrderContext) bool {
|
||||
|
||||
// PurchaseTaskRequest 是一条采购任务的人工确认输入。价格单位固定为人民币分。
|
||||
type PurchaseTaskRequest struct {
|
||||
SybID string
|
||||
MaxPriceCent int64
|
||||
SybID string
|
||||
MaxPriceCent int64
|
||||
MappingOptionKey string
|
||||
ContextVersion string
|
||||
}
|
||||
|
||||
const LivePurchaseConfirmation = "创建未付款订单"
|
||||
@@ -308,6 +310,12 @@ func validatePurchaseRequest(q repository.Execer, request PurchaseTaskRequest) (
|
||||
if choice == nil {
|
||||
return "已保存的 PDD 规格已失效,请重新匹配", context, "", nil
|
||||
}
|
||||
if request.MappingOptionKey != "" && request.MappingOptionKey != context.MappingOptionKey {
|
||||
return "规格映射已变化,请刷新页面后重新确认", context, "", nil
|
||||
}
|
||||
if request.ContextVersion != "" && request.ContextVersion != mappingContextVersion(*context) {
|
||||
return "商品或规格数据已变化,请刷新页面后重新确认", context, "", nil
|
||||
}
|
||||
if context.Order.Quantity <= 0 {
|
||||
return "采购数量必须大于 0", context, "", nil
|
||||
}
|
||||
|
||||
@@ -146,6 +146,11 @@ func TestSybMapping_动态维度保存并复用(t *testing.T) {
|
||||
if list.Total != 1 || !list.Rows[0].CanPurchase || list.Rows[0].DefaultMaxPrice != "39.90" {
|
||||
t.Fatalf("可采购阶段不对: %+v", list)
|
||||
}
|
||||
row := list.Rows[0]
|
||||
if row.MappedPddChoice != "颜色:黑色 / 尺码:M码 / 款式:常规" ||
|
||||
strings.Contains(row.MappedPddChoice, "白色") || row.MappingOptionKey != key || row.ContextVersion == "" {
|
||||
t.Fatalf("采购确认只能带出当前映射组合,实际 %+v", row)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSybMapping_同商品同规格第二条明细自动命中(t *testing.T) {
|
||||
@@ -446,6 +451,31 @@ func TestCreatePurchaseTasksWithOptions_页面打开后客户端离线会被拒
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreatePurchaseTasksWithOptions_拒绝弹窗打开后变化的规格映射(t *testing.T) {
|
||||
db := newTestDB(t)
|
||||
blackKey := seedPurchasableWorkflow(t, db, "SYB-STALE")
|
||||
SaveSybMapping(db, "SYB-STALE", blackKey, "USR-1")
|
||||
context, err := repository.GetSybOrderContext(db, "SYB-STALE")
|
||||
if err != nil || context == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
oldVersion := mappingContextVersion(*context)
|
||||
whiteKey, _ := OptionKey(map[string]string{"color": "白色", "size": "L码", "style": "加绒"})
|
||||
SaveSybMapping(db, "SYB-STALE", whiteKey, "USR-2")
|
||||
admin, _, _ := prepareClientAssignmentUsers(t, db)
|
||||
RegisterClient(db, model.Client{ClientID: "CLIENT-LIVE", Capabilities: `{"purchase_mode":"live"}`}, true)
|
||||
|
||||
result, err := CreatePurchaseTasksWithOptions(db, admin,
|
||||
[]PurchaseTaskRequest{{SybID: "SYB-STALE", MaxPriceCent: 3990,
|
||||
MappingOptionKey: blackKey, ContextVersion: oldVersion}},
|
||||
PurchaseTaskOptions{ClientID: "CLIENT-LIVE", ExecutionMode: model.TaskExecutionLive,
|
||||
LiveAcknowledged: true, LiveConfirmation: LivePurchaseConfirmation})
|
||||
if err != nil || result.Created != 0 || len(result.Failures) != 1 ||
|
||||
!strings.Contains(result.Failures[0].Reason, "已变化") {
|
||||
t.Fatalf("旧弹窗必须因映射变化被拒绝: result=%+v err=%v", result, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePriceYuanToCent_精确转换(t *testing.T) {
|
||||
for raw, want := range map[string]int64{"39.90": 3990, "1": 100, "0.01": 1, "12.3": 1230} {
|
||||
got, err := ParsePriceYuanToCent(raw)
|
||||
|
||||
+30
-28
@@ -917,22 +917,25 @@ func EnsureSybSession(db *sql.DB, client *syb.Client, username string, now time.
|
||||
// SybOrderView 是列表页一行要显示的全部内容,已经格式化成字符串,
|
||||
// 模板里不做判断和格式化,和其余四个模块的做法一致。
|
||||
type SybOrderView struct {
|
||||
SybID string
|
||||
OrderNo string
|
||||
Title string
|
||||
ProductSpec string
|
||||
ShopeeGoodsID string
|
||||
Quantity int
|
||||
PriceText string // "NT$239.00",和人民币价格一眼分得清
|
||||
ImageURL string
|
||||
Stage string
|
||||
StageText string
|
||||
StageHelp string
|
||||
ActionText string
|
||||
NeedsAttention bool
|
||||
CanPurchase bool
|
||||
DefaultMaxPrice string
|
||||
UpdatedAt string
|
||||
SybID string
|
||||
OrderNo string
|
||||
Title string
|
||||
ProductSpec string
|
||||
ShopeeGoodsID string
|
||||
Quantity int
|
||||
PriceText string // "NT$239.00",和人民币价格一眼分得清
|
||||
ImageURL string
|
||||
Stage string
|
||||
StageText string
|
||||
StageHelp string
|
||||
ActionText string
|
||||
NeedsAttention bool
|
||||
CanPurchase bool
|
||||
DefaultMaxPrice string
|
||||
MappedPddChoice string
|
||||
MappingOptionKey string
|
||||
ContextVersion string
|
||||
UpdatedAt string
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -1117,7 +1120,17 @@ func sybOrderViewFor(context repository.SybOrderContext) SybOrderView {
|
||||
UpdatedAt: formatLocalTime(o.UpdatedAt)}
|
||||
v.Stage, v.StageText, v.StageHelp, v.ActionText = sybStageFor(context)
|
||||
v.CanPurchase = v.Stage == SybStagePurchaseReady
|
||||
v.DefaultMaxPrice = defaultMaxPrice(context)
|
||||
if v.CanPurchase {
|
||||
v.MappingOptionKey = context.MappingOptionKey
|
||||
v.ContextVersion = mappingContextVersion(context)
|
||||
choice, err := findPddChoice(context.PddSkusJSON, context.MappingOptionKey)
|
||||
if err == nil && choice != nil {
|
||||
v.MappedPddChoice = choice.Label
|
||||
if choice.HasPrice {
|
||||
v.DefaultMaxPrice = fmt.Sprintf("%d.%02d", choice.PriceCent/100, choice.PriceCent%100)
|
||||
}
|
||||
}
|
||||
}
|
||||
v.NeedsAttention = v.Stage != SybStagePddCollecting && v.Stage != SybStageTaskCreated
|
||||
if o.PriceTwdCent > 0 {
|
||||
v.PriceText = fmt.Sprintf("NT$%.2f", float64(o.PriceTwdCent)/100)
|
||||
@@ -1133,17 +1146,6 @@ func sybOrderViewFor(context repository.SybOrderContext) SybOrderView {
|
||||
return v
|
||||
}
|
||||
|
||||
func defaultMaxPrice(context repository.SybOrderContext) string {
|
||||
if !mappingIsValid(context) {
|
||||
return ""
|
||||
}
|
||||
choice, err := findPddChoice(context.PddSkusJSON, context.MappingOptionKey)
|
||||
if err != nil || choice == nil || !choice.HasPrice {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%d.%02d", choice.PriceCent/100, choice.PriceCent%100)
|
||||
}
|
||||
|
||||
// SybProcessingDetail 是顺运宝“下一步”弹窗第一阶段需要的上下文。
|
||||
type SybProcessingDetail struct {
|
||||
SybID string
|
||||
|
||||
Reference in New Issue
Block a user