fix(12): CR-02 stop folding company-match review counts into sync_history.records_deleted

This commit is contained in:
lorentz 2026-07-11 07:18:27 -04:00
parent 591fc5cf10
commit bddf612c7b
3 changed files with 24 additions and 6 deletions

View file

@ -194,7 +194,11 @@ describe('Pax8SyncService — syncOrders + syncCompanyMatches', () => {
expect(matchEntity).toBeDefined();
expect(matchEntity!.success).toBe(true);
expect(matchEntity!.upserted).toBe(1); // autoLinked
expect(matchEntity!.tombstoned).toBe(1); // flaggedAmbiguous + flaggedNoCandidate
// No soft-deletes are performed by matching (CR-02) — tombstoned must
// always be 0 so sync_history.records_deleted never fabricates a
// "deleted" count for review-flagged companies.
expect(matchEntity!.tombstoned).toBe(0);
expect(matchEntity!.flaggedForReview).toBe(1); // flaggedAmbiguous + flaggedNoCandidate
});
it("fullSync's entities array includes an 'orders' result and a 'company_matches' result", async () => {

View file

@ -108,12 +108,13 @@ export class Pax8SyncService {
const status: 'completed' | 'failed' = success ? 'completed' : 'failed';
const totalUpserted = entities.reduce((sum, e) => sum + e.upserted, 0);
const totalTombstoned = entities.reduce((sum, e) => sum + e.tombstoned, 0);
const totalFlaggedForReview = entities.reduce((sum, e) => sum + (e.flaggedForReview ?? 0), 0);
const errors = entities.filter(e => e.error).map(e => `${e.entity}: ${e.error}`);
await this.updateHistory(startedAt, status, totalUpserted, totalTombstoned, errors.join('; ') || null);
console.log(
`[Pax8Sync] Full sync ${status} (${syncId}) — ${totalUpserted} upserted, ${totalTombstoned} tombstoned`
`[Pax8Sync] Full sync ${status} (${syncId}) — ${totalUpserted} upserted, ${totalTombstoned} tombstoned, ${totalFlaggedForReview} flagged for review`
);
return {
@ -520,14 +521,21 @@ export class Pax8SyncService {
const start = Date.now();
try {
const result = await matchPax8Companies();
const flaggedForReview = result.flaggedAmbiguous + result.flaggedNoCandidate;
if (flaggedForReview > 0) {
console.log(`[Pax8Sync] Company match: ${flaggedForReview} compan${flaggedForReview === 1 ? 'y' : 'ies'} flagged for manual review (${result.flaggedAmbiguous} ambiguous, ${result.flaggedNoCandidate} no-candidate)`);
}
return {
entity: 'company_matches',
success: true,
upserted: result.autoLinked,
// Repurposed: not a soft-delete tombstone count — the number of
// pax8_companies rows flagged for manual review this run (ambiguous
// + no-candidate), surfaced through the same rollup field.
tombstoned: result.flaggedAmbiguous + result.flaggedNoCandidate,
// No soft-deletes are ever performed by matching — real deletion
// counts belong in `tombstoned`/sync_history.records_deleted, and
// conflating the two fabricates "deleted" counts in the admin Sync
// History UI (CR-02). Review-flag counts are reported separately via
// `flaggedForReview`.
tombstoned: 0,
flaggedForReview,
durationMs: result.durationMs,
};
} catch (err) {

View file

@ -120,6 +120,12 @@ export interface Pax8EntitySyncResult {
tombstoned: number;
durationMs: number;
error?: string;
// Populated only by the 'company_matches' entity: pax8_companies rows
// flagged for manual review this run (ambiguous + no-candidate). Kept
// separate from `tombstoned` (a real soft-delete count) so it is never
// folded into sync_history.records_deleted / the Sync History UI's
// "deleted" column.
flaggedForReview?: number;
}
export interface Pax8SyncResult {