feat(sp9): multi-payer, multi-NPI, SFTP stub for dzinesco/TOC

- providers table seeded with 3 NPIs (Montrose, Delta, Salida)
- payers + payer_configs (per-tx config) join table
- clearhouse singleton (dzinesco identity, SFTP block, filename block)
- config/payers.yaml loader with Pydantic schema validation
- cyclone/edi/filenames.py: HCPF X12 File Naming Standards helpers
- cyclone/clearhouse/sftp.py: stub that copies to ./var/sftp/staging/...
- cyclone/secrets.py: macOS Keychain wrapper via keyring
- 11 new validation rules R200-R210 (CO MAP + HCPF naming)
- 6 new API endpoints (/api/clearhouse/*, /api/config/*, /api/admin/reload-config)
- migration 0007
- 72 new tests (646 total passing, was 574)

See spec: docs/superpowers/specs/2026-06-20-cyclone-multi-payer-npi-sftp-design.md
This commit is contained in:
Tyler
2026-06-20 23:07:31 -06:00
parent fbe9940a3f
commit c62826daea
23 changed files with 2869 additions and 6 deletions
+96
View File
@@ -15,6 +15,102 @@ Financing (HCPF) requires.
These appear in the `NM1*PR` (payer) and `NM1*40` (receiver) segments of
the 837P file.
## dzinesco's TPID (clearinghouse identity)
| Field | Value |
|---|---|
| Clearinghouse name | `dzinesco` |
| dzinesco TPID | `11525703` |
| Submitter name (`NM1*41`) | `Dzinesco` |
| Submitter contact | Tyler Martinez <tyler@dzinesco.com> |
| SFTP host | `mft.gainwelltechnologies.com` |
| SFTP username | `colorado-fts\coxix_prod_11525703` |
| SFTP outbound dir | `/CO XIX/PROD/coxix_prod_11525703/FromHPE` |
| SFTP inbound dir | `/CO XIX/PROD/coxix_prod_11525703/ToHPE` |
## dzinesco's 3 billing-provider NPIs
All 3 NPIs are registered to the same Montrose corporate office. They
share tax ID `721587149` and taxonomy `251E00000X` (Home Health).
| NPI | Label | Legal name | Address |
|---|---|---|---|
| `1881068062` | Montrose | TOC, Inc. | 1100 East Main St, Suite A, Montrose, CO 814014063 |
| `1851446637` | Delta | TOC, Inc. | 1100 East Main St, Suite A, Montrose, CO 814014063 |
| `1467507269` | Salida | TOC, Inc. | 1100 East Main St, Suite A, Montrose, CO 814014063 |
Production data (June 2026) confirms all 3 NPIs submit through the same
Montrose address block. See SP9 spec
`docs/superpowers/specs/2026-06-20-cyclone-multi-payer-npi-sftp-design.md`.
## SFTP submission to Gainwell
dzinesco submits 837P files to Gainwell's MFT (Managed File Transfer)
at `mft.gainwelltechnologies.com`. The full SFTP path layout is
specified by the user (2026-06-20):
- **Outbound** (we send): `/CO XIX/PROD/coxix_prod_11525703/FromHPE`
- **Inbound** (HPE/Gainwell sends to us): `/CO XIX/PROD/coxix_prod_11525703/ToHPE`
### File naming
Per the HCPF X12 File Naming Standards Quick Guide
(<https://hcpf.colorado.gov/tp-x12-filenaming>):
- **Outbound** (we send): `TPID-TransactionType-yyyymmddhhmmssSSS-1of1.X12`
- Example: `11525703-837P-20260620132243505-1of1.x12`
- 17-digit millisecond precision, Mountain Time (NOT UTC)
- "1of1" is the only accepted sequence value
- **Inbound** (HPE sends): `TP<TPID>-<OrigTx>_M<Tracking>-<ts>-1of1_<FileType>.x12`
- Example: `TP11525703-837P_M019048402-20260520231513488-1of1_999.x12`
- `FileType` is one of: `999`, `TA1`, `270`, `271`, `276`, `277`, `277CA`, `278`, `820`, `834`, `835`, `ENCR`
- 277CA is distinguished by `ST*277CA` content (filename uses `277`)
### SP9 stub vs SP13 wire-up
The SP9 SFTP client is a **stub** that writes generated 837 files to
`./var/sftp/staging/{outbound_path}/{filename}` instead of opening a
real SFTP connection. The structural interface matches the future
`paramiko`-backed implementation, so SP13 is a one-file swap.
`clearhouse.sftp_block.stub` is `true` by default. Set to `false` (and
create the Keychain entry, see below) to enable real SFTP.
### Keychain setup (one-time, by operator)
```sh
security add-generic-password -s cyclone -a sftp.gainwell.password -w '<password>'
security find-generic-password -s cyclone -a sftp.gainwell.password -w
```
The `cyclone/secrets.py` module fetches the secret by name. When the
entry is missing or `keyring` is not installed, the SFTP stub falls
back to `<stub-secret>` so the local flow still works.
### Verification
```sh
# List providers (3 NPIs)
curl http://localhost:8000/api/config/providers
# List payers (1 = CO_TXIX)
curl http://localhost:8000/api/config/payers
# List CO_TXIX configs
curl http://localhost:8000/api/config/payers/CO_TXIX/configs
# Clearhouse identity
curl http://localhost:8000/api/clearhouse
# Submit 2 claims to the SFTP stub
curl -X POST http://localhost:8000/api/clearhouse/submit \
-H 'Content-Type: application/json' \
-d '{"claim_ids": ["CLM-1", "CLM-2"], "payer_id": "CO_TXIX"}'
# Files appear at ./var/sftp/staging/CO XIX/PROD/coxix_prod_11525703/FromHPE/
ls -la "./var/sftp/staging/CO XIX/PROD/coxix_prod_11525703/FromHPE/"
```
## Payer IDs
### 837P (claims)