End-to-end component view — what runs where.
flowchart TB
subgraph Browser["Browser"]
FE[React Frontend\n@zango-core/crud]
end
subgraph Docker["Docker Compose"]
APP[Django App\nZango Framework\n:8000]
CELERY[Celery Workers\n2+ concurrent]
BEAT[Celery Beat\nScheduler]
REDIS[(Redis\nBroker + Cache)]
PG[(PostgreSQL\nzango_project DB)]
end
subgraph Claude["Claude API"]
CV[ClaimValidator\nAgent]
DA[DenialAnalyzer\nAgent]
AD[AppealDrafter\nAgent]
end
FE -->|HTTP / REST| APP
APP --> PG
APP --> REDIS
CELERY --> REDIS
CELERY --> PG
BEAT --> REDIS
CELERY -->|"agent.run()"| CV
CELERY -->|"agent.run() concurrent"| DA
CELERY -->|"agent.run() concurrent"| AD
CV -->|update_claim_ai_result| PG
DA -->|update_claim_ai_result| PG
AD -->|update_claim_ai_result| PG
classDef frontend fill:#e0f2fe,stroke:#0284c7,color:#0c4a6e;
classDef service fill:#fef3c7,stroke:#d97706,color:#78350f;
classDef datastore fill:#dcfce7,stroke:#16a34a,color:#14532d;
classDef agent fill:#ede9fe,stroke:#7c3aed,color:#3b0764;
class FE frontend;
class APP,CELERY,BEAT service;
class REDIS,PG datastore;
class CV,DA,AD agent
[CORRECTED 2026-07-19] Reconciled against claims/workflows.py — previous version had direct Submitted→Approved/Denied paths that don't exist (UnderReview is mandatory before either), stale Claim "PartiallyPaid" transitions that belong to Invoice not Claim, a direct Denied→Closed path the code doesn't have, and was missing reopen/approve_appeal entirely. Also: deny is BillingManager-only, not BillingStaff — BillingStaff can only submit and appeal.
stateDiagram-v2
[*] --> Draft
Draft --> Submitted : submit\n[any role]\n→ fires ClaimValidator
Submitted --> UnderReview : begin_review\n[BillingManager]
UnderReview --> Approved : approve\n[BillingManager]
UnderReview --> Denied : deny + form\n[BillingManager]\n→ fires DenialAnalyzer\n+ AppealDrafter CONCURRENTLY
Denied --> Appealed : appeal\n[any role]
Denied --> UnderReview : reopen\n[BillingManager]
Appealed --> Approved : approve_appeal\n[BillingManager]
Appealed --> Closed : close_from_appealed\n[BillingManager]
Approved --> Closed : close\n[BillingManager]
note right of Denied
Two Celery tasks
dispatched simultaneously:
run_denial_analyzer()
run_appeal_drafter()
end note
What happens the moment a claim is denied.
%%{init: {"theme": "dark"}}%%
sequenceDiagram
participant Staff as BillingManager
participant UI as React UI
participant APP as Django App
participant WF as ClaimWorkflow
participant REDIS as Redis Broker
participant W1 as Celery Worker 1
participant W2 as Celery Worker 2
participant CLAUDE as Claude API
participant DB as PostgreSQL
Note over Staff: Deny is BillingManager-only (corrected 2026-07-19); participant renamed from BillingStaff
Staff->>UI: Click Deny and fill denial form
UI->>APP: POST /claims/workflow/transition/
APP->>WF: execute denial transition
WF->>DB: update Claim status to Denied
WF->>DB: save denial_reason_code and description
WF->>REDIS: delay(run_denial_analyzer, claim_id)
WF->>REDIS: delay(run_appeal_drafter, claim_id)
APP-->>UI: 200 OK transition complete
par Concurrent execution
REDIS->>W1: dequeue run_denial_analyzer
W1->>CLAUDE: DenialAnalyzer agent.run()
CLAUDE-->>W1: JSON analysis result
W1->>DB: Claim.ai_denial_analysis = result
and
REDIS->>W2: dequeue run_appeal_drafter
W2->>CLAUDE: AppealDrafter agent.run()
CLAUDE-->>W2: Appeal letter text
W2->>DB: Claim.ai_appeal_draft = letter
end
Note over UI: AI Insights tab polls every 5s
UI->>APP: GET /claims/{id}/ poll
APP->>DB: fetch Claim
DB-->>APP: ai_denial_analysis and ai_appeal_draft populated
APP-->>UI: both fields present
UI->>Staff: Show DenialAnalysis and AppealDraft
[CORRECTED 2026-07-19] Reconciled against invoices/workflows.py — send is available to any role, not BillingStaff-only; a direct Sent→Paid path and an Overdue→PartiallyPaid path both exist and were missing.
stateDiagram-v2
[*] --> Draft
Draft --> Sent : send\n[any role]
Sent --> PartiallyPaid : record_partial\n[any role]
Sent --> Paid : mark_paid_from_sent\n[any role]
Sent --> Overdue : mark_overdue\n[BillingManager]
Sent --> Voided : void\n[BillingManager]
Overdue --> PartiallyPaid : record_partial_from_overdue\n[any role]
Overdue --> Paid : mark_paid_from_overdue\n[any role]
PartiallyPaid --> Paid : mark_paid\n[any role]
graph LR
subgraph "Zango App: patientbilling"
PAT[patients module\nPatient model]
CLM[claims module\nInsurancePayer\nClaim\nClaimLineItem\nClaimWorkflow]
INV[invoices module\nInvoice\nInvoiceLineItem\nPayment\nInvoiceWorkflow]
AGT[agents module\ntools.py\ntasks.py]
APP[app module\nserves React build]
PAY[payers module\nInsurancePayer CRUD views/forms/policies\n**added 2026-07-19: was missing from this diagram**]
end
CLM -->|ZForeignKey| PAT
INV -->|ZForeignKey| PAT
AGT -->|imports models| CLM
AGT -->|imports models| PAT
CLM -->|"done() triggers"| AGT
APP -.->|static files| FE[React Build]
CLM -.->|InsurancePayer FK, but CRUD views live in payers module| PAY