feat: add EF Core models, DTOs and AppDbContext

This commit is contained in:
Martin 2026-03-20 00:37:22 +01:00
parent 65edd731b8
commit 6099e99a46
12 changed files with 159 additions and 0 deletions

View File

@ -0,0 +1,31 @@
using AccountTracking.Api.Models;
using Microsoft.EntityFrameworkCore;
namespace AccountTracking.Api.Data;
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<Transaction> Transactions => Set<Transaction>();
public DbSet<ImportLog> ImportLogs => Set<ImportLog>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// transaction_id: case-sensitive unique index using utf8mb4_bin
modelBuilder.Entity<Transaction>()
.HasIndex(t => t.TransactionId)
.IsUnique();
modelBuilder.Entity<Transaction>()
.Property(t => t.TransactionId)
.UseCollation("utf8mb4_bin");
// ImportedAt stored as UTC
modelBuilder.Entity<ImportLog>()
.Property(l => l.ImportedAt)
.HasConversion(
v => DateTime.SpecifyKind(v, DateTimeKind.Utc),
v => DateTime.SpecifyKind(v, DateTimeKind.Utc));
}
}

View File

@ -0,0 +1,2 @@
namespace AccountTracking.Api.Models.Dtos;
public record CategorySpendingDto(string Category, decimal Total);

View File

@ -0,0 +1,2 @@
namespace AccountTracking.Api.Models.Dtos;
public record CumulativeSpendingDto(int Day, decimal CumulativeSpent);

View File

@ -0,0 +1,3 @@
namespace AccountTracking.Api.Models.Dtos;
public record ImportResult(int RecordsImported, int RecordsSkipped);
public record ErrorResult(string Error);

View File

@ -0,0 +1,2 @@
namespace AccountTracking.Api.Models.Dtos;
public record LoginRequest(string Username, string Password);

View File

@ -0,0 +1,2 @@
namespace AccountTracking.Api.Models.Dtos;
public record LoginResponse(string Token, string ExpiresAt);

View File

@ -0,0 +1,2 @@
namespace AccountTracking.Api.Models.Dtos;
public record MonthlyBalanceDto(int Month, decimal ClosingBalance);

View File

@ -0,0 +1,2 @@
namespace AccountTracking.Api.Models.Dtos;
public record SummaryDto(decimal TotalSpent, decimal TotalIncome);

View File

@ -0,0 +1,10 @@
namespace AccountTracking.Api.Models.Dtos;
public record TransactionDto(
int Id,
DateOnly BookingDate,
string? CounterPartyName,
string? Category,
decimal Amount,
decimal Balance,
string? Message
);

View File

@ -0,0 +1,7 @@
namespace AccountTracking.Api.Models.Dtos;
public record TransactionListResponse(
IEnumerable<TransactionDto> Items,
int TotalCount,
int Page,
int PageSize
);

View File

@ -0,0 +1,25 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace AccountTracking.Api.Models;
[Table("import_logs")]
public class ImportLog
{
[Key]
[Column("id")]
public int Id { get; set; }
[Column("imported_at")]
public DateTime ImportedAt { get; set; } = DateTime.UtcNow;
[Column("filename")]
[MaxLength(255)]
public string Filename { get; set; } = "";
[Column("records_imported")]
public int RecordsImported { get; set; }
[Column("records_skipped")]
public int RecordsSkipped { get; set; }
}

View File

@ -0,0 +1,71 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace AccountTracking.Api.Models;
[Table("transactions")]
public class Transaction
{
[Key]
[Column("id")]
public int Id { get; set; }
[Column("account_number")]
[MaxLength(30)]
public string AccountNumber { get; set; } = "";
[Column("booking_date")]
public DateOnly BookingDate { get; set; }
[Column("amount")]
[Precision(15, 2)]
public decimal Amount { get; set; }
[Column("currency")]
[MaxLength(3)]
public string Currency { get; set; } = "CZK";
[Column("balance")]
[Precision(15, 2)]
public decimal Balance { get; set; }
[Column("counter_party_name")]
[MaxLength(255)]
public string? CounterPartyName { get; set; }
[Column("operation_description")]
[MaxLength(255)]
public string? OperationDescription { get; set; }
[Column("message")]
public string? Message { get; set; }
[Column("category")]
[MaxLength(100)]
public string? Category { get; set; }
[Column("variable_symbol")]
[MaxLength(30)]
public string? VariableSymbol { get; set; }
[Column("bank_note")]
[MaxLength(255)]
public string? BankNote { get; set; }
[Column("transaction_id")]
[MaxLength(512)]
public string TransactionId { get; set; } = "";
// Extra CSV columns stored but not used in UI
[Column("counter_bank_code")] [MaxLength(255)] public string? CounterBankCode { get; set; }
[Column("constant_symbol")] [MaxLength(255)] public string? ConstantSymbol { get; set; }
[Column("specific_symbol")] [MaxLength(255)] public string? SpecificSymbol { get; set; }
[Column("order_name")] [MaxLength(255)] public string? OrderName { get; set; }
[Column("exchange_rate")] [MaxLength(255)] public string? ExchangeRate { get; set; }
[Column("e2e_id")] [MaxLength(255)] public string? E2EId { get; set; }
[Column("payer_reference")] [MaxLength(255)] public string? PayerReference { get; set; }
[Column("original_payer")] [MaxLength(255)] public string? OriginalPayer { get; set; }
[Column("final_recipient")] [MaxLength(255)] public string? FinalRecipient { get; set; }
[Column("original_transaction")] [MaxLength(255)] public string? OriginalTransaction { get; set; }
}