feat: add Plaid transaction handling and async queue processing

- introduced new migration to add Cursor column to AccountMetadata.
- updated AppDbContext model snapshot to reflect changes in the database schema.
- implemented async queue processing with ChannelAsyncQueue and AsyncQueueHostedService.
- created Plaid transaction handlers for added, modified, and removed transactions.
- developed PlaidTransactionService and PlaidTransactionSyncer for syncing transactions.
- added SyncUpdatesProcessor to handle sync updates from Plaid.
- updated tests to accommodate changes in institution and account metadata handling.
This commit is contained in:
Stevan Freeborn
2026-03-09 04:29:01 -05:00
parent 48e95e5242
commit a9f1ced0d5
62 changed files with 3625 additions and 68 deletions
@@ -0,0 +1,54 @@
using Account = FiscalOS.Core.Accounts.Account;
using Transaction = FiscalOS.Core.Transactions.Transaction;
namespace FiscalOS.Infra.Transactions.Plaid;
internal interface IPlaidAddedTransactionHandler
{
void Handle(Account account, IEnumerable<Going.Plaid.Entity.Transaction> added);
}
internal sealed class PlaidAddedTransactionHandler : IPlaidAddedTransactionHandler
{
private readonly ILogger<PlaidAddedTransactionHandler> _logger;
private PlaidAddedTransactionHandler(ILogger<PlaidAddedTransactionHandler> logger)
{
_logger = logger;
}
public static PlaidAddedTransactionHandler From(IServiceProvider serviceProvider)
{
return new(
serviceProvider.GetRequiredService<ILogger<PlaidAddedTransactionHandler>>()
);
}
public void Handle(Account account, IEnumerable<Going.Plaid.Entity.Transaction> added)
{
foreach (var addedTransaction in added)
{
if (addedTransaction.Pending.GetValueOrDefault())
{
_logger.LogInformation(
"Skipping pending transaction {TransactionId} for account {AccountId} as it has not been posted yet",
addedTransaction.TransactionId,
account.Id
);
continue;
}
var transactionMetadata = PlaidTransactionMetadata.From(addedTransaction.TransactionId);
var transaction = Transaction.From(
account.UserId,
account.Id,
addedTransaction.MerchantName,
addedTransaction.OriginalDescription,
addedTransaction.Amount,
addedTransaction.Datetime,
transactionMetadata
);
account.AddTransaction(transaction);
}
}
}