feat(inventory-service): add shopId filter to transactions endpoint

BFF needs to query inventory transactions by shopId. The existing
endpoint only supported inventoryItemId. Now accepts either shopId
or inventoryItemId as query parameters.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ho Ngoc Hai
2026-03-04 10:35:54 +07:00
parent 4a1094b080
commit 37042b48b7
5 changed files with 86 additions and 7 deletions

View File

@@ -32,6 +32,15 @@ public record GetTransactionsQuery(
int Skip = 0,
int Take = 50) : IRequest<PagedResult<InventoryTransactionDto>>;
/// <summary>
/// EN: Query to get transactions for a shop.
/// VI: Query lấy transactions cho một shop.
/// </summary>
public record GetTransactionsByShopQuery(
Guid ShopId,
int Skip = 0,
int Take = 50) : IRequest<PagedResult<InventoryTransactionDto>>;
/// <summary>
/// EN: Query to get low stock items.
/// VI: Query lấy các items stock thấp.

View File

@@ -95,6 +95,36 @@ public class GetTransactionsQueryHandler
}
}
/// <summary>
/// EN: Handler for GetTransactionsByShopQuery.
/// VI: Handler cho GetTransactionsByShopQuery.
/// </summary>
public class GetTransactionsByShopQueryHandler
: IRequestHandler<GetTransactionsByShopQuery, PagedResult<InventoryTransactionDto>>
{
private readonly IInventoryRepository _repository;
public GetTransactionsByShopQueryHandler(IInventoryRepository repository)
{
_repository = repository;
}
public async Task<PagedResult<InventoryTransactionDto>> Handle(
GetTransactionsByShopQuery request,
CancellationToken ct)
{
var (transactions, total) = await _repository.GetTransactionsByShopAsync(
request.ShopId,
request.Skip,
request.Take,
ct);
var dtos = transactions.Select(t => t.ToDto()).ToList();
return new PagedResult<InventoryTransactionDto>(dtos, total);
}
}
/// <summary>
/// EN: Handler for GetLowStockItemsQuery.
/// VI: Handler cho GetLowStockItemsQuery.

View File

@@ -251,23 +251,31 @@ public class InventoryController : ControllerBase
}
/// <summary>
/// EN: Get transaction history for inventory item.
/// VI: Lấy lịch sử transactions cho inventory item.
/// EN: Get transaction history by inventory item ID or shop ID.
/// VI: Lấy lịch sử transactions theo inventory item ID hoặc shop ID.
/// </summary>
[HttpGet("transactions")]
[SwaggerOperation(Summary = "Get transaction history")]
[SwaggerOperation(Summary = "Get transaction history by item or shop")]
[SwaggerResponse(200, "Transactions retrieved successfully")]
[SwaggerResponse(400, "Invalid request")]
public async Task<ActionResult<ApiResponse<PagedResult<InventoryTransactionDto>>>> GetTransactions(
[FromQuery] Guid inventoryItemId,
[FromQuery] Guid? inventoryItemId = null,
[FromQuery] Guid? shopId = null,
[FromQuery] int skip = 0,
[FromQuery] int take = 50,
CancellationToken ct = default)
{
if (inventoryItemId == Guid.Empty)
return BadRequest(ApiResponse<PagedResult<InventoryTransactionDto>>.Fail("Inventory item ID is required"));
if (shopId.HasValue && shopId.Value != Guid.Empty)
{
var shopQuery = new GetTransactionsByShopQuery(shopId.Value, skip, take);
var shopResult = await _mediator.Send(shopQuery, ct);
return Ok(ApiResponse<PagedResult<InventoryTransactionDto>>.Ok(shopResult));
}
var query = new GetTransactionsQuery(inventoryItemId, skip, take);
if (!inventoryItemId.HasValue || inventoryItemId.Value == Guid.Empty)
return BadRequest(ApiResponse<PagedResult<InventoryTransactionDto>>.Fail("Either shopId or inventoryItemId is required"));
var query = new GetTransactionsQuery(inventoryItemId.Value, skip, take);
var result = await _mediator.Send(query, ct);
return Ok(ApiResponse<PagedResult<InventoryTransactionDto>>.Ok(result));

View File

@@ -77,6 +77,16 @@ public interface IInventoryRepository : IRepository<InventoryItem>
int take = 50,
CancellationToken cancellationToken = default);
/// <summary>
/// EN: Get all transactions for a shop with pagination.
/// VI: Lấy tất cả transactions cho một shop với phân trang.
/// </summary>
Task<(IReadOnlyList<InventoryTransaction> Transactions, int Total)> GetTransactionsByShopAsync(
Guid shopId,
int skip = 0,
int take = 50,
CancellationToken cancellationToken = default);
/// <summary>
/// EN: Add inventory item asynchronously.
/// VI: Thêm inventory item bất đồng bộ.

View File

@@ -110,6 +110,28 @@ public class InventoryRepository : IInventoryRepository
return (items, total);
}
public async Task<(IReadOnlyList<InventoryTransaction> Transactions, int Total)> GetTransactionsByShopAsync(
Guid shopId,
int skip = 0,
int take = 50,
CancellationToken cancellationToken = default)
{
var items = await _context.InventoryItems
.Include(i => i.Transactions)
.Where(i => i.ShopId == shopId)
.ToListAsync(cancellationToken);
var allTransactions = items
.SelectMany(i => i.Transactions)
.OrderByDescending(t => t.CreatedAt)
.ToList();
var total = allTransactions.Count;
var paged = allTransactions.Skip(skip).Take(take).ToList();
return (paged, total);
}
public async Task<InventoryItem> AddAsync(InventoryItem item, CancellationToken cancellationToken = default)
{
var entity = await _context.InventoryItems.AddAsync(item, cancellationToken);