using IBKRTrader.Core.Configuration;
using IBKRTrader.Modules.Accounting.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
namespace IBKRTrader.Modules.Accounting.Persistence;
///
/// EF-Kontext des Accounting-Moduls (gleiche MariaDB, Tabellen mit Präfix acc_). Append-only Ledger mit
/// Autoincrement-PKs und Unique-Index auf dem Idempotenz-Schlüssel (kein Doppel-Buchen).
///
public class AccountingDbContext : DbContext
{
public AccountingDbContext(DbContextOptions options) : base(options) { }
public DbSet Ledger => Set();
public DbSet IngestRuns => Set();
public DbSet RawSnapshots => Set();
public DbSet FxRates => Set();
protected override void OnModelCreating(ModelBuilder b)
{
b.Entity(e =>
{
e.ToTable("acc_ledger");
e.HasKey(x => x.Id);
e.Property(x => x.Id).ValueGeneratedOnAdd();
e.Property(x => x.AccountId).HasMaxLength(30);
e.Property(x => x.EventType).HasConversion().HasMaxLength(20);
e.Property(x => x.Symbol).HasMaxLength(30);
e.Property(x => x.AssetClass).HasMaxLength(10);
e.Property(x => x.Currency).HasMaxLength(5);
e.Property(x => x.Side).HasMaxLength(10);
e.Property(x => x.Source).HasMaxLength(40);
e.Property(x => x.TransactionId).HasMaxLength(60);
e.Property(x => x.IdempotencyKey).HasMaxLength(120);
e.Property(x => x.Quantity).HasPrecision(28, 8);
e.Property(x => x.PriceNative).HasPrecision(18, 6);
e.Property(x => x.GrossBase).HasPrecision(28, 8);
e.Property(x => x.FeeBase).HasPrecision(28, 8);
e.Property(x => x.NetBase).HasPrecision(28, 8);
e.HasIndex(x => x.IdempotencyKey).IsUnique(); // Idempotenz: kein Doppel-Buchen
e.HasIndex(x => new { x.AccountId, x.Timestamp });
e.HasIndex(x => x.EventType);
});
b.Entity(e =>
{
e.ToTable("acc_ingest_runs");
e.HasKey(x => x.Id);
e.Property(x => x.Id).ValueGeneratedOnAdd();
e.Property(x => x.AccountId).HasMaxLength(30);
e.Property(x => x.Message).HasMaxLength(1000);
e.Property(x => x.BalanceAnchorBase).HasPrecision(28, 8);
e.Property(x => x.LedgerNetBase).HasPrecision(28, 8);
e.Property(x => x.BalanceDeltaBase).HasPrecision(28, 8);
e.HasIndex(x => new { x.AccountId, x.StartedAt });
});
b.Entity(e =>
{
e.ToTable("acc_raw");
e.HasKey(x => x.Id);
e.Property(x => x.Id).ValueGeneratedOnAdd();
e.Property(x => x.AccountId).HasMaxLength(30);
e.Property(x => x.SourceKind).HasMaxLength(20);
e.Property(x => x.Json).HasColumnType("longtext");
e.HasIndex(x => x.IngestRunId);
});
b.Entity(e =>
{
e.ToTable("acc_fx_rates");
e.HasKey(x => x.Date);
e.Property(x => x.Date).HasColumnType("date");
e.Property(x => x.UsdToEur).HasPrecision(18, 8);
e.Property(x => x.Source).HasMaxLength(40);
});
}
}
/// Design-Time-Factory für EF-Tooling (dotnet ef). Connection aus env IBKRTRADER_MYSQL.
public class AccountingDbContextFactory : IDesignTimeDbContextFactory
{
public AccountingDbContext CreateDbContext(string[] args)
{
var conn = Environment.GetEnvironmentVariable("IBKRTRADER_MYSQL")
?? "Server=localhost;Port=3306;Database=ibkrtrader;User ID=root;Password=;";
var options = new DbContextOptionsBuilder()
.UseMySql(conn, DatabaseServerVersion.Value)
.Options;
return new AccountingDbContext(options);
}
}