# Salesforce sync fix — apply

    cp -R salesforce-sync-fix/app/*    ./app/
    cp -R salesforce-sync-fix/config/* ./config/
    cp -R salesforce-sync-fix/tests/*  ./tests/

Then apply the two patches by hand (both are short):
  docs/adapter-patch.md      — SalesforceAdapter uses LeadQueryBuilder for Lead
  docs/pagination-patch.md   — SyncManager follows nextCursor until done

## Test + re-sync
    php artisan test --filter=LeadQueryBuilder
    php artisan tinker --execute="
    \$org = App\Organizations\Models\Organization::first();
    app(App\Platform\Tenancy\CurrentOrganization::class)->set(\$org);
    App\Ingestion\Models\RawPayload::where('organization_id',\$org->id)->where('resource','Lead')->delete();
    \$a = App\Integrations\Models\IntegrationAccount::whereHas('integration', fn(\$q)=>\$q->where('key','salesforce'))->first();
    \$run = app(App\Sync\Services\SyncManager::class)->sync(\$a, 'Lead', 'full');
    echo 'sync: '.\$run->status->value.PHP_EOL;
    echo 'leads: '.App\Ingestion\Models\RawPayload::where('organization_id',\$org->id)->where('resource','Lead')->count().PHP_EOL;
    "

(The delete clears the 392 partial rows so the re-sync starts clean.)

## Why the counts were wrong
- This Salesforce org holds EVERY Salons by JC franchise: 139,649 leads, 36,334 in the
  last 12 months. Only ~7,400 belong to our 14 Rentec buildings.
- Lead.Rentec_Building_Name__c is always null — the field lives on Building__c, so the
  query has to traverse Building__r.
- Status 'Spam' (junk) and 'Service' (existing tenants ringing about service) aren't
  prospects and are excluded. Spam maps 1:1 to Exclude_from_Datorama__c.
- SyncManager never followed nextCursor, so every sync kept only the first 2,000 rows —
  a general bug that affected every provider, not just Salesforce.
