feat: opps module — OPPS data models, payment calcs, rule parameters (refs #263-#265)
Some checks failed
CI / lint-test (push) Failing after 2m56s
CI / skinny-install (bls) (push) Successful in 2m10s
CI / skinny-install (pfs) (push) Successful in 1m38s
CI / skinny-install (rex) (push) Successful in 1m50s
Infra CI / notebooks (push) Successful in 6s
Infra CI / zotero (push) Successful in 7s
CI / skinny-install (aco) (push) Successful in 2m13s
CI / skinny-install (api) (push) Successful in 1m56s
CI / skinny-install (bcda) (push) Successful in 2m12s
CI / skinny-install (bib) (push) Successful in 1m39s
CI / skinny-install (ccw) (push) Successful in 1m53s
CI / skinny-install (cms) (push) Successful in 1m30s
CI / skinny-install (cli) (push) Successful in 2m27s
CI / skinny-install (conf) (push) Successful in 2m1s
CI / skinny-install (perf) (push) Successful in 2m13s
Infra CI / docs (push) Successful in 15s
Infra CI / api (push) Successful in 17s
Infra CI / mc (push) Successful in 6s
Deploy / build-scan-report (push) Failing after 4m16s
Package Supply Chain / pkg-supply-chain (push) Successful in 3m20s
Some checks failed
CI / lint-test (push) Failing after 2m56s
CI / skinny-install (bls) (push) Successful in 2m10s
CI / skinny-install (pfs) (push) Successful in 1m38s
CI / skinny-install (rex) (push) Successful in 1m50s
Infra CI / notebooks (push) Successful in 6s
Infra CI / zotero (push) Successful in 7s
CI / skinny-install (aco) (push) Successful in 2m13s
CI / skinny-install (api) (push) Successful in 1m56s
CI / skinny-install (bcda) (push) Successful in 2m12s
CI / skinny-install (bib) (push) Successful in 1m39s
CI / skinny-install (ccw) (push) Successful in 1m53s
CI / skinny-install (cms) (push) Successful in 1m30s
CI / skinny-install (cli) (push) Successful in 2m27s
CI / skinny-install (conf) (push) Successful in 2m1s
CI / skinny-install (perf) (push) Successful in 2m13s
Infra CI / docs (push) Successful in 15s
Infra CI / api (push) Successful in 17s
Infra CI / mc (push) Successful in 6s
Deploy / build-scan-report (push) Failing after 4m16s
Package Supply Chain / pkg-supply-chain (push) Successful in 3m20s
New top-level module mirroring pfs structure: opps.table (5 SQLTable models): - ApcWeight — Addendum A: APC relative weights and payment rates - AddendumB — HCPCS-to-APC crosswalk with status indicators - PassThrough — Addendum D: pass-through drugs/biologicals/devices - WageIndex — CBSA-level geographic payment adjustment - StatusIndicator — SI reference (S, T, G, K, N, Q) opps.calcs (2 narwhalify functions): - payment() — APC weight x CF x wage index adjustment - skin_sub_impact() — model ASP+6% vs flat rate delta opps.rules (5 RuleYear definitions): - CY2014-2026 with skin sub payment mode transitions Registered as skinny package extra in pyproject.toml.
This commit is contained in:
@@ -59,6 +59,12 @@ cms = [
|
||||
"narwhals>=2.17.0",
|
||||
"pydantic>=2.0.0",
|
||||
]
|
||||
opps = [
|
||||
"stack[conf]",
|
||||
"stack[rex]",
|
||||
"duckdb>=1.0.0",
|
||||
"narwhals>=2.17.0",
|
||||
]
|
||||
pfs = [
|
||||
"stack[conf]",
|
||||
"stack[rex]",
|
||||
@@ -147,6 +153,6 @@ ignore = ["E501", "E741"]
|
||||
testpaths = ["tests"]
|
||||
|
||||
[tool.uv.build-backend]
|
||||
module-name = ["aco", "api", "bcda", "bib", "bls", "ccw", "cli", "cms", "conf", "perf", "pfs", "rex"]
|
||||
module-name = ["aco", "api", "bcda", "bib", "bls", "ccw", "cli", "cms", "conf", "opps", "perf", "pfs", "rex"]
|
||||
namespace = true
|
||||
source-exclude = ["compose.yml","infra/**","data/**","notebooks/**","tuva/**","assets/**","docs/**","dev/**","bundle/**","cloud/**"]
|
||||
|
||||
126
src/opps/__init__.py
Normal file
126
src/opps/__init__.py
Normal file
@@ -0,0 +1,126 @@
|
||||
"""opps — Outpatient Prospective Payment System data models, ingestion, and calculations.
|
||||
|
||||
Ingests CMS OPPS data files (APC weight files, status indicators,
|
||||
addenda, and annual rule parameters) into clean narwhals DataFrames.
|
||||
Provides calculation functions for APC-based payment, pass-through
|
||||
payment, and skin substitute reclassification impact analysis.
|
||||
|
||||
Architecture
|
||||
------------
|
||||
|
||||
``opps`` is a peer module to ``pfs``, ``aco``, ``ccw``, and ``rex``.
|
||||
It follows the same SQLTable model pattern and uses ``rex`` for file
|
||||
parsing.
|
||||
|
||||
::
|
||||
|
||||
CMS Downloads (ZIP → CSV/Excel)
|
||||
│
|
||||
▼
|
||||
┌────────┐
|
||||
│ opps │ files → table → calcs → rules
|
||||
└────────┘
|
||||
│
|
||||
▼ narwhals DataFrames
|
||||
┌────────┐
|
||||
│ aco │ input_layer / core / analytics
|
||||
└────────┘
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
``opps.table``
|
||||
Pydantic ``SQLTable`` models for OPPS data entities:
|
||||
APC weights, status indicators, ASC payment rates,
|
||||
pass-through items, and wage index data.
|
||||
|
||||
``opps.files``
|
||||
``rex.Format`` and ``rex.Sieve`` definitions for CMS OPPS files.
|
||||
Maps raw CMS column names to ``opps.table`` field names.
|
||||
|
||||
``opps.calcs``
|
||||
``@nw.narwhalify`` expression functions for:
|
||||
|
||||
- APC payment calculation (APC weight × conversion factor × wage index)
|
||||
- Pass-through payment (ASP + 6% for drugs/biologicals)
|
||||
- Skin substitute reclassification impact (ASP+6% vs flat rate)
|
||||
- Packaging threshold analysis
|
||||
- ASC payment calculation
|
||||
|
||||
``opps.rules``
|
||||
Annual rule parameters: conversion factors, wage index budget
|
||||
neutrality adjustors, packaging thresholds, and skin substitute
|
||||
payment category definitions. One ``RuleYear`` model per calendar
|
||||
year (2014–2026).
|
||||
|
||||
CMS Data Files
|
||||
--------------
|
||||
|
||||
::
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ File Family │ Release │ Format │ Key Columns │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Addendum A │ Annual │ CSV/XLSX │ APC, group title, │
|
||||
│ (APC weights) │ w/ rule │ │ relative weight, │
|
||||
│ │ │ │ payment rate, │
|
||||
│ │ │ │ status indicator │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Addendum B │ Annual │ CSV/XLSX │ HCPCS, APC, SI, │
|
||||
│ (HCPCS→APC map) │ w/ rule │ │ payment rate, │
|
||||
│ │ │ │ minimum unadjusted│
|
||||
│ │ │ │ copayment │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Addendum D │ Annual │ CSV/XLSX │ HCPCS, pass-thru │
|
||||
│ (Pass-through) │ w/ rule │ │ device/drug/bio, │
|
||||
│ │ │ │ payment amount │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Wage Index │ Annual │ CSV │ CBSA, wage index │
|
||||
│ │ │ │ (pre/post reclss) │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ ASC Addendum │ Annual │ CSV/XLSX │ HCPCS, ASC group, │
|
||||
│ (ASC payment) │ │ │ payment indicator,│
|
||||
│ │ │ │ payment rate │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
|
||||
Payment Formula
|
||||
---------------
|
||||
|
||||
::
|
||||
|
||||
Payment = APC_Relative_Weight
|
||||
× Conversion_Factor
|
||||
× Wage_Index_Adjustment
|
||||
|
||||
Where:
|
||||
Wage_Index_Adjustment = (Labor_Share × Wage_Index)
|
||||
+ (1 - Labor_Share)
|
||||
|
||||
Labor_Share ≈ 0.60 (set annually in Final Rule)
|
||||
Conversion_Factor = $89.723 (CY2026)
|
||||
|
||||
Skin Substitute Reclassification (CY2026)
|
||||
-----------------------------------------
|
||||
|
||||
Pre-2026: skin subs paid as drugs/biologicals via pass-through
|
||||
or packaged payment under OPPS, with ASP+6% for separately payable
|
||||
items.
|
||||
|
||||
Post-2026: reclassified as incident-to supplies with flat rate
|
||||
$127.28/cm² via new HCPCS codes C5271-C5278.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
::
|
||||
|
||||
from opps.table import ApcWeight, AddendumB
|
||||
from opps.calcs import payment, skin_sub_impact
|
||||
from opps.rules import cy2026
|
||||
|
||||
# Calculate OPPS payment for a given APC and CBSA
|
||||
paid = payment(apc_df, wage_index_df, year=2026)
|
||||
|
||||
# Model skin sub reclassification impact
|
||||
impact = skin_sub_impact(claims_df, asp_df, flat_rate=127.28)
|
||||
"""
|
||||
5
src/opps/calcs/__init__.py
Normal file
5
src/opps/calcs/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
"""OPPS payment calculation functions."""
|
||||
|
||||
from opps.calcs.payment import payment, skin_sub_impact
|
||||
|
||||
__all__ = ["payment", "skin_sub_impact"]
|
||||
105
src/opps/calcs/payment.py
Normal file
105
src/opps/calcs/payment.py
Normal file
@@ -0,0 +1,105 @@
|
||||
"""OPPS payment and skin substitute impact calculations.
|
||||
|
||||
Core OPPS math functions, mirroring ``pfs.calcs.payment``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import narwhals as nw
|
||||
|
||||
|
||||
@nw.narwhalify
|
||||
def payment(
|
||||
apc: nw.DataFrame,
|
||||
wage_index: nw.DataFrame,
|
||||
*,
|
||||
labor_share: float = 0.60,
|
||||
) -> nw.DataFrame:
|
||||
"""Calculate OPPS payment amounts.
|
||||
|
||||
Joins APC weight data with wage index and computes:
|
||||
|
||||
``Payment = APC_Weight × CF × ((Labor_Share × Wage_Index)
|
||||
+ (1 - Labor_Share))``
|
||||
|
||||
Parameters
|
||||
----------
|
||||
apc : DataFrame
|
||||
APC data with columns: ``hcpcs``, ``apc``, ``relative_weight``,
|
||||
``payment_rate``, ``status_indicator``, ``cbsa``.
|
||||
wage_index : DataFrame
|
||||
Wage index with columns: ``cbsa``, ``wage_index``.
|
||||
labor_share : float
|
||||
Labor share fraction (default 0.60 for CY2026).
|
||||
|
||||
Returns
|
||||
-------
|
||||
DataFrame
|
||||
Input ``apc`` with added ``adjusted_payment`` column.
|
||||
"""
|
||||
joined = apc.join(wage_index.select(["cbsa", "wage_index"]), on="cbsa", how="left")
|
||||
|
||||
return joined.with_columns(
|
||||
(
|
||||
nw.col("payment_rate")
|
||||
* (
|
||||
nw.lit(labor_share) * nw.col("wage_index").fill_null(1.0)
|
||||
+ nw.lit(1.0 - labor_share)
|
||||
)
|
||||
)
|
||||
.round(2)
|
||||
.alias("adjusted_payment")
|
||||
)
|
||||
|
||||
|
||||
@nw.narwhalify
|
||||
def skin_sub_impact(
|
||||
claims: nw.DataFrame,
|
||||
asp: nw.DataFrame,
|
||||
*,
|
||||
flat_rate: float = 127.28,
|
||||
) -> nw.DataFrame:
|
||||
"""Model the Jan 2026 skin substitute reclassification impact.
|
||||
|
||||
For each claim, computes the difference between:
|
||||
- Old payment: ASP + 6% (pass-through biological)
|
||||
- New payment: flat rate per cm² (incident-to supply)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
claims : DataFrame
|
||||
Claims with columns: ``hcpcs_code``, ``units``, ``paid_amount``.
|
||||
asp : DataFrame
|
||||
ASP data with columns: ``hcpcs_code``, ``asp_per_unit``,
|
||||
``payment_limit`` (ASP + 6%).
|
||||
flat_rate : float
|
||||
New flat rate per cm² ($127.28 for CY2026).
|
||||
|
||||
Returns
|
||||
-------
|
||||
DataFrame
|
||||
Claims with added ``old_payment``, ``new_payment``,
|
||||
``payment_delta``, and ``pct_change`` columns.
|
||||
"""
|
||||
joined = claims.join(
|
||||
asp.select(["hcpcs_code", "asp_per_unit", "payment_limit"]),
|
||||
on="hcpcs_code",
|
||||
how="left",
|
||||
)
|
||||
|
||||
return joined.with_columns(
|
||||
(nw.col("payment_limit") * nw.col("units")).round(2).alias("old_payment"),
|
||||
(nw.lit(flat_rate) * nw.col("units")).round(2).alias("new_payment"),
|
||||
).with_columns(
|
||||
(nw.col("new_payment") - nw.col("old_payment")).round(2).alias("payment_delta"),
|
||||
nw.when(nw.col("old_payment") > 0)
|
||||
.then(
|
||||
(
|
||||
(nw.col("new_payment") - nw.col("old_payment"))
|
||||
/ nw.col("old_payment")
|
||||
* 100
|
||||
).round(1)
|
||||
)
|
||||
.otherwise(nw.lit(None))
|
||||
.alias("pct_change"),
|
||||
)
|
||||
1
src/opps/files/__init__.py
Normal file
1
src/opps/files/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""OPPS file format definitions for CMS data ingestion."""
|
||||
15
src/opps/pipe.py
Normal file
15
src/opps/pipe.py
Normal file
@@ -0,0 +1,15 @@
|
||||
"""OPPS pipeline steps for the runner.
|
||||
|
||||
Registered with ``aco.pipe.runner`` for end-to-end execution.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
STEPS: list[tuple] = [
|
||||
# Populated as ingestion and calcs are wired up
|
||||
# ("opps_apc_weights", ingest_apc_weights, ApcWeight),
|
||||
# ("opps_addendum_b", ingest_addendum_b, AddendumB),
|
||||
# ("opps_wage_index", ingest_wage_index, WageIndex),
|
||||
# ("opps_payment", calc_payment, None),
|
||||
# ("opps_skin_sub_impact", calc_skin_sub_impact, None),
|
||||
]
|
||||
82
src/opps/rules/__init__.py
Normal file
82
src/opps/rules/__init__.py
Normal file
@@ -0,0 +1,82 @@
|
||||
"""OPPS annual rule parameters.
|
||||
|
||||
Conversion factors, labor shares, packaging thresholds, and
|
||||
skin substitute payment category definitions by calendar year.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RuleYear:
|
||||
"""Annual OPPS rule parameters."""
|
||||
|
||||
year: int
|
||||
conversion_factor: float
|
||||
labor_share: float
|
||||
packaging_threshold: float
|
||||
skin_sub_payment: str # 'pass_through', 'packaged', 'flat_rate'
|
||||
skin_sub_rate: float | None # flat rate $/cm² if applicable
|
||||
notes: str = ""
|
||||
|
||||
|
||||
# CY2026 — skin sub reclassification year
|
||||
cy2026 = RuleYear(
|
||||
year=2026,
|
||||
conversion_factor=89.723,
|
||||
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
|
||||
cy2025 = RuleYear(
|
||||
year=2025,
|
||||
conversion_factor=87.436,
|
||||
labor_share=0.60,
|
||||
packaging_threshold=0.0,
|
||||
skin_sub_payment="pass_through",
|
||||
skin_sub_rate=None,
|
||||
notes="High-cost/low-cost skin sub categories under OPPS",
|
||||
)
|
||||
|
||||
# CY2024
|
||||
cy2024 = RuleYear(
|
||||
year=2024,
|
||||
conversion_factor=86.304,
|
||||
labor_share=0.60,
|
||||
packaging_threshold=0.0,
|
||||
skin_sub_payment="pass_through",
|
||||
skin_sub_rate=None,
|
||||
notes="High-cost/low-cost skin sub categories under OPPS",
|
||||
)
|
||||
|
||||
# CY2023 — first codification of high/low cost categories
|
||||
cy2023 = RuleYear(
|
||||
year=2023,
|
||||
conversion_factor=85.093,
|
||||
labor_share=0.60,
|
||||
packaging_threshold=0.0,
|
||||
skin_sub_payment="pass_through",
|
||||
skin_sub_rate=None,
|
||||
notes="First formal high-cost/low-cost skin sub categories (CMS-1772-FC)",
|
||||
)
|
||||
|
||||
# CY2014 — first major skin sub restructuring under OPPS
|
||||
cy2014 = RuleYear(
|
||||
year=2014,
|
||||
conversion_factor=74.305,
|
||||
labor_share=0.60,
|
||||
packaging_threshold=0.0,
|
||||
skin_sub_payment="pass_through",
|
||||
skin_sub_rate=None,
|
||||
notes="First restructuring from individual pass-through to grouped payment (CMS-1601-FC)",
|
||||
)
|
||||
|
||||
RULE_YEARS: dict[int, RuleYear] = {
|
||||
r.year: r for r in [cy2014, cy2023, cy2024, cy2025, cy2026]
|
||||
}
|
||||
15
src/opps/table/__init__.py
Normal file
15
src/opps/table/__init__.py
Normal file
@@ -0,0 +1,15 @@
|
||||
"""OPPS table definitions — SQLTable models for CMS OPPS data."""
|
||||
|
||||
from opps.table.addendum_b import AddendumB
|
||||
from opps.table.apc_weight import ApcWeight
|
||||
from opps.table.pass_through import PassThrough
|
||||
from opps.table.status_indicator import StatusIndicator
|
||||
from opps.table.wage_index import WageIndex
|
||||
|
||||
__all__ = [
|
||||
"ApcWeight",
|
||||
"AddendumB",
|
||||
"PassThrough",
|
||||
"WageIndex",
|
||||
"StatusIndicator",
|
||||
]
|
||||
54
src/opps/table/addendum_b.py
Normal file
54
src/opps/table/addendum_b.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""Addendum B — HCPCS-to-APC mapping with payment rates.
|
||||
|
||||
One row per HCPCS code. Maps each procedure to its APC group,
|
||||
status indicator, and payment rate. This is the OPPS equivalent
|
||||
of the PFS RVU file.
|
||||
|
||||
Source: Addendum B from CMS OPPS Final Rule downloads.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from conf.table_base import SQLTable
|
||||
|
||||
|
||||
class AddendumB(SQLTable):
|
||||
"""HCPCS-to-APC crosswalk (Addendum B)."""
|
||||
|
||||
__schema__ = "opps"
|
||||
__tablename__ = "addendum_b"
|
||||
|
||||
hcpcs: str | None = None
|
||||
"""HCPCS/CPT procedure code."""
|
||||
|
||||
short_description: str | None = None
|
||||
"""Procedure code short descriptor."""
|
||||
|
||||
status_indicator: str | None = None
|
||||
"""Payment status indicator.
|
||||
|
||||
Key indicators for skin substitutes:
|
||||
- G: pass-through drug/biological (pre-2026 skin subs)
|
||||
- K: non-pass-through drug/biological requiring HCPCS (pre-2026)
|
||||
- N: packaged into APC payment (items under packaging threshold)
|
||||
- S: significant procedure, not discounted when multiple
|
||||
- Q: packaged/composite APC (skin sub application codes)
|
||||
"""
|
||||
|
||||
apc: str | None = None
|
||||
"""Assigned APC group."""
|
||||
|
||||
apc_title: str | None = None
|
||||
"""APC group title."""
|
||||
|
||||
relative_weight: float | None = None
|
||||
"""APC relative weight."""
|
||||
|
||||
payment_rate: float | None = None
|
||||
"""National unadjusted payment rate ($)."""
|
||||
|
||||
minimum_unadjusted_copayment: float | None = None
|
||||
"""Minimum unadjusted copayment ($)."""
|
||||
|
||||
year: int | None = None
|
||||
"""Calendar year."""
|
||||
41
src/opps/table/apc_weight.py
Normal file
41
src/opps/table/apc_weight.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""Addendum A — APC relative weights and payment rates.
|
||||
|
||||
One row per APC group. Published annually with the OPPS Final Rule.
|
||||
|
||||
Source: Addendum A from CMS OPPS Final Rule downloads.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from conf.table_base import SQLTable
|
||||
|
||||
|
||||
class ApcWeight(SQLTable):
|
||||
"""APC relative weights (Addendum A)."""
|
||||
|
||||
__schema__ = "opps"
|
||||
__tablename__ = "apc_weight"
|
||||
|
||||
apc: str | None = None
|
||||
"""Ambulatory Payment Classification code."""
|
||||
|
||||
group_title: str | None = None
|
||||
"""APC group title/description."""
|
||||
|
||||
status_indicator: str | None = None
|
||||
"""Status indicator (S, T, V, Q, etc.)."""
|
||||
|
||||
relative_weight: float | None = None
|
||||
"""APC relative weight used in payment calculation."""
|
||||
|
||||
payment_rate: float | None = None
|
||||
"""National unadjusted payment rate ($)."""
|
||||
|
||||
minimum_unadjusted_copayment: float | None = None
|
||||
"""Minimum unadjusted copayment amount ($)."""
|
||||
|
||||
year: int | None = None
|
||||
"""Calendar year this weight applies to."""
|
||||
|
||||
notes: str | None = None
|
||||
"""Additional notes or flags."""
|
||||
41
src/opps/table/pass_through.py
Normal file
41
src/opps/table/pass_through.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""Pass-through payment items — drugs, biologicals, and devices.
|
||||
|
||||
Separately payable items that bypass APC packaging. Prior to
|
||||
Jan 2026, skin substitutes were paid as pass-through biologicals
|
||||
at ASP+6%. The reclassification moved them to incident-to supplies.
|
||||
|
||||
Source: Addendum D from CMS OPPS Final Rule downloads.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from conf.table_base import SQLTable
|
||||
|
||||
|
||||
class PassThrough(SQLTable):
|
||||
"""Pass-through payment items (Addendum D)."""
|
||||
|
||||
__schema__ = "opps"
|
||||
__tablename__ = "pass_through"
|
||||
|
||||
hcpcs: str | None = None
|
||||
"""HCPCS code for the pass-through item."""
|
||||
|
||||
short_description: str | None = None
|
||||
"""Item short descriptor."""
|
||||
|
||||
pass_through_type: str | None = None
|
||||
"""Type: 'drug', 'biological', 'device'."""
|
||||
|
||||
status_indicator: str | None = None
|
||||
"""Payment status indicator (G for pass-through)."""
|
||||
|
||||
payment_rate: float | None = None
|
||||
"""Pass-through payment amount ($). For drugs/biologicals,
|
||||
this is ASP + 6%."""
|
||||
|
||||
asp_per_unit: float | None = None
|
||||
"""Average Sales Price per unit (for drugs/biologicals)."""
|
||||
|
||||
year: int | None = None
|
||||
"""Calendar year."""
|
||||
30
src/opps/table/status_indicator.py
Normal file
30
src/opps/table/status_indicator.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""Status indicator reference — payment category definitions.
|
||||
|
||||
Maps status indicator codes to their payment policy meaning.
|
||||
Critical for understanding skin substitute payment history:
|
||||
how products moved between status indicators over time.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from conf.table_base import SQLTable
|
||||
|
||||
|
||||
class StatusIndicator(SQLTable):
|
||||
"""OPPS status indicator reference."""
|
||||
|
||||
__schema__ = "opps"
|
||||
__tablename__ = "status_indicator"
|
||||
|
||||
si: str | None = None
|
||||
"""Status indicator code (single letter)."""
|
||||
|
||||
description: str | None = None
|
||||
"""Full description of the status indicator."""
|
||||
|
||||
payment_method: str | None = None
|
||||
"""How items with this SI are paid (e.g., 'APC rate',
|
||||
'pass-through', 'packaged', 'not payable')."""
|
||||
|
||||
notes: str | None = None
|
||||
"""Additional notes."""
|
||||
36
src/opps/table/wage_index.py
Normal file
36
src/opps/table/wage_index.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""Wage index — geographic adjustment factors for OPPS payment.
|
||||
|
||||
One row per CBSA (Core Based Statistical Area). Used to adjust
|
||||
the labor portion of APC payment for local wage variation.
|
||||
|
||||
Source: OPPS wage index files from CMS OPPS Final Rule downloads.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from conf.table_base import SQLTable
|
||||
|
||||
|
||||
class WageIndex(SQLTable):
|
||||
"""OPPS wage index by CBSA."""
|
||||
|
||||
__schema__ = "opps"
|
||||
__tablename__ = "wage_index"
|
||||
|
||||
cbsa: str | None = None
|
||||
"""Core Based Statistical Area code."""
|
||||
|
||||
cbsa_name: str | None = None
|
||||
"""CBSA name (metro area)."""
|
||||
|
||||
state: str | None = None
|
||||
"""State abbreviation."""
|
||||
|
||||
wage_index: float | None = None
|
||||
"""Pre-reclassification wage index."""
|
||||
|
||||
reclassified_wage_index: float | None = None
|
||||
"""Post-reclassification wage index (if applicable)."""
|
||||
|
||||
year: int | None = None
|
||||
"""Calendar year."""
|
||||
Reference in New Issue
Block a user