diff --git a/frontend/src/services/bankSync.ts b/frontend/src/services/bankSync.ts index 62cc1de..1d14fc4 100644 --- a/frontend/src/services/bankSync.ts +++ b/frontend/src/services/bankSync.ts @@ -46,6 +46,9 @@ export const bankSyncApi = { updateLinkedAccount: (linkedAccountId: string, data: { accountId: string | null; isEnabled: boolean }) => api.put(`/bank-sync/linked-accounts/${linkedAccountId}`, data), + resetSync: (connectionId: string) => + api.post(`/bank-sync/connections/${connectionId}/reset-sync`), + syncNow: (connectionId: string) => api.post(`/bank-sync/connections/${connectionId}/sync-now`), diff --git a/frontend/src/views/settings/SettingsView.vue b/frontend/src/views/settings/SettingsView.vue index 8911081..5c116d9 100644 --- a/frontend/src/views/settings/SettingsView.vue +++ b/frontend/src/views/settings/SettingsView.vue @@ -184,6 +184,16 @@ > Sync Now + + Re-sync + ([]) const accounts = ref([]) const syncIntervals = reactive>({}) const syncingConnections = reactive>({}) +const resyncingConnections = reactive>({}) // Plaid credentials const plaidCredentials = ref({ isConfigured: false, environment: 'sandbox' }) @@ -814,6 +825,20 @@ async function syncNow(conn: SyncConnection) { } } +async function resyncFromScratch(conn: SyncConnection) { + resyncingConnections[conn.id] = true + try { + await bankSyncApi.resetSync(conn.id) + await bankSyncApi.syncNow(conn.id) + syncingConnections[conn.id] = true + } catch (err: any) { + errorMessage.value = err.response?.data?.error || 'Re-sync failed' + showError.value = true + } finally { + resyncingConnections[conn.id] = false + } +} + // Handle sync results via SignalR notification const { notifications } = useNotifications() watch(notifications, (notifs) => { diff --git a/src/Purrse.Api/Controllers/BankSyncController.cs b/src/Purrse.Api/Controllers/BankSyncController.cs index 0f1e364..bc7d4c9 100644 --- a/src/Purrse.Api/Controllers/BankSyncController.cs +++ b/src/Purrse.Api/Controllers/BankSyncController.cs @@ -74,6 +74,13 @@ public class BankSyncController : ControllerBase public async Task> UpdateLinkedAccount(Guid id, UpdateLinkedAccountRequest request) => Ok(await _bankSyncService.UpdateLinkedAccountAsync(UserId, id, request)); + [HttpPost("connections/{id}/reset-sync")] + public async Task ResetSync(Guid id) + { + await _bankSyncService.ResetSyncDatesAsync(UserId, id); + return NoContent(); + } + [HttpPost("connections/{id}/sync-now")] public IActionResult SyncNow(Guid id) { diff --git a/src/Purrse.Api/Services/BankSyncService.cs b/src/Purrse.Api/Services/BankSyncService.cs index 16fbc7f..4cdeb56 100644 --- a/src/Purrse.Api/Services/BankSyncService.cs +++ b/src/Purrse.Api/Services/BankSyncService.cs @@ -311,6 +311,23 @@ public class BankSyncService : IBankSyncService // --- Sync --- + public async Task ResetSyncDatesAsync(Guid userId, Guid connectionId) + { + var connection = await _db.SyncConnections + .Include(c => c.LinkedAccounts) + .FirstOrDefaultAsync(c => c.Id == connectionId && c.UserId == userId) + ?? throw new KeyNotFoundException("Connection not found"); + + foreach (var la in connection.LinkedAccounts) + { + la.LastSyncAt = null; + } + + connection.LastSyncAt = null; + connection.UpdatedAt = DateTime.UtcNow; + await _db.SaveChangesAsync(); + } + public async Task SyncNowAsync(Guid userId, Guid connectionId) { var connection = await _db.SyncConnections diff --git a/src/Purrse.Core/Interfaces/Services/IBankSyncService.cs b/src/Purrse.Core/Interfaces/Services/IBankSyncService.cs index 73a44cd..5a2da5c 100644 --- a/src/Purrse.Core/Interfaces/Services/IBankSyncService.cs +++ b/src/Purrse.Core/Interfaces/Services/IBankSyncService.cs @@ -28,6 +28,7 @@ public interface IBankSyncService // Sync Task SyncNowAsync(Guid userId, Guid connectionId); + Task ResetSyncDatesAsync(Guid userId, Guid connectionId); Task SyncAllAsync(Guid userId); // Logs