feat(npi): wire 6 healthcare services into catalog, intake, items, handlers, portal
This commit is contained in:
parent
8748c0a141
commit
e67db156e8
6 changed files with 345 additions and 0 deletions
|
|
@ -464,6 +464,48 @@ const COMPLIANCE_SERVICES: Record<
|
|||
erpnext_item: "ENTITY-UPGRADE-BUNDLE",
|
||||
discountable: true,
|
||||
},
|
||||
// ── Healthcare / NPI compliance ──────────────────────────────────────
|
||||
// CMS/NPPES provider compliance. Handlers are review-staged (a human
|
||||
// files in PECOS/NPPES) — same safety default as the FCC auto-filing
|
||||
// toggle. HIPAA is intentionally out of scope (separate specialty).
|
||||
// Flagship = npi-revalidation (CMS 5-yr Medicare revalidation, dateable
|
||||
// overdue signal from the free CMS revalidation list).
|
||||
"npi-revalidation": {
|
||||
name: "Medicare PECOS Revalidation Filing",
|
||||
price_cents: 39900,
|
||||
erpnext_item: "NPI-REVALIDATION",
|
||||
discountable: true,
|
||||
},
|
||||
"npi-reactivation": {
|
||||
name: "NPI Reactivation",
|
||||
price_cents: 24900,
|
||||
erpnext_item: "NPI-REACTIVATION",
|
||||
discountable: true,
|
||||
},
|
||||
"nppes-update": {
|
||||
name: "NPPES Data Update / Attestation",
|
||||
price_cents: 14900,
|
||||
erpnext_item: "NPPES-UPDATE",
|
||||
discountable: true,
|
||||
},
|
||||
"medicare-enrollment": {
|
||||
name: "Medicare Enrollment (PECOS)",
|
||||
price_cents: 49900,
|
||||
erpnext_item: "MEDICARE-ENROLLMENT",
|
||||
discountable: true,
|
||||
},
|
||||
"oig-sam-screening": {
|
||||
name: "OIG/SAM Exclusion Screening (Annual)",
|
||||
price_cents: 9900,
|
||||
erpnext_item: "OIG-SAM-SCREENING",
|
||||
discountable: false,
|
||||
},
|
||||
"provider-compliance-bundle": {
|
||||
name: "Provider Compliance Bundle (Annual)",
|
||||
price_cents: 69900,
|
||||
erpnext_item: "PROVIDER-COMPLIANCE-BUNDLE",
|
||||
discountable: true,
|
||||
},
|
||||
};
|
||||
|
||||
// ── Intake validation map ─────────────────────────────────────────────
|
||||
|
|
@ -643,6 +685,14 @@ const REQUIRED_FIELDS: Record<string, FieldSpec> = {
|
|||
// ── Hazmat / Emissions ───────────────────────────────────────────────
|
||||
"hazmat-phmsa": { required: ["dot_number", "legal_name", "email", "hazmat_classes"], soft: ["bulk_packaging", "small_business", "ein"] },
|
||||
"state-emissions": { required: ["dot_number", "legal_name", "base_state", "email"], soft: ["engine_model_years", "power_units"] },
|
||||
|
||||
// ── Healthcare / NPI ─────────────────────────────────────────────────
|
||||
"npi-revalidation": { required: ["npi", "provider_name", "email"], soft: ["pecos_enrollment_id", "specialty", "practice_state"] },
|
||||
"npi-reactivation": { required: ["npi", "provider_name", "email"], soft: ["deactivation_reason", "specialty"] },
|
||||
"nppes-update": { required: ["npi", "provider_name", "email"], soft: ["fields_to_update", "practice_state"] },
|
||||
"medicare-enrollment": { required: ["npi", "provider_name", "email", "practice_state"], soft: ["pecos_enrollment_id", "specialty", "entity_type"] },
|
||||
"oig-sam-screening": { required: ["npi", "provider_name", "email"], soft: ["organization_name", "staff_count"] },
|
||||
"provider-compliance-bundle": { required: ["npi", "provider_name", "email"], soft: ["pecos_enrollment_id", "specialty", "practice_state"] },
|
||||
};
|
||||
|
||||
// ── Bundle composition + incompatibility (single source of truth) ──────────
|
||||
|
|
|
|||
|
|
@ -58,5 +58,77 @@
|
|||
"include_item_in_manufacturing": 0,
|
||||
"standard_rate": 0.0,
|
||||
"currency": "USD"
|
||||
},
|
||||
{
|
||||
"doctype": "Item",
|
||||
"item_code": "NPI-REVALIDATION",
|
||||
"item_name": "Medicare PECOS Revalidation Filing",
|
||||
"description": "Prepare and file the provider's Medicare revalidation in PECOS (5-year cycle).",
|
||||
"item_group": "Services",
|
||||
"stock_uom": "Nos",
|
||||
"is_stock_item": 0,
|
||||
"include_item_in_manufacturing": 0,
|
||||
"standard_rate": 399.0,
|
||||
"currency": "USD"
|
||||
},
|
||||
{
|
||||
"doctype": "Item",
|
||||
"item_code": "NPI-REACTIVATION",
|
||||
"item_name": "NPI Reactivation",
|
||||
"description": "Reactivate a deactivated NPI in NPPES and re-certify the provider record.",
|
||||
"item_group": "Services",
|
||||
"stock_uom": "Nos",
|
||||
"is_stock_item": 0,
|
||||
"include_item_in_manufacturing": 0,
|
||||
"standard_rate": 249.0,
|
||||
"currency": "USD"
|
||||
},
|
||||
{
|
||||
"doctype": "Item",
|
||||
"item_code": "NPPES-UPDATE",
|
||||
"item_name": "NPPES Data Update / Attestation",
|
||||
"description": "Update and re-attest a provider's NPPES record (CMS requires updates within 30 days of changes).",
|
||||
"item_group": "Services",
|
||||
"stock_uom": "Nos",
|
||||
"is_stock_item": 0,
|
||||
"include_item_in_manufacturing": 0,
|
||||
"standard_rate": 149.0,
|
||||
"currency": "USD"
|
||||
},
|
||||
{
|
||||
"doctype": "Item",
|
||||
"item_code": "MEDICARE-ENROLLMENT",
|
||||
"item_name": "Medicare Enrollment (PECOS)",
|
||||
"description": "Complete a provider's Medicare enrollment via PECOS (CMS-855).",
|
||||
"item_group": "Services",
|
||||
"stock_uom": "Nos",
|
||||
"is_stock_item": 0,
|
||||
"include_item_in_manufacturing": 0,
|
||||
"standard_rate": 499.0,
|
||||
"currency": "USD"
|
||||
},
|
||||
{
|
||||
"doctype": "Item",
|
||||
"item_code": "OIG-SAM-SCREENING",
|
||||
"item_name": "OIG/SAM Exclusion Screening (Annual)",
|
||||
"description": "Annual OIG LEIE + SAM exclusion screening for a provider and listed staff, with certificate.",
|
||||
"item_group": "Services",
|
||||
"stock_uom": "Nos",
|
||||
"is_stock_item": 0,
|
||||
"include_item_in_manufacturing": 0,
|
||||
"standard_rate": 99.0,
|
||||
"currency": "USD"
|
||||
},
|
||||
{
|
||||
"doctype": "Item",
|
||||
"item_code": "PROVIDER-COMPLIANCE-BUNDLE",
|
||||
"item_name": "Provider Compliance Bundle (Annual)",
|
||||
"description": "Annual provider compliance bundle: revalidation watch, OIG/SAM screening, and NPPES upkeep.",
|
||||
"item_group": "Services",
|
||||
"stock_uom": "Nos",
|
||||
"is_stock_item": 0,
|
||||
"include_item_in_manufacturing": 0,
|
||||
"standard_rate": 699.0,
|
||||
"currency": "USD"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -27,6 +27,13 @@ COMPLIANCE_SERVICE_LABELS = {
|
|||
"stir-shaken": "STIR/SHAKEN Implementation Assistance",
|
||||
"dc-agent": "D.C. Registered Agent (Annual)",
|
||||
"bdc-filing": "BDC / Form 477 Filing",
|
||||
# Healthcare / NPI
|
||||
"npi-revalidation": "Medicare PECOS Revalidation Filing",
|
||||
"npi-reactivation": "NPI Reactivation",
|
||||
"nppes-update": "NPPES Data Update / Attestation",
|
||||
"medicare-enrollment": "Medicare Enrollment (PECOS)",
|
||||
"oig-sam-screening": "OIG/SAM Exclusion Screening (Annual)",
|
||||
"provider-compliance-bundle": "Provider Compliance Bundle (Annual)",
|
||||
}
|
||||
|
||||
# Map the per-filing timestamp columns on telecom_entities → the slugs that
|
||||
|
|
|
|||
|
|
@ -58,6 +58,15 @@ from .mailbox_setup import MailboxSetupHandler
|
|||
from .carrier_closeout import CarrierCloseoutHandler
|
||||
# PHMSA hazmat registration (admin-assisted, 49 CFR Part 107)
|
||||
from .hazmat_phmsa import HazmatPHMSAHandler
|
||||
# Healthcare / NPI provider compliance (review-staged PECOS/NPPES filings)
|
||||
from .npi_provider import (
|
||||
NPIRevalidationHandler,
|
||||
NPIReactivationHandler,
|
||||
NPPESUpdateHandler,
|
||||
MedicareEnrollmentHandler,
|
||||
OIGSAMScreeningHandler,
|
||||
ProviderComplianceBundleHandler,
|
||||
)
|
||||
|
||||
SERVICE_HANDLERS: dict[str, type] = {
|
||||
"flsa-audit": FLSAAuditHandler,
|
||||
|
|
@ -141,6 +150,13 @@ SERVICE_HANDLERS: dict[str, type] = {
|
|||
"hazmat-phmsa": HazmatPHMSAHandler,
|
||||
# ── State emissions / clean-truck (admin-assisted) ────────────────
|
||||
"state-emissions": StateTruckingHandler,
|
||||
# ── Healthcare / NPI (review-staged) ──────────────────────────────
|
||||
"npi-revalidation": NPIRevalidationHandler,
|
||||
"npi-reactivation": NPIReactivationHandler,
|
||||
"nppes-update": NPPESUpdateHandler,
|
||||
"medicare-enrollment": MedicareEnrollmentHandler,
|
||||
"oig-sam-screening": OIGSAMScreeningHandler,
|
||||
"provider-compliance-bundle": ProviderComplianceBundleHandler,
|
||||
}
|
||||
|
||||
# Service slugs that operate on a telecom entity — used by job_server.py
|
||||
|
|
|
|||
184
scripts/workers/services/npi_provider.py
Normal file
184
scripts/workers/services/npi_provider.py
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
"""Healthcare / NPI provider compliance handlers.
|
||||
|
||||
Review-staged: each NPI service generates an admin todo with the provider's
|
||||
NPI + intake details for a human to file in CMS PECOS / NPPES. This mirrors the
|
||||
FCC auto-filing-off safety default — no automated submission to government
|
||||
portals until the Playwright flows are proven.
|
||||
|
||||
Covers slugs:
|
||||
npi-revalidation Medicare PECOS revalidation (5-yr cycle)
|
||||
npi-reactivation reactivate a deactivated NPI
|
||||
nppes-update NPPES data update / attestation
|
||||
medicare-enrollment new Medicare enrollment via PECOS
|
||||
oig-sam-screening OIG LEIE + SAM exclusion screening (annual)
|
||||
provider-compliance-bundle revalidation watch + screening + NPPES upkeep
|
||||
|
||||
Intake data needed (collected by the npi-intake wizard step):
|
||||
- npi provider's 10-digit NPI
|
||||
- provider_name legal/provider name
|
||||
- email contact email
|
||||
- pecos_enrollment_id (optional) PECOS enrollment ID
|
||||
- specialty (optional) taxonomy/specialty
|
||||
- practice_state (optional) practice location state
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
LOG = logging.getLogger("workers.services.npi_provider")
|
||||
|
||||
# Per-slug admin todo metadata: human-readable action + the portal a human uses.
|
||||
_SLUG_META = {
|
||||
"npi-revalidation": {
|
||||
"name": "Medicare PECOS Revalidation Filing",
|
||||
"portal": "https://pecos.cms.hhs.gov/",
|
||||
"action": (
|
||||
"File the Medicare revalidation in PECOS for this provider. Confirm "
|
||||
"the revalidation due date on the CMS revalidation list, update the "
|
||||
"enrollment record, and submit. Capture the PECOS tracking ID."
|
||||
),
|
||||
"priority": "high",
|
||||
},
|
||||
"npi-reactivation": {
|
||||
"name": "NPI Reactivation",
|
||||
"portal": "https://nppes.cms.hhs.gov/",
|
||||
"action": (
|
||||
"Reactivate the deactivated NPI in NPPES. Verify the deactivation "
|
||||
"reason, correct any stale data, and re-certify the record."
|
||||
),
|
||||
"priority": "high",
|
||||
},
|
||||
"nppes-update": {
|
||||
"name": "NPPES Data Update / Attestation",
|
||||
"portal": "https://nppes.cms.hhs.gov/",
|
||||
"action": (
|
||||
"Update + re-attest the provider's NPPES record (CMS requires "
|
||||
"updates within 30 days of any change). Apply the requested field "
|
||||
"changes and certify."
|
||||
),
|
||||
"priority": "normal",
|
||||
},
|
||||
"medicare-enrollment": {
|
||||
"name": "Medicare Enrollment (PECOS)",
|
||||
"portal": "https://pecos.cms.hhs.gov/",
|
||||
"action": (
|
||||
"Complete the provider's Medicare enrollment in PECOS (CMS-855). "
|
||||
"Confirm taxonomy, practice location, and authorized official."
|
||||
),
|
||||
"priority": "high",
|
||||
},
|
||||
"oig-sam-screening": {
|
||||
"name": "OIG/SAM Exclusion Screening (Annual)",
|
||||
"portal": "https://oig.hhs.gov/exclusions/ + https://sam.gov/",
|
||||
"action": (
|
||||
"Run the provider (and any listed staff) against the OIG LEIE and "
|
||||
"SAM exclusion lists. Produce the screening certificate and flag any "
|
||||
"matches for escalation."
|
||||
),
|
||||
"priority": "normal",
|
||||
},
|
||||
"provider-compliance-bundle": {
|
||||
"name": "Provider Compliance Bundle (Annual)",
|
||||
"portal": "https://pecos.cms.hhs.gov/ + https://nppes.cms.hhs.gov/",
|
||||
"action": (
|
||||
"Onboard the provider into the annual compliance bundle: enroll in "
|
||||
"revalidation watch, run OIG/SAM screening, and refresh the NPPES "
|
||||
"record. Set the next revalidation reminder."
|
||||
),
|
||||
"priority": "high",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class _BaseNPIHandler:
|
||||
"""Shared review-staged behaviour for all NPI services."""
|
||||
|
||||
SERVICE_SLUG = ""
|
||||
|
||||
async def process(self, order_data: dict) -> list[str]:
|
||||
order_number = order_data.get("order_number", order_data.get("name", ""))
|
||||
return await self.handle(order_data, order_number)
|
||||
|
||||
async def handle(self, order_data: dict, order_number: str) -> list[str]:
|
||||
meta = _SLUG_META[self.SERVICE_SLUG]
|
||||
LOG.info("[%s] Processing %s", order_number, self.SERVICE_SLUG)
|
||||
|
||||
intake = order_data.get("intake_data") or {}
|
||||
if isinstance(intake, str):
|
||||
intake = json.loads(intake)
|
||||
|
||||
npi = intake.get("npi", "N/A")
|
||||
provider = intake.get("provider_name", order_data.get("customer_name", "Unknown"))
|
||||
specialty = intake.get("specialty", "")
|
||||
practice_state = intake.get("practice_state", "")
|
||||
pecos_id = intake.get("pecos_enrollment_id", "")
|
||||
|
||||
description = (
|
||||
f"{meta['action']}\n\n"
|
||||
f"Provider: {provider}\n"
|
||||
f"NPI: {npi}\n"
|
||||
f"PECOS Enrollment ID: {pecos_id or 'not provided'}\n"
|
||||
f"Specialty: {specialty or 'not provided'}\n"
|
||||
f"Practice state: {practice_state or 'not provided'}\n"
|
||||
f"Portal: {meta['portal']}\n\n"
|
||||
f"Review-staged: file manually, then mark this order complete."
|
||||
)
|
||||
|
||||
self._create_todo(
|
||||
order_number,
|
||||
intake,
|
||||
title=f"{meta['name']} — {provider} (NPI {npi})",
|
||||
description=description,
|
||||
priority=meta["priority"],
|
||||
)
|
||||
return []
|
||||
|
||||
def _create_todo(self, order_number, intake, title, description, priority="normal"):
|
||||
try:
|
||||
import psycopg2
|
||||
|
||||
conn = psycopg2.connect(os.environ.get("DATABASE_URL", ""))
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO admin_todos (
|
||||
title, category, priority, order_number, service_slug,
|
||||
description, data, status
|
||||
) VALUES (%s, %s, %s, %s, %s, %s, %s, 'pending')
|
||||
""",
|
||||
(
|
||||
title, "filing", priority, order_number,
|
||||
self.SERVICE_SLUG, description, json.dumps(intake),
|
||||
),
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
except Exception as exc:
|
||||
LOG.error("[%s] Failed to create NPI todo: %s", order_number, exc)
|
||||
|
||||
|
||||
class NPIRevalidationHandler(_BaseNPIHandler):
|
||||
SERVICE_SLUG = "npi-revalidation"
|
||||
|
||||
|
||||
class NPIReactivationHandler(_BaseNPIHandler):
|
||||
SERVICE_SLUG = "npi-reactivation"
|
||||
|
||||
|
||||
class NPPESUpdateHandler(_BaseNPIHandler):
|
||||
SERVICE_SLUG = "nppes-update"
|
||||
|
||||
|
||||
class MedicareEnrollmentHandler(_BaseNPIHandler):
|
||||
SERVICE_SLUG = "medicare-enrollment"
|
||||
|
||||
|
||||
class OIGSAMScreeningHandler(_BaseNPIHandler):
|
||||
SERVICE_SLUG = "oig-sam-screening"
|
||||
|
||||
|
||||
class ProviderComplianceBundleHandler(_BaseNPIHandler):
|
||||
SERVICE_SLUG = "provider-compliance-bundle"
|
||||
|
|
@ -38,6 +38,7 @@ export type IntakeStep =
|
|||
| "mcs150" // MCS-150 biennial update form fields (standalone)
|
||||
| "dot-intake" // Unified DOT intake — shows sections based on services ordered
|
||||
| "state-trucking" // State-level trucking + hazmat/emissions intake (slug-gated sections)
|
||||
| "npi-intake" // Healthcare / NPI provider intake (NPI, PECOS, practice)
|
||||
| "review"
|
||||
| "payment";
|
||||
|
||||
|
|
@ -143,6 +144,14 @@ export const INTAKE_MANIFEST: Record<string, IntakeStep[]> = {
|
|||
|
||||
// ── Entity / Corporate Upgrade ─────────────────────────────────────
|
||||
"entity-upgrade-bundle": ["dot-intake", "review", "payment"],
|
||||
|
||||
// ── Healthcare / NPI ───────────────────────────────────────────────
|
||||
"npi-revalidation": ["npi-intake", "review", "payment"],
|
||||
"npi-reactivation": ["npi-intake", "review", "payment"],
|
||||
"nppes-update": ["npi-intake", "review", "payment"],
|
||||
"medicare-enrollment": ["npi-intake", "review", "payment"],
|
||||
"oig-sam-screening": ["npi-intake", "review", "payment"],
|
||||
"provider-compliance-bundle": ["npi-intake", "review", "payment"],
|
||||
};
|
||||
|
||||
// Category-gated dynamic steps. The Wizard inserts these after the `category`
|
||||
|
|
@ -204,6 +213,13 @@ export const SERVICE_META: Record<string, { name: string; price_cents: number; g
|
|||
// Hazmat / emissions
|
||||
"hazmat-phmsa": { name: "PHMSA Hazmat Registration", price_cents: 14900, gov_fee_label: "PHMSA registration fee, by business size" },
|
||||
"state-emissions": { name: "State Clean-Truck / Emissions Compliance", price_cents: 10900 },
|
||||
// Healthcare / NPI
|
||||
"npi-revalidation": { name: "Medicare PECOS Revalidation Filing", price_cents: 39900 },
|
||||
"npi-reactivation": { name: "NPI Reactivation", price_cents: 24900 },
|
||||
"nppes-update": { name: "NPPES Data Update / Attestation", price_cents: 14900 },
|
||||
"medicare-enrollment": { name: "Medicare Enrollment (PECOS)", price_cents: 49900 },
|
||||
"oig-sam-screening": { name: "OIG/SAM Exclusion Screening (Annual)", price_cents: 9900 },
|
||||
"provider-compliance-bundle": { name: "Provider Compliance Bundle (Annual)", price_cents: 69900 },
|
||||
};
|
||||
|
||||
export function formatUSD(cents: number): string {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue