using foodmarket.Application.Inventory; using Xunit; namespace foodmarket.UnitTests; /// Логика разбивки результата инвентаризации на Оприходование/Списание /// (модель МойСклада). diff==0 не участвует; суммы = кол-во × себестоимость. public class InventoryResultTests { private static (Guid, decimal, decimal) L(decimal diff, decimal cost) => (Guid.NewGuid(), diff, cost); [Fact] public void Surplus_goes_to_enter_shortage_to_loss() { var p1 = Guid.NewGuid(); var p2 = Guid.NewGuid(); var split = InventoryResult.Build(new[] { (p1, 3m, 100m), // излишек +3 (p2, -2m, 50m), // недостача -2 }); Assert.Single(split.Surplus); Assert.Single(split.Shortage); Assert.Equal(p1, split.Surplus[0].ProductId); Assert.Equal(3m, split.Surplus[0].Quantity); Assert.Equal(p2, split.Shortage[0].ProductId); Assert.Equal(2m, split.Shortage[0].Quantity); // |-2| } [Fact] public void Zero_diff_lines_are_ignored() { var split = InventoryResult.Build(new[] { L(0m, 100m), // совпало — не трогаем L(0m, 999m), L(1m, 10m), // только эта }); Assert.Single(split.Surplus); Assert.Empty(split.Shortage); Assert.Equal(1m, split.Surplus[0].Quantity); } [Fact] public void Sums_are_quantity_times_cost() { var split = InventoryResult.Build(new[] { L(2m, 150m), // +2 × 150 = 300 L(5m, 20m), // +5 × 20 = 100 L(-3m, 40m), // 3 × 40 = 120 (недостача) }); Assert.Equal(400m, split.SurplusTotal); Assert.Equal(120m, split.ShortageTotal); Assert.True(split.HasSurplus); Assert.True(split.HasShortage); } [Fact] public void Line_total_rounds_to_two_decimals() { var split = InventoryResult.Build(new[] { L(3m, 548.6441m) }); // 3 × 548.6441 = 1645.9323 → округление до 2 знаков Assert.Equal(1645.93m, split.Surplus[0].LineTotal); } [Fact] public void No_diffs_gives_empty_split() { var split = InventoryResult.Build(new[] { L(0m, 10m), L(0m, 20m) }); Assert.False(split.HasSurplus); Assert.False(split.HasShortage); Assert.Equal(0m, split.SurplusTotal); Assert.Equal(0m, split.ShortageTotal); } }