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,