From 3afd122067623bf3390c680d2f568ccfad38aed5 Mon Sep 17 00:00:00 2001 From: Alex Garcia Date: Wed, 30 Sep 2026 15:27:29 -0700 Subject: [PATCH 1/3] Only count individuals in "Individual Contributions by State" The by-state query summed every non-memo Schedule A row, so PAC contributions (11C), party contributions (11B), other receipts (15) and joint fundraising transfers (12) were attributed to the sending committee's state. Restrict to line 11(a)(i) plus the line 12 memo entries for individuals behind JFC transfers, matching FEC.gov's by-state breakdown. State links use the same predicate via _where. Co-Authored-By: Claude Opus 5.5 (1M context) --- frontend/src/Committee.svelte | 1 - frontend/src/forms/F3/F3.svelte | 1 - .../src/forms/shared/StateContributors.svelte | 12 ++++------ .../forms/shared/stateContributorsFetcher.ts | 23 ++++++++++++++----- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/frontend/src/Committee.svelte b/frontend/src/Committee.svelte index 273eb72..b4dae19 100644 --- a/frontend/src/Committee.svelte +++ b/frontend/src/Committee.svelte @@ -327,7 +327,6 @@ SELECT * FROM final`;
diff --git a/frontend/src/forms/F3/F3.svelte b/frontend/src/forms/F3/F3.svelte index 0f0d0d9..c594cf9 100644 --- a/frontend/src/forms/F3/F3.svelte +++ b/frontend/src/forms/F3/F3.svelte @@ -176,7 +176,6 @@
diff --git a/frontend/src/forms/shared/StateContributors.svelte b/frontend/src/forms/shared/StateContributors.svelte index ea140f1..f0cc5d5 100644 --- a/frontend/src/forms/shared/StateContributors.svelte +++ b/frontend/src/forms/shared/StateContributors.svelte @@ -15,10 +15,9 @@ interface Props { scope: FilingScope; homeState?: string | null; - formTypeFilter?: string; } - let { scope, homeState = null, formTypeFilter }: Props = $props(); + let { scope, homeState = null }: Props = $props(); const dbName = get(databaseName); @@ -60,7 +59,7 @@ } function stateUrl(stateCode: string): string { - return buildStateUrl(dbName, scope, stateCode, formTypeFilter, filingIdsResult.data); + return buildStateUrl(dbName, scope, stateCode, filingIdsResult.data); } const total = $derived( @@ -79,10 +78,7 @@ const sourceNote = $derived(() => { const formType = metadata.data?.form_type; if (!formType) return null; - if (formTypeFilter === 'SA11AI') { - return `Source: ${formType}, Schedule A, Line 11(a)(i)`; - } - return `Source: ${formType}, Schedule A`; + return `Source: ${formType}, Schedule A, Line 11(a)(i) and Line 12 joint fundraising memo entries`; }); const N = 10; @@ -146,7 +142,7 @@
-

Only includes individuals who have given $200 or more this cycle.

+

Only includes itemized contributions (generally donors who have given more than $200 this cycle).

{#if sourceNote()}

{sourceNote()}

{/if} diff --git a/frontend/src/forms/shared/stateContributorsFetcher.ts b/frontend/src/forms/shared/stateContributorsFetcher.ts index b5604ba..17ff8f4 100644 --- a/frontend/src/forms/shared/stateContributorsFetcher.ts +++ b/frontend/src/forms/shared/stateContributorsFetcher.ts @@ -40,6 +40,21 @@ export async function fetchScopeMetadata( ); } +/** + * Schedule A rows that represent contributions from individuals, matching how + * FEC.gov builds its by-state breakdown: + * - Line 11(a)(i) itemized individual contributions (non-memo). Earmarked + * contributions (e.g. via WinRed) are itemized here under the donor; the + * conduit's own "earmarked-conduit details" rows are memo entries. + * - Line 12 memo entries for individuals: the donors behind joint fundraising + * committee transfers. The non-memo line 12 row is the transfer from the JFC + * itself, which would otherwise be attributed to the JFC's state. + */ +export const INDIVIDUAL_CONTRIBUTIONS_WHERE = `( + (form_type = 'SA11AI' AND memo_code IS NOT 'X') + OR (form_type = 'SA12' AND memo_code = 'X' AND entity_type = 'IND') +)`; + export function fetchStateContributions( dbName: string, scope: FilingScope @@ -54,7 +69,7 @@ export function fetchStateContributions( WHERE ${where} AND contributor_state IS NOT NULL AND contributor_state != '' - AND memo_code != 'X' + AND ${INDIVIDUAL_CONTRIBUTIONS_WHERE} GROUP BY contributor_state ORDER BY total_contributions DESC `; @@ -65,7 +80,6 @@ export function buildStateUrl( dbName: string, scope: FilingScope, stateCode: string, - formTypeFilter?: string, filingIds?: string[] ): string { const scopeParams = filingScopeUrlParams(scope, filingIds); @@ -73,10 +87,7 @@ export function buildStateUrl( _sort: 'rowid', contributor_state__exact: stateCode, ...scopeParams, - memo_code__not: 'X', + _where: INDIVIDUAL_CONTRIBUTIONS_WHERE.replace(/\s+/g, ' ').trim(), }); - if (formTypeFilter) { - params.set('form_type__exact', formTypeFilter); - } return `/${dbName}/libfec_schedule_a?${params}`; } From 7934a74f5b6bc29d13ff5507d3edd12450787dc6 Mon Sep 17 00:00:00 2001 From: Alex Garcia Date: Wed, 30 Sep 2026 15:39:33 -0700 Subject: [PATCH 2/3] Count tribal contributions routed through JFCs Line 12 memo attributions can be to tribes (entity ORG), which FEC counts alongside individuals, as it does for tribes on line 11(a)(i). Co-Authored-By: Claude Opus 5.5 (1M context) --- .../src/forms/shared/stateContributorsFetcher.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/src/forms/shared/stateContributorsFetcher.ts b/frontend/src/forms/shared/stateContributorsFetcher.ts index 17ff8f4..b5c404e 100644 --- a/frontend/src/forms/shared/stateContributorsFetcher.ts +++ b/frontend/src/forms/shared/stateContributorsFetcher.ts @@ -46,13 +46,17 @@ export async function fetchScopeMetadata( * - Line 11(a)(i) itemized individual contributions (non-memo). Earmarked * contributions (e.g. via WinRed) are itemized here under the donor; the * conduit's own "earmarked-conduit details" rows are memo entries. - * - Line 12 memo entries for individuals: the donors behind joint fundraising - * committee transfers. The non-memo line 12 row is the transfer from the JFC - * itself, which would otherwise be attributed to the JFC's state. + * - Line 12 memo entries for individuals (and tribes, which FEC also counts): + * the donors behind joint fundraising committee transfers. The non-memo line + * 12 row is the transfer from the JFC itself, which would otherwise be + * attributed to the JFC's state. + * + * Partnership/LLC contributions are counted once, via the non-memo 11(a)(i) + * row; the memo attributions to individual partners are skipped. */ export const INDIVIDUAL_CONTRIBUTIONS_WHERE = `( (form_type = 'SA11AI' AND memo_code IS NOT 'X') - OR (form_type = 'SA12' AND memo_code = 'X' AND entity_type = 'IND') + OR (form_type = 'SA12' AND memo_code = 'X' AND entity_type IN ('IND', 'ORG')) )`; export function fetchStateContributions( From 67a366316f8d02d58848c24decfc8cfec4525bb1 Mon Sep 17 00:00:00 2001 From: Alex Garcia Date: Wed, 30 Sep 2026 17:02:24 -0700 Subject: [PATCH 3/3] Relabel state contributions widget for F3X filers On F3X, line 11(a)(i) also carries contributions from corporations, LLCs and other non-committee persons, which dominate at super PACs (e.g. Fairshake: ~$316M org vs ~$7.6M individual). Title the widget "Contributions from Individuals & Other Persons by State" there and note that it includes corporations and LLCs. F3 is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../src/forms/shared/StateContributors.svelte | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/frontend/src/forms/shared/StateContributors.svelte b/frontend/src/forms/shared/StateContributors.svelte index f0cc5d5..8140dce 100644 --- a/frontend/src/forms/shared/StateContributors.svelte +++ b/frontend/src/forms/shared/StateContributors.svelte @@ -65,10 +65,23 @@ const total = $derived( (contributions.data ?? []).reduce((sum, row) => sum + (row.total_contributions || 0), 0) ); + // On F3X (PACs, party committees, super PACs) line 11(a)(i) also carries + // contributions from corporations, LLCs and other non-committee "persons", + // which can dwarf the money from actual individuals at super PACs. + const isF3X = $derived(metadata.data?.form_type === 'F3X'); + const title = $derived( + isF3X + ? 'Contributions from Individuals & Other Persons by State' + : 'Individual Contributions by State' + ); + const subtitle = $derived(() => { const m = metadata.data; if (!m?.committee_name) return null; - const parts = [`Top individual contributions to ${m.committee_name}`]; + const kind = isF3X + ? 'contributions from individuals and other persons' + : 'individual contributions'; + const parts = [`Top ${kind} to ${m.committee_name}`]; if (m.coverage_from_date && m.coverage_through_date) { parts[0] += ` between ${formatDate(m.coverage_from_date)} and ${formatDate(m.coverage_through_date)}`; } @@ -92,12 +105,12 @@ {#if contributions.isLoading}
-

Individual Contributions by State

+

{title}

Loading...
{:else if contributions.data && contributions.data.length > 0}
-

Individual Contributions by State

+

{title}

{#if subtitle()}

{subtitle()}

{/if} @@ -142,7 +155,15 @@
-

Only includes itemized contributions (generally donors who have given more than $200 this cycle).

+

+ Only includes itemized contributions (generally donors who have given more than $200 this + cycle). +

+ {#if isF3X} +

+ Includes contributions from corporations, LLCs and other non-committee sources. +

+ {/if} {#if sourceNote()}

{sourceNote()}

{/if}