Files
timebank-cc-public/references/BANK_CLIENTS_BACKFILL_COMMAND.md
T

95 lines
5.3 KiB
Markdown

# 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
```bash
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
1. Finds every **level 2** Bank that has a `Location` record with a `city_id` set. 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.
2. For each such bank, builds a `city_id → Bank` map.
3. For every `User` and `Organization` that has a `Location` record whose `city_id` matches one of those banks, links the profile to that bank via `attachBankClient($bank, 'local')` — unless the profile already has a `local` bank client link, in which case it's skipped.
4. 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_clients` row.
## Examples
```bash
# 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
1. **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.
```bash
php artisan bank-clients:backfill-by-location --dry-run
```
2. Confirm every bank you expect to receive clients actually appears in the "Bank #… -> city_id …" list. If a bank you expected is missing, its `Location` record likely has no `city_id` set — fix that first (via the bank's profile location settings) and re-run the dry-run.
3. If the numbers look right, run it for real:
```bash
php artisan bank-clients:backfill-by-location
```
4. 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 `Location` records — 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 one `local` bank 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 by `Profiles\Manage.php` to scope a bank-manager's view to their own clients.
- `bank_clients` table (migration `2025_04_30_001906_create_bank_clients_table.php`) — polymorphic pivot: `bank_id`, `client_type`, `client_id`, `relationship_type` (unique per client + relationship_type).