diff --git a/backend/src/cyclone/cli.py b/backend/src/cyclone/cli.py index a7cdd20..6519d92 100644 --- a/backend/src/cyclone/cli.py +++ b/backend/src/cyclone/cli.py @@ -577,7 +577,6 @@ def resubmit_rejected_claims( from cyclone import db as db_mod from cyclone.audit_log import AuditEvent, append_event - from cyclone.clearhouse import SftpClient from cyclone.parsers.parse_837 import parse as parse_837_text from cyclone.parsers.payer import PayerConfig from cyclone.store import store as cycl_store @@ -614,7 +613,6 @@ def resubmit_rejected_claims( failed = 0 validated = 0 payer_cfg = PayerConfig.co_medicaid() - sftp_client = SftpClient(sftp_block) start = time.monotonic() last_progress = start @@ -641,36 +639,39 @@ def resubmit_rejected_claims( try: ssh.close() except Exception: pass + ssh: paramiko.SSHClient | None = None + sftp: paramiko.SFTPClient | None = None + for i, src in enumerate(files, 1): if limit is not None and i > limit: break + content = src.read_bytes() + # Validate: parse must succeed AND payer_id must match the # companion-guide CO_TXIX (catches a bad byte-fix early). if validate: try: - parsed = parse_837_text(src.read_text(), payer_cfg) + parsed = parse_837_text(content.decode(), payer_cfg) except Exception as exc: # noqa: BLE001 failed += 1 click.echo(f"PARSE FAIL {src.name}: {exc.__class__.__name__}: {exc}", err=True) continue - for c in parsed.claims: - if c.payer.id != "CO_TXIX": - failed += 1 - click.echo( - f"PAYER MISMATCH {src.name}: payer.id={c.payer.id!r} " - f"(expected 'CO_TXIX')", err=True, - ) - break - else: - validated += 1 + mismatch = next( + (c for c in parsed.claims if c.payer.id != "CO_TXIX"), None + ) + if mismatch is not None: + failed += 1 + click.echo( + f"PAYER MISMATCH {src.name}: payer.id={mismatch.payer.id!r} " + f"(expected 'CO_TXIX')", err=True, + ) + continue + validated += 1 - content = src.read_bytes() local_size = len(content) remote_path = f"{remote_root}/{src.name}" - ssh: paramiko.SSHClient | None = None - sftp: paramiko.SFTPClient | None = None attempts = 0 ok = False while attempts < 3: @@ -713,12 +714,6 @@ def resubmit_rejected_claims( failed += 1 continue - # Reconnect periodically to dodge MOVEit's per-session cap. - if uploaded > 0 and uploaded % reconnect_every == 0: - click.echo(f"reconnecting (after {uploaded} uploads)", err=True) - _close_session(ssh) - ssh, sftp = None, None - # Audit (best-effort; if the DB is unavailable we still keep # the file on the wire). try: @@ -741,6 +736,12 @@ def resubmit_rejected_claims( uploaded += 1 + # Reconnect periodically to dodge MOVEit's per-session cap. + if uploaded % reconnect_every == 0: + click.echo(f"reconnecting (after {uploaded} uploads)", err=True) + _close_session(ssh) + ssh, sftp = None, None + # Progress every 10s of wall-clock (or at end). now = time.monotonic() if now - last_progress >= 10 or i == len(files): diff --git a/backend/src/cyclone/inbox_state.py b/backend/src/cyclone/inbox_state.py index c5a6cfd..3907d50 100644 --- a/backend/src/cyclone/inbox_state.py +++ b/backend/src/cyclone/inbox_state.py @@ -35,7 +35,7 @@ def apply_999_rejections( claim_lookup: Callable[[str], Claim | None], batch_envelope_index: dict[str, list[str]] | None = None, ) -> Apply999Result: - """For each set response with code R or E, look up the matching claim and + """For each set response with code R, E, or X, look up the matching claim and move it to REJECTED. Idempotent on already-rejected claims. Args: