using Admin.NET.Plugin.AiDOP.MaterialWarehouse.InventoryPosting.Idempotency;
using Admin.NET.Plugin.AiDOP.MaterialWarehouse.InventoryPosting.L1;
using Admin.NET.Plugin.AiDOP.MaterialWarehouse.InventoryPosting.Numbering;
using Admin.NET.Plugin.AiDOP.MaterialWarehouse.InventoryPosting.Receipt;
using Xunit;
namespace Admin.NET.Plugin.AiDOP.Tests.S5.MaterialWarehouse;
/// Phase 5B-3 接线单测:IqcDomainResolver(无 8010 fallback)+ orchestrator 事务内 RctNbr 取号。
public class IqcWiringTests
{
private const long T = 950000000000001L;
[Fact]
public void DomainResolver_NonEmpty_Trims()
=> Assert.Equal("2400", new IqcDomainResolver().Resolve(T, " 2400 "));
[Fact]
public void DomainResolver_Empty_Throws_NoFallback()
{
var r = new IqcDomainResolver();
Assert.Throws(() => r.Resolve(T, ""));
Assert.Throws(() => r.Resolve(T, null));
Assert.Throws(() => r.Resolve(T, " "));
}
[Fact]
public async Task Orchestrator_GeneratesRctNbr_InTx_StampsC6AndTrans()
{
var db = new FakeInventoryDatabase();
db.SeedMaster(T, "D5B3", "IT", "L");
var prov = new InMemoryBusinessNumberProvider(new[]
{
new BusinessNumberRule { RuleCode = "P5B3_RCT", Prefix1 = "RC", IniValue = 1 },
});
var orch = new IqcReceiptOrchestrator(new FakeIqcReceiptUnitOfWorkFactory(db), prov, "P5B3_RCT");
var receipt = new IqcAcceptedReceipt
{
TenantId = T, DomainCode = "D5B3", PostingId = 1, TransactionGroupId = 1, RctNbr = "OLD-PLACEHOLDER",
ItemNum = "IT", Location = "L", AcceptedQty = 100m, UmConversion = 1m,
PurOrd = "PO", PurLine = 1, Potype = "po", OrderedQty = 100m, Fbillno = "FB", User = "u",
};
var gate = new PurchaseReceiptGateInput { HasPendingReceipt = true, AcceptedQty = 100m, HasReceivableDetails = true };
var r = await orch.PostAsync(receipt, gate);
Assert.Equal(IqcReceiptOutcome.Success, r.Outcome);
var c6 = db.ReceiptWriteback.Values.Single();
Assert.StartsWith("RC", c6.RctNbr); // 用取号结果(新收货号落 C6.rct_nbr)
Assert.NotEqual("OLD-PLACEHOLDER", c6.RctNbr); // 非入参占位
Assert.NotEmpty(db.Transactions); // C5 流水已生成(trans 经 posting_id/group 关联 C6,DOP shadow 无 ord_nbr 列)
}
[Fact]
public async Task Consumer_EmptyDomain_WithResolver_Retryable()
{
var db = new FakeInventoryDatabase();
var store = new InMemoryIqcPostingStore();
var idem = new IqcPostingIdempotencyService(store);
var factory = new FakeIqcReceiptUnitOfWorkFactory(db);
var consumer = new IqcInventoryEventConsumer(
idem, new InMemoryBusinessCompletionStore(db), new FakeIqcReceiptStateLoader { State = new IqcReceiptState { Found = true } },
new IqcReceiptOrchestrator(factory), new IqcMrbSelectionTaskService(factory),
null, new IqcDomainResolver());
var e = new IqcInventoryEvent { TenantId = T, DomainCode = "", Fbillno = "FB-X", Pd = 0, Clfs = "", Dhsl = 100m, EventTime = new DateTime(2026, 8, 9) };
var r = await consumer.ConsumeAsync(e);
Assert.Equal(IqcConsumeOutcome.Retryable, r.Outcome);
Assert.Equal(0, db.TotalPersisted); // domain 未解析 → 未认领未过账
}
}