Private
Public Access
1
0

Split SimpleFIN connections by institution

Group discovered SimpleFIN accounts by institution and create a
separate SyncConnection per institution so each bank appears as
its own card in the UI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Catherine Renelle
2026-02-08 23:26:49 -05:00
parent 54a15cf3cf
commit a2b12d9abd
5 changed files with 46 additions and 29 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ export const bankSyncApi = {
api.post<SyncConnection>('/bank-sync/plaid/complete-link', { publicToken }),
connectSimpleFin: (setupToken: string) =>
api.post<SyncConnection>('/bank-sync/simplefin/connect', { setupToken }),
api.post<SyncConnection[]>('/bank-sync/simplefin/connect', { setupToken }),
discoverAccounts: (connectionId: string) =>
api.get<DiscoveredAccount[]>(`/bank-sync/connections/${connectionId}/discover-accounts`),
+6 -2
View File
@@ -728,12 +728,16 @@ function loadPlaidScript(): Promise<void> {
async function connectSimpleFin() {
simpleFinLoading.value = true
try {
const { data: conn } = await bankSyncApi.connectSimpleFin(simpleFinToken.value)
const { data: newConnections } = await bankSyncApi.connectSimpleFin(simpleFinToken.value)
for (const conn of newConnections) {
connections.value.unshift(conn)
syncIntervals[conn.id] = conn.syncIntervalMinutes
}
showSimpleFinDialog.value = false
simpleFinToken.value = ''
openMappingDialog(conn)
if (newConnections.length > 0) {
openMappingDialog(newConnections[0])
}
} catch (err: any) {
errorMessage.value = err.response?.data?.error || 'Failed to connect via SimpleFIN. Check your setup token.'
showError.value = true
@@ -59,7 +59,7 @@ public class BankSyncController : ControllerBase
=> Ok(await _bankSyncService.CompletePlaidLinkAsync(UserId, request));
[HttpPost("simplefin/connect")]
public async Task<ActionResult<SyncConnectionResponse>> ConnectSimpleFin(CreateSimpleFinConnectionRequest request)
public async Task<ActionResult<List<SyncConnectionResponse>>> ConnectSimpleFin(CreateSimpleFinConnectionRequest request)
=> Ok(await _bankSyncService.CreateSimpleFinConnectionAsync(UserId, request));
[HttpGet("connections/{id}/discover-accounts")]
+19 -6
View File
@@ -180,26 +180,32 @@ public class BankSyncService : IBankSyncService
// --- SimpleFIN Flow ---
public async Task<SyncConnectionResponse> CreateSimpleFinConnectionAsync(Guid userId, CreateSimpleFinConnectionRequest request)
public async Task<List<SyncConnectionResponse>> CreateSimpleFinConnectionAsync(Guid userId, CreateSimpleFinConnectionRequest request)
{
var accessUrl = await _simpleFin.ExchangeSetupTokenAsync(request.SetupToken);
var config = new Dictionary<string, string> { ["AccessUrl"] = accessUrl };
var accounts = await _simpleFin.GetAccountsAsync(config);
var institutionName = accounts.FirstOrDefault()?.Institution ?? "SimpleFIN Connection";
var encryptedAccessUrl = _encryption.Encrypt(accessUrl);
var groupedByInstitution = accounts.GroupBy(a => a.Institution ?? "SimpleFIN Connection");
var connectionIds = new List<Guid>();
foreach (var group in groupedByInstitution)
{
var connection = new SyncConnection
{
Id = Guid.NewGuid(),
UserId = userId,
Provider = SyncProvider.SimpleFIN,
InstitutionName = institutionName,
EncryptedAccessToken = _encryption.Encrypt(accessUrl)
InstitutionName = group.Key,
EncryptedAccessToken = encryptedAccessUrl
};
_db.SyncConnections.Add(connection);
connectionIds.Add(connection.Id);
foreach (var account in accounts)
foreach (var account in group)
{
_db.LinkedAccounts.Add(new LinkedAccount
{
@@ -211,10 +217,17 @@ public class BankSyncService : IBankSyncService
LastKnownBalance = account.Balance
});
}
}
await _db.SaveChangesAsync();
return await GetConnectionAsync(userId, connection.Id);
var results = new List<SyncConnectionResponse>();
foreach (var id in connectionIds)
{
results.Add(await GetConnectionAsync(userId, id));
}
return results;
}
// --- Account Linking ---
@@ -19,7 +19,7 @@ public interface IBankSyncService
Task<SyncConnectionResponse> CompletePlaidLinkAsync(Guid userId, CreatePlaidConnectionRequest request);
// SimpleFIN flow
Task<SyncConnectionResponse> CreateSimpleFinConnectionAsync(Guid userId, CreateSimpleFinConnectionRequest request);
Task<List<SyncConnectionResponse>> CreateSimpleFinConnectionAsync(Guid userId, CreateSimpleFinConnectionRequest request);
// Account linking
Task<List<DiscoveredAccountResponse>> DiscoverAccountsAsync(Guid userId, Guid connectionId);