using Admin.NET.Plugin.AiDOP.MaterialWarehouse.InventoryPosting.Receipt; using Xunit; namespace Admin.NET.Plugin.AiDOP.Tests.S5.MaterialWarehouse; /// /// Phase 6C-2:C6 采购收货回写对账(据 pr_WMS_UpdatePurOrdRctTransactionProcessing2 LIVE:RctQty+=Accepted;完成 (收-退)>=订)。 /// 纯计算 + 编排后 shadow 字段填充,供 L4 逐单对账。 /// public class PurchaseReceiptWritebackTests { private const long Tenant = 797403760988229L; // §九 示例:订100 已收60 本次40 → 收满、余0、完成 [Fact] public void Compute_PartialToComplete_MatchesLive() { var r = PurchaseReceiptWritebackCalculator.Compute(orderedQty: 100m, beforeReceivedQty: 60m, returnedQty: 0m, acceptedQty: 40m); Assert.Equal(60m, r.BeforeReceivedQty); Assert.Equal(40m, r.AcceptedQty); Assert.Equal(100m, r.AfterReceivedQty); Assert.Equal(40m, r.BeforeOutstandingQty); Assert.Equal(0m, r.AfterOutstandingQty); Assert.True(r.DetailCompleted); } [Fact] public void Compute_PartialNotComplete() { var r = PurchaseReceiptWritebackCalculator.Compute(100m, 20m, 0m, 30m); Assert.Equal(50m, r.AfterReceivedQty); Assert.Equal(50m, r.AfterOutstandingQty); Assert.False(r.DetailCompleted); } [Fact] public void Compute_WithReturn_CompletionUsesReceivedMinusReturned() { // 订100 已收100 退20 本次20 → (收120-退20)=100>=订 → 完成 var r = PurchaseReceiptWritebackCalculator.Compute(100m, 100m, 20m, 20m); Assert.Equal(120m, r.AfterReceivedQty); Assert.Equal(0m, r.AfterOutstandingQty); Assert.True(r.DetailCompleted); } [Fact] public void Compute_OverReceipt_OutstandingNegative_Completed() { var r = PurchaseReceiptWritebackCalculator.Compute(100m, 90m, 0m, 20m); Assert.Equal(110m, r.AfterReceivedQty); Assert.Equal(-10m, r.AfterOutstandingQty); // 超收 → 余量负 Assert.True(r.DetailCompleted); } // 编排后 C6 shadow 落库并含对账字段 [Fact] public async Task Orchestrator_Populates_C6ShadowReconciliationFields() { var db = new FakeInventoryDatabase(); db.SeedMaster(Tenant, "8010", "IT-1", "L1"); var orch = new IqcReceiptOrchestrator(new FakeIqcReceiptUnitOfWorkFactory(db)); var receipt = new IqcAcceptedReceipt { TenantId = Tenant, DomainCode = "8010", PostingId = 1, TransactionGroupId = 100, RctNbr = "RCT-1", ItemNum = "IT-1", Location = "L1", AcceptedQty = 40m, UmConversion = 1m, PurOrd = "PO-1", PurLine = 1, Potype = "po", OrderedQty = 100m, BeforeReceivedQty = 60m, ReturnedQty = 0m, User = "t", }; var gate = new PurchaseReceiptGateInput { HasPendingReceipt = true, AcceptedQty = 40m, HasReceivableDetails = true }; var r = await orch.PostAsync(receipt, gate); Assert.Equal(IqcReceiptOutcome.Success, r.Outcome); var key = $"{Tenant}|8010|1|PO-1|1"; var wb = db.ReceiptWriteback[key]; Assert.Equal(40m, wb.AcceptedQty); Assert.Equal(100m, wb.AfterReceivedQty); Assert.Equal(0m, wb.AfterOutstandingQty); Assert.True(wb.DetailCompleted); } }