Search by

claimrevolution / oe-module-claimrev-connect

OpenEMR Custom Module Claim Revolution, LLC Connector

Maintainers

Package info

github.com/claimrevolution/oe-module-claimrev-connect

Type:openemr-module

pkg:composer/claimrevolution/oe-module-claimrev-connect

Transparency log

Statistics

Installs: 374 118

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v2.1.9 2026-08-26 21:20 UTC

This package is auto-updated.

Last update: 2026-08-26 21:21:15 UTC


README

Connects OpenEMR to the Claim Revolution, LLC clearinghouse and adds revenue-cycle dashboards on top of the data the clearinghouse returns. Claim and ERA exchange runs over the ClaimRev REST API; eligibility, reconciliation, denial analytics, AR aging, and a claim status dashboard run on the local copy of that data plus OpenEMR's billing tables.

Features

Billing flow

Page What it does
public/x12Tracker.php Tracks claim files sent to ClaimRev (status, retry).
public/era.php Lists ERA / 835 files received from ClaimRev with download.
public/payment_advice.php ERA-driven payment advice search + posting to OpenEMR's ar_session / ar_activity. Single-row + batch post supported.
public/claims.php Claim search against the ClaimRev portal with detail expansion, sort, CSV export, requeue, mark-worked, and editor links.
public/claim_status.php Work queue: claims needing attention, with timeline of every status event we've seen.
public/reconciliation.php Side-by-side OpenEMR claim status vs ClaimRev status with discrepancy classification.

Eligibility

Page What it does
public/appointments.php Upcoming appointments with their primary-insurance eligibility status and "check now" / queue actions.
Eligibility card on patient chart Per-product (Eligibility / Coverage Discovery / Demographics / MBI Finder) results with AI chat over the saved response.
Calendar indicators Color codes events on the OpenEMR calendar by eligibility status — green / red / yellow / grey. Off by default.
Eligibility sweep Background service that proactively queues eligibility checks for the next N days of appointments on configured weekdays.

Reporting / dashboards

Page What it does
public/aging_report.php AR aging by payer with current / 30 / 60 / 90 / 120 / 120+ buckets and CSV export.
public/denial_analytics.php Denial rollups by reason code, by payer, by month with date filters.
public/recoupment_report.php Identifies negative ERA adjustments (recoupments / takebacks).
public/patient_balance.php Patient-responsibility queue: encounters with outstanding balance after insurance closed, with statement tracking.
Dashboard KPIs DashboardService exposes claim throughput, AR, denial, collection, and patient-AR metrics for the home tile.

Configuration

All settings live under Admin → Globals → ClaimRev Connect. Names are the oe_claimrev_* keys defined in src/GlobalConfig.php.

Required to function

Setting Notes
oe_claimrev_config_environment P for production (default), D for a custom identity provider (Entra ID External, Zitadel, …).
oe_claimrev_config_clientid OAuth client ID. Available in the ClaimRev Portal under Client Connect.
oe_claimrev_config_clientsecret OAuth client secret. Encrypted at rest. Contact ClaimRev support for this value.

Without these three set, GlobalConfig::isConfigured() returns false and the module skips event registration entirely (no menu item, no eligibility events, no posting endpoints) — pages that try to load show ModuleNotConfiguredException.

Optional

Setting What it does
oe_claimrev_x12_partner_name X12 partner record name. Default ClaimRev. Used by ClaimRevModuleSetup::createPartnerRecord.
oe_claimrev_config_service_type_codes Comma-separated 270 service-type codes. Empty asks for all benefits.
oe_claimrev_benefit_code_filter Comma-separated benefit codes (1, 6, A, B, C, …). Filters the eligibility detail view locally; not sent to ClaimRev.
oe_claimrev_config_auto_send_claim_files Auto-send X12 claim files. When off, files queue but are sent manually.
oe_claimrev_config_add_menu_button Add the module's top-nav menu item (re-login required).
oe_claimrev_config_add_eligibility_card Add the eligibility card to the patient dashboard.
oe_claimrev_config_use_facility_for_eligibility Use the facility (rather than the appointment provider) as the 270 information receiver.
oe_claimrev_enable_rte Real-time eligibility — kick off a check when an appointment is created.
oe_claimrev_eligibility_results_age Days before a stored eligibility result is considered stale. Used by the sweep + calendar indicators.
oe_claimrev_send_eligibility Master switch for the background eligibility send service.
oe_claimrev_enable_watchdog Auto-resets ClaimRev background services that stay in running=1 for >10 minutes (PHP crash, OOM kill). Default on.
oe_claimrev_enable_notifications Poll ClaimRev for portal notifications and deliver them as OpenEMR pnotes. Default on.
oe_claimrev_notification_recipient Semicolon-separated OpenEMR usernames to receive notifications. Default admin.
oe_claimrev_enable_test_mode Switches every test-mode-capable page (Payment Advice, ERA, Reconciliation, Eligibility) to mock data generated from local billing rows. ERA Download returns a 0-byte placeholder; Reconciliation row actions short-circuit to fake-success.
oe_claimrev_enable_sweep Master switch for the eligibility sweep background service.
oe_claimrev_sweep_days Comma-separated day-of-week numbers (0 = Sunday … 6 = Saturday). Default 1,4 (Mon + Thu).
oe_claimrev_sweep_lookahead Days ahead to sweep. Default 7.
oe_claimrev_enable_calendar_indicators Color-code OpenEMR calendar events by eligibility status. May impact calendar performance on busy schedules.

Identity-provider overrides

Auto-configured for production. Only override when pointing at a custom identity provider:

oe_claimrev_config_portal_url, oe_claimrev_config_dev_api_url, oe_claimrev_config_dev_scope, oe_claimrev_config_dev_authority.

Background services

Registered in background_services by table.sql and re-enabled on module enable via ModuleManagerListener::enable. Each one has a @phpstan-ignore openemr.noGlobalNsFunctions shim that delegates to a namespaced service class so the cron table can address it by name.

Service Function Cadence Delegate
ClaimRev_Send start_X12_Claimrev_send_files 1 minute ClaimUpload::sendWaitingFiles
ClaimRev_Receive start_X12_Claimrev_get_reports 240 minutes ReportDownload::getWaitingFiles
ClaimRev_Elig_Send_Receive start_send_eligibility 1 minute EligibilityTransfer::sendWaitingEligibility
ClaimRev_Notifications start_claimrev_notifications 60 minutes NotificationPollService::run
ClaimRev_Watchdog start_claimrev_watchdog 20 minutes ClaimRevModuleSetup::resetStuckServices
ClaimRev_Elig_Sweep start_eligibility_sweep 1440 minutes (daily) EligibilitySweepService::run

The watchdog only resets services other than itself; otherwise a watchdog run that exceeds 10 minutes would clear its own running flag mid-execution.

Database tables

All created by table.sql. Migrations run once on enable via ClaimRevModuleSetup::runMigrations, which understands the standard OpenEMR #IfNotRow / #IfNotColumnType / #IfNotTable / #EndIf directives.

Table Purpose
mod_claimrev_eligibility One row per (patient, payer responsibility, request) eligibility check + JSON result.
mod_claimrev_notifications Tracks which portal notifications have already been delivered as pnotes.
mod_claimrev_claims Per-claim mirror of ClaimRev status (object id, status name/id, ERA classification, paid amount, ar_session_id, last sync).
mod_claimrev_claim_events Append-only event log per claim (submitted, rejected, accepted, denied, status_check_276, era_received, payment_posted, requeued, corrected, manual_note, claimrev_sync).
mod_claimrev_patient_statements Statement history for the patient balance queue (date, method, amount, status, notes).

Development

Code conventions

This module follows the OpenEMR developer conventions documented in the top-level CLAUDE.md. In particular:

  • Every PHP file starts with declare(strict_types=1).
  • Superglobal access goes through ModuleInput (which routes through filter_input so the openemr.forbiddenRequestGlobals PHPStan rule passes).
  • Mixed cells from QueryUtils::fetchRecords are narrowed with TypeCoerce::asString / asInt / asFloat / asBool / asNullableInt rather than bare casts ((int) $mixed is rejected at PHPStan level 10).
  • catch (\Throwable) and catch (\Exception) are forbidden by openemr.forbiddenCatchType. Use catch (\RuntimeException | \LogicException) instead. ClaimRevException extends RuntimeException so this still catches our own throws.
  • Database calls go through QueryUtils; legacy sqlStatement / sqlInsert / sqlQuery / sqlStatementNoLog are blocked by openemr.deprecatedSqlFunction.
  • Error reporting uses the PSR-3 logger via ServiceContainer::getLogger, not error_log().

Tests

Pure helpers have isolated test coverage in tests/Tests/Isolated/Modules/ClaimRevConnector/:

TypeCoerceTest                  asString / asInt / asFloat / asBool / asNullableInt
ValueMappingTest                mapPayerResponsibility (case-insensitive primary/secondary/tertiary → p/s/t)
AgingReportServiceTest          toCsv with RFC 4180 escape
ClaimTrackingServiceTest        parsePcn (mirrors PaymentAdvicePostingService)
PaymentAdvicePostingServiceTest buildIdempotencyReference, parsePatientControlNumber, getClaimStatusLabel, sumServiceAmounts
ReconciliationServiceTest       computeDiscrepancy classifier

Run them from the repo root:

composer phpunit-isolated -- --filter ClaimRevConnector

DB-bound and API-bound code paths (the reconcile() / searchClaims() / post() entry points) need a real OpenEMR install; they are exercised by manual QA through the UI.

Static analysis

composer phpstan runs at level 10. Custom rules in tests/PHPStan/Rules/ enforce the conventions above. Avoid adding new baseline entries — fix the underlying type error.

composer rector-check keeps the module on modern PHP idioms; run composer rector-fix to apply suggestions.

Branches and version support

There is one maintained line, main. A single build covers OpenEMR 7.x and 8.x: the src/Compat/ shim layer activates per host, and CsrfHelper detects the 7.x versus 8.x CsrfUtils::collectCsrfToken signature at runtime, so the same binary works on both.

Older cores are supported by pinning a release, not by a branch:

composer require claimrevolution/oe-module-claimrev-connect:2.1.7

Every tag stays on Packagist, so a pin costs nothing to maintain — unlike a parallel branch, which has to be kept in sync by hand and drifts the moment someone forgets.

Which version to pin is documented in the compatibility matrix in the OpenEMR manual. The short version: use the latest release on any OpenEMR 8.x, because v2.1.6 and earlier fatal on the 8.0.x patch line.

Troubleshooting

ModuleNotConfiguredException on every module page

The required globals (oe_claimrev_config_clientid, oe_claimrev_config_clientsecret, oe_claimrev_config_environment) aren't set. Go to Admin → Globals → ClaimRev Connect, fill the three values, save. Re-login (the module's menu item only renders after a fresh login).

Background services stuck in running=1

The watchdog should reset stuck services every 20 minutes. To reset manually:

UPDATE background_services
SET running = 0
WHERE running = 1
  AND name LIKE '%ClaimRev%'
  AND name != 'ClaimRev_Watchdog';

The watchdog excludes itself for the reason noted above.

Wrong module version for the OpenEMR version

There is no separate build per core — one release covers 7.x and 8.x — but the version still matters. On the OpenEMR 8.0.x patch line, v2.1.6 and earlier fatal with Call to undefined method OEGlobalsBag::getKernel(), because 8.0.x ships an OEGlobalsBag without that method while the flex/master line has it. The visible symptoms are blank module pages, or the Patient Portal / API panel vanishing from the patient dashboard.

Upgrading to v2.1.7 or later resolves it. See the compatibility matrix in the OpenEMR manual.

"Already posted" warnings on a payment advice that wasn't

PaymentAdvicePostingService::isAlreadyPosted keys off the ar_session.reference value, which uses a fixed ClaimRev-{paymentAdviceId} prefix (PaymentAdvicePostingService::REFERENCE_PREFIX). If the prefix or the paymentAdviceId changes between runs, the dedup check stops matching and the same advice can post twice. The shape is regression-tested in PaymentAdvicePostingServiceTest::testReferencePrefixHasExpectedShape.

License

GPL v3 — see LICENSE and the OpenEMR project license.

Contributing

Open an issue or send a pull request against claimrevolution/openemr_fork or the upstream module path interface/modules/custom_modules/oe-module-claimrev-connect in the main OpenEMR repo.