healthcare: verify CMS-10114 update path, correct NPI Enumerator address, build CMS-10114 filler
Verified firsthand against the live CMS-10114 (Rev. 02/25, OMB 0938-0931): - Section 1A confirms paper is valid for Change of Information (#2) AND Reactivation (#4), not just initial enumeration. Resolves the UNCERTAIN flag. - Current mailing address is CMS NPI Enumerator Services, Mail Stop DO-01-51, 7500 Security Blvd, Baltimore MD 21244. The old Fargo PO Box 6059 is retired; corrected in mac_routing.NPI_ENUMERATOR + all docs. - No electronic no-login equivalent exists for CMS (NPI Registry API is read-only; PECOS/NPPES-IA require login), unlike FMCSA's ask.fmcsa ticket form. So tiers stay: Standard=paper CMS-10114 (no login), Expedited=NPPES surrogate. New: cms10114_pdf_filler.py fills the flat official form via text overlay (reason checkbox + NPI + Section 2A identity + Section 4A cert name + signature anchor); wired into npi_provider._generate_10114_for_signing for nppes-update. Signed forms route to the NPI Enumerator via the existing daily batch. Tests: test_cms10114.py 27/27, test_paper_batch.py 15/15, Astro build 58 pages.
This commit is contained in:
parent
f9c294e962
commit
e6a630ada1
10 changed files with 540 additions and 48 deletions
|
|
@ -48,8 +48,8 @@ def _is_postal_working_day(d: date) -> bool:
|
|||
def _destination_for(practice_state: str, document_type: str):
|
||||
"""Return the (key, name, address_lines) destination for a filing.
|
||||
|
||||
NPPES/CMS-10114 updates go to the NPI Enumerator (Fargo); CMS-855s go to the
|
||||
provider's MAC by state.
|
||||
NPPES/CMS-10114 updates go to the NPI Enumerator (Baltimore, MD); CMS-855s go
|
||||
to the provider's MAC by state.
|
||||
"""
|
||||
try:
|
||||
from scripts.workers import mac_routing as mr
|
||||
|
|
|
|||
|
|
@ -107,9 +107,13 @@ STATE_TO_MAC: dict[str, MAC] = {
|
|||
|
||||
# NPI Enumerator paper address (NPPES / CMS-10114 paper path) — not a MAC, but a
|
||||
# destination the daily batch groups by, same as a MAC.
|
||||
# VERIFIED 2025 against CMS-10114 (Rev. 02/25), OMB 0938-0931, page 5 "Or send the
|
||||
# completed signed application to:". The earlier Fargo, ND PO Box 6059 address is
|
||||
# RETIRED; the current address printed on the form is the Baltimore one below.
|
||||
NPI_ENUMERATOR = MAC(
|
||||
"npi_enumerator", "NPI Enumerator (NPPES / CMS-10114 paper)",
|
||||
("NPI Enumerator", "P.O. Box 6059", "Fargo, ND 58108-6059"),
|
||||
("CMS NPI Enumerator Services", "Mail Stop DO-01-51",
|
||||
"7500 Security Blvd.", "Baltimore, MD 21244"),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -126,16 +126,22 @@ _SLUG_META = {
|
|||
# batched mailing). The bundle's revalidation piece is handled by the dedicated
|
||||
# revalidation order it spawns, so it is not listed here.
|
||||
#
|
||||
# nppes-update is intentionally NOT here: its Standard path is the CMS-10114 (NPPES
|
||||
# update mailed to the NPI Enumerator in Fargo), handled by a separate filler when
|
||||
# built. Until then, nppes-update falls to the admin todo (Expedited if surrogate
|
||||
# granted, otherwise manual CMS-10114 prep).
|
||||
# nppes-update is NOT a CMS-855 filing: its Standard (no-login) path is the
|
||||
# CMS-10114 NPI Application/Update form, signed by the provider and mailed to the
|
||||
# NPI Enumerator (CMS NPI Enumerator Services, Baltimore MD). It is handled by
|
||||
# the dedicated CMS-10114 filler below (_STANDARD_10114_SLUGS).
|
||||
_STANDARD_FILING_SLUGS = {
|
||||
"npi-revalidation",
|
||||
"npi-reactivation",
|
||||
"medicare-enrollment",
|
||||
}
|
||||
|
||||
# Slugs whose Standard path is the CMS-10114 (NPPES update/reactivation/deactivation
|
||||
# mailed to the NPI Enumerator). The reason checkbox is derived from the slug.
|
||||
_STANDARD_10114_SLUGS = {
|
||||
"nppes-update",
|
||||
}
|
||||
|
||||
|
||||
class _BaseNPIHandler:
|
||||
"""Shared review-staged behaviour for all NPI services."""
|
||||
|
|
@ -178,6 +184,14 @@ class _BaseNPIHandler:
|
|||
except Exception as exc: # never block the admin todo on PDF issues
|
||||
LOG.error("[%s] CMS-855 generation failed: %s", order_number, exc)
|
||||
filing_note = f"CMS-855 auto-generation FAILED ({exc}); prepare the form manually."
|
||||
elif self.SERVICE_SLUG in _STANDARD_10114_SLUGS:
|
||||
try:
|
||||
filing_note = self._generate_10114_for_signing(
|
||||
order_number, intake, provider, customer_email
|
||||
)
|
||||
except Exception as exc: # never block the admin todo on PDF issues
|
||||
LOG.error("[%s] CMS-10114 generation failed: %s", order_number, exc)
|
||||
filing_note = f"CMS-10114 auto-generation FAILED ({exc}); prepare the form manually."
|
||||
|
||||
surrogate_label = {
|
||||
"yes": "YES — client can grant I&A Surrogate access -> file the EXPEDITED way (online via surrogate).",
|
||||
|
|
@ -267,16 +281,78 @@ class _BaseNPIHandler:
|
|||
note_lines.extend(f" - {m}" for m in missing)
|
||||
return "\n".join(note_lines)
|
||||
|
||||
def _generate_10114_for_signing(self, order_number, intake, provider, customer_email) -> str:
|
||||
"""Generate the official CMS-10114 (NPPES update path), upload it, and
|
||||
request an e-signature.
|
||||
|
||||
Standard (no-login) path for NPPES data updates / NPI reactivation: the
|
||||
provider signs the certification and the signed form is mailed to the NPI
|
||||
Enumerator (CMS NPI Enumerator Services, Baltimore MD) via the daily batch.
|
||||
Returns a human-readable note for the admin todo.
|
||||
"""
|
||||
try:
|
||||
from scripts.document_gen.templates.cms10114_pdf_filler import (
|
||||
fill_cms10114, normalize_reason,
|
||||
)
|
||||
except ImportError:
|
||||
from document_gen.templates.cms10114_pdf_filler import ( # type: ignore
|
||||
fill_cms10114, normalize_reason,
|
||||
)
|
||||
|
||||
# Reason comes from the explicit intake reason if present, else the slug.
|
||||
reason = normalize_reason(intake.get("filing_reason") or self.SERVICE_SLUG)
|
||||
pdf_bytes, anchors, missing = fill_cms10114(intake, reason=reason, order_number=order_number)
|
||||
|
||||
document_key = f"compliance/{order_number}/cms10114_unsigned.pdf"
|
||||
try:
|
||||
import tempfile
|
||||
try:
|
||||
from scripts.document_gen.minio_client import MinioStorage
|
||||
except ImportError:
|
||||
from document_gen.minio_client import MinioStorage # type: ignore
|
||||
storage = MinioStorage()
|
||||
with tempfile.NamedTemporaryFile(suffix=".pdf", delete=True) as tf:
|
||||
tf.write(pdf_bytes)
|
||||
tf.flush()
|
||||
storage.upload(tf.name, document_key, content_type="application/pdf")
|
||||
except Exception as exc:
|
||||
LOG.error("[%s] CMS-10114 upload failed: %s", order_number, exc)
|
||||
return f"CMS-10114 generated but upload FAILED ({exc})."
|
||||
|
||||
signed = self._create_855_esign_record(
|
||||
order_number, intake, provider, customer_email,
|
||||
form_type="10114", document_key=document_key, anchors=anchors,
|
||||
document_type="cms10114",
|
||||
document_title="NPI Application/Update Form (CMS-10114)",
|
||||
)
|
||||
|
||||
note_lines = [
|
||||
f"CMS-10114 generated (reason: {reason}; official form, auto-filled where possible).",
|
||||
f"Unsigned PDF: {document_key}",
|
||||
]
|
||||
if signed and customer_email:
|
||||
note_lines.append(f"E-sign link emailed to {customer_email}. After signing, the form joins the daily mail batch to the NPI Enumerator (standard) or file via NPPES surrogate access (expedited).")
|
||||
else:
|
||||
note_lines.append("No customer email or esign infra — send the form for signature manually.")
|
||||
if missing:
|
||||
note_lines.append("MANUAL COMPLETION NEEDED:")
|
||||
note_lines.extend(f" - {m}" for m in missing)
|
||||
return "\n".join(note_lines)
|
||||
|
||||
def _create_855_esign_record(self, order_number, intake, provider, customer_email,
|
||||
form_type, document_key, anchors) -> bool:
|
||||
form_type, document_key, anchors,
|
||||
document_type=None, document_title=None) -> bool:
|
||||
"""Create the esign record + email the signing link via the shared helper,
|
||||
then attach the official signature anchors. Returns True on success.
|
||||
|
||||
Used for both CMS-855 and CMS-10114 (pass document_type/document_title to
|
||||
override the defaults).
|
||||
"""
|
||||
if not customer_email:
|
||||
return False
|
||||
|
||||
document_type = f"cms{form_type}"
|
||||
document_title = f"Medicare Enrollment Form (CMS-{form_type.upper()})"
|
||||
document_type = document_type or f"cms{form_type}"
|
||||
document_title = document_title or f"Medicare Enrollment Form (CMS-{form_type.upper()})"
|
||||
|
||||
try:
|
||||
try:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue