Add Stock Tracking endpoint to Inventory

Adds GET /client/inventory/stock-movements, a unified read-only ledger
flattening every GRN line, sale line and return across all products
into one shape for the new Stock Tracking tab's table.
This commit is contained in:
2026-10-09 14:22:08 +05:30
parent 2eb88b058e
commit 1a502767ba
4 changed files with 91 additions and 0 deletions
+61
View File
@@ -576,6 +576,67 @@ namespace Fitness_core_BE.Services
return MapToDto(stockReturn);
}
// --- Stock Tracking (unified ledger, new) ---
/// <summary>Every GRN line, sale line and return across every product, flattened into one
/// shape for the Stock Tracking tab's table. Built from the same three already-tenant-
/// scoped reads the GRN/Sales/Returns tabs use -- no new repository queries needed, and
/// the dataset is small enough (a single gym's "simple inventory") that filtering happens
/// client-side rather than via query params.</summary>
public async Task<object> GetStockMovements(object payload, HttpContext httpContext)
{
var clientId = await _userRoleHelper.GetClientId(DataExtractor.GetUserIdFromContext(httpContext));
var grns = await _inventoryRepository.GetGrns(clientId);
var sales = await _inventoryRepository.GetSales(clientId);
var returns = await _inventoryRepository.GetStockReturns(clientId);
var movements = new List<StockMovementDto>();
foreach (var g in grns)
foreach (var i in g.Items)
movements.Add(new StockMovementDto
{
Type = "GRN",
ProductId = i.FKProductId,
ProductName = i.Product?.Name ?? string.Empty,
Reference = g.GrnNumber,
QuantityChange = i.Quantity,
Amount = i.LineTotal,
Date = g.ReceivedAt
});
foreach (var s in sales)
foreach (var i in s.Items)
movements.Add(new StockMovementDto
{
Type = "Sale",
ProductId = i.FKProductId,
ProductName = i.Product?.Name ?? string.Empty,
Reference = s.SaleNumber,
QuantityChange = -i.Quantity,
Amount = i.LineTotal,
Date = s.SoldAt
});
foreach (var r in returns)
movements.Add(new StockMovementDto
{
Type = "Return",
ProductId = r.FKProductId,
ProductName = r.Product?.Name ?? string.Empty,
Reference = r.Sale?.SaleNumber ?? r.Grn?.GrnNumber ?? r.Category,
Category = r.Category,
// CustomerReturn is the one category that adds back to stock -- see
// StockReturn's doc comment.
QuantityChange = r.Category == "CustomerReturn" ? r.Quantity : -r.Quantity,
Amount = r.Amount,
Date = r.ReturnedAt
});
return movements.OrderByDescending(m => m.Date).ToList();
}
/// <summary>Staff optionally picking an existing registered member to attach to a sale
/// (search-and-pick, not free-typed) -- purely for identification, mirrors
/// BookingService.ResolveOptionalCustomerLinkAsync exactly.</summary>