diff --git a/docs/filters-sweep-progress.md b/docs/filters-sweep-progress.md index e908c18..8b547c0 100644 --- a/docs/filters-sweep-progress.md +++ b/docs/filters-sweep-progress.md @@ -96,10 +96,20 @@ _(pageSize=50, время полного HTTP-ответа)_ - [ ] P1–P4, каждый контрол, positive + границы + ошибки ввода + права + mobile 390px. ## Найденные попутно баги (фикс в этой же сессии) -- [ ] showcase «Последние продажи» — будущие времена (TZ double-shift) — фикс по корню. -- [ ] causeUserId=null в уведомлениях о минусах — не пишется автор документа (все типы). -- [ ] Ошибки ввода приходят техническим английским (JSON-conversion) — перевести все - model-binding/validation ошибки в дружелюбный русский (web toast + mobile API). +- [x] showcase «Последние продажи» — будущие времена. Корень: дата чека бралась от + клиента без нормализации. `BusinessTime.ToInstantUtc` (Kind→UTC + clamp будущего к now) + в рознице и приёме чеков с кассы. Юнит-тесты. Коммит f3a177a. +- [x] causeUserId=null в уведомлениях о минусах. Корень: StockService писал автора + только из черновика; теперь fallback на `ITenantContext.UserId` в единой точке + ApplyMovementAsync → пишется для ВСЕХ типов документов. Тест. Коммит 9dc8f6e. +- [x] Ошибки ввода техническим английским (JSON-conversion) → `ModelErrorTranslator`: + «Неверное значение поля «Причина»», без классов/путей/англ.; сырой текст только в лог. + Действует на web (toast) и mobile API. Тест. Коммит 4aceda3. +- [x] (НЕ баг, устаревший тест) `RetailOversellingTests` ждал 409 на продажу сверх + остатка — но по решению владельца 08.10 продажа в минус РАЗРЕШЕНА (создаётся + предупреждение). Тест обновлён на актуальную политику (204 + алерт). +- [x] (попутно) `food-market.UnitTests` не компилировался (FakeServer не реализовывал + новый QuickCreateProductAsync) → все юнит-тесты не запускались. Стаб добавлен. Коммит 1324ffd. ## Что НЕ трогаем Прод, shared-postgres, global.json, gateway nginx, sklad. diff --git a/tests/food-market.IntegrationTests/RetailOversellingTests.cs b/tests/food-market.IntegrationTests/RetailOversellingTests.cs index af49abd..09720e1 100644 --- a/tests/food-market.IntegrationTests/RetailOversellingTests.cs +++ b/tests/food-market.IntegrationTests/RetailOversellingTests.cs @@ -15,8 +15,11 @@ public class RetailOversellingTests private static string RandomBarcode() => string.Concat(Enumerable.Range(0, 13).Select(_ => Random.Shared.Next(0, 10))); + // Решение владельца 08.10.2026: продажа в минус РАЗРЕШЕНА (как у МойСклада по + // умолчанию) — вместо блокировки создаётся предупреждение об отрицательном + // остатке. Этот тест раньше ждал 409 (старая политика) и был устаревшим. [Fact] - public async Task Posting_sale_above_available_stock_is_rejected() + public async Task Posting_sale_above_available_stock_is_allowed_and_creates_negative_alert() { var api = new ApiActor(_factory.CreateClient()); await api.SignupAndLoginAsync($"oversell-{Guid.NewGuid():N}"); @@ -42,7 +45,13 @@ public async Task Posting_sale_above_available_stock_is_rejected() var saleId = (await draftResp.Content.ReadFromJsonAsync()).GetProperty("id").GetString(); using var post = await api.Http.PostAsJsonAsync($"/api/sales/retail/{saleId}/post", new { }); - ((int)post.StatusCode).Should().Be(409, "продажа сверх остатка должна блокироваться"); + post.IsSuccessStatusCode.Should().BeTrue( + $"продажа в минус разрешена: {(int)post.StatusCode} {await post.Content.ReadAsStringAsync()}"); + + // Остаток ушёл в −5, и создано предупреждение об отрицательном остатке. + (await api.StockOfAsync(refs.StoreId, productId)).Should().Be(-5m); + var cnt = await api.GetJsonAsync("/api/negative-stock/count"); + cnt.GetProperty("total").GetInt32().Should().BeGreaterThan(0, "минус создаёт предупреждение"); } [Fact]