diff --git a/CLAUDE.md b/CLAUDE.md index 999be10..94c4804 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -709,3 +709,100 @@ if (!hasLoTWConfirmation && hasDCLData && missingEntity) { - Clarified that DCL API doesn't send DXCC fields (current limitation) - Implemented priority logic: LoTW entity data takes precedence over DCL - System ready to auto-use DCL DXCC data if they add it in future API updates + +### Critical LoTW Sync Behavior (LEARNED THE HARD WAY) + +**⚠️ IMPORTANT: LoTW sync MUST only import confirmed QSOs** + +After attempting to implement "QSO Delta" sync (all QSOs, confirmed + unconfirmed), we discovered: + +**The Problem:** +LoTW ADIF export with `qso_qsl=no` (all QSOs mode) only includes: +- `CALL` (callsign) +- `QSL_RCVD` (confirmation status: Y/N) + +**Missing Fields for Unconfirmed QSOs:** +- `DXCC` (entity ID) ← **CRITICAL for awards!** +- `COUNTRY` (entity name) +- `CONTINENT` +- `CQ_ZONE` +- `ITU_ZONE` + +**Result:** Unconfirmed QSOs have `entityId: null` and `entity: ""`, breaking award calculations. + +**Current Implementation (CORRECT):** +```javascript +// lotw.service.js - fetchQSOsFromLoTW() +const params = new URLSearchParams({ + login: lotwUsername, + password: loTWPassword, + qso_query: '1', + qso_qsl: 'yes', // ONLY confirmed QSOs + qso_qslsince: dateStr, // Incremental sync +}); +``` + +**Why This Matters:** +- Awards require `entityId` to count entities +- Without `entityId`, QSOs can't be counted toward DXCC, WAS, etc. +- Users can still see "worked" stations in QSO list, but awards only count confirmed +- DCL sync can import all QSOs because it provides entity data via callsign lookup + +**Attempted Solution (REVERTED):** +- Tried implementing callsign prefix lookup to populate missing `entityId` +- Created `src/backend/utils/callsign-lookup.js` with basic prefix mappings +- Complexity: 1000+ DXCC entities, many special event callsigns, portable designators +- Decision: Too complex, reverted (commit 310b154) + +**Takeaway:** LoTW confirmed QSOs have reliable DXCC data. Don't try to workaround this fundamental limitation. + +### QSO Confirmation Filters + +Added "Confirmed by at least 1 service" filter to QSO view (commit 688b0fc): + +**Filter Options:** +- "All QSOs" - No filter +- "Confirmed by at least 1 service" (NEW) - LoTW OR DCL confirmed +- "LoTW Only" - Confirmed by LoTW but NOT DCL +- "DCL Only" - Confirmed by DCL but NOT LoTW +- "Both Confirmed" - Confirmed by BOTH LoTW AND DCL +- "Not Confirmed" - Confirmed by NEITHER + +**SQL Logic:** +```sql +-- "Confirmed by at least 1 service" +WHERE lotwQslRstatus = 'Y' OR dclQslRstatus = 'Y' + +-- "LoTW Only" +WHERE lotwQslRstatus = 'Y' AND (dclQslRstatus IS NULL OR dclQslRstatus != 'Y') + +-- "DCL Only" +WHERE dclQslRstatus = 'Y' AND (lotwQslRstatus IS NULL OR lotwQslRstatus != 'Y') + +-- "Both Confirmed" +WHERE lotwQslRstatus = 'Y' AND dclQslRstatus = 'Y' + +-- "Not Confirmed" +WHERE (lotwQslRstatus IS NULL OR lotwQslRstatus != 'Y') +AND (dclQslRstatus IS NULL OR dclQslRstatus != 'Y') +``` + +### Recent Development Work (January 2025) + +**Sync Type Support (ATTEMPTED & REVERTED):** +- Commit 5b78935: Added LoTW sync type support (QSL/QSO delta/full) +- Commit 310b154: Reverted - LoTW doesn't provide entity data for unconfirmed QSOs +- **Lesson:** Keep it simple - only sync confirmed QSOs from LoTW + +**Dashboard Enhancements:** +- Added sync job history display with real-time polling (every 2 seconds) +- Shows job progress, status, and import logs +- Cancel button for stale/failed jobs with rollback capability +- Tracks all QSO changes in `qso_changes` table for rollback + +**Rollback System:** +- `cancelJob(jobId, userId)` - Cancels and rolls back sync jobs +- Tracks added QSOs (deletes them on rollback) +- Tracks updated QSOs (restores previous state) +- Only allows canceling failed jobs or stale running jobs (>1 hour) +- Server-side validation prevents unauthorized cancellations