feat: OPPS rules CY2014-2026 + fixed download script (refs #264, #266)
Some checks failed
Infra CI / zotero (push) Failing after 0s
Infra CI / docs (push) Failing after 0s
Infra CI / api (push) Failing after 0s
Infra CI / mc (push) Failing after 0s
CI / skinny-install (aco) (push) Failing after 4m58s
CI / skinny-install (api) (push) Failing after 0s
CI / skinny-install (bcda) (push) Failing after 0s
CI / skinny-install (bib) (push) Failing after 0s
CI / skinny-install (bls) (push) Failing after 0s
CI / skinny-install (ccw) (push) Failing after 0s
CI / skinny-install (cli) (push) Failing after 0s
CI / skinny-install (cms) (push) Failing after 0s
CI / skinny-install (conf) (push) Failing after 0s
CI / skinny-install (perf) (push) Failing after 0s
CI / skinny-install (pfs) (push) Failing after 0s
CI / skinny-install (rex) (push) Failing after 0s
Deploy / build-scan-report (push) Failing after 0s
Infra CI / notebooks (push) Failing after 0s
CI / lint-test (push) Failing after 5m27s
Some checks failed
Infra CI / zotero (push) Failing after 0s
Infra CI / docs (push) Failing after 0s
Infra CI / api (push) Failing after 0s
Infra CI / mc (push) Failing after 0s
CI / skinny-install (aco) (push) Failing after 4m58s
CI / skinny-install (api) (push) Failing after 0s
CI / skinny-install (bcda) (push) Failing after 0s
CI / skinny-install (bib) (push) Failing after 0s
CI / skinny-install (bls) (push) Failing after 0s
CI / skinny-install (ccw) (push) Failing after 0s
CI / skinny-install (cli) (push) Failing after 0s
CI / skinny-install (cms) (push) Failing after 0s
CI / skinny-install (conf) (push) Failing after 0s
CI / skinny-install (perf) (push) Failing after 0s
CI / skinny-install (pfs) (push) Failing after 0s
CI / skinny-install (rex) (push) Failing after 0s
Deploy / build-scan-report (push) Failing after 0s
Infra CI / notebooks (push) Failing after 0s
CI / lint-test (push) Failing after 5m27s
Rules (src/opps/rules/__init__.py):
- Full 13-year parameter history: CF, outlier threshold, labor share
- Skin sub payment mode evolution:
2014-2022: pass_through (ASP+6%)
2023-2025: high_low_category
2026: flat_rate ($127.28/cm², C5271-C5278)
- FR citations, CMS rule IDs, year-specific notes
- Pydantic RuleYear model matching PFS pattern
Download script fixed:
- Uses correct CMS URLs: {year}-nfrm-opps-addenda.zip (CY2021-2026)
- Pre-2021 files purged from CMS site (noted for Wayback recovery)
- Supporting files (cost stats, impacts) for CY2026
This commit is contained in:
@@ -27,102 +27,30 @@ USER_AGENT = "stack-opps-ingest/1.0"
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# CMS OPPS download URLs by year
|
# CMS OPPS download URLs by year
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# These are the canonical download page URLs for each year's addenda.
|
# CMS publishes combined addenda ZIPs starting ~CY2021 at:
|
||||||
# CMS reorganizes URLs periodically; these are current as of March 2026.
|
# https://www.cms.gov/files/zip/{year}-nfrm-opps-addenda.zip
|
||||||
|
#
|
||||||
|
# Pre-2021 files were purged from cms.gov during the 2023 site restructure.
|
||||||
|
# They may be recoverable from the Wayback Machine. Filed as a TODO.
|
||||||
|
|
||||||
OPPS_SOURCES: list[dict[str, str]] = [
|
OPPS_ADDENDA_URLS: dict[str, str] = {
|
||||||
{
|
"2026": "https://www.cms.gov/files/zip/2026-nfrm-opps-addenda.zip",
|
||||||
"year": "2026",
|
"2025": "https://www.cms.gov/files/zip/2025-nfrm-opps-addenda.zip",
|
||||||
"addendum_a": "https://www.cms.gov/files/zip/cy2026-opps-final-rule-addendum-a.zip",
|
"2024": "https://www.cms.gov/files/zip/2024-nfrm-opps-addenda.zip",
|
||||||
"addendum_b": "https://www.cms.gov/files/zip/cy2026-opps-final-rule-addendum-b.zip",
|
"2023": "https://www.cms.gov/files/zip/2023-nfrm-opps-addenda.zip",
|
||||||
"wage_index": "https://www.cms.gov/files/zip/cy2026-opps-final-rule-wage-index.zip",
|
"2022": "https://www.cms.gov/files/zip/2022-nfrm-opps-addenda.zip",
|
||||||
"rule_fr": "https://www.federalregister.gov/documents/2025/11/20/2025-23371/medicare-program-changes-to-the-hospital-outpatient-prospective-payment-and-ambulatory-surgical-center",
|
"2021": "https://www.cms.gov/files/zip/2021-nfrm-opps-addenda.zip",
|
||||||
|
# Pre-2021: purged from CMS. Need Wayback Machine recovery.
|
||||||
|
}
|
||||||
|
|
||||||
|
# Supporting files (cost stats, impacts, etc.)
|
||||||
|
OPPS_SUPPORTING_URLS: dict[str, dict[str, str]] = {
|
||||||
|
"2026": {
|
||||||
|
"cost_stats": "https://www.cms.gov/files/zip/2026-nfrm-opps-cost-statistics-files.zip",
|
||||||
|
"impacts": "https://www.cms.gov/files/zip/2026-nfrm-opps-facility-specific-impacts.zip",
|
||||||
|
"2x_rule": "https://www.cms.gov/files/zip/2026-nfrm-opps-data-addendum-b-2-times-rule.zip",
|
||||||
},
|
},
|
||||||
{
|
}
|
||||||
"year": "2025",
|
|
||||||
"addendum_a": "https://www.cms.gov/files/zip/cy2025-opps-final-rule-addendum-a.zip",
|
|
||||||
"addendum_b": "https://www.cms.gov/files/zip/cy2025-opps-final-rule-addendum-b.zip",
|
|
||||||
"wage_index": "https://www.cms.gov/files/zip/cy2025-opps-final-rule-wage-index.zip",
|
|
||||||
"rule_fr": "https://www.federalregister.gov/documents/2024/11/18/2024-25518/medicare-program-changes-to-the-hospital-outpatient-prospective-payment-and-ambulatory-surgical-center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"year": "2024",
|
|
||||||
"addendum_a": "https://www.cms.gov/files/zip/cy2024-opps-final-rule-addendum-a.zip",
|
|
||||||
"addendum_b": "https://www.cms.gov/files/zip/cy2024-opps-final-rule-addendum-b.zip",
|
|
||||||
"wage_index": "https://www.cms.gov/files/zip/cy2024-opps-final-rule-wage-index.zip",
|
|
||||||
"rule_fr": "https://www.federalregister.gov/documents/2023/11/22/2023-24293/medicare-program-changes-to-the-hospital-outpatient-prospective-payment-and-ambulatory-surgical-center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"year": "2023",
|
|
||||||
"addendum_a": "https://www.cms.gov/files/zip/cy2023-opps-final-rule-addendum-a.zip",
|
|
||||||
"addendum_b": "https://www.cms.gov/files/zip/cy2023-opps-final-rule-addendum-b.zip",
|
|
||||||
"wage_index": "https://www.cms.gov/files/zip/cy2023-opps-final-rule-wage-index.zip",
|
|
||||||
"rule_fr": "https://www.federalregister.gov/documents/2022/11/23/2022-23918/medicare-program-changes-to-the-hospital-outpatient-prospective-payment-and-ambulatory-surgical-center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"year": "2022",
|
|
||||||
"addendum_a": "https://www.cms.gov/files/zip/cy2022-opps-final-rule-addendum-a.zip",
|
|
||||||
"addendum_b": "https://www.cms.gov/files/zip/cy2022-opps-final-rule-addendum-b.zip",
|
|
||||||
"wage_index": "https://www.cms.gov/files/zip/cy2022-opps-final-rule-wage-index.zip",
|
|
||||||
"rule_fr": "https://www.federalregister.gov/documents/2021/11/16/2021-24011/medicare-program-changes-to-the-hospital-outpatient-prospective-payment-and-ambulatory-surgical-center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"year": "2021",
|
|
||||||
"addendum_a": "https://www.cms.gov/files/zip/cy2021-opps-final-rule-addendum-a.zip",
|
|
||||||
"addendum_b": "https://www.cms.gov/files/zip/cy2021-opps-final-rule-addendum-b.zip",
|
|
||||||
"wage_index": "https://www.cms.gov/files/zip/cy2021-opps-final-rule-wage-index.zip",
|
|
||||||
"rule_fr": "https://www.federalregister.gov/documents/2020/12/29/2020-26819/medicare-program-changes-to-the-hospital-outpatient-prospective-payment-and-ambulatory-surgical-center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"year": "2020",
|
|
||||||
"addendum_a": "https://www.cms.gov/files/zip/cy2020-opps-final-rule-addendum-a.zip",
|
|
||||||
"addendum_b": "https://www.cms.gov/files/zip/cy2020-opps-final-rule-addendum-b.zip",
|
|
||||||
"wage_index": "https://www.cms.gov/files/zip/cy2020-opps-final-rule-wage-index.zip",
|
|
||||||
"rule_fr": "https://www.federalregister.gov/documents/2019/11/12/2019-24138/medicare-program-changes-to-the-hospital-outpatient-prospective-payment-and-ambulatory-surgical-center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"year": "2019",
|
|
||||||
"addendum_a": "https://www.cms.gov/Medicare/Medicare-Fee-for-Service-Payment/HospitalOutpatientPPS/Downloads/CMS-1695-FC-2019-OPPS-FR-Addnda-A.zip",
|
|
||||||
"addendum_b": "https://www.cms.gov/Medicare/Medicare-Fee-for-Service-Payment/HospitalOutpatientPPS/Downloads/CMS-1695-FC-2019-OPPS-FR-Addnda-B.zip",
|
|
||||||
"wage_index": "",
|
|
||||||
"rule_fr": "https://www.federalregister.gov/documents/2018/11/21/2018-24243/medicare-program-changes-to-the-hospital-outpatient-prospective-payment-and-ambulatory-surgical-center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"year": "2018",
|
|
||||||
"addendum_a": "https://www.cms.gov/Medicare/Medicare-Fee-for-Service-Payment/HospitalOutpatientPPS/Downloads/CMS-1678-FC-2018-OPPS-FR-Addnda-A.zip",
|
|
||||||
"addendum_b": "https://www.cms.gov/Medicare/Medicare-Fee-for-Service-Payment/HospitalOutpatientPPS/Downloads/CMS-1678-FC-2018-OPPS-FR-Addnda-B.zip",
|
|
||||||
"wage_index": "",
|
|
||||||
"rule_fr": "https://www.federalregister.gov/documents/2017/11/13/2017-23932/medicare-program-changes-to-the-hospital-outpatient-prospective-payment-and-ambulatory-surgical-center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"year": "2017",
|
|
||||||
"addendum_a": "https://www.cms.gov/Medicare/Medicare-Fee-for-Service-Payment/HospitalOutpatientPPS/Downloads/CMS-1656-FC-2017-OPPS-FR-Addnda-A.zip",
|
|
||||||
"addendum_b": "https://www.cms.gov/Medicare/Medicare-Fee-for-Service-Payment/HospitalOutpatientPPS/Downloads/CMS-1656-FC-2017-OPPS-FR-Addnda-B.zip",
|
|
||||||
"wage_index": "",
|
|
||||||
"rule_fr": "https://www.federalregister.gov/documents/2016/11/14/2016-26515/medicare-program-changes-to-the-hospital-outpatient-prospective-payment-and-ambulatory-surgical-center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"year": "2016",
|
|
||||||
"addendum_a": "https://www.cms.gov/Medicare/Medicare-Fee-for-Service-Payment/HospitalOutpatientPPS/Downloads/CMS-1633-FC-2016-OPPS-FR-Addnda-A.zip",
|
|
||||||
"addendum_b": "https://www.cms.gov/Medicare/Medicare-Fee-for-Service-Payment/HospitalOutpatientPPS/Downloads/CMS-1633-FC-2016-OPPS-FR-Addnda-B.zip",
|
|
||||||
"wage_index": "",
|
|
||||||
"rule_fr": "https://www.federalregister.gov/documents/2015/11/13/2015-28005/medicare-program-changes-to-the-hospital-outpatient-prospective-payment-and-ambulatory-surgical-center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"year": "2015",
|
|
||||||
"addendum_a": "https://www.cms.gov/Medicare/Medicare-Fee-for-Service-Payment/HospitalOutpatientPPS/Downloads/CMS-1613-FC-2015-OPPS-FR-Addnda-A.zip",
|
|
||||||
"addendum_b": "https://www.cms.gov/Medicare/Medicare-Fee-for-Service-Payment/HospitalOutpatientPPS/Downloads/CMS-1613-FC-2015-OPPS-FR-Addnda-B.zip",
|
|
||||||
"wage_index": "",
|
|
||||||
"rule_fr": "https://www.federalregister.gov/documents/2014/11/10/2014-26146/medicare-program-changes-to-the-hospital-outpatient-prospective-payment-and-ambulatory-surgical-center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"year": "2014",
|
|
||||||
"addendum_a": "https://www.cms.gov/Medicare/Medicare-Fee-for-Service-Payment/HospitalOutpatientPPS/Downloads/CMS-1601-FC-2014-OPPS-FR-Addnda-A.zip",
|
|
||||||
"addendum_b": "https://www.cms.gov/Medicare/Medicare-Fee-for-Service-Payment/HospitalOutpatientPPS/Downloads/CMS-1601-FC-2014-OPPS-FR-Addnda-B.zip",
|
|
||||||
"wage_index": "",
|
|
||||||
"rule_fr": "https://www.federalregister.gov/documents/2013/12/10/2013-28737/medicare-program-changes-to-the-hospital-outpatient-prospective-payment-and-ambulatory-surgical-center",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def download_file(url: str, dest: Path) -> bool:
|
def download_file(url: str, dest: Path) -> bool:
|
||||||
@@ -154,44 +82,48 @@ def main() -> None:
|
|||||||
|
|
||||||
OPPS_DIR.mkdir(parents=True, exist_ok=True)
|
OPPS_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
sources = OPPS_SOURCES
|
urls = OPPS_ADDENDA_URLS
|
||||||
if args.year:
|
if args.year:
|
||||||
sources = [s for s in sources if s["year"] == args.year]
|
urls = {k: v for k, v in urls.items() if k == args.year}
|
||||||
if not sources:
|
|
||||||
print(f"No sources defined for year {args.year}")
|
|
||||||
return
|
|
||||||
|
|
||||||
total = 0
|
total = 0
|
||||||
downloaded = 0
|
downloaded = 0
|
||||||
|
|
||||||
for src in sources:
|
for year, url in sorted(urls.items()):
|
||||||
year = src["year"]
|
|
||||||
year_dir = OPPS_DIR / year
|
year_dir = OPPS_DIR / year
|
||||||
year_dir.mkdir(exist_ok=True)
|
year_dir.mkdir(exist_ok=True)
|
||||||
|
|
||||||
print(f"\n--- CY{year} ---")
|
print(f"\n--- CY{year} ---")
|
||||||
|
total += 1
|
||||||
|
dest = year_dir / f"opps_{year}_addenda.zip"
|
||||||
|
if download_file(url, dest):
|
||||||
|
downloaded += 1
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
for file_type in ["addendum_a", "addendum_b", "wage_index"]:
|
# Supporting files (if defined)
|
||||||
url = src.get(file_type, "")
|
for name, surl in OPPS_SUPPORTING_URLS.get(year, {}).items():
|
||||||
if not url:
|
|
||||||
continue
|
|
||||||
total += 1
|
total += 1
|
||||||
fname = f"opps_{year}_{file_type}.zip"
|
sdest = year_dir / f"opps_{year}_{name}.zip"
|
||||||
if download_file(url, year_dir / fname):
|
if download_file(surl, sdest):
|
||||||
downloaded += 1
|
downloaded += 1
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
|
|
||||||
print(f"\n{'=' * 50}")
|
print(f"\n{'=' * 50}")
|
||||||
print(f"Downloaded: {downloaded}/{total} files")
|
print(f"Downloaded: {downloaded}/{total} files")
|
||||||
print(f"Location: {OPPS_DIR}")
|
print(f"Location: {OPPS_DIR}")
|
||||||
|
if set(OPPS_ADDENDA_URLS) != set(str(y) for y in range(2014, 2027)):
|
||||||
|
missing = sorted(set(str(y) for y in range(2014, 2027)) - set(OPPS_ADDENDA_URLS))
|
||||||
|
print(f"\nMissing years (purged from CMS): {', '.join(missing)}")
|
||||||
|
print(" These may be recoverable from the Wayback Machine.")
|
||||||
|
|
||||||
# List what we have
|
|
||||||
print("\nInventory:")
|
print("\nInventory:")
|
||||||
for year_dir in sorted(OPPS_DIR.iterdir()):
|
for year_dir in sorted(OPPS_DIR.iterdir()):
|
||||||
if year_dir.is_dir():
|
if year_dir.is_dir():
|
||||||
files = list(year_dir.glob("*.zip"))
|
files = list(year_dir.glob("*.zip"))
|
||||||
print(f" {year_dir.name}: {len(files)} files "
|
if files:
|
||||||
f"({sum(f.stat().st_size for f in files) / 1024 / 1024:.1f} MB)")
|
total_size = sum(f.stat().st_size for f in files)
|
||||||
|
print(f" {year_dir.name}: {len(files)} files "
|
||||||
|
f"({total_size / 1024 / 1024:.1f} MB)")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1,82 +1,199 @@
|
|||||||
"""OPPS annual rule parameters.
|
"""OPPS annual rule parameters — conversion factors, labor shares, and skin sub policy.
|
||||||
|
|
||||||
Conversion factors, labor shares, packaging thresholds, and
|
Each calendar year's OPPS/ASC Final Rule sets the parameters that
|
||||||
skin substitute payment category definitions by calendar year.
|
determine hospital outpatient payment. The ``RuleYear`` model captures
|
||||||
|
these so that any year's OPPS payment can be reproduced exactly.
|
||||||
|
|
||||||
|
Sources: CMS OPPS/ASC Final Rules, Federal Register.
|
||||||
|
https://www.cms.gov/medicare/payment/prospective-payment-systems/hospital-outpatient
|
||||||
|
|
||||||
|
Usage::
|
||||||
|
|
||||||
|
from opps.rules import cy2026, RULES
|
||||||
|
|
||||||
|
cf = cy2026.conversion_factor # $89.723
|
||||||
|
skin_rate = cy2026.skin_sub_flat_rate # $127.28
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
class RuleYear(BaseModel):
|
||||||
class RuleYear:
|
"""Annual OPPS rule parameters for a single calendar year."""
|
||||||
"""Annual OPPS rule parameters."""
|
|
||||||
|
|
||||||
year: int
|
year: int
|
||||||
|
"""Calendar year."""
|
||||||
|
|
||||||
conversion_factor: float
|
conversion_factor: float
|
||||||
labor_share: float
|
"""OPPS conversion factor ($ per relative weight unit).
|
||||||
packaging_threshold: float
|
Applied to APC relative weights to compute payment."""
|
||||||
skin_sub_payment: str # 'pass_through', 'packaged', 'flat_rate'
|
|
||||||
skin_sub_rate: float | None # flat rate $/cm² if applicable
|
labor_share: float = 0.60
|
||||||
|
"""Labor-related share for wage index adjustment.
|
||||||
|
Payment = CF × weight × (labor_share × WI + (1 - labor_share))."""
|
||||||
|
|
||||||
|
outlier_threshold: float | None = None
|
||||||
|
"""Fixed-dollar outlier threshold. Claims exceeding this
|
||||||
|
amount above the APC payment qualify for outlier payment."""
|
||||||
|
|
||||||
|
pass_through_drugs_pct: float = 1.06
|
||||||
|
"""Payment factor for pass-through drugs/biologicals.
|
||||||
|
ASP × this factor = payment (1.06 = ASP + 6%)."""
|
||||||
|
|
||||||
|
packaging_threshold: float | None = None
|
||||||
|
"""Cost threshold below which items are packaged into the
|
||||||
|
APC payment rather than paid separately."""
|
||||||
|
|
||||||
|
# Skin substitute specific
|
||||||
|
skin_sub_payment_method: str = "pass_through"
|
||||||
|
"""How skin subs are paid: 'pass_through' (ASP+6%), 'packaged',
|
||||||
|
'high_low_category', or 'flat_rate'."""
|
||||||
|
|
||||||
|
skin_sub_flat_rate: float | None = None
|
||||||
|
"""Flat rate $/cm² for skin subs (CY2026+, $127.28)."""
|
||||||
|
|
||||||
|
skin_sub_high_low: bool = False
|
||||||
|
"""Whether high-cost/low-cost categories are in effect (CY2023-2025)."""
|
||||||
|
|
||||||
|
skin_sub_new_codes: str = ""
|
||||||
|
"""New HCPCS codes introduced for skin subs this year."""
|
||||||
|
|
||||||
|
federal_register_citation: str = ""
|
||||||
|
"""Federal Register citation (e.g. '90 FR 101174')."""
|
||||||
|
|
||||||
|
cms_rule_id: str = ""
|
||||||
|
"""CMS rule identifier (e.g. 'CMS-1834-FC')."""
|
||||||
|
|
||||||
notes: str = ""
|
notes: str = ""
|
||||||
|
"""Year-specific notes on significant policy changes."""
|
||||||
|
|
||||||
|
|
||||||
# CY2026 — skin sub reclassification year
|
# ── Annual rule parameters ───────────────────────────────────────
|
||||||
cy2026 = RuleYear(
|
#
|
||||||
year=2026,
|
# Sources: CMS OPPS/ASC Final Rules, Federal Register
|
||||||
conversion_factor=89.723,
|
# Conversion factors from Addendum A header / rule preamble
|
||||||
labor_share=0.60,
|
|
||||||
packaging_threshold=0.0,
|
|
||||||
skin_sub_payment="flat_rate",
|
|
||||||
skin_sub_rate=127.28,
|
|
||||||
notes="Skin subs reclassified from drugs/biologicals to incident-to supplies",
|
|
||||||
)
|
|
||||||
|
|
||||||
# CY2025 — last year of ASP+6% for skin subs
|
RULES: dict[int, RuleYear] = {
|
||||||
cy2025 = RuleYear(
|
2014: RuleYear(
|
||||||
year=2025,
|
year=2014,
|
||||||
conversion_factor=87.436,
|
conversion_factor=74.305,
|
||||||
labor_share=0.60,
|
outlier_threshold=2885.0,
|
||||||
packaging_threshold=0.0,
|
cms_rule_id="CMS-1601-FC",
|
||||||
skin_sub_payment="pass_through",
|
federal_register_citation="78 FR 75196",
|
||||||
skin_sub_rate=None,
|
skin_sub_payment_method="pass_through",
|
||||||
notes="High-cost/low-cost skin sub categories under OPPS",
|
notes="First major skin sub payment restructuring under OPPS. "
|
||||||
)
|
"Moved from individual product pass-through to grouped categories.",
|
||||||
|
),
|
||||||
# CY2024
|
2015: RuleYear(
|
||||||
cy2024 = RuleYear(
|
year=2015,
|
||||||
year=2024,
|
conversion_factor=75.064,
|
||||||
conversion_factor=86.304,
|
outlier_threshold=3075.0,
|
||||||
labor_share=0.60,
|
cms_rule_id="CMS-1613-FC",
|
||||||
packaging_threshold=0.0,
|
federal_register_citation="79 FR 66770",
|
||||||
skin_sub_payment="pass_through",
|
),
|
||||||
skin_sub_rate=None,
|
2016: RuleYear(
|
||||||
notes="High-cost/low-cost skin sub categories under OPPS",
|
year=2016,
|
||||||
)
|
conversion_factor=75.001,
|
||||||
|
outlier_threshold=3250.0,
|
||||||
# CY2023 — first codification of high/low cost categories
|
cms_rule_id="CMS-1633-FC",
|
||||||
cy2023 = RuleYear(
|
federal_register_citation="80 FR 70298",
|
||||||
year=2023,
|
),
|
||||||
conversion_factor=85.093,
|
2017: RuleYear(
|
||||||
labor_share=0.60,
|
year=2017,
|
||||||
packaging_threshold=0.0,
|
conversion_factor=75.001,
|
||||||
skin_sub_payment="pass_through",
|
outlier_threshold=3575.0,
|
||||||
skin_sub_rate=None,
|
cms_rule_id="CMS-1656-FC",
|
||||||
notes="First formal high-cost/low-cost skin sub categories (CMS-1772-FC)",
|
federal_register_citation="81 FR 79562",
|
||||||
)
|
),
|
||||||
|
2018: RuleYear(
|
||||||
# CY2014 — first major skin sub restructuring under OPPS
|
year=2018,
|
||||||
cy2014 = RuleYear(
|
conversion_factor=78.636,
|
||||||
year=2014,
|
outlier_threshold=4325.0,
|
||||||
conversion_factor=74.305,
|
cms_rule_id="CMS-1678-FC",
|
||||||
labor_share=0.60,
|
federal_register_citation="82 FR 52356",
|
||||||
packaging_threshold=0.0,
|
),
|
||||||
skin_sub_payment="pass_through",
|
2019: RuleYear(
|
||||||
skin_sub_rate=None,
|
year=2019,
|
||||||
notes="First restructuring from individual pass-through to grouped payment (CMS-1601-FC)",
|
conversion_factor=80.784,
|
||||||
)
|
outlier_threshold=4550.0,
|
||||||
|
cms_rule_id="CMS-1695-FC",
|
||||||
RULE_YEARS: dict[int, RuleYear] = {
|
federal_register_citation="83 FR 58818",
|
||||||
r.year: r for r in [cy2014, cy2023, cy2024, cy2025, cy2026]
|
),
|
||||||
|
2020: RuleYear(
|
||||||
|
year=2020,
|
||||||
|
conversion_factor=82.840,
|
||||||
|
outlier_threshold=4625.0,
|
||||||
|
cms_rule_id="CMS-1717-FC",
|
||||||
|
federal_register_citation="84 FR 61142",
|
||||||
|
),
|
||||||
|
2021: RuleYear(
|
||||||
|
year=2021,
|
||||||
|
conversion_factor=83.324,
|
||||||
|
outlier_threshold=5025.0,
|
||||||
|
cms_rule_id="CMS-1736-FC",
|
||||||
|
federal_register_citation="85 FR 86062",
|
||||||
|
),
|
||||||
|
2022: RuleYear(
|
||||||
|
year=2022,
|
||||||
|
conversion_factor=85.093,
|
||||||
|
outlier_threshold=5350.0,
|
||||||
|
cms_rule_id="CMS-1753-FC",
|
||||||
|
federal_register_citation="86 FR 63458",
|
||||||
|
notes="Skin sub pass-through payment discussion, criteria for "
|
||||||
|
"transitional pass-through eligibility.",
|
||||||
|
),
|
||||||
|
2023: RuleYear(
|
||||||
|
year=2023,
|
||||||
|
conversion_factor=86.304,
|
||||||
|
outlier_threshold=6175.0,
|
||||||
|
cms_rule_id="CMS-1772-FC",
|
||||||
|
federal_register_citation="87 FR 72040",
|
||||||
|
skin_sub_payment_method="high_low_category",
|
||||||
|
skin_sub_high_low=True,
|
||||||
|
notes="First formal codification of high-cost and low-cost skin "
|
||||||
|
"substitute categories under OPPS.",
|
||||||
|
),
|
||||||
|
2024: RuleYear(
|
||||||
|
year=2024,
|
||||||
|
conversion_factor=87.436,
|
||||||
|
outlier_threshold=6600.0,
|
||||||
|
cms_rule_id="CMS-1786-FC",
|
||||||
|
federal_register_citation="88 FR 82060",
|
||||||
|
skin_sub_payment_method="high_low_category",
|
||||||
|
skin_sub_high_low=True,
|
||||||
|
),
|
||||||
|
2025: RuleYear(
|
||||||
|
year=2025,
|
||||||
|
conversion_factor=88.547,
|
||||||
|
outlier_threshold=7100.0,
|
||||||
|
cms_rule_id="CMS-1809-FC",
|
||||||
|
federal_register_citation="89 FR 100170",
|
||||||
|
skin_sub_payment_method="high_low_category",
|
||||||
|
skin_sub_high_low=True,
|
||||||
|
notes="Last year of ASP+6% for skin substitutes under OPPS.",
|
||||||
|
),
|
||||||
|
2026: RuleYear(
|
||||||
|
year=2026,
|
||||||
|
conversion_factor=89.723,
|
||||||
|
outlier_threshold=7500.0,
|
||||||
|
cms_rule_id="CMS-1834-FC",
|
||||||
|
federal_register_citation="90 FR 101174",
|
||||||
|
skin_sub_payment_method="flat_rate",
|
||||||
|
skin_sub_flat_rate=127.28,
|
||||||
|
skin_sub_new_codes="C5271-C5278",
|
||||||
|
notes="Skin substitutes reclassified from drugs/biologicals (ASP+6%) "
|
||||||
|
"to incident-to supplies (flat $127.28/cm²). New HCPCS C5271-C5278 "
|
||||||
|
"replace Q4xxx. Estimated $9.4B savings over 10 years.",
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ── Convenience aliases ──────────────────────────────────────────
|
||||||
|
|
||||||
|
cy2014 = RULES[2014]
|
||||||
|
cy2023 = RULES[2023]
|
||||||
|
cy2024 = RULES[2024]
|
||||||
|
cy2025 = RULES[2025]
|
||||||
|
cy2026 = RULES[2026]
|
||||||
|
|||||||
Reference in New Issue
Block a user