IqcReceiptPureTests.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. using Admin.NET.Plugin.AiDOP.MaterialWarehouse.InventoryPosting.Inventory;
  2. using Admin.NET.Plugin.AiDOP.MaterialWarehouse.InventoryPosting.Receipt;
  3. using Xunit;
  4. namespace Admin.NET.Plugin.AiDOP.Tests.S5.MaterialWarehouse;
  5. /// <summary>
  6. /// Phase 6B 纯逻辑件测试:Runtime Gate + CheckInventoryStatus 轻校验 + IqcInventoryBatchBuilder。
  7. /// 据 Phase 6A LIVE 契约:IQC 收货 `rct-po`(→Avail,Assay=0),留样 `iss-unp`(-Sample);最终 +Accepted-Sample。
  8. /// </summary>
  9. public class IqcReceiptPureTests
  10. {
  11. // ── Runtime Gate ──
  12. [Fact]
  13. public void Gate_AllSatisfied_CanCreate()
  14. => Assert.True(PurchaseReceiptRuntimeGate.Evaluate(
  15. new PurchaseReceiptGateInput { HasPendingReceipt = true, AcceptedQty = 100m, HasReceivableDetails = true }).CanCreate);
  16. [Fact]
  17. public void Gate_AcceptedQtyZero_CannotCreate()
  18. => Assert.False(PurchaseReceiptRuntimeGate.Evaluate(
  19. new PurchaseReceiptGateInput { HasPendingReceipt = true, AcceptedQty = 0m, HasReceivableDetails = true }).CanCreate);
  20. [Fact]
  21. public void Gate_NoReceivableDetails_CannotCreate()
  22. => Assert.False(PurchaseReceiptRuntimeGate.Evaluate(
  23. new PurchaseReceiptGateInput { HasPendingReceipt = true, AcceptedQty = 100m, HasReceivableDetails = false }).CanCreate);
  24. [Fact]
  25. public void Gate_NoPendingReceipt_CannotCreate()
  26. => Assert.False(PurchaseReceiptRuntimeGate.Evaluate(
  27. new PurchaseReceiptGateInput { HasPendingReceipt = false, AcceptedQty = 100m, HasReceivableDetails = true }).CanCreate);
  28. // ── CheckInventoryStatus 轻校验 ──
  29. [Fact]
  30. public void LocationCheck_AllNonEmpty_Ok()
  31. {
  32. var cmds = new[] { new InventoryTransactionCommand { ItemNum = "IT-1", Location = "L1" } };
  33. Assert.True(InventoryLocationCheck.Validate(cmds).Ok);
  34. }
  35. [Fact]
  36. public void LocationCheck_EmptyLocation_Fails()
  37. {
  38. var cmds = new[] { new InventoryTransactionCommand { ItemNum = "IT-1", Location = "" } };
  39. var r = InventoryLocationCheck.Validate(cmds);
  40. Assert.False(r.Ok);
  41. Assert.Contains("IT-1", r.OffendingItems);
  42. }
  43. // ── Batch Builder ──
  44. private static IqcAcceptedReceipt Receipt(decimal accepted, decimal sample)
  45. => new IqcAcceptedReceipt
  46. {
  47. TenantId = 797403760988229L, DomainCode = "8010", PostingId = 1, TransactionGroupId = 100,
  48. RctNbr = "RCT-001", ItemNum = "IT-1", Location = "L1", LotSerial = "",
  49. AcceptedQty = accepted, SampleQty = sample, UmConversion = 1m, User = "t",
  50. };
  51. [Fact]
  52. public void Build_NoSample_OnlyRctPo()
  53. {
  54. var b = IqcInventoryBatchBuilder.Build(Receipt(100m, 0m));
  55. var c = Assert.Single(b.Commands);
  56. Assert.Equal(InventoryTransType.RctPo, c.TransType);
  57. Assert.Equal(100m, c.ChangeQty);
  58. Assert.Equal("RCT-001", c.OrdNbr);
  59. Assert.Equal(1, c.Seq);
  60. }
  61. [Fact]
  62. public void Build_WithSample_RctPoThenIssUnp_OrderAndSigns()
  63. {
  64. var b = IqcInventoryBatchBuilder.Build(Receipt(100m, 10m));
  65. Assert.Equal(2, b.Commands.Count);
  66. Assert.Equal(InventoryTransType.RctPo, b.Commands[0].TransType);
  67. Assert.Equal(100m, b.Commands[0].ChangeQty);
  68. Assert.Equal(InventoryTransType.IssUnp, b.Commands[1].TransType);
  69. Assert.Equal(-10m, b.Commands[1].ChangeQty); // 留样恒负
  70. Assert.Equal("iqc", b.Commands[1].Reason);
  71. Assert.Equal(1, b.Commands[0].Seq);
  72. Assert.Equal(2, b.Commands[1].Seq);
  73. }
  74. // 经 Calculator 验证最终库存语义
  75. [Fact]
  76. public void Build_RctPoAlone_IntoAvail_NotAssay()
  77. {
  78. var b = IqcInventoryBatchBuilder.Build(Receipt(100m, 0m));
  79. var after = InventoryBalanceCalculator.Project(new InventoryBalance(), b.Commands[0]).After;
  80. Assert.Equal(100m, after.QtyOnHand);
  81. Assert.Equal(100m, after.AvailStatusQty); // 进可用库存
  82. Assert.Equal(0m, after.Assay); // 非质检(修正 rct-po-ins 旧误判)
  83. }
  84. [Fact]
  85. public void Build_WithSample_FinalBalance_AcceptedMinusSample()
  86. {
  87. var b = IqcInventoryBatchBuilder.Build(Receipt(100m, 10m));
  88. var bal = new InventoryBalance();
  89. foreach (var c in b.Commands)
  90. bal = InventoryBalanceCalculator.Project(bal, c).After;
  91. Assert.Equal(90m, bal.QtyOnHand); // +100 -10
  92. Assert.Equal(90m, bal.AvailStatusQty);
  93. Assert.Equal(0m, bal.Assay);
  94. }
  95. }