using System.Net.Http.Json; using System.Text.Json; using FluentAssertions; using foodmarket.IntegrationTests.Support; using Xunit; namespace foodmarket.IntegrationTests; [Collection(ApiCollection.Name)] public class DemandPostUnpostTests { private readonly ApiFactory _factory; public DemandPostUnpostTests(ApiFactory factory) => _factory = factory; private static string RandomBarcode() => string.Concat(Enumerable.Range(0, 13).Select(_ => Random.Shared.Next(0, 10))); [Fact] public async Task Post_decrements_stock_unpost_restores() { var api = new ApiActor(_factory.CreateClient()); await api.SignupAndLoginAsync($"dem-{Guid.NewGuid():N}"); var refs = await api.LoadRefsAsync(); var customerId = await api.CreateCounterpartyAsync($"Cust-{Guid.NewGuid():N}"); var p1 = await api.CreateProductAsync(refs, $"P-{Guid.NewGuid():N}", 200m, RandomBarcode()); var enter = await api.Http.PostAsJsonAsync("/api/inventory/enters", new { date = DateTime.UtcNow, storeId = refs.StoreId, currencyId = refs.CurrencyId, notes = "seed", lines = new[] { new { productId = p1, quantity = 20m, unitCost = 100m } }, }); enter.EnsureSuccessStatusCode(); var eid = (await enter.Content.ReadFromJsonAsync()).GetProperty("id").GetString(); (await api.Http.PostAsJsonAsync($"/api/inventory/enters/{eid}/post", new { })).EnsureSuccessStatusCode(); var demand = await api.Http.PostAsJsonAsync("/api/sales/demands", new { date = DateTime.UtcNow, customerId, storeId = refs.StoreId, currencyId = refs.CurrencyId, payment = 2, // BankTransfer paidAmount = 0m, notes = "test demand", lines = new[] { new { productId = p1, quantity = 5m, unitPrice = 180m, discount = 0m, vatPercent = 12m } }, }); demand.EnsureSuccessStatusCode(); var did = (await demand.Content.ReadFromJsonAsync()).GetProperty("id").GetString(); using var post = await api.Http.PostAsJsonAsync($"/api/sales/demands/{did}/post", new { }); post.IsSuccessStatusCode.Should().BeTrue($"post вернул {(int)post.StatusCode}: {await post.Content.ReadAsStringAsync()}"); (await api.StockOfAsync(refs.StoreId, p1)).Should().Be(15m); using var unpost = await api.Http.PostAsJsonAsync($"/api/sales/demands/{did}/unpost", new { }); unpost.IsSuccessStatusCode.Should().BeTrue(); (await api.StockOfAsync(refs.StoreId, p1)).Should().Be(20m); } [Fact] public async Task Cannot_post_when_stock_insufficient() { var api = new ApiActor(_factory.CreateClient()); await api.SignupAndLoginAsync($"dem-short-{Guid.NewGuid():N}"); var refs = await api.LoadRefsAsync(); var customerId = await api.CreateCounterpartyAsync($"Cust-{Guid.NewGuid():N}"); var p1 = await api.CreateProductAsync(refs, $"P-{Guid.NewGuid():N}", 100m, RandomBarcode()); var enter = await api.Http.PostAsJsonAsync("/api/inventory/enters", new { date = DateTime.UtcNow, storeId = refs.StoreId, currencyId = refs.CurrencyId, notes = "seed", lines = new[] { new { productId = p1, quantity = 2m, unitCost = 50m } }, }); enter.EnsureSuccessStatusCode(); var eid = (await enter.Content.ReadFromJsonAsync()).GetProperty("id").GetString(); (await api.Http.PostAsJsonAsync($"/api/inventory/enters/{eid}/post", new { })).EnsureSuccessStatusCode(); var demand = await api.Http.PostAsJsonAsync("/api/sales/demands", new { date = DateTime.UtcNow, customerId, storeId = refs.StoreId, currencyId = refs.CurrencyId, payment = 2, paidAmount = 0m, notes = "over", lines = new[] { new { productId = p1, quantity = 5m, unitPrice = 100m, discount = 0m, vatPercent = 12m } }, }); demand.EnsureSuccessStatusCode(); var did = (await demand.Content.ReadFromJsonAsync()).GetProperty("id").GetString(); using var post = await api.Http.PostAsJsonAsync($"/api/sales/demands/{did}/post", new { }); ((int)post.StatusCode).Should().Be(409); (await api.StockOfAsync(refs.StoreId, p1)).Should().Be(2m); } [Fact] public async Task Tenant_isolation_demand() { var a = new ApiActor(_factory.CreateClient()); var b = new ApiActor(_factory.CreateClient()); await a.SignupAndLoginAsync($"dem-iso-a-{Guid.NewGuid():N}"); await b.SignupAndLoginAsync($"dem-iso-b-{Guid.NewGuid():N}"); var refsA = await a.LoadRefsAsync(); var customerA = await a.CreateCounterpartyAsync($"C-{Guid.NewGuid():N}"); var pA = await a.CreateProductAsync(refsA, $"PA-{Guid.NewGuid():N}", 100m, RandomBarcode()); var demand = await a.Http.PostAsJsonAsync("/api/sales/demands", new { date = DateTime.UtcNow, customerId = customerA, storeId = refsA.StoreId, currencyId = refsA.CurrencyId, payment = 2, paidAmount = 0m, notes = "iso", lines = new[] { new { productId = pA, quantity = 1m, unitPrice = 10m, discount = 0m, vatPercent = 12m } }, }); demand.EnsureSuccessStatusCode(); var did = (await demand.Content.ReadFromJsonAsync()).GetProperty("id").GetString(); var bList = await b.ListAsync("/api/sales/demands?pageSize=200"); bList.Should().NotContain(x => x.GetProperty("id").GetString() == did); using var direct = await b.Http.GetAsync($"/api/sales/demands/{did}"); direct.StatusCode.Should().Be(System.Net.HttpStatusCode.NotFound); } }