v11.1 is live — Pharmacy Deep Dive & AI Transparency

Release Notes

Every version of CURA. From a single appointments tool to an AI-first Care Operating System.

v11.1CURRENT

Pharmacy Deep Dive & AI Transparency

March 2026

v11.1 deepens every layer introduced in v11.0. The pharmacy module gains OCR invoice scanning (GPT-4o vision), bulk receiving for up to 100 batches, a redesigned multi-item receiving table with HSN/GST columns, a drug interaction checker with severity-coded banners, LASA badges, thermal print receipts, a WhatsApp dispense receipt, a 4-band expiry hub, quarantine controls, and a full 4-report suite (Expiry, Sales, GRN, GST Summary). AI key management goes per-tenant with BYOK support, usage logging, and a super admin AI panel. The Dental module gets 11 power-user tools including PatientContextStrip, click-to-paint, multi-tooth BatchActionBar, and an end-of-visit workflow. Ambient Scribe gets hardened for Safari/iOS with an HTTP fallback, rate-limit recovery, and a first-chunk fast path. Insights gains a transparent WhatsApp billing breakdown with category-aware billing.

⚡ See what's new in v11.1 →
💊Pharmacy Deep Dive

OCR invoice scan — GPT-4o vision extracts line items + invoice number from photo/PDF

POST /api/pharmacy/ocr-invoice: accepts multipart image or PDF, sends to GPT-4o vision with structured extraction prompt. Returns { invoice_number, items[] } with drug name, batch, expiry, qty, MRP, HSN code, GST%. PharmacyReceivingView pre-fills all columns automatically from the scan. Backward-compatible: bare array response still handled. Falls back to env key if tenant key absent.

Bulk receiving API — up to 100 batches in one call with per-row validation

POST /api/pharmacy/stock-bulk: receives array of up to 100 batch rows. Per-row validation (required fields, qty > 0, valid expiry). On partial failure: successful rows committed, failed rows returned with error detail. Top-level supplier_invoice_no applies to all rows; per-row override supported. Idempotency via supplier_invoice_no + batch_no uniqueness check.

Multi-item receiving table — redesigned with HSN, GST%, computed totals footer

PharmacyReceivingView fully redesigned: per-row catalog autocomplete, barcode scan fills first empty row, OCR fills entire table. Added HSN code and GST% columns (editable, OCR-populated). Computed read-only columns: GST Amt and Net Amt per row. Totals footer: aggregate GST and Net across all rows. Invoice No. header field auto-populated by OCR.

Drug interaction checker — severity-colour-coded banner with dismiss

PharmacyDispenseView: debounced drug interaction check fires on cart change via /api/drug-interactions/check. Returns interactions with severity (contraindicated / major / moderate / minor). Banner colour: red/orange/yellow/blue by severity. Dismissible per session. LASA (Look-Alike Sound-Alike) badge rendered on drug search results and in cart item rows.

Thermal print receipt + WhatsApp dispense receipt

Print receipt: popout window with thermal-style layout (80mm column, drug list, totals, clinic header). WhatsApp receipt: useWhatsAppSend hook fires on dispense confirm. Receipt modal carries tenant prop and cart items for both print and WA paths.

Expiry Hub — 4-band view with quarantine and CSV export

PharmacyInventoryView: view toggle pill (All Inventory / Expiry Alerts) with count badge. Expiry hub shows batches in 4 bands: Expired (red), 0–30 days (orange), 31–60 days (amber), 61–90 days (yellow). Quarantine button per batch calls PUT /api/pharmacy/stock to flip status. Export Expiry CSV downloads filtered view.

Reports Suite — 4 CSV reports covering all pharmacy compliance needs

PharmacyReportsView: 2×2 card grid (responsive). Card 1: Expiry Report — configurable day window (30/60/90/180). Card 2: Sales/Dispense Report — date range, itemised with GST. Card 3: GRN/Purchase Register — date range, supplier + invoice detail. Card 4: GST Summary — month/year picker, CGST/SGST/IGST by rate slab with TOTAL row. All downloads use Bearer token auth.

🔑AI Key Management (BYOK)

Per-tenant OpenAI key — BYOK with platform fallback removed

getTenantOpenAI() now returns { client, keySource } ('byok' | 'platform'). Shared OPENAI_API_KEY env fallback removed from all AI routes. All AI routes (scribe, dental-ai, ocr-invoice) migrated to getTenantOpenAI() with 402 + user-facing error when no key configured. Fixes latent bug in dental/ambient-scribe/chunk.js where bare OpenAI instance was returned but { client } destructured.

AI usage logging — tokens, cost estimate, key_source per call

logAiUsage() helper writes fire-and-forget to new ai_usage_logs table: tenant_id, feature, model, prompt_tokens, completion_tokens, estimated_cost_inr, key_source. New migration: ai_usage_logs table with indexes on tenant_id + created_at for admin queries. One-time migration script moves legacy openai_api_key plaintext to ai_config.openai_key_enc.

Super admin AI panel — key status, MTD usage, per-feature breakdown

New AI KEYS tab in /admin dashboard: tenant list with key status (BYOK / Platform / None) and MTD usage. Detail panel: key provisioning (set/rotate/remove), usage breakdown by feature, model, and key_source (billable vs BYOK). API: GET/POST/DELETE /api/super-admin/ai/keys and GET /api/super-admin/ai/usage/[tenantId].

🦷Dental Power Tools

PatientContextStrip — last visit, plan status, advance balance, lab cases, overdue count always visible

Persistent strip at top of odontogram: last visit date, active plan status (total / paid / balance), advance balance, active lab cases count, overdue follow-up count. Eliminates tab-switching to get patient context during consultation.

Click-to-paint condition palette + Shift+click multi-tooth BatchActionBar

Condition palette above chart: select a condition, click any tooth to apply directly — no modal for fast single-tooth work. Shift+click selects multiple teeth; floating BatchActionBar appears to apply the same condition to all selected teeth at once. OverdueFollowUpBanner: dismissible amber banner listing past-due follow-ups by tooth.

Phase chips, AI Proposal card, active lab cases inline, end-of-visit workflow

Phase chips (P1/P2/P3) rendered on each tooth in the chart when an active plan has phase data. AI Proposal summary card in sidebar shows phase breakdown + cost estimate immediately after AI Propose — no modal needed. Active lab cases inline in sidebar with overdue warnings. End-of-Visit section: Collect Payment, Generate Invoice, Visit Summary + WhatsApp as a single linear flow when completed treatments exist.

DentalVisitCard trigger strip + ToothActionModal price badge

DentalVisitCard trigger strip shows procedure names, tooth numbers, and advance balance on first expand. ToothActionModal: price displayed as a prominent colour badge in search results and selected-service block. Ambient scribe live status chip in header shows recording duration, extracted findings count, and plan items in real-time.

🎙️Ambient Scribe Reliability

Safari/iOS support — audio/mp4 MediaRecorder fallback

Safari does not support WebM MediaRecorder. v11.1 adds MediaRecorder format detection: prefers audio/webm;codecs=opus, falls back to audio/mp4 (Safari/iOS). Removed audioBitsPerSecond and sampleRate constraints that caused Safari to throw on recorder start.

HTTP fallback, stale duration fix, server chunk_interval_ms honored

HTTP fallback: when WebSocket is disconnected, chunk.js HTTP endpoint receives findings and applies them to session state. Fixed stale duration in stopSession — uses durationRef instead of state closure. chunk_interval_ms now read from /start response instead of hardcoded 8000ms.

KEY_INVALID permanent stop, RATE_LIMITED 30s pause, first-chunk fast path

KEY_INVALID server event: stops extraction permanently (no more retry loop). RATE_LIMITED server event: pauses extraction for 30s then auto-resumes. First-chunk fast path: fires extraction after ~2s if ≥8 words accumulated — eliminates initial dead-air delay. VAD removed — all audio sent directly to Deepgram for maximum reliability.

Fixed double session_id in WS URL and null send race condition

session_id was being appended twice to the WebSocket URL query string. Fixed null send race: sendChunk() now guards against ws.readyState !== OPEN before calling ws.send(). Both bugs caused silent session failures on slow connections.

📊Insights: WhatsApp Billing Transparency

WhatsApp usage breakdown report — category-aware billing with stacked bar chart

New WhatsAppUsageReport component in InsightsTab: 4 summary cards (total spend, marketing ₹0.88, utility ₹0.15, free service). Recharts stacked bar chart by day: amber = marketing, teal = utility, slate = free. Receipt-style table: category badge, message count, rate, subtotal. Grand total row. Driven by existing date-range selector.

Accurate category billing — NPS reclassified as marketing, manual inbox free

NPS/feedback request messages reclassified from service to marketing bucket (₹0.88). Manual inbox messages (internal bucket) kept free — only automated messages billed. Category mapping: marketing ₹0.88, utility ₹0.15, service ₹0.00, authentication ₹0.15.

Paginated WA fetch — bypasses PostgREST 1,000-row cap

PostgREST enforces server-side row limit that silently truncates at 1,000 rows. Replaced single query with paginated loop using .range() fetching 1,000 rows at a time until exhausted. Clinics with high message volume now get accurate monthly totals and cost breakdowns.

All Changes

  • OCR invoice scan — GPT-4o vision extracts drug name, batch, expiry, qty, MRP, HSN, GST%, invoice number
  • Bulk receiving API — up to 100 batches per call; per-row validation with partial-failure error reporting
  • Multi-item receiving table redesigned — HSN/GST columns, computed Net Amt + GST Amt, totals footer
  • Drug interaction checker in dispense view — severity-colour-coded banner (red/orange/yellow/blue), dismissible
  • LASA badges in drug search results and cart items
  • Thermal print receipt (popout) + WhatsApp dispense receipt on every order
  • Expiry Hub — 4-band view (Expired / 0–30d / 31–60d / 61–90d) with quarantine and CSV export
  • Reports Suite — Expiry, Sales/Dispense, GRN/Purchase Register, GST Summary; all CSV downloads
  • BYOK OpenAI keys — shared platform key fallback removed; all AI routes require per-tenant key
  • AI usage logging — tokens, cost estimate, key_source written to ai_usage_logs on every call
  • Super admin AI KEYS tab — key status, MTD usage, per-feature breakdown with provisioning controls
  • PatientContextStrip — last visit, plan status, advance balance, lab cases, overdue count always visible
  • Click-to-paint condition palette + Shift+click multi-tooth BatchActionBar for batch condition application
  • Phase chips (P1/P2/P3) on teeth in odontogram chart; AI Proposal summary card in sidebar
  • End-of-Visit workflow — Collect Payment, Invoice, Visit Summary + WhatsApp as one linear section
  • Ambient Scribe: Safari/iOS audio/mp4 fallback — VAD removed, all audio direct to Deepgram
  • Ambient Scribe: HTTP fallback, KEY_INVALID stop, RATE_LIMITED 30s pause, first-chunk 2s fast path
  • WhatsApp billing breakdown — category-aware stacked bar chart + receipt table in Insights
  • WA fetch paginated — bypasses PostgREST 1,000-row cap for accurate high-volume billing