2026-03-01 19:56:29 -06:00
|
|
|
using Account = FiscalOS.Core.Accounts.Account;
|
|
|
|
|
using Transaction = FiscalOS.Core.Transactions.Transaction;
|
|
|
|
|
|
|
|
|
|
namespace FiscalOS.Infra.Transactions.Plaid;
|
|
|
|
|
|
|
|
|
|
internal interface IPlaidAddedTransactionHandler
|
|
|
|
|
{
|
2026-03-03 05:09:03 -06:00
|
|
|
Task<int> HandleAsync(
|
|
|
|
|
Account account,
|
|
|
|
|
IEnumerable<Going.Plaid.Entity.Transaction> added,
|
|
|
|
|
CancellationToken ct
|
|
|
|
|
);
|
2026-03-01 19:56:29 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal sealed class PlaidAddedTransactionHandler : IPlaidAddedTransactionHandler
|
|
|
|
|
{
|
|
|
|
|
private readonly ILogger<PlaidAddedTransactionHandler> _logger;
|
2026-03-03 05:09:03 -06:00
|
|
|
private readonly AppDbContext _appDbContext;
|
2026-03-01 19:56:29 -06:00
|
|
|
|
2026-03-03 05:09:03 -06:00
|
|
|
private PlaidAddedTransactionHandler(
|
|
|
|
|
ILogger<PlaidAddedTransactionHandler> logger,
|
|
|
|
|
AppDbContext appDbContext
|
|
|
|
|
)
|
2026-03-01 19:56:29 -06:00
|
|
|
{
|
|
|
|
|
_logger = logger;
|
2026-03-03 05:09:03 -06:00
|
|
|
_appDbContext = appDbContext;
|
2026-03-01 19:56:29 -06:00
|
|
|
}
|
|
|
|
|
|
2026-03-05 13:58:00 -06:00
|
|
|
public static PlaidAddedTransactionHandler From(
|
|
|
|
|
ILogger<PlaidAddedTransactionHandler> logger,
|
|
|
|
|
AppDbContext appDbContext
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
return new(logger, appDbContext);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-01 19:56:29 -06:00
|
|
|
public static PlaidAddedTransactionHandler From(IServiceProvider serviceProvider)
|
|
|
|
|
{
|
|
|
|
|
return new(
|
2026-03-03 05:09:03 -06:00
|
|
|
serviceProvider.GetRequiredService<ILogger<PlaidAddedTransactionHandler>>(),
|
|
|
|
|
serviceProvider.GetRequiredService<AppDbContext>()
|
2026-03-01 19:56:29 -06:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-03 05:09:03 -06:00
|
|
|
public async Task<int> HandleAsync(Account account, IEnumerable<Going.Plaid.Entity.Transaction> added, CancellationToken ct)
|
2026-03-01 19:56:29 -06:00
|
|
|
{
|
2026-03-03 05:09:03 -06:00
|
|
|
var existingTransactionIds = added.Select(t => t.TransactionId);
|
|
|
|
|
var existingTransactions = await _appDbContext.Transactions
|
|
|
|
|
.Where(t => t.Metadata is PlaidTransactionMetadata && existingTransactionIds.Contains(((PlaidTransactionMetadata)t.Metadata).PlaidId))
|
|
|
|
|
.ToListAsync(ct)
|
|
|
|
|
.ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
var addedCount = 0;
|
|
|
|
|
|
2026-03-01 19:56:29 -06:00
|
|
|
foreach (var addedTransaction in added)
|
|
|
|
|
{
|
2026-03-03 05:09:03 -06:00
|
|
|
try
|
2026-03-01 19:56:29 -06:00
|
|
|
{
|
2026-03-03 05:09:03 -06:00
|
|
|
if (addedTransaction.Pending.GetValueOrDefault())
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation(
|
|
|
|
|
"Skipping pending transaction {TransactionId} for account {AccountId} as it has not been posted yet",
|
|
|
|
|
addedTransaction.TransactionId,
|
|
|
|
|
account.Id
|
|
|
|
|
);
|
|
|
|
|
addedCount++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2026-03-01 19:56:29 -06:00
|
|
|
|
2026-03-03 05:09:03 -06:00
|
|
|
var existingTransaction = existingTransactions.FirstOrDefault(
|
|
|
|
|
t => t.Metadata is PlaidTransactionMetadata metadata && metadata.PlaidId == addedTransaction.TransactionId
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (existingTransaction is not null)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation(
|
|
|
|
|
"Skipping added transaction {PlaidTransactionId} for account {AccountId} as it has already been added as transaction {TransactionId}",
|
|
|
|
|
addedTransaction.TransactionId,
|
|
|
|
|
account.Id,
|
|
|
|
|
existingTransaction.Id
|
|
|
|
|
);
|
|
|
|
|
addedCount++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var transactionMetadata = PlaidTransactionMetadata.From(addedTransaction.TransactionId);
|
|
|
|
|
var transaction = Transaction.From(
|
|
|
|
|
account.UserId,
|
|
|
|
|
account.Id,
|
|
|
|
|
addedTransaction.Merchant,
|
|
|
|
|
addedTransaction.Description,
|
2026-03-11 20:01:23 -05:00
|
|
|
addedTransaction.CanonicalAmount,
|
2026-03-03 05:09:03 -06:00
|
|
|
addedTransaction.PostedDate,
|
|
|
|
|
transactionMetadata
|
|
|
|
|
);
|
|
|
|
|
account.AddTransaction(transaction);
|
|
|
|
|
addedCount++;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogError(
|
|
|
|
|
ex,
|
|
|
|
|
"Failed to added transaction {PlaidTransactionId} to account {AccountId}",
|
|
|
|
|
account.Id,
|
|
|
|
|
addedTransaction.TransactionId
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-03-01 19:56:29 -06:00
|
|
|
}
|
2026-03-03 05:09:03 -06:00
|
|
|
|
|
|
|
|
_logger.LogInformation("Added {AddedCount} transactions to account {AccountId}", addedCount, account.Id);
|
|
|
|
|
|
|
|
|
|
return addedCount;
|
2026-03-01 19:56:29 -06:00
|
|
|
}
|
|
|
|
|
}
|