From 7231c83d23ce2f7af4d183b0ff7f38c549a69813 Mon Sep 17 00:00:00 2001 From: nns Date: Thu, 8 Oct 2026 05:39:37 +0500 Subject: [PATCH] =?UTF-8?q?feat(inventory):=20=D1=84=D0=B8=D0=BB=D1=8C?= =?UTF-8?q?=D1=82=D1=80=20=D0=BE=D1=81=D1=82=D0=B0=D1=82=D0=BA=D0=BE=D0=B2?= =?UTF-8?q?=20=D0=BF=D0=BE=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D0=B5=20=D1=82?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D1=80=D0=BE=D0=B2=20=D1=81=20=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B6=D0=B5=D0=BD=D0=BD=D1=8B=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GET /api/inventory/stock?groupId= — остатки по группе включая все вложенные подгруппы (через денормализованный ProductGroup.Path). Комбинируется с storeId/search/сортировкой. Нужно мобильному «FM Склад» для навигации по категориям. Co-Authored-By: Claude Opus 5.5 --- .../Controllers/Inventory/StockController.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/food-market.api/Controllers/Inventory/StockController.cs b/src/food-market.api/Controllers/Inventory/StockController.cs index 6f25edc..f22b396 100644 --- a/src/food-market.api/Controllers/Inventory/StockController.cs +++ b/src/food-market.api/Controllers/Inventory/StockController.cs @@ -24,6 +24,7 @@ public record StockRow( public async Task>> GetStock( [FromQuery] Guid? storeId, [FromQuery] Guid? productId, + [FromQuery] Guid? groupId, [FromQuery] string? search, [FromQuery] bool includeZero = false, [FromQuery] int page = 1, @@ -40,6 +41,11 @@ public record StockRow( if (storeId.HasValue) q = q.Where(x => x.s.StoreId == storeId.Value); if (productId.HasValue) q = q.Where(x => x.s.ProductId == productId.Value); + if (groupId.HasValue) + { + var groupIds = await DescendantGroupIdsAsync(groupId.Value, ct); + q = q.Where(x => groupIds.Contains(x.p.ProductGroupId)); + } if (!includeZero) q = q.Where(x => x.s.Quantity != 0); if (!string.IsNullOrWhiteSpace(search)) { @@ -111,6 +117,19 @@ public record StockRow( public record StockExportRow(string Name, string? Article, string Unit, string Store, decimal Quantity, decimal Reserved, decimal Available); + /// Id группы + всех её вложенных подгрупп (по денормализованному + /// Path). Пустой список, если группа не найдена → фильтр вернёт пусто. + private async Task> DescendantGroupIdsAsync(Guid groupId, CancellationToken ct) + { + var path = await _db.ProductGroups.Where(g => g.Id == groupId) + .Select(g => g.Path).FirstOrDefaultAsync(ct); + if (string.IsNullOrEmpty(path)) + return await _db.ProductGroups.Where(g => g.Id == groupId).Select(g => g.Id).ToListAsync(ct); + return await _db.ProductGroups + .Where(g => g.Path == path || g.Path.StartsWith(path + "/")) + .Select(g => g.Id).ToListAsync(ct); + } + public record MovementRow( Guid Id, DateTime OccurredAt, Guid ProductId, string ProductName, string? Article,