fix(quick-260521-foj-01): widen Companies full sync to fetch all companies

- Add buildCompaniesFilter() returning id > 0 in lib/utils/sync-helpers.ts
- Route COMPANIES through buildCompaniesFilter on full sync instead of
  the generic buildActiveFilter (which applied isActive=true and missed
  inactive companies with tickets, causing tickets_company_id_fkey on
  weekly-full and full syncs since 2026-05-15)
- hasAppliedFilters stays true (filter is non-empty), so soft-delete of
  companies is not triggered
This commit is contained in:
lorentz 2026-05-21 11:21:48 -04:00
parent d02796e863
commit 1ecaefe85a
2 changed files with 25 additions and 5 deletions

View file

@ -10,18 +10,19 @@ import { EntityType } from '../types/sync';
import { mapAutotaskToDatabase, mapAutotaskBatch } from '../utils/entity-mapper'; import { mapAutotaskToDatabase, mapAutotaskBatch } from '../utils/entity-mapper';
import { bulkUpsertRecords, getLastSyncTime, softDeleteMissingRecords } from '../utils/db-helpers'; import { bulkUpsertRecords, getLastSyncTime, softDeleteMissingRecords } from '../utils/db-helpers';
import { syncProgressTracker } from './sync-progress-tracker'; import { syncProgressTracker } from './sync-progress-tracker';
import { import {
getAutotaskEntityName, getAutotaskEntityName,
buildIncrementalFilter, buildIncrementalFilter,
buildActiveFilter, buildActiveFilter,
buildDateRangeFilter, buildDateRangeFilter,
buildCompaniesFilter,
buildContractsFilter, buildContractsFilter,
buildContractServicesFilter, buildContractServicesFilter,
buildProjectsFilter, buildProjectsFilter,
buildProjectPhasesFilter, buildProjectPhasesFilter,
buildTimeEntriesFilter, buildTimeEntriesFilter,
buildBillingItemsFilter, buildBillingItemsFilter,
getTableName getTableName
} from '../utils/sync-helpers'; } from '../utils/sync-helpers';
import { createSyncLogger, SyncPhase, categorizeError } from '../utils/sync-logger'; import { createSyncLogger, SyncPhase, categorizeError } from '../utils/sync-logger';
@ -128,9 +129,12 @@ export class EntitySyncService {
} }
const filters: Array<{ field: string; op: string; value: any }> = []; const filters: Array<{ field: string; op: string; value: any }> = [];
// Special handling for entities that require filters // Special handling for entities that require filters
if (entity === EntityType.CONTRACTS) { if (entity === EntityType.COMPANIES) {
filters.push(...buildCompaniesFilter());
entityLogger.info('Full sync of all companies (active + inactive)');
} else if (entity === EntityType.CONTRACTS) {
filters.push(...buildContractsFilter()); filters.push(...buildContractsFilter());
entityLogger.info('Full sync with status filter for active contracts'); entityLogger.info('Full sync with status filter for active contracts');
} else if (entity === EntityType.CONTRACT_SERVICES) { } else if (entity === EntityType.CONTRACT_SERVICES) {

View file

@ -91,6 +91,22 @@ export function buildContractServicesFilter(): Array<{ field: string; op: string
]; ];
} }
/**
* Build filter for companies (Companies endpoint requires a filter use id > 0 to fetch all
* companies regardless of isActive. Active-only filtering misses inactive companies that
* have tickets, causing tickets_company_id_fkey violations on full sync.)
* @returns Query filter array for all companies
*/
export function buildCompaniesFilter(): Array<{ field: string; op: string; value: any }> {
return [
{
field: 'id',
op: 'gt',
value: 0,
},
];
}
/** /**
* Get table name for entity type * Get table name for entity type
* @param entity Entity type * @param entity Entity type