5.3 KiB
bank-clients:backfill-by-location — Bank Client Assignment Backfill
Links existing Users and Organizations to a local (level 2) Bank via the bank_clients pivot table, matched by shared city. This populates the client relationship that bank-manager features (e.g. the "Manage Profiles" scoped view and the transaction reversal feature) rely on to determine "which profiles belong to my bank."
Why this exists
bank_clients is not populated during normal registration — there is no signup flow that assigns a new User/Organization to a bank. Historically the table was only ever written to by one admin-only "Create Profile" form. As a result, on any environment where bank-manager features are newly enabled, most (or all) profiles have no bank client link at all, and a bank-manager's scoped views will appear empty even though the profiles clearly belong to that bank's city.
This command derives the missing links after the fact: it matches each profile's own Location (city) against a level-2 bank's own Location (city), and inserts a bank_clients row with relationship_type = 'local' for every match that doesn't already have one.
Usage
php artisan bank-clients:backfill-by-location [options]
Options
| Option | Description |
|---|---|
--dry-run |
Preview the assignments — prints counts and the bank/city mapping, writes nothing to the database |
How matching works
- Finds every level 2 Bank that has a
Locationrecord with acity_idset. Banks without a location, or banks at level 0 (central) / level 1 (system/bridge), are never targets — client relationships only ever point to the public-facing local bank for a city. - For each such bank, builds a
city_id → Bankmap. - For every
UserandOrganizationthat has aLocationrecord whosecity_idmatches one of those banks, links the profile to that bank viaattachBankClient($bank, 'local')— unless the profile already has alocalbank client link, in which case it's skipped. - Profiles whose city doesn't match any bank's city are left unlinked. This is expected — not every registered profile has a nearby bank onboarded yet.
Only User and Organization are considered. Banks and Admins are never assigned as someone else's client.
Idempotency
Safe to re-run at any time:
- Already-linked profiles (checked via
$client->banksClient()->exists()) are always skipped, never duplicated or reassigned. - Newly registered profiles, or newly added banks with a location, will pick up correct links on the next run without disturbing existing ones.
- Never deletes or modifies an existing
bank_clientsrow.
Examples
# Preview only — see how many users/organizations would be linked, and to which banks
php artisan bank-clients:backfill-by-location --dry-run
# Apply the backfill for real
php artisan bank-clients:backfill-by-location
Output
Bank #5 "Timebank Amsterdam" -> city_id 188
Bank #6 "Timebank Den Haag" -> city_id 305
Bank #8 "Timebank BXL" -> city_id 345
DRY RUN — no changes will be written.
Users matched by city: 1812
1812/1812 [============================] 100%
Organizations matched by city: 4
4/4 [============================] 100%
Linked: 1816
Skipped (already linked): 0
Run without --dry-run to apply these changes.
On a subsequent run (or the real run after a dry-run), previously-linked profiles show up under "Skipped (already linked)" instead of "Linked".
Running this on a production server
- Always dry-run first and sanity-check the bank/city list and counts before writing anything — this command performs a real, potentially large (thousands of rows) database write.
php artisan bank-clients:backfill-by-location --dry-run - Confirm every bank you expect to receive clients actually appears in the "Bank #… -> city_id …" list. If a bank you expected is missing, its
Locationrecord likely has nocity_idset — fix that first (via the bank's profile location settings) and re-run the dry-run. - If the numbers look right, run it for real:
php artisan bank-clients:backfill-by-location - Re-running later (e.g. after onboarding a new bank, or after a batch of new registrations) is safe — it only adds new links, never touches existing ones.
What this command does NOT do
- Does not create or update Bank
Locationrecords — a bank with no city set is silently skipped from the mapping. - Does not resolve conflicts if a profile is later moved to a different city — the unique constraint on
bank_clients(client_id,client_type,relationship_type) means a profile can only have onelocalbank client link at a time; changing a profile's city does not automatically re-link them, this command only fills in missing links. - Does not link Banks or Admins to other banks.
Related code
App\Models\User::attachBankClient()/App\Models\Organization::attachBankClient()— the underlying pivot-sync method this command calls.App\Models\Bank::userClients()/App\Models\Bank::organizationClients()— read-side relations used byProfiles\Manage.phpto scope a bank-manager's view to their own clients.bank_clientstable (migration2025_04_30_001906_create_bank_clients_table.php) — polymorphic pivot:bank_id,client_type,client_id,relationship_type(unique per client + relationship_type).