| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- using Admin.NET.Plugin.AiDOP.MaterialWarehouse.InventoryPosting;
- using Admin.NET.Plugin.AiDOP.MaterialWarehouse.InventoryPosting.Inventory;
- using Admin.NET.Plugin.AiDOP.MaterialWarehouse.InventoryPosting.Receipt;
- using Xunit;
- namespace Admin.NET.Plugin.AiDOP.Tests.S5.MaterialWarehouse;
- /// <summary>
- /// Phase 7-1:C7 移动任务(InvUpShelf / PurOrdRctByMRBChoose)+ MRB 五分支最终路由 + C3 排除。
- /// 据 Phase 6A/7 LIVE:①④ 收货→C5+InvUpShelf;②⑤ 挑选→PurOrdRctByMRBChoose 任务(无 C5);③ 退货→无任务无 C5。
- /// </summary>
- public class IqcC7AndMrbRoutingTests
- {
- private const long T1 = 797403760988229L;
- private const long T2 = 824585161322565L;
- private static IqcAcceptedReceipt Receipt(long tenant = T1, string receiver = "RCV-1")
- => new IqcAcceptedReceipt
- {
- TenantId = tenant, DomainCode = "8010", PostingId = 1, TransactionGroupId = 100, RctNbr = "RCT-1",
- ItemNum = "IT-1", Location = "L1", AcceptedQty = 100m, UmConversion = 1m,
- PurOrd = "PO-1", PurLine = 1, Potype = "po", OrderedQty = 100m, BeforeReceivedQty = 0m,
- Receiver = receiver, RctQcNbr = "QC-1", Fbillno = "FB-1", User = "t",
- };
- private static PurchaseReceiptGateInput GateOk()
- => new PurchaseReceiptGateInput { HasPendingReceipt = true, AcceptedQty = 100m, HasReceivableDetails = true };
- private static IqcMrbSelectionCommand MrbCmd(long tenant = T1, string qcNbr = "QC-9")
- => new IqcMrbSelectionCommand { TenantId = tenant, DomainCode = "8010", PostingId = 2, QcNbr = qcNbr, ItemNum = "IT-1", Fbillno = "FB-2", Receiver = "RCV-2", User = "t" };
- private static FakeInventoryDatabase DbWithMaster()
- {
- var db = new FakeInventoryDatabase();
- db.SeedMaster(T1, "8010", "IT-1", "L1");
- db.SeedMaster(T2, "8010", "IT-1", "L1");
- return db;
- }
- // ── C7 InvUpShelf 经编排创建 ──
- [Fact]
- public async Task C7_InvUpShelf_CreatedViaOrchestrator()
- {
- var db = DbWithMaster();
- var orch = new IqcReceiptOrchestrator(new FakeIqcReceiptUnitOfWorkFactory(db));
- var r = await orch.PostAsync(Receipt(), GateOk());
- Assert.Equal(IqcReceiptOutcome.Success, r.Outcome);
- var task = db.MobileTasks[$"{T1}|8010|RCV-1"];
- Assert.Equal("InvUpShelf", task.Name);
- Assert.Equal("RCV-1", task.TaskId); // TaskID=Receiver
- }
- // ── T7:C7 失败 → C6/C5/C7 全回滚 ──
- [Fact]
- public async Task T7_C7Fail_AllRollback()
- {
- var db = DbWithMaster();
- var orch = new IqcReceiptOrchestrator(new FakeIqcReceiptUnitOfWorkFactory(db, FakeReceiptFailPhase.C7));
- var r = await orch.PostAsync(Receipt(), GateOk());
- Assert.Equal(IqcReceiptOutcome.Error, r.Outcome);
- Assert.Equal(0, db.TotalPersisted); // C6+C5+C7 全滚
- Assert.Equal(new[] { "C6", "C5", "C7" }, db.CallOrder.ToArray()); // C7 抛错→完成锚未达
- }
- // ── TaskID 重复 upsert(同 QCNbr 两次 MRB 选择 → 一个任务)──
- [Fact]
- public async Task T9_SameTaskId_UpsertsToOne()
- {
- var db = DbWithMaster();
- var svc = new IqcMrbSelectionTaskService(new FakeIqcReceiptUnitOfWorkFactory(db));
- await svc.PostAsync(MrbCmd(qcNbr: "QC-DUP"));
- await svc.PostAsync(MrbCmd(qcNbr: "QC-DUP"));
- Assert.Equal(1, db.MobileTasks.Count); // 幂等 upsert → 1
- }
- // ── T10:同 TaskID 不同 tenant → 各自独立任务 ──
- [Fact]
- public async Task T10_SameTaskId_DifferentTenant_Isolated()
- {
- var db = DbWithMaster();
- var svc = new IqcMrbSelectionTaskService(new FakeIqcReceiptUnitOfWorkFactory(db));
- await svc.PostAsync(MrbCmd(tenant: T1, qcNbr: "QC-X"));
- await svc.PostAsync(MrbCmd(tenant: T2, qcNbr: "QC-X"));
- Assert.Equal(2, db.MobileTasks.Count); // tenant 隔离
- }
- // ── MRB ①④:C5 + InvUpShelf(经编排)──
- [Theory]
- [InlineData(0, "")] // ① 合格无处理
- [InlineData(1, "2")] // ④ 让步接收
- public async Task Mrb1And4_Orchestrator_C5PlusInvUpShelf(int pd, string clfs)
- {
- var m = IqcResultMapping.Map(new IqcResultMappingInput { Pd = pd, Clfs = clfs, Dhsl = 100m, Bhgsl = 0m });
- Assert.True(m.EntersInventoryPosting); // 路由=进 C5
- var db = DbWithMaster();
- var r = await new IqcReceiptOrchestrator(new FakeIqcReceiptUnitOfWorkFactory(db)).PostAsync(Receipt(), GateOk());
- Assert.Equal(IqcReceiptOutcome.Success, r.Outcome);
- Assert.True(db.Transactions.Count > 0); // C5 过账
- Assert.Equal(1, db.MobileTasks.Count); // InvUpShelf
- Assert.Equal("InvUpShelf", db.MobileTasks[$"{T1}|8010|RCV-1"].Name);
- }
- // ── MRB ②⑤:PurOrdRctByMRBChoose 任务,无 C5 ──
- [Theory]
- [InlineData(0, "3")] // ② 挑选
- [InlineData(1, "3")] // ⑤ 挑选
- public async Task Mrb2And5_MrbSelection_ChooseTask_NoC5(int pd, string clfs)
- {
- var m = IqcResultMapping.Map(new IqcResultMappingInput { Pd = pd, Clfs = clfs, Dhsl = 100m, Bhgsl = 3m });
- Assert.False(m.EntersInventoryPosting); // 路由=不进 C5
- Assert.Equal(IqcRoutingKind.MrbChooseMobileTask, m.RoutingKind);
- var db = DbWithMaster();
- var r = await new IqcMrbSelectionTaskService(new FakeIqcReceiptUnitOfWorkFactory(db)).PostAsync(MrbCmd());
- Assert.Equal(IqcReceiptOutcome.Success, r.Outcome);
- Assert.Equal(1, db.MobileTasks.Count);
- Assert.Equal("PurOrdRctByMRBChoose", db.MobileTasks[$"{T1}|8010|QC-9"].Name);
- Assert.Equal(0, db.Transactions.Count); // 无 C5 过账
- Assert.Equal(0, db.InvMaster.Count);
- }
- // ── MRB ③ 退货:无 C5、无任务 ──
- [Fact]
- public void Mrb3_ReturnOnly_NoC5_NoTask()
- {
- var m = IqcResultMapping.Map(new IqcResultMappingInput { Pd = 1, Clfs = "0", Dhsl = 10m, Bhgsl = 3m });
- Assert.False(m.EntersInventoryPosting);
- Assert.Equal(IqcRoutingKind.ReturnOnly, m.RoutingKind); // 既不进 C5 也不建挑选任务
- }
- // ── C3 仍为 0(接线后不复现)──
- [Fact]
- public async Task C3_StaysZero_AfterC7Wiring()
- {
- var db = DbWithMaster();
- await new IqcReceiptOrchestrator(new FakeIqcReceiptUnitOfWorkFactory(db)).PostAsync(Receipt(), GateOk());
- Assert.Equal(0, db.BarcodeCardWrites);
- }
- }
|