fix(quick-260521-foj-02): defensively nullify ticket company_id for missing companies

- Add getValidCompanyIds() helper mirroring getValidResourceIds()
- Add a new TICKETS validation block that nullifies ticket.company_id when
  the referenced company is not present (is_deleted=false) in the Pulse
  mirror, instead of letting tickets_company_id_fkey roll back the
  bulkUpsert transaction
- Block runs after the existing recordsWithoutCompany filter and before
  the existing resource-FK nullification block (correct ordering)
- Belt-and-suspenders on top of Task 1: covers hard-deleted-in-Autotask
  companies that Task 1's widening still won't fetch
This commit is contained in:
lorentz 2026-05-21 11:22:33 -04:00
parent 1ecaefe85a
commit 62c529fb91

View file

@ -255,10 +255,35 @@ export class EntitySyncService {
}
}
// Validate company_id foreign keys for tickets (defensive — Task 1 widening
// the Companies filter should make this a near-zero count, but catches the
// truly hard-deleted Autotask company case).
if (entity === EntityType.TICKETS) {
const validCompanyIds = await this.getValidCompanyIds();
let nullifiedCompanyCount = 0;
const nullifiedCompanySamples: number[] = [];
mappedRecords = mappedRecords.map(ticket => {
if (ticket.company_id && !validCompanyIds.has(ticket.company_id)) {
if (nullifiedCompanySamples.length < 5) {
nullifiedCompanySamples.push(ticket.id);
}
ticket.company_id = null;
nullifiedCompanyCount++;
}
return ticket;
});
if (nullifiedCompanyCount > 0) {
entityLogger.warn('Nullified ticket company_id for companies missing from mirror', {
nullifiedCount: nullifiedCompanyCount,
sampleTicketIds: nullifiedCompanySamples,
});
}
}
// Validate resource foreign keys for tickets
if (entity === EntityType.TICKETS) {
const initialCount = mappedRecords.length;
// Get all valid resource IDs from database
const validResourceIds = await this.getValidResourceIds();
@ -721,6 +746,17 @@ export class EntitySyncService {
return new Set(result.rows.map(row => Number(row.id)));
}
/**
* Get all valid company IDs from the database
* Used to validate foreign key references before insert
* @returns Set of valid company IDs
*/
private async getValidCompanyIds(): Promise<Set<number>> {
const query = 'SELECT id FROM companies WHERE is_deleted = false';
const result = await postgresClient.query<{ id: number }>(query);
return new Set(result.rows.map(row => Number(row.id)));
}
/**
* Used to validate foreign key references before insert
* @returns Set of valid contact IDs