feat: rename transaction table to transactions and update related references

This commit is contained in:
Stevan Freeborn
2026-03-01 20:35:33 -06:00
parent b9d0869ae3
commit 2bc9cab43d
7 changed files with 714 additions and 6 deletions
+11
View File
@@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "aspire",
"request": "launch",
"name": "Aspire: Launch default apphost",
"program": "${workspaceFolder}"
}
]
}
@@ -0,0 +1,507 @@
// <auto-generated />
using System;
using FiscalOS.Infra.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace FiscalOS.Infra.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20260302015827_AddInstitutionsDbSet")]
partial class AddInstitutionsDbSet
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "10.0.2");
modelBuilder.Entity("FiscalOS.Core.Accounts.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("TEXT");
b.Property<Guid>("InstitutionId")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<Guid>("UserId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("InstitutionId");
b.HasIndex("UserId");
b.ToTable("Account");
});
modelBuilder.Entity("FiscalOS.Core.Accounts.AccountMetadata", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<Guid>("AccountId")
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(21)
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("AccountId")
.IsUnique();
b.ToTable("AccountMetadata");
b.HasDiscriminator<string>("Type").HasValue("AccountMetadata");
b.UseTphMappingStrategy();
});
modelBuilder.Entity("FiscalOS.Core.Accounts.Balance", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<Guid>("AccountId")
.HasColumnType("TEXT");
b.Property<decimal>("Available")
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("CurrencyCode")
.IsRequired()
.HasColumnType("TEXT");
b.Property<decimal>("Current")
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("AccountId");
b.ToTable("Balance");
});
modelBuilder.Entity("FiscalOS.Core.Accounts.Institution", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<Guid>("UserId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Institutions");
});
modelBuilder.Entity("FiscalOS.Core.Accounts.InstitutionMetadata", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("TEXT");
b.Property<Guid>("InstitutionId")
.HasColumnType("TEXT");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(21)
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("InstitutionId")
.IsUnique();
b.ToTable("InstitutionMetadata");
b.HasDiscriminator<string>("Type").HasValue("InstitutionMetadata");
b.UseTphMappingStrategy();
});
modelBuilder.Entity("FiscalOS.Core.Identity.RefreshToken", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("ExpiresAt")
.HasColumnType("TEXT");
b.Property<bool>("Revoked")
.HasColumnType("INTEGER");
b.Property<string>("Token")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<Guid>("UserId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Token")
.IsUnique();
b.HasIndex("UserId");
b.ToTable("RefreshTokens");
});
modelBuilder.Entity("FiscalOS.Core.Identity.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("EncryptedDataKey")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("EncryptionKeyId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("HashedPassword")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Username")
.IsUnique();
b.ToTable("Users");
});
modelBuilder.Entity("FiscalOS.Core.Transactions.Transaction", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<Guid>("AccountId")
.HasColumnType("TEXT");
b.Property<Guid?>("AccountId1")
.HasColumnType("TEXT");
b.Property<decimal>("Amount")
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("Date")
.HasColumnType("TEXT");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("MerchantName")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<Guid>("UserId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("AccountId1");
b.HasIndex("UserId");
b.ToTable("Transactions");
});
modelBuilder.Entity("FiscalOS.Core.Transactions.TransactionMetadata", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("TEXT");
b.Property<Guid>("TransactionId")
.HasColumnType("TEXT");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(5)
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("TransactionId")
.IsUnique();
b.ToTable("TransactionMetadata");
b.HasDiscriminator<string>("Type").HasValue("Plaid");
b.UseTphMappingStrategy();
});
modelBuilder.Entity("FiscalOS.Infra.Accounts.Plaid.PlaidAccountMetadata", b =>
{
b.HasBaseType("FiscalOS.Core.Accounts.AccountMetadata");
b.Property<string>("Cursor")
.HasColumnType("TEXT");
b.Property<string>("PlaidId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("PlaidName")
.IsRequired()
.HasColumnType("TEXT");
b.HasDiscriminator().HasValue("Plaid");
});
modelBuilder.Entity("FiscalOS.Infra.Accounts.Plaid.PlaidInstitutionMetadata", b =>
{
b.HasBaseType("FiscalOS.Core.Accounts.InstitutionMetadata");
b.Property<string>("EncryptedAccessToken")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("ItemId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("PlaidId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("PlaidName")
.IsRequired()
.HasColumnType("TEXT");
b.HasDiscriminator().HasValue("Plaid");
});
modelBuilder.Entity("FiscalOS.Infra.Transactions.Plaid.PlaidTransactionMetadata", b =>
{
b.HasBaseType("FiscalOS.Core.Transactions.TransactionMetadata");
b.Property<string>("PlaidId")
.IsRequired()
.HasColumnType("TEXT");
b.HasDiscriminator().HasValue("Plaid");
});
modelBuilder.Entity("FiscalOS.Core.Accounts.Account", b =>
{
b.HasOne("FiscalOS.Core.Accounts.Institution", "Institution")
.WithMany("Accounts")
.HasForeignKey("InstitutionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("FiscalOS.Core.Identity.User", null)
.WithMany("Accounts")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Institution");
});
modelBuilder.Entity("FiscalOS.Core.Accounts.AccountMetadata", b =>
{
b.HasOne("FiscalOS.Core.Accounts.Account", null)
.WithOne("Metadata")
.HasForeignKey("FiscalOS.Core.Accounts.AccountMetadata", "AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("FiscalOS.Core.Accounts.Balance", b =>
{
b.HasOne("FiscalOS.Core.Accounts.Account", null)
.WithMany("Balances")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("FiscalOS.Core.Accounts.Institution", b =>
{
b.HasOne("FiscalOS.Core.Identity.User", "User")
.WithMany("Institutions")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("FiscalOS.Core.Accounts.InstitutionMetadata", b =>
{
b.HasOne("FiscalOS.Core.Accounts.Institution", null)
.WithOne("Metadata")
.HasForeignKey("FiscalOS.Core.Accounts.InstitutionMetadata", "InstitutionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("FiscalOS.Core.Identity.RefreshToken", b =>
{
b.HasOne("FiscalOS.Core.Identity.User", "User")
.WithMany("RefreshTokens")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("FiscalOS.Core.Transactions.Transaction", b =>
{
b.HasOne("FiscalOS.Core.Accounts.Account", "Account")
.WithMany()
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("FiscalOS.Core.Accounts.Account", null)
.WithMany("Transactions")
.HasForeignKey("AccountId1");
b.HasOne("FiscalOS.Core.Identity.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("User");
});
modelBuilder.Entity("FiscalOS.Core.Transactions.TransactionMetadata", b =>
{
b.HasOne("FiscalOS.Core.Transactions.Transaction", null)
.WithOne("Metadata")
.HasForeignKey("FiscalOS.Core.Transactions.TransactionMetadata", "TransactionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("FiscalOS.Core.Accounts.Account", b =>
{
b.Navigation("Balances");
b.Navigation("Metadata");
b.Navigation("Transactions");
});
modelBuilder.Entity("FiscalOS.Core.Accounts.Institution", b =>
{
b.Navigation("Accounts");
b.Navigation("Metadata");
});
modelBuilder.Entity("FiscalOS.Core.Identity.User", b =>
{
b.Navigation("Accounts");
b.Navigation("Institutions");
b.Navigation("RefreshTokens");
});
modelBuilder.Entity("FiscalOS.Core.Transactions.Transaction", b =>
{
b.Navigation("Metadata");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,168 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace FiscalOS.Infra.Migrations
{
/// <inheritdoc />
public partial class AddInstitutionsDbSet : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Transaction_Account_AccountId",
table: "Transaction");
migrationBuilder.DropForeignKey(
name: "FK_Transaction_Account_AccountId1",
table: "Transaction");
migrationBuilder.DropForeignKey(
name: "FK_Transaction_Users_UserId",
table: "Transaction");
migrationBuilder.DropForeignKey(
name: "FK_TransactionMetadata_Transaction_TransactionId",
table: "TransactionMetadata");
migrationBuilder.DropPrimaryKey(
name: "PK_Transaction",
table: "Transaction");
migrationBuilder.RenameTable(
name: "Transaction",
newName: "Transactions");
migrationBuilder.RenameIndex(
name: "IX_Transaction_UserId",
table: "Transactions",
newName: "IX_Transactions_UserId");
migrationBuilder.RenameIndex(
name: "IX_Transaction_AccountId1",
table: "Transactions",
newName: "IX_Transactions_AccountId1");
migrationBuilder.RenameIndex(
name: "IX_Transaction_AccountId",
table: "Transactions",
newName: "IX_Transactions_AccountId");
migrationBuilder.AddPrimaryKey(
name: "PK_Transactions",
table: "Transactions",
column: "Id");
migrationBuilder.AddForeignKey(
name: "FK_TransactionMetadata_Transactions_TransactionId",
table: "TransactionMetadata",
column: "TransactionId",
principalTable: "Transactions",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Transactions_Account_AccountId",
table: "Transactions",
column: "AccountId",
principalTable: "Account",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Transactions_Account_AccountId1",
table: "Transactions",
column: "AccountId1",
principalTable: "Account",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Transactions_Users_UserId",
table: "Transactions",
column: "UserId",
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_TransactionMetadata_Transactions_TransactionId",
table: "TransactionMetadata");
migrationBuilder.DropForeignKey(
name: "FK_Transactions_Account_AccountId",
table: "Transactions");
migrationBuilder.DropForeignKey(
name: "FK_Transactions_Account_AccountId1",
table: "Transactions");
migrationBuilder.DropForeignKey(
name: "FK_Transactions_Users_UserId",
table: "Transactions");
migrationBuilder.DropPrimaryKey(
name: "PK_Transactions",
table: "Transactions");
migrationBuilder.RenameTable(
name: "Transactions",
newName: "Transaction");
migrationBuilder.RenameIndex(
name: "IX_Transactions_UserId",
table: "Transaction",
newName: "IX_Transaction_UserId");
migrationBuilder.RenameIndex(
name: "IX_Transactions_AccountId1",
table: "Transaction",
newName: "IX_Transaction_AccountId1");
migrationBuilder.RenameIndex(
name: "IX_Transactions_AccountId",
table: "Transaction",
newName: "IX_Transaction_AccountId");
migrationBuilder.AddPrimaryKey(
name: "PK_Transaction",
table: "Transaction",
column: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Transaction_Account_AccountId",
table: "Transaction",
column: "AccountId",
principalTable: "Account",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Transaction_Account_AccountId1",
table: "Transaction",
column: "AccountId1",
principalTable: "Account",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Transaction_Users_UserId",
table: "Transaction",
column: "UserId",
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_TransactionMetadata_Transaction_TransactionId",
table: "TransactionMetadata",
column: "TransactionId",
principalTable: "Transaction",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
}
}
@@ -284,7 +284,7 @@ namespace FiscalOS.Infra.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("Transaction"); b.ToTable("Transactions");
}); });
modelBuilder.Entity("FiscalOS.Core.Transactions.TransactionMetadata", b => modelBuilder.Entity("FiscalOS.Core.Transactions.TransactionMetadata", b =>
@@ -324,7 +324,6 @@ namespace FiscalOS.Infra.Migrations
b.HasBaseType("FiscalOS.Core.Accounts.AccountMetadata"); b.HasBaseType("FiscalOS.Core.Accounts.AccountMetadata");
b.Property<string>("Cursor") b.Property<string>("Cursor")
.IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<string>("PlaidId") b.Property<string>("PlaidId")
@@ -43,9 +43,9 @@ internal sealed class PlaidAddedTransactionHandler : IPlaidAddedTransactionHandl
account.UserId, account.UserId,
account.Id, account.Id,
addedTransaction.MerchantName, addedTransaction.MerchantName,
addedTransaction.OriginalDescription, addedTransaction.Description,
addedTransaction.Amount, addedTransaction.Amount,
addedTransaction.Datetime, addedTransaction.PostedDate,
transactionMetadata transactionMetadata
); );
account.AddTransaction(transaction); account.AddTransaction(transaction);
@@ -0,0 +1,23 @@
namespace FiscalOS.Infra.Transactions.Plaid;
public static class PlaidExtensions
{
#pragma warning disable CA1034
extension(Transaction transaction)
{
public string Description => transaction.OriginalDescription ?? "";
public DateTimeOffset PostedDate => transaction.Datetime ?? (
transaction.Date.HasValue
? new DateTimeOffset(
transaction.Date.Value.Year,
transaction.Date.Value.Month,
transaction.Date.Value.Day,
0,
0,
0,
TimeSpan.Zero
)
: throw new InvalidOperationException($"Transaction {transaction.TransactionId} does not have a valid date or datetime")
);
}
}
@@ -63,9 +63,9 @@ internal sealed class PlaidModifiedTransactionHandler : IPlaidModifiedTransactio
existing.UserId, existing.UserId,
existing.AccountId, existing.AccountId,
plaidModifiedTransaction.MerchantName, plaidModifiedTransaction.MerchantName,
plaidModifiedTransaction.OriginalDescription, plaidModifiedTransaction.Description,
plaidModifiedTransaction.Amount, plaidModifiedTransaction.Amount,
plaidModifiedTransaction.Datetime, plaidModifiedTransaction.PostedDate,
plaidMetadata plaidMetadata
); );