diff --git a/src/AccountTracking.Api/Controllers/TransactionsController.cs b/src/AccountTracking.Api/Controllers/TransactionsController.cs new file mode 100644 index 0000000..f2506ad --- /dev/null +++ b/src/AccountTracking.Api/Controllers/TransactionsController.cs @@ -0,0 +1,92 @@ +using AccountTracking.Api.Data; +using AccountTracking.Api.Models.Dtos; +using AccountTracking.Api.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace AccountTracking.Api.Controllers; + +[ApiController] +[Route("api/transactions")] +[Authorize] +public class TransactionsController : ControllerBase +{ + private readonly AppDbContext _db; + private readonly CsvImportService _importService; + + public TransactionsController(AppDbContext db, CsvImportService importService) + { + _db = db; + _importService = importService; + } + + [HttpPost("import")] + public async Task Import(IFormFile file) + { + if (file == null || file.Length == 0) + return BadRequest(new ErrorResult("Nebyl vybrán žádný soubor.")); + + string content; + using (var reader = new StreamReader(file.OpenReadStream(), System.Text.Encoding.UTF8)) + content = await reader.ReadToEndAsync(); + + var result = await _importService.ImportAsync(content, file.FileName); + + if (!result.IsSuccess) + return BadRequest(new ErrorResult(result.Error!)); + + return Ok(result.Value); + } + + [HttpGet] + public async Task List( + [FromQuery] int year, + [FromQuery] int? month, + [FromQuery] string? category, + [FromQuery] string? search, + [FromQuery] int page = 1, + [FromQuery] int pageSize = 50) + { + pageSize = Math.Clamp(pageSize, 1, 200); + page = Math.Max(1, page); + + var query = _db.Transactions.AsQueryable(); + + query = query.Where(t => t.BookingDate.Year == year); + if (month.HasValue) + query = query.Where(t => t.BookingDate.Month == month.Value); + if (!string.IsNullOrWhiteSpace(category)) + query = query.Where(t => t.Category == category); + if (!string.IsNullOrWhiteSpace(search)) + query = query.Where(t => + (t.CounterPartyName != null && EF.Functions.Like(t.CounterPartyName, $"%{search}%")) || + (t.Message != null && EF.Functions.Like(t.Message, $"%{search}%"))); + + var total = await query.CountAsync(); + var items = await query + .OrderByDescending(t => t.BookingDate) + .ThenByDescending(t => t.Id) + .Skip((page - 1) * pageSize) + .Take(pageSize) + .Select(t => new TransactionDto( + t.Id, t.BookingDate, t.CounterPartyName, + t.Category, t.Amount, t.Balance, t.Message)) + .ToListAsync(); + + return Ok(new TransactionListResponse(items, total, page, pageSize)); + } + + [HttpGet("categories")] + public async Task Categories() + { + var cats = await _db.Transactions + .Where(t => t.Category != null) + .Select(t => t.Category!) + .Distinct() + .OrderBy(c => c) + .ToListAsync(); + + return Ok(cats); + } +}