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:
@@ -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`),
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user