fix: 多件档口入库码逐件回写 (#250)

This commit is contained in:
chengma
2026-08-18 15:18:35 +08:00
parent d2acbd821c
commit b8f49fe4bb
14 changed files with 547 additions and 111 deletions
+111 -22
View File
@@ -13,14 +13,17 @@ import (
)
type fakeInnerCodeWriter struct {
stock syb.StockDetail
readErr error
deleteErr error
updateErr error
readCount int
deleteCount int
updateCount int
updatedCode string
stock syb.StockDetail
readErr error
deleteErr error
createErr error
updateErr error
readCount int
deleteCount int
createCount int
updateCount int
updatedCode string
nextDetailID int64
}
func TestQueueInnerCodeApplyBatch_超过20条可一次排队(t *testing.T) {
@@ -81,6 +84,20 @@ func (f *fakeInnerCodeWriter) DeleteInnerCode(_ context.Context, detailID int64)
return nil
}
func (f *fakeInnerCodeWriter) CreateInnerCodeDetail(_ context.Context, stockID int64, title string) (int64, error) {
f.createCount++
if f.createErr != nil {
return 0, f.createErr
}
if f.nextDetailID == 0 {
f.nextDetailID = 100
}
f.nextDetailID++
f.stock.Details = append(f.stock.Details, syb.DetailItem{ID: f.nextDetailID, ProductTitle: title,
ProductQty: 1, ProductPrice: 0, Raw: map[string]any{"innerExpCode": ""}})
return f.nextDetailID, nil
}
func (f *fakeInnerCodeWriter) UpdateDetailCode(_ context.Context, _, detailID int64, code string) error {
f.updateCount++
f.updatedCode = code
@@ -95,21 +112,93 @@ func (f *fakeInnerCodeWriter) UpdateDetailCode(_ context.Context, _, detailID in
return nil
}
func TestApplyClaimedInnerCode_聚合码只写一次并按完整值复读(t *testing.T) {
func TestApplyClaimedInnerCode_两个单件码写入两个明细(t *testing.T) {
record := innerCodeApplyTestRecord("")
record.InnerCode = "DK260815A160101,DK260815A160102"
record.SourceDuplicateCount = 2
writer := innerCodeApplyTestWriter(record, "")
outcome := applyClaimedInnerCode(context.Background(), writer, record)
if outcome.Status != model.InnerCodeUpdated || writer.updateCount != 1 || writer.updatedCode != record.InnerCode ||
outcome.RemoteCode != record.InnerCode || writer.readCount != 2 {
t.Fatalf("outcome=%+v update=%d code=%q read=%d", outcome, writer.updateCount, writer.updatedCode, writer.readCount)
outcome := applyClaimedInnerCode(context.Background(), writer, record, nil)
if outcome.Status != model.InnerCodeUpdated || writer.updateCount != 2 || writer.createCount != 1 ||
outcome.RemoteCode != record.InnerCode || writer.readCount != 3 {
t.Fatalf("outcome=%+v update=%d create=%d read=%d", outcome, writer.updateCount, writer.createCount, writer.readCount)
}
}
func TestApplyClaimedInnerCode_三个单件码创建两条零价明细(t *testing.T) {
record := innerCodeApplyTestRecord("")
record.InnerCode = "DK-001,DK-002,DK-003"
record.SourceDuplicateCount = 3
writer := innerCodeApplyTestWriter(record, "")
outcome := applyClaimedInnerCode(context.Background(), writer, record, nil)
if outcome.Status != model.InnerCodeUpdated || writer.updateCount != 3 || writer.createCount != 2 {
t.Fatalf("outcome=%+v update=%d create=%d", outcome, writer.updateCount, writer.createCount)
}
for _, detail := range writer.stock.Details[1:] {
if detail.ProductQty != 1 || detail.ProductPrice != 0 {
t.Fatalf("占位明细必须数量 1、价格 0: %+v", detail)
}
}
}
func TestApplyClaimedInnerCode_复用人工明细已有目标码(t *testing.T) {
record := innerCodeApplyTestRecord("")
record.InnerCode = "DK-001,DK-002"
record.SourceDuplicateCount = 2
writer := innerCodeApplyTestWriter(record, "")
writer.stock.Details = append(writer.stock.Details, syb.DetailItem{ID: 21, ProductTitle: "人工明细", ProductQty: 1,
Raw: map[string]any{"innerExpCode": "DK-002"}})
outcome := applyClaimedInnerCode(context.Background(), writer, record, nil)
if outcome.Status != model.InnerCodeUpdated || writer.updateCount != 1 || writer.createCount != 0 || writer.deleteCount != 0 {
t.Fatalf("outcome=%+v update=%d create=%d delete=%d", outcome, writer.updateCount, writer.createCount, writer.deleteCount)
}
}
func TestApplyClaimedInnerCode_数量不一致零写入(t *testing.T) {
record := innerCodeApplyTestRecord("")
record.InnerCode = "DK-001,DK-002"
record.SourceDuplicateCount = 2
writer := innerCodeApplyTestWriter(record, "")
writer.stock.Details[0].ProductQty = 1
outcome := applyClaimedInnerCode(context.Background(), writer, record, nil)
if outcome.Status != model.InnerCodeFailed || writer.updateCount != 0 || writer.createCount != 0 || writer.deleteCount != 0 {
t.Fatalf("outcome=%+v update=%d create=%d delete=%d", outcome, writer.updateCount, writer.createCount, writer.deleteCount)
}
}
func TestApplyClaimedInnerCode_创建结果未知立即停止(t *testing.T) {
record := innerCodeApplyTestRecord("")
record.InnerCode = "DK-001,DK-002"
record.SourceDuplicateCount = 2
writer := innerCodeApplyTestWriter(record, "")
writer.createErr = fmtUnknownInnerCodeWriteError()
checkpoints := 0
outcome := applyClaimedInnerCode(context.Background(), writer, record, func([]innerCodeRemoteItem, string) error {
checkpoints++
return nil
})
if outcome.Status != model.InnerCodeNeedsCheck || writer.createCount != 1 || writer.updateCount != 1 || checkpoints < 3 {
t.Fatalf("outcome=%+v create=%d update=%d checkpoints=%d", outcome, writer.createCount, writer.updateCount, checkpoints)
}
}
func TestApplyClaimedInnerCode_崩溃遗留占位明细会复用而不重复创建(t *testing.T) {
record := innerCodeApplyTestRecord("")
record.InnerCode = "DK-001,DK-002"
record.SourceDuplicateCount = 2
writer := innerCodeApplyTestWriter(record, "")
writer.stock.Details = append(writer.stock.Details, syb.DetailItem{ID: 99,
ProductTitle: innerCodePlaceholderTitle(record.ID, 2), ProductQty: 1, ProductPrice: 0,
Raw: map[string]any{"innerExpCode": ""}})
outcome := applyClaimedInnerCode(context.Background(), writer, record, nil)
if outcome.Status != model.InnerCodeUpdated || writer.createCount != 0 || writer.updateCount != 2 {
t.Fatalf("outcome=%+v create=%d update=%d", outcome, writer.createCount, writer.updateCount)
}
}
func TestApplyClaimedInnerCode_空旧值直接写并复读确认(t *testing.T) {
record := innerCodeApplyTestRecord("")
writer := innerCodeApplyTestWriter(record, "")
outcome := applyClaimedInnerCode(context.Background(), writer, record)
outcome := applyClaimedInnerCode(context.Background(), writer, record, nil)
if outcome.Status != model.InnerCodeUpdated || writer.deleteCount != 0 || writer.updateCount != 1 || writer.readCount != 2 {
t.Fatalf("outcome=%+v counts read=%d delete=%d update=%d", outcome, writer.readCount, writer.deleteCount, writer.updateCount)
}
@@ -118,7 +207,7 @@ func TestApplyClaimedInnerCode_空旧值直接写并复读确认(t *testing.T) {
func TestApplyClaimedInnerCode_有旧值先删后写(t *testing.T) {
record := innerCodeApplyTestRecord("OLD")
writer := innerCodeApplyTestWriter(record, "OLD")
outcome := applyClaimedInnerCode(context.Background(), writer, record)
outcome := applyClaimedInnerCode(context.Background(), writer, record, nil)
if outcome.Status != model.InnerCodeUpdated || writer.deleteCount != 1 || writer.updateCount != 1 {
t.Fatalf("outcome=%+v delete=%d update=%d", outcome, writer.deleteCount, writer.updateCount)
}
@@ -127,7 +216,7 @@ func TestApplyClaimedInnerCode_有旧值先删后写(t *testing.T) {
func TestApplyClaimedInnerCode_规划后远端值变化不写入(t *testing.T) {
record := innerCodeApplyTestRecord("OLD")
writer := innerCodeApplyTestWriter(record, "OTHER")
outcome := applyClaimedInnerCode(context.Background(), writer, record)
outcome := applyClaimedInnerCode(context.Background(), writer, record, nil)
if outcome.Status != model.InnerCodeNeedsCheck || writer.deleteCount != 0 || writer.updateCount != 0 {
t.Fatalf("outcome=%+v delete=%d update=%d", outcome, writer.deleteCount, writer.updateCount)
}
@@ -137,7 +226,7 @@ func TestApplyClaimedInnerCode_删除明确失败不继续写(t *testing.T) {
record := innerCodeApplyTestRecord("OLD")
writer := innerCodeApplyTestWriter(record, "OLD")
writer.deleteErr = errors.New("已打单数据不能清除")
outcome := applyClaimedInnerCode(context.Background(), writer, record)
outcome := applyClaimedInnerCode(context.Background(), writer, record, nil)
if outcome.Status != model.InnerCodeFailed || writer.deleteCount != 1 || writer.updateCount != 0 {
t.Fatalf("outcome=%+v delete=%d update=%d", outcome, writer.deleteCount, writer.updateCount)
}
@@ -147,7 +236,7 @@ func TestApplyClaimedInnerCode_写入未知结果不复读也不重试(t *testin
record := innerCodeApplyTestRecord("")
writer := innerCodeApplyTestWriter(record, "")
writer.updateErr = fmtUnknownInnerCodeWriteError()
outcome := applyClaimedInnerCode(context.Background(), writer, record)
outcome := applyClaimedInnerCode(context.Background(), writer, record, nil)
if outcome.Status != model.InnerCodeNeedsCheck || writer.updateCount != 1 || writer.readCount != 1 {
t.Fatalf("outcome=%+v update=%d read=%d", outcome, writer.updateCount, writer.readCount)
}
@@ -157,7 +246,7 @@ func TestApplyClaimedInnerCode_采购字段出现后停止(t *testing.T) {
record := innerCodeApplyTestRecord("")
writer := innerCodeApplyTestWriter(record, "")
writer.stock.Details[0].Raw["purchaseCode"] = "CG-1"
outcome := applyClaimedInnerCode(context.Background(), writer, record)
outcome := applyClaimedInnerCode(context.Background(), writer, record, nil)
if outcome.Status != model.InnerCodeSkipped || writer.updateCount != 0 {
t.Fatalf("outcome=%+v update=%d", outcome, writer.updateCount)
}
@@ -168,7 +257,7 @@ func TestApplyClaimedInnerCode_档口身份变化后停止(t *testing.T) {
record.SybSKU = "A#1"
writer := innerCodeApplyTestWriter(record, "")
writer.stock.Details[0].Raw["sku"] = "B#2"
outcome := applyClaimedInnerCode(context.Background(), writer, record)
outcome := applyClaimedInnerCode(context.Background(), writer, record, nil)
if outcome.Status != model.InnerCodeSkipped || writer.updateCount != 0 {
t.Fatalf("outcome=%+v update=%d", outcome, writer.updateCount)
}
@@ -176,11 +265,11 @@ func TestApplyClaimedInnerCode_档口身份变化后停止(t *testing.T) {
func innerCodeApplyTestRecord(oldCode string) model.InnerCodeRecord {
return model.InnerCodeRecord{ID: 1, StockID: 10, DetailID: 20, SybSpec: "黑色,M",
InnerCode: "DK-001", RemoteInnerCode: oldCode, Status: model.InnerCodeApplying}
InnerCode: "DK-001", SourceDuplicateCount: 1, RemoteInnerCode: oldCode, Status: model.InnerCodeApplying}
}
func innerCodeApplyTestWriter(record model.InnerCodeRecord, currentCode string) *fakeInnerCodeWriter {
item := syb.DetailItem{ID: record.DetailID, ProductSpec: record.SybSpec, Raw: map[string]any{
item := syb.DetailItem{ID: record.DetailID, ProductSpec: record.SybSpec, ProductQty: record.SourceDuplicateCount, Raw: map[string]any{
"id": record.DetailID, "productSpec": record.SybSpec, "innerExpCode": currentCode,
"purchasePlatform": "", "purchaseCode": "",
}}