From 31c2d94a1b650fb6fb067640a0272216d2189e5a Mon Sep 17 00:00:00 2001 From: root Date: Sat, 24 Jan 2026 09:57:01 -0500 Subject: [PATCH] fix: skip soft deletes for ALL entities with filters applied MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit More robust fix based on user feedback: instead of hardcoding specific entity types, now dynamically checks if ANY filters were applied during the sync and skips soft deletes accordingly. Previous approach (hardcoded): - Maintained list of specific entities to skip - Easy to miss new entities (like billing_items bug) - Required manual updates when adding new filters New approach (dynamic): - Tracks whether params.filter has any values - Applies to ALL entities with ANY filter type: * Date filters (tickets, tasks, time_entries, billing_items) * Status filters (contracts, projects) * Active filters (companies, resources, contacts, etc.) - Future-proof: automatically handles new filtered entities Logic: - hasAppliedFilters = params.filter && params.filter.length > 0 - if (!isIncremental && !hasAppliedFilters) → perform soft deletes - if (!isIncremental && hasAppliedFilters) → skip soft deletes This prevents deleting records that exist outside any filter criteria, not just date ranges. --- lib/services/entity-sync.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/services/entity-sync.ts b/lib/services/entity-sync.ts index a3c7129..2fd77a8 100644 --- a/lib/services/entity-sync.ts +++ b/lib/services/entity-sync.ts @@ -136,6 +136,9 @@ export class EntitySyncService { params.filter = filters; } } + + // Track whether any filters were applied - if so, skip soft deletes + const hasAppliedFilters = params.filter && params.filter.length > 0; // Fetch data from Autotask with pagination entityLogger.phase(SyncPhase.FETCHING, 'Fetching records from Autotask API'); @@ -345,16 +348,11 @@ export class EntitySyncService { entityLogger.info('Upserted records to PostgreSQL', { upsertedCount }); // For full sync, soft delete records not in the fetched set - // IMPORTANT: Only delete for entities without date filters to avoid deleting records outside sync window + // IMPORTANT: Skip soft deletes if ANY filters were applied (date, status, active, etc.) + // because we cannot know what records exist outside the filter criteria let deletedCount = 0; - const hasDateFilter = entity === EntityType.TICKETS || - entity === EntityType.TASKS || - entity === EntityType.TIME_ENTRIES || - entity === EntityType.BILLING_ITEMS || - entity === EntityType.PROJECTS || - entity === EntityType.CONTRACTS; - if (!isIncremental && !hasDateFilter) { + if (!isIncremental && !hasAppliedFilters) { entityLogger.phase(SyncPhase.DELETING, 'Checking for records to soft delete'); syncProgressTracker.updateProgress(trackingId, { phase: 'deleting' }); @@ -367,8 +365,8 @@ export class EntitySyncService { entityLogger.warn('Soft delete failed, continuing sync', {}, err); // Don't throw - soft delete failure shouldn't fail the entire sync } - } else if (!isIncremental && hasDateFilter) { - entityLogger.info('Skipping soft-delete for date-filtered sync (would delete records outside sync window)'); + } else if (!isIncremental && hasAppliedFilters) { + entityLogger.info('Skipping soft-delete because filters were applied (would delete records outside filter criteria)'); } // Calculate added vs updated (simplified - actual count would require tracking)