| Source | What It Contains | How It Gets In |
|---|---|---|
| Golf Genius Roster | Member number, first & last name, GHIN number — the authoritative link between ClubCorp members and GHIN | Manual upload on dashboard |
| GHIN Active Roster | GHIN number, golfer name, H.I., status, email, gender | Automated — GHIN emails report, processed daily at 8am ET (scores) and Monday 8am ET (roster) |
| Scores Posted Report | GHIN number, date played, date posted, AGS, holes, course, differential | Automated — same GHIN email pipeline |
| ClubCorp Tee Sheets | Per-player: ClubCorp ID, name, member code, holes, cart type, member type, player category | Manual upload — save .htm from ClubCorp Tee Times, upload per course per day |
The tool connects tee sheet players to roster members, then checks whether their scores were posted. This happens in three steps.
Step 1 — Identify the Player
Every tee sheet entry includes a member number (e.g., S0262). The system looks up that number in the Golf Genius Master Roster:
| Scenario | What Happens |
|---|---|
| Only one member on that code | That's the member. Done — no name matching needed. |
| Multiple members on that code | Check if the player's ClubCorp ID (a unique per-person identifier from the tee sheet) has already been stored from a previous match. If found, match confirmed. |
| No stored ClubCorp ID yet | Fall back to name matching: filter by last name, then score by first name using exact match, a 100+ nickname map (Bob↔Robert, Tom↔Thomas, Mike↔Michael, etc.), and first-3-character comparison. Clear winner = matched. |
| After a successful match | The player's ClubCorp ID is saved to their roster record. Next time they play, the match is instant — no name matching required. |
Step 2 — Check Score Posting
Once the player is identified, the system uses their GHIN number from the roster to look up posted Treesdale scores. Only Treesdale-course scores count — away-play scores are ignored. Matching runs in three passes:
Pass A — Exact date (4 tiers, best match wins)
| Tier | Match Criteria | Note |
|---|---|---|
| 1 | Same date + same course + same holes | Best possible match |
| 2 | Same date + same holes (course differs) | Holes take priority over course |
| 3 | Same date + same course (holes differ) | Holes flagged as ⚠ mismatch |
| 4 | Same date, any Treesdale score | Last resort; holes flagged ⚠ |
Pass B — Fuzzy date (1–3 days after tee date)
For rounds still missing after Pass A, the system searches for a Treesdale score whose date_played is 1–3 days after the tee sheet date (same course required when known). This catches scores entered with a next-day or delayed date. The window is forward-only — a score cannot predate the round. Fuzzy-matched rounds are flagged with a ⚠ warning and the actual score date shown on hover.
Pass C — Family member fallback
If a round is still missing after Passes A & B, the system checks whether a family member (sharing the same member number) posted a score for that date. Family members who have their own tee sheet entry on the same date are excluded — their score is reserved for their own slot. When a family score is used, the booked name appears struck through on the tee sheet with the actual player's name shown below, and the audit flags the round as ✓ Family.
Each score can only be consumed once across all three passes. If a member played 2 rounds but posted 1 score, the best-matching round shows Posted and the other shows Missing.
Step 3 — Tee Sheet Display
| Dot Color | Meaning |
|---|---|
| Green | Matched member — score posted for this date |
| Red | Matched member — score missing for this date |
| Gold | Round credited to a family member's posted score |
| Grey | Not matched — guest, family member without GHIN, social member, or unresolved |
Concrete examples of how a tee-sheet entry is resolved to a member and how a score is matched. "Code" = ClubCorp member code; "CC ID" = the unique per-person ClubCorp ID.
Identifying the player
| Situation | What happens | Result |
|---|---|---|
| Code has one GHIN holder; CC ID already on file | The stored CC ID matches instantly — no name comparison needed. | Matched (Tier 1) |
| Family code "S0262" — booked as "Bob Smith" | Two members share the code: Robert and Susan Smith. First name "Bob" maps to "Robert" via the nickname table. | Matched → Robert |
| Same code — booked as "Susan Smith" | First-name score favors Susan over Robert. | Matched → Susan |
| Nickname/short forms (Bob/Robert, Mike/Michael, Tom/Thomas, Chris/Christopher) | A 100+ entry nickname map plus first-3-character comparison resolves the first name. | Matched |
| Matched member's CC ID on file differs from the entry's CC ID | The entry belongs to a different person on the shared code (e.g. a dependent) — the match is rejected to prevent mis-attribution. | Not matched (grey) |
| Two members on a code are genuinely indistinguishable (e.g. two "Mike Davis") | No first-name winner — surfaced for a human to confirm. | Needs Review |
| Member flagged affiliated with another club | Excluded from Treesdale compliance entirely. | Affiliated (grey) |
| Associate / play-away code (e.g. 12345-67890) | Not a Treesdale member — skipped. | Play Away |
| Guest placeholder CC ID (1–4) | Reserved guest IDs — never matched to a member. | Guest (grey) |
Matching the score
| Situation | What happens | Result |
|---|---|---|
| Played Grove/Lakes 18; posted Treesdale G/L 18 same day | Exact match on date + course + holes. | Posted |
| Played a Treesdale round; only posted score is an away course | Away-play scores never count toward Treesdale compliance. | Missing |
| Round on the 1st; score's date_played is the 3rd | Fuzzy pass matches a score 1–3 days after the round (forward-only — a score can't predate the round). | Posted ⚠ |
| Score's date is 2 days before the round | Forward-only window rejects it. | Missing |
| Same course & date, but booked 18 / posted 9 | Matched, with a holes-mismatch warning on hover. | Posted ⚠ |
| Played 2 rounds that day, posted 1 score | Each score is consumed once. The best-matching round gets it. | 1 Posted, 1 Missing |
| Parent booked but didn't post; a family member posted that day | Family fallback credits the round to the family member who has an unused score within 0–3 days (and isn't on the tee sheet themselves). | ✓ Family |
| Member played alone with no posted score | Solo rounds aren't required by the USGA. (A postable partner round claims any score first.) | Non-Postable |
The tee sheet shows ALL players. Each gets a badge. Only entry_type=member + player_category=Golf are audited for compliance.
| Badge | Type | Audited? |
|---|---|---|
| M | Member (Golf) | Yes — tracked for compliance |
| G | Guest of Member | No |
| U | Unaccompanied Guest | No |
| A | Associate Member | No |
| AA | Access Advantage | No |
| P | Play Away Premier | No |
| E | Employee | No |
| Schedule | What | How |
|---|---|---|
| Daily 8am ET | Scores Posted Report | GHIN emails .xlsx → Gmail → Edge Function. Only inserts scores with posted_date = yesterday or day before. Quick edit check on last 7 days. |
| Monday 8am ET | GHIN Active Roster | GHIN emails .xlsx → Gmail → Edge Function. Full upsert by GHIN number — updates status, H.I., email. |
| After tee sheet upload | ClubCorp ID capture | captureClubcorpIds() links tee sheet ClubCorp IDs to Golf Genius members for Tier 1 matching. |
| After GG upload | Display name sync | Syncs display_name and display_email from Golf Genius to the GHIN roster for canonical name/email display. |
Select a date range (up to 1 month). The audit loads tee sheet entries, runs the full 3-pass matching pipeline (exact date → fuzzy → family fallback), cross-references GHIN scores, and groups results by member. Rounds outside a member's active membership window are excluded. Date pickers only allow today or earlier.
Audit columns — By Member view
| Column | Meaning |
|---|---|
| Played | Total tee sheet entries for the member in the selected period |
| Postable | Rounds the member was responsible for posting (Played minus Non-Postable) |
| Posted | Of those postable rounds, how many have a matched GHIN score |
| Non-Post | Rounds not counted toward compliance: solo rounds (played alone, no score required by the system) and admin-exempted rounds (checkbox in the round detail). Played = Postable + Non-Post. |
| Missing | Postable − Posted. These are the rounds that still need action. |
Member status badges
| Status | Meaning |
|---|---|
| Compliant | All postable rounds have a matched score |
| Emailed | All missing rounds have had a reminder email sent |
| Partial | Some missing rounds emailed, some not yet contacted |
| Missing | Has unposted rounds, no reminder emails sent yet |
Round detail (click a member row to expand)
Each round shows date, course, time, holes played (actual posted holes, not booking holes), score (AGS), differential, post date, and status:
| Round Status | Meaning |
|---|---|
| Posted | Score matched — hover for AGS, differential, and post date |
| Posted ⚠ | Score matched via fuzzy date or with a holes mismatch — hover for details |
| ✓ Family | Score matched to a family member who played the round — hover to see who |
| Emailed & Posted | Score posted on or after the day a reminder was sent |
| ⚠ Emailed in Error | A reminder was sent even though the score had already been posted on an earlier day — usually because the Scores Posted Report hadn't updated yet. Surfaced here and on the Email History page so these can be tracked. |
| Missing & Emailed | Still no score, but a reminder was sent |
| Missing | No score found, no email sent |
| Non-Postable | Solo round or admin-exempted — excluded from compliance math |
The Non-Postable checkbox in each round row lets admins mark a round exempt (e.g. a member played as a guest on their own account, or a round that shouldn't count). Checking it removes the round from Postable and Missing counts immediately and persists to the database.
| Issue | How It's Handled |
|---|---|
| Apostrophes in names (O'Connor, L'Herbier) | nlk() strips apostrophes for all lookups |
| Titles (Dr., Mr., Mrs.) in GHIN names | nlk() strips all titles before matching |
| Suffixes (Jr., Sr., III, IV) in names | nlk() strips suffixes |
| Professional designations (CPA, MD, DA) | nlk() strips them |
| Family members sharing a member number | Roster Step 1 disambiguates by ClubCorp ID first, then first name + 100+ nickname map. Score Pass C then checks family scores as a last resort for any round still missing. |
| Member joined or left mid-period | Rounds played before a member's active date — or on/after their inactive date — are excluded from compliance (not counted as missing) |
| GG members with NULL member_number (156) | Tier 4 matches by name → GHIN roster as fallback |
| Multiple rounds on same date | 4-tier priority matching by course + holes; each score consumed only once across all passes. |
| Score entered with wrong date (off by 1–3 days) | Fuzzy pass searches for a Treesdale score 1–3 days after the tee sheet date (forward-only — a score cannot predate a round). Matched rounds are flagged ⚠ with the actual score date shown on hover. |
| CC ID ≠ Member# (membership transfers) | Flagged as "Needs Review" — excluded from compliance until manually confirmed |
| Guest placeholder ClubCorp IDs (1-4) | Skipped — never matched |
| Play-away codes (XXXXX-XXXXX) | Skipped — not Treesdale members |