Billing & Claims Management Data Specifications
Shared Entity References
This module references the following shared entities defined in their owning modules. billing-claims does not redefine these tables — it uses foreign-key references only.
| Shared Entity |
Owning Module |
Table(s) |
FK Used Here |
| Patients |
ehr-patient-mgmt |
patients |
patients.patient_id |
| Providers |
ehr-patient-mgmt |
providers |
providers.provider_id |
| Encounters |
scheduling |
encounters, encounter_details |
encounters.encounter_id |
| Users & Auth |
ehr-patient-mgmt |
users, roles, permissions |
users.user_id |
| Facilities |
ehr-patient-mgmt |
facilities, departments |
facilities.facility_id, departments.department_id |
| Payers |
policy-contract-mgmt |
payers |
payers.payer_id |
| Insurance Plans |
policy-contract-mgmt |
insurance_plans |
insurance_plans.plan_id |
| Contracts |
policy-contract-mgmt |
contracts |
contracts.contract_id |
| Fee Schedules |
policy-contract-mgmt |
fee_schedule_items |
fee_schedule_items.item_id |
Entity Relationship Diagram
erDiagram
patients ||--o{ charges : "incurs"
encounters ||--o{ charges : "has"
providers ||--o{ charges : "rendered by"
facilities ||--o{ charges : "at"
departments ||--o{ charges : "in"
charges ||--o{ charge_details : "details"
encounters ||--o{ claims : "billed as"
patients ||--o{ claims : "for"
payers ||--o{ claims : "to"
insurance_plans ||--o{ claims : "under"
contracts ||--o{ claims : "per"
claims ||--o{ claim_lines : "includes"
charges ||--o{ claim_lines : "sourced from"
claims ||--o{ claim_submissions : "submitted as"
claim_submissions ||--o{ claim_responses : "result in"
remittance_advice ||--o{ claim_responses : "summarises"
payers ||--o{ payments : "pay"
remittance_advice ||--o{ payments : "contains"
payments ||--o{ payment_allocations : "allocated to"
claims ||--o{ payment_allocations : "receive"
claim_lines ||--o{ payment_allocations : "receive"
patients ||--o{ patient_invoices : "billed"
patient_invoices ||--o{ patient_payments : "paid by"
encounters ||--o{ patient_payments : "at"
users ||--o{ patient_payments : "collected by"
patients ||--o{ refunds : "refunded"
payers ||--o{ refunds : "to/from"
payments ||--o{ refunds : "orig payment"
claims ||--o{ ar_aging : "snapshot"
patients ||--o{ ar_aging : "owes"
payers ||--o{ ar_aging : "by"
charge_capture_rules ||--o{ charges : "applied to"
claim_edits ||--o{ claim_submissions : "validate"
payers ||--o{ claim_edits : "payer specific"
charges {
bigint charge_id PK
bigint patient_id FK
bigint encounter_id FK
bigint provider_id FK
bigint facility_id FK
bigint department_id FK
bigint fee_schedule_item_id FK
bigint created_by FK
bigint updated_by FK
}
charge_details {
bigint detail_id PK
bigint charge_id FK
}
claims {
bigint claim_id PK
bigint patient_id FK
bigint encounter_id FK
bigint payer_id FK
bigint plan_id FK
bigint contract_id FK
bigint created_by FK
bigint updated_by FK
}
claim_lines {
bigint line_id PK
bigint claim_id FK
bigint charge_id FK
}
claim_submissions {
bigint submission_id PK
bigint claim_id FK
}
claim_responses {
bigint response_id PK
bigint claim_id FK
bigint submission_id FK
bigint remittance_id FK
}
remittance_advice {
bigint remittance_id PK
bigint payer_id FK
}
payments {
bigint payment_id PK
bigint payer_id FK
bigint remittance_id FK
bigint created_by FK
}
payment_allocations {
bigint allocation_id PK
bigint payment_id FK
bigint claim_id FK
bigint claim_line_id FK
}
patient_invoices {
bigint invoice_id PK
bigint patient_id FK
bigint created_by FK
}
patient_payments {
bigint payment_id PK
bigint patient_id FK
bigint invoice_id FK
bigint encounter_id FK
bigint collected_by FK
}
refunds {
bigint refund_id PK
bigint patient_id FK
bigint payer_id FK
bigint original_payment_id FK
bigint approved_by FK
}
ar_aging {
bigint aging_id PK
bigint payer_id FK
bigint patient_id FK
bigint claim_id FK
}
charge_capture_rules {
bigint rule_id PK
}
claim_edits {
bigint edit_id PK
bigint payer_id FK
}
Table Definitions
Notes:
• All timestamps are in UTC with local time derivable via application logic.
• UAE VAT fields are included where relevant.
• ICD-10-AM, CPT, HCPCS, CARC/RARC, and UAE payer-specific codes are used as applicable.
1. charges
Purpose: Charge header — one record per billable service event, created via automated charge capture or manual entry. Forms the basis for claim lines and patient billing.
Field Specification
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
charge_id |
BIGINT |
NO |
IDENTITY |
PK |
Unique charge identifier |
patient_id |
BIGINT |
NO |
— |
FK → patients.patient_id |
Patient receiving service |
encounter_id |
BIGINT |
NO |
— |
FK → encounters.encounter_id |
Encounter context |
provider_id |
BIGINT |
NO |
— |
FK → providers.provider_id |
Rendering provider |
facility_id |
BIGINT |
NO |
— |
FK → facilities.facility_id |
Facility where service rendered |
department_id |
BIGINT |
NO |
— |
FK → departments.department_id |
Department/cost center |
fee_schedule_item_id |
BIGINT |
YES |
— |
FK → fee_schedule_items.item_id |
Linked fee schedule entry used for pricing |
service_date |
DATE |
NO |
— |
— |
Date of service (DOS) |
cpt_code |
VARCHAR(10) |
YES |
— |
CPT format |
Primary procedure code |
hcpcs_code |
VARCHAR(10) |
YES |
— |
HCPCS format |
HCPCS code if applicable |
icd10_codes |
VARCHAR(255) |
YES |
— |
ICD-10-AM, comma-separated |
Related diagnoses for medical necessity |
modifier_codes |
VARCHAR(50) |
YES |
— |
Comma-separated CPT modifiers |
Up to 4 modifiers |
quantity |
INTEGER |
NO |
1 |
> 0 |
Units of service |
charge_amount |
NUMERIC(12,2) |
NO |
0.00 |
≥ 0 |
Gross charge before discounts/VAT |
contracted_rate |
NUMERIC(12,2) |
YES |
— |
≥ 0 |
Expected allowed amount per contract |
charge_status |
VARCHAR(30) |
NO |
'pending' |
IN ('pending','validated','on_hold','posted','voided') |
Lifecycle status |
source_module |
VARCHAR(50) |
NO |
— |
— |
Origin (e.g., cpoe, ris, lis, pis, scheduling) |
source_order_id |
VARCHAR(50) |
YES |
— |
— |
ID of originating order/procedure |
is_uae_vat_applicable |
BOOLEAN |
NO |
FALSE |
— |
Whether VAT applies to this charge |
vat_rate |
NUMERIC(5,2) |
YES |
— |
BETWEEN 0 AND 100 |
VAT % (e.g., 5.00) |
vat_amount |
NUMERIC(12,2) |
YES |
— |
≥ 0 |
VAT amount for this charge |
notes |
TEXT |
YES |
— |
— |
Internal billing notes |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Creation timestamp |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
User/system creating charge |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
ON UPDATE |
Last update timestamp |
updated_by |
BIGINT |
YES |
— |
FK → users.user_id |
Last updating user |
SQL DDL
SQLCREATE TABLE charges (
charge_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
patient_id BIGINT NOT NULL,
encounter_id BIGINT NOT NULL,
provider_id BIGINT NOT NULL,
facility_id BIGINT NOT NULL,
department_id BIGINT NOT NULL,
fee_schedule_item_id BIGINT,
service_date DATE NOT NULL,
cpt_code VARCHAR(10),
hcpcs_code VARCHAR(10),
icd10_codes VARCHAR(255),
modifier_codes VARCHAR(50),
quantity INTEGER NOT NULL DEFAULT 1,
charge_amount NUMERIC(12,2) NOT NULL DEFAULT 0.00,
contracted_rate NUMERIC(12,2),
charge_status VARCHAR(30) NOT NULL DEFAULT 'pending',
source_module VARCHAR(50) NOT NULL,
source_order_id VARCHAR(50),
is_uae_vat_applicable BOOLEAN NOT NULL DEFAULT FALSE,
vat_rate NUMERIC(5,2),
vat_amount NUMERIC(12,2),
notes TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by BIGINT NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_by BIGINT,
CONSTRAINT fk_charges_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_charges_encounter
FOREIGN KEY (encounter_id) REFERENCES encounters(encounter_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_charges_provider
FOREIGN KEY (provider_id) REFERENCES providers(provider_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_charges_facility
FOREIGN KEY (facility_id) REFERENCES facilities(facility_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_charges_department
FOREIGN KEY (department_id) REFERENCES departments(department_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_charges_fee_schedule_item
FOREIGN KEY (fee_schedule_item_id) REFERENCES fee_schedule_items(item_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT fk_charges_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_charges_updated_by
FOREIGN KEY (updated_by) REFERENCES users(user_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT chk_charges_quantity_positive
CHECK (quantity > 0),
CONSTRAINT chk_charges_amounts_nonnegative
CHECK (charge_amount >= 0 AND (contracted_rate IS NULL OR contracted_rate >= 0)),
CONSTRAINT chk_charges_vat
CHECK (
(is_uae_vat_applicable = FALSE AND vat_rate IS NULL AND vat_amount IS NULL)
OR (is_uae_vat_applicable = TRUE AND vat_rate IS NOT NULL AND vat_amount IS NOT NULL)
),
CONSTRAINT chk_charges_status
CHECK (charge_status IN ('pending','validated','on_hold','posted','voided'))
);
CREATE INDEX idx_charges_patient ON charges(patient_id);
CREATE INDEX idx_charges_encounter ON charges(encounter_id);
CREATE INDEX idx_charges_provider ON charges(provider_id);
CREATE INDEX idx_charges_status ON charges(charge_status);
CREATE INDEX idx_charges_service_date ON charges(service_date);
CREATE INDEX idx_charges_source ON charges(source_module, source_order_id);
CREATE INDEX idx_charges_facility_dept_date
ON charges(facility_id, department_id, service_date);
Terminology Bindings
| Field |
Terminology |
Example Value |
cpt_code |
CPT |
99213 |
hcpcs_code |
HCPCS |
J1885 |
icd10_codes |
ICD-10-AM |
E11.9,I10 |
modifier_codes |
CPT Modifiers |
25,59 |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
charge_id |
ChargeItem |
ChargeItem.id |
patient_id |
ChargeItem |
ChargeItem.subject.reference (Patient/{id}) |
encounter_id |
ChargeItem |
ChargeItem.context.reference (Encounter/{id}) |
provider_id |
ChargeItem |
ChargeItem.performer.actor.reference (Practitioner/{id}) |
service_date |
ChargeItem |
ChargeItem.occurrenceDateTime |
cpt_code |
ChargeItem |
ChargeItem.code.coding[system='http://www.ama-assn.org/go/cpt'].code |
quantity |
ChargeItem |
ChargeItem.quantity.value |
charge_amount |
ChargeItem |
ChargeItem.extension[chargeAmount].valueMoney.value |
contracted_rate |
ChargeItem |
ChargeItem.extension[expectedAllowed].valueMoney.value |
icd10_codes |
Claim |
Claim.diagnosis.diagnosisCodeableConcept.coding[ICD-10-AM] (via claim_lines) |
2. charge_details
Purpose: Extended metadata for a charge, including revenue code, DRG, cost center, and bilingual descriptions. Supports UAE facility costing and reporting.
Field Specification
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
detail_id |
BIGINT |
NO |
IDENTITY |
PK |
Unique detail identifier |
charge_id |
BIGINT |
NO |
— |
FK → charges.charge_id |
Parent charge |
revenue_code |
VARCHAR(4) |
YES |
— |
NUBC revenue code |
Revenue center |
cost_center |
VARCHAR(50) |
YES |
— |
— |
Internal cost center code |
drg_code |
VARCHAR(10) |
YES |
— |
DRG format |
Diagnosis Related Group |
case_weight |
NUMERIC(6,3) |
YES |
— |
≥ 0 |
DRG case weight |
ndc_code |
VARCHAR(20) |
YES |
— |
— |
Drug code if needed |
units |
INTEGER |
YES |
— |
≥ 0 |
Costing units |
service_description_en |
VARCHAR(255) |
YES |
— |
— |
English description |
service_description_ar |
VARCHAR(255) |
YES |
— |
— |
Arabic description |
additional_attributes |
JSONB |
YES |
— |
— |
Payer/facility-specific attributes |
SQL DDL
SQLCREATE TABLE charge_details (
detail_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
charge_id BIGINT NOT NULL,
revenue_code VARCHAR(4),
cost_center VARCHAR(50),
drg_code VARCHAR(10),
case_weight NUMERIC(6,3),
ndc_code VARCHAR(20),
units INTEGER,
service_description_en VARCHAR(255),
service_description_ar VARCHAR(255),
additional_attributes JSONB,
CONSTRAINT fk_charge_details_charge
FOREIGN KEY (charge_id) REFERENCES charges(charge_id)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT chk_charge_details_case_weight
CHECK (case_weight IS NULL OR case_weight >= 0),
CONSTRAINT chk_charge_details_units
CHECK (units IS NULL OR units >= 0)
);
CREATE INDEX idx_charge_details_charge ON charge_details(charge_id);
CREATE INDEX idx_charge_details_revenue ON charge_details(revenue_code);
Terminology Bindings
| Field |
Terminology |
Example Value |
revenue_code |
NUBC Revenue Codes |
0450 |
drg_code |
DRG (local/UAE) |
470 |
ndc_code |
NDC / local drug code |
0002-8215-01 |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
revenue_code |
Claim |
Claim.item.revenue.coding.code |
cost_center |
Encounter |
Encounter.serviceProvider.identifier (org-specific) |
service_description_en |
ChargeItem |
ChargeItem.code.text |
service_description_ar |
ChargeItem |
ChargeItem.extension[ar-description].valueString |
3. claims
Purpose: Claim header — one per encounter per payer. Tracks financial totals and lifecycle status for UAE eClaimLink/DOH eClaims and other payers.
Field Specification
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
claim_id |
BIGINT |
NO |
IDENTITY |
PK |
Unique claim identifier |
patient_id |
BIGINT |
NO |
— |
FK → patients.patient_id |
Patient |
encounter_id |
BIGINT |
NO |
— |
FK → encounters.encounter_id |
Encounter |
payer_id |
BIGINT |
NO |
— |
FK → payers.payer_id |
Primary payer |
plan_id |
BIGINT |
YES |
— |
FK → insurance_plans.plan_id |
Insurance plan |
contract_id |
BIGINT |
YES |
— |
FK → contracts.contract_id |
Contract used for pricing |
claim_type |
VARCHAR(20) |
NO |
— |
IN ('inpatient','outpatient','emergency','day_case','pharmacy') |
Claim category |
claim_number |
VARCHAR(30) |
NO |
— |
UNIQUE per facility |
Internal claim number |
total_charge_amount |
NUMERIC(14,2) |
NO |
0.00 |
≥ 0 |
Sum of line charges |
total_allowed_amount |
NUMERIC(14,2) |
YES |
— |
≥ 0 |
Sum of allowed amounts |
total_paid_amount |
NUMERIC(14,2) |
YES |
— |
≥ 0 |
Sum of payer payments |
total_adjusted_amount |
NUMERIC(14,2) |
YES |
— |
≥ 0 |
Sum of adjustments/write-offs |
patient_responsibility |
NUMERIC(14,2) |
YES |
— |
≥ 0 |
Co-pay, co-insurance, deductible |
claim_status |
VARCHAR(30) |
NO |
'draft' |
e.g. 'draft','ready','submitted','in_review','paid','partially_paid','denied','voided' |
Lifecycle status |
submission_date |
DATE |
YES |
— |
— |
First submission date |
adjudication_date |
DATE |
YES |
— |
— |
Final adjudication date |
is_resubmission |
BOOLEAN |
NO |
FALSE |
— |
Indicates resubmission |
original_claim_number |
VARCHAR(30) |
YES |
— |
— |
Original claim reference for resubmissions |
emirate_code |
VARCHAR(3) |
NO |
— |
IN ('DXB','AUH','SHJ','AJM','RAK','UAQ','FJR') |
Emirate of facility |
notes |
TEXT |
YES |
— |
— |
Internal notes |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Creation timestamp |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
Creator |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
ON UPDATE |
Last update |
updated_by |
BIGINT |
YES |
— |
FK → users.user_id |
Last updater |
SQL DDL
SQLCREATE TABLE claims (
claim_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
patient_id BIGINT NOT NULL,
encounter_id BIGINT NOT NULL,
payer_id BIGINT NOT NULL,
plan_id BIGINT,
contract_id BIGINT,
claim_type VARCHAR(20) NOT NULL,
claim_number VARCHAR(30) NOT NULL,
total_charge_amount NUMERIC(14,2) NOT NULL DEFAULT 0.00,
total_allowed_amount NUMERIC(14,2),
total_paid_amount NUMERIC(14,2),
total_adjusted_amount NUMERIC(14,2),
patient_responsibility NUMERIC(14,2),
claim_status VARCHAR(30) NOT NULL DEFAULT 'draft',
submission_date DATE,
adjudication_date DATE,
is_resubmission BOOLEAN NOT NULL DEFAULT FALSE,
original_claim_number VARCHAR(30),
emirate_code VARCHAR(3) NOT NULL,
notes TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by BIGINT NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_by BIGINT,
CONSTRAINT uq_claims_number UNIQUE (claim_number),
CONSTRAINT fk_claims_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_claims_encounter
FOREIGN KEY (encounter_id) REFERENCES encounters(encounter_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_claims_payer
FOREIGN KEY (payer_id) REFERENCES payers(payer_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_claims_plan
FOREIGN KEY (plan_id) REFERENCES insurance_plans(plan_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT fk_claims_contract
FOREIGN KEY (contract_id) REFERENCES contracts(contract_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT fk_claims_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_claims_updated_by
FOREIGN KEY (updated_by) REFERENCES users(user_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT chk_claims_type
CHECK (claim_type IN ('inpatient','outpatient','emergency','day_case','pharmacy')),
CONSTRAINT chk_claims_status
CHECK (claim_status IN (
'draft','ready','submitted','in_review',
'paid','partially_paid','denied','voided'
)),
CONSTRAINT chk_claims_amounts_nonnegative
CHECK (
total_charge_amount >= 0
AND (total_allowed_amount IS NULL OR total_allowed_amount >= 0)
AND (total_paid_amount IS NULL OR total_paid_amount >= 0)
AND (total_adjusted_amount IS NULL OR total_adjusted_amount >= 0)
AND (patient_responsibility IS NULL OR patient_responsibility >= 0)
),
CONSTRAINT chk_claims_emirate
CHECK (emirate_code IN ('DXB','AUH','SHJ','AJM','RAK','UAQ','FJR'))
);
CREATE INDEX idx_claims_patient ON claims(patient_id);
CREATE INDEX idx_claims_payer_status ON claims(payer_id, claim_status);
CREATE INDEX idx_claims_encounter ON claims(encounter_id);
CREATE INDEX idx_claims_submission_date ON claims(submission_date);
CREATE INDEX idx_claims_emirate ON claims(emirate_code);
Terminology Bindings
| Field |
Terminology |
Example Value |
claim_type |
Local value set |
outpatient |
claim_status |
Local value set aligned to eClaimLink/DOH |
submitted |
emirate_code |
UAE Emirate Codes |
DXB |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
claim_id |
Claim |
Claim.id |
patient_id |
Claim |
Claim.patient.reference (Patient/{id}) |
encounter_id |
Claim |
Claim.encounter.reference |
payer_id |
ClaimResponse |
ClaimResponse.insurer.reference (Organization/{id}) |
claim_type |
Claim |
Claim.type.coding.code |
total_charge_amount |
Claim |
Claim.total.value |
claim_status |
Claim |
Claim.status |
submission_date |
Claim |
Claim.created |
adjudication_date |
ClaimResponse |
ClaimResponse.created |
emirate_code |
Claim |
Claim.extension[emirate].valueCode |
4. claim_lines
Purpose: Individual service lines within a claim, each typically mapped to a single charge and CPT/HCPCS code.
Field Specification
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
line_id |
BIGINT |
NO |
IDENTITY |
PK |
Unique line identifier |
claim_id |
BIGINT |
NO |
— |
FK → claims.claim_id |
Parent claim |
charge_id |
BIGINT |
YES |
— |
FK → charges.charge_id |
Source charge (nullable for non-charge lines) |
line_number |
INTEGER |
NO |
— |
> 0 |
Line sequence within claim |
cpt_code |
VARCHAR(10) |
NO |
— |
CPT |
Procedure code |
modifier_codes |
VARCHAR(50) |
YES |
— |
Comma-separated |
Line modifiers |
icd10_pointer |
VARCHAR(20) |
YES |
— |
e.g. 1,2 |
Pointer to diagnosis sequence |
quantity |
INTEGER |
NO |
1 |
> 0 |
Units |
charge_amount |
NUMERIC(12,2) |
NO |
0.00 |
≥ 0 |
Line charge |
allowed_amount |
NUMERIC(12,2) |
YES |
— |
≥ 0 |
Contracted allowed |
paid_amount |
NUMERIC(12,2) |
YES |
— |
≥ 0 |
Payer paid |
adjustment_amount |
NUMERIC(12,2) |
YES |
— |
≥ 0 |
Adjustments total |
denial_code |
VARCHAR(10) |
YES |
— |
Denial code set |
Primary denial reason |
line_status |
VARCHAR(30) |
NO |
'pending' |
e.g. 'pending','paid','partially_paid','denied','adjusted' |
Line status |
SQL DDL
SQLCREATE TABLE claim_lines (
line_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
claim_id BIGINT NOT NULL,
charge_id BIGINT,
line_number INTEGER NOT NULL,
cpt_code VARCHAR(10) NOT NULL,
modifier_codes VARCHAR(50),
icd10_pointer VARCHAR(20),
quantity INTEGER NOT NULL DEFAULT 1,
charge_amount NUMERIC(12,2) NOT NULL DEFAULT 0.00,
allowed_amount NUMERIC(12,2),
paid_amount NUMERIC(12,2),
adjustment_amount NUMERIC(12,2),
denial_code VARCHAR(10),
line_status VARCHAR(30) NOT NULL DEFAULT 'pending',
CONSTRAINT fk_claim_lines_claim
FOREIGN KEY (claim_id) REFERENCES claims(claim_id)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT fk_claim_lines_charge
FOREIGN KEY (charge_id) REFERENCES charges(charge_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT uq_claim_lines_claim_line
UNIQUE (claim_id, line_number),
CONSTRAINT chk_claim_lines_quantity
CHECK (quantity > 0),
CONSTRAINT chk_claim_lines_amounts
CHECK (
charge_amount >= 0
AND (allowed_amount IS NULL OR allowed_amount >= 0)
AND (paid_amount IS NULL OR paid_amount >= 0)
AND (adjustment_amount IS NULL OR adjustment_amount >= 0)
),
CONSTRAINT chk_claim_lines_status
CHECK (line_status IN ('pending','paid','partially_paid','denied','adjusted'))
);
CREATE INDEX idx_claim_lines_claim ON claim_lines(claim_id);
CREATE INDEX idx_claim_lines_charge ON claim_lines(charge_id);
CREATE INDEX idx_claim_lines_status ON claim_lines(line_status);
CREATE INDEX idx_claim_lines_cpt ON claim_lines(cpt_code);
Terminology Bindings
| Field |
Terminology |
Example Value |
cpt_code |
CPT |
70450 |
modifier_codes |
CPT Modifiers |
26,TC |
icd10_pointer |
ICD-10-AM sequence |
1,3 |
denial_code |
Denial Reason Codes |
CO-50 (from CARC) |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
line_id |
Claim |
Claim.item.id |
claim_id |
Claim |
Claim.id (parent) |
cpt_code |
Claim |
Claim.item.productOrService.coding[system='http://www.ama-assn.org/go/cpt'].code |
modifier_codes |
Claim |
Claim.item.modifier.coding.code |
quantity |
Claim |
Claim.item.quantity.value |
charge_amount |
Claim |
Claim.item.unitPrice.value |
allowed_amount |
ClaimResponse |
ClaimResponse.item.adjudication[category='eligible'].amount.value |
paid_amount |
ClaimResponse |
ClaimResponse.item.adjudication[category='payment'].amount.value |
denial_code |
ClaimResponse |
ClaimResponse.item.adjudication.reason.coding.code |
5. claim_submissions
Purpose: Tracks each submission of a claim to a payer or gateway (DHA eClaimLink, DOH eClaims, others), including batch info and gateway responses.
Field Specification
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
submission_id |
BIGINT |
NO |
IDENTITY |
PK |
Unique submission identifier |
claim_id |
BIGINT |
NO |
— |
FK → claims.claim_id |
Claim being submitted |
submission_channel |
VARCHAR(30) |
NO |
— |
e.g. DHA_ECLAIMLINK,DOH_ECLAIMS,PORTAL,PAPER |
Channel |
submission_datetime |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
When submitted |
batch_id |
VARCHAR(50) |
YES |
— |
— |
Batch/group identifier |
gateway_status |
VARCHAR(30) |
NO |
'pending' |
e.g. 'pending','accepted','rejected' |
Gateway status |
gateway_response |
TEXT |
YES |
— |
— |
Raw response or summary |
response_datetime |
TIMESTAMP |
YES |
— |
— |
When response received |
rejection_reason |
TEXT |
YES |
— |
— |
Reason if rejected |
payer_submission_reference |
VARCHAR(50) |
YES |
— |
— |
eClaimLink/DOH reference |
SQL DDL
SQLCREATE TABLE claim_submissions (
submission_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
claim_id BIGINT NOT NULL,
submission_channel VARCHAR(30) NOT NULL,
submission_datetime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
batch_id VARCHAR(50),
gateway_status VARCHAR(30) NOT NULL DEFAULT 'pending',
gateway_response TEXT,
response_datetime TIMESTAMP,
rejection_reason TEXT,
payer_submission_reference VARCHAR(50),
CONSTRAINT fk_claim_submissions_claim
FOREIGN KEY (claim_id) REFERENCES claims(claim_id)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT chk_claim_submissions_channel
CHECK (submission_channel IN ('DHA_ECLAIMLINK','DOH_ECLAIMS','PORTAL','PAPER','OTHER')),
CONSTRAINT chk_claim_submissions_status
CHECK (gateway_status IN ('pending','accepted','rejected'))
);
CREATE INDEX idx_claim_submissions_claim ON claim_submissions(claim_id);
CREATE INDEX idx_claim_submissions_channel_status
ON claim_submissions(submission_channel, gateway_status);
CREATE INDEX idx_claim_submissions_batch ON claim_submissions(batch_id);
Terminology Bindings
| Field |
Terminology |
Example Value |
submission_channel |
Local value set |
DHA_ECLAIMLINK |
gateway_status |
Local value set |
accepted |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
submission_datetime |
Claim |
Claim.created |
submission_channel |
Claim |
Claim.extension[channel].valueCode |
payer_submission_reference |
Claim |
Claim.identifier[system='eClaimLink'].value |
6. claim_responses
Purpose: Stores payer adjudication responses at the claim level, linked to submissions and remittance advice.
Field Specification
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
response_id |
BIGINT |
NO |
IDENTITY |
PK |
Unique response identifier |
claim_id |
BIGINT |
NO |
— |
FK → claims.claim_id |
Claim |
submission_id |
BIGINT |
YES |
— |
FK → claim_submissions.submission_id |
Related submission |
remittance_id |
BIGINT |
YES |
— |
FK → remittance_advice.remittance_id |
ERA/EOB summary |
response_type |
VARCHAR(20) |
NO |
— |
e.g. 'initial','adjustment','appeal' |
Response type |
response_datetime |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
When adjudicated |
allowed_amount |
NUMERIC(14,2) |
YES |
— |
≥ 0 |
Total allowed |
paid_amount |
NUMERIC(14,2) |
YES |
— |
≥ 0 |
Total paid |
total_adjustment_amount |
NUMERIC(14,2) |
YES |
— |
≥ 0 |
Total adjustments |
adjustment_codes |
VARCHAR(255) |
YES |
— |
CARC/RARC codes |
Comma-separated |
denial_codes |
VARCHAR(255) |
YES |
— |
Denial codes |
Comma-separated |
patient_responsibility |
NUMERIC(14,2) |
YES |
— |
≥ 0 |
Patient share per payer |
SQL DDL
SQLCREATE TABLE claim_responses (
response_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
claim_id BIGINT NOT NULL,
submission_id BIGINT,
remittance_id BIGINT,
response_type VARCHAR(20) NOT NULL,
response_datetime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
allowed_amount NUMERIC(14,2),
paid_amount NUMERIC(14,2),
total_adjustment_amount NUMERIC(14,2),
adjustment_codes VARCHAR(255),
denial_codes VARCHAR(255),
patient_responsibility NUMERIC(14,2),
CONSTRAINT fk_claim_responses_claim
FOREIGN KEY (claim_id) REFERENCES claims(claim_id)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT fk_claim_responses_submission
FOREIGN KEY (submission_id) REFERENCES claim_submissions(submission_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT fk_claim_responses_remittance
FOREIGN KEY (remittance_id) REFERENCES remittance_advice(remittance_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT chk_claim_responses_type
CHECK (response_type IN ('initial','adjustment','appeal')),
CONSTRAINT chk_claim_responses_amounts
CHECK (
(allowed_amount IS NULL OR allowed_amount >= 0) AND
(paid_amount IS NULL OR paid_amount >= 0) AND
(total_adjustment_amount IS NULL OR total_adjustment_amount >= 0) AND
(patient_responsibility IS NULL OR patient_responsibility >= 0)
)
);
CREATE INDEX idx_claim_responses_claim ON claim_responses(claim_id);
CREATE INDEX idx_claim_responses_remittance ON claim_responses(remittance_id);
CREATE INDEX idx_claim_responses_type ON claim_responses(response_type);
Terminology Bindings
| Field |
Terminology |
Example Value |
adjustment_codes |
CARC/RARC |
CO-45,PR-1 |
denial_codes |
Denial Reason Codes |
AUTH_MISSING |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
response_id |
ClaimResponse |
ClaimResponse.id |
claim_id |
ClaimResponse |
ClaimResponse.request.reference (Claim/{id}) |
response_datetime |
ClaimResponse |
ClaimResponse.created |
allowed_amount |
ClaimResponse |
ClaimResponse.total[category='eligible'].amount.value |
paid_amount |
ClaimResponse |
ClaimResponse.payment.amount.value |
patient_responsibility |
ClaimResponse |
ClaimResponse.total[category='patient'].amount.value |
denial_codes |
ClaimResponse |
ClaimResponse.error.code.coding.code |
7. payments
Purpose: Payer payment batches (cheques, EFTs) typically corresponding to remittance advice files.
Field Specification
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
payment_id |
BIGINT |
NO |
IDENTITY |
PK |
Unique payment batch |
payer_id |
BIGINT |
NO |
— |
FK → payers.payer_id |
Paying entity |
remittance_id |
BIGINT |
YES |
— |
FK → remittance_advice.remittance_id |
Related ERA |
payment_date |
DATE |
NO |
— |
— |
Date on cheque/EFT |
payment_amount |
NUMERIC(16,2) |
NO |
0.00 |
≥ 0 |
Total amount |
payment_method |
VARCHAR(20) |
NO |
— |
e.g. 'CHEQUE','EFT','CASH' |
Method |
check_number |
VARCHAR(50) |
YES |
— |
— |
Cheque number |
eft_reference |
VARCHAR(50) |
YES |
— |
— |
Bank/EFT reference |
bank_deposit_date |
DATE |
YES |
— |
— |
Date deposited |
reconciled |
BOOLEAN |
NO |
FALSE |
— |
Bank reconciliation flag |
batch_id |
VARCHAR(50) |
YES |
— |
— |
Internal batch ID |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Creation timestamp |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
Creator |
SQL DDL
SQLCREATE TABLE payments (
payment_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
payer_id BIGINT NOT NULL,
remittance_id BIGINT,
payment_date DATE NOT NULL,
payment_amount NUMERIC(16,2) NOT NULL DEFAULT 0.00,
payment_method VARCHAR(20) NOT NULL,
check_number VARCHAR(50),
eft_reference VARCHAR(50),
bank_deposit_date DATE,
reconciled BOOLEAN NOT NULL DEFAULT FALSE,
batch_id VARCHAR(50),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by BIGINT NOT NULL,
CONSTRAINT fk_payments_payer
FOREIGN KEY (payer_id) REFERENCES payers(payer_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_payments_remittance
FOREIGN KEY (remittance_id) REFERENCES remittance_advice(remittance_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT fk_payments_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT chk_payments_amount
CHECK (payment_amount >= 0),
CONSTRAINT chk_payments_method
CHECK (payment_method IN ('CHEQUE','EFT','CASH','OTHER'))
);
CREATE INDEX idx_payments_payer_date ON payments(payer_id, payment_date);
CREATE INDEX idx_payments_remittance ON payments(remittance_id);
CREATE INDEX idx_payments_reconciled ON payments(reconciled);
Terminology Bindings
| Field |
Terminology |
Example Value |
payment_method |
Payment Method Types |
EFT |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
payment_id |
PaymentNotice |
PaymentNotice.id |
payer_id |
PaymentNotice |
PaymentNotice.payee.reference (Organization/{id}) |
payment_date |
PaymentNotice |
PaymentNotice.paymentDate |
payment_amount |
PaymentNotice |
PaymentNotice.amount.value |
8. payment_allocations
Purpose: Allocation of payer payment amounts to specific claims and claim lines, including adjustments and patient responsibility.
Field Specification
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
allocation_id |
BIGINT |
NO |
IDENTITY |
PK |
Unique allocation |
payment_id |
BIGINT |
NO |
— |
FK → payments.payment_id |
Source payment |
claim_id |
BIGINT |
NO |
— |
FK → claims.claim_id |
Claim |
claim_line_id |
BIGINT |
YES |
— |
FK → claim_lines.line_id |
Line (nullable for header-level) |
allocated_amount |
NUMERIC(14,2) |
NO |
0.00 |
≥ 0 |
Amount applied |
adjustment_amount |
NUMERIC(14,2) |
YES |
— |
≥ 0 |
Adjustment amount |
adjustment_code |
VARCHAR(10) |
YES |
— |
CARC/RARC |
Adjustment reason |
patient_responsibility_amount |
NUMERIC(14,2) |
YES |
— |
≥ 0 |
Patient share |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Creation timestamp |
SQL DDL
SQLCREATE TABLE payment_allocations (
allocation_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
payment_id BIGINT NOT NULL,
claim_id BIGINT NOT NULL,
claim_line_id BIGINT,
allocated_amount NUMERIC(14,2) NOT NULL DEFAULT 0.00,
adjustment_amount NUMERIC(14,2),
adjustment_code VARCHAR(10),
patient_responsibility_amount NUMERIC(14,2),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_payment_allocations_payment
FOREIGN KEY (payment_id) REFERENCES payments(payment_id)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT fk_payment_allocations_claim
FOREIGN KEY (claim_id) REFERENCES claims(claim_id)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT fk_payment_allocations_claim_line
FOREIGN KEY (claim_line_id) REFERENCES claim_lines(line_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT chk_payment_allocations_amounts
CHECK (
allocated_amount >= 0
AND (adjustment_amount IS NULL OR adjustment_amount >= 0)
AND (patient_responsibility_amount IS NULL OR patient_responsibility_amount >= 0)
)
);
CREATE INDEX idx_payment_allocations_payment ON payment_allocations(payment_id);
CREATE INDEX idx_payment_allocations_claim ON payment_allocations(claim_id);
CREATE INDEX idx_payment_allocations_claim_line ON payment_allocations(claim_line_id);
Terminology Bindings
| Field |
Terminology |
Example Value |
adjustment_code |
CARC/RARC |
CO-45 |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
allocated_amount |
ClaimResponse |
ClaimResponse.item.adjudication[category='payment'].amount.value |
adjustment_amount |
ClaimResponse |
ClaimResponse.item.adjudication[category='adjustment'].amount.value |
adjustment_code |
ClaimResponse |
ClaimResponse.item.adjudication.reason.coding.code |
9. patient_invoices
Purpose: Patient-facing billing statements aggregating balances across encounters/claims, including payment plans and delivery details.
Field Specification
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
invoice_id |
BIGINT |
NO |
IDENTITY |
PK |
Unique invoice |
patient_id |
BIGINT |
NO |
— |
FK → patients.patient_id |
Patient |
invoice_date |
DATE |
NO |
— |
— |
Invoice date |
total_amount |
NUMERIC(14,2) |
NO |
0.00 |
≥ 0 |
Total billed |
paid_amount |
NUMERIC(14,2) |
NO |
0.00 |
≥ 0 |
Total paid |
balance |
NUMERIC(14,2) |
NO |
0.00 |
≥ 0 |
Outstanding balance |
due_date |
DATE |
YES |
— |
— |
Payment due date |
statement_number |
VARCHAR(30) |
NO |
— |
UNIQUE per patient |
External statement ID |
delivery_method |
VARCHAR(20) |
YES |
— |
e.g. 'PORTAL','EMAIL','SMS','PRINT' |
Delivery channel |
sent_datetime |
TIMESTAMP |
YES |
— |
— |
When sent |
status |
VARCHAR(30) |
NO |
'open' |
e.g. 'open','paid','partial','cancelled','written_off' |
Status |
is_payment_plan |
BOOLEAN |
NO |
FALSE |
— |
Payment plan flag |
payment_plan_details |
JSONB |
YES |
— |
— |
Installments, schedule |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Creation timestamp |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
Creator |
SQL DDL
SQLCREATE TABLE patient_invoices (
invoice_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
patient_id BIGINT NOT NULL,
invoice_date DATE NOT NULL,
total_amount NUMERIC(14,2) NOT NULL DEFAULT 0.00,
paid_amount NUMERIC(14,2) NOT NULL DEFAULT 0.00,
balance NUMERIC(14,2) NOT NULL DEFAULT 0.00,
due_date DATE,
statement_number VARCHAR(30) NOT NULL,
delivery_method VARCHAR(20),
sent_datetime TIMESTAMP,
status VARCHAR(30) NOT NULL DEFAULT 'open',
is_payment_plan BOOLEAN NOT NULL DEFAULT FALSE,
payment_plan_details JSONB,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by BIGINT NOT NULL,
CONSTRAINT uq_patient_invoices_statement UNIQUE (statement_number),
CONSTRAINT fk_patient_invoices_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_patient_invoices_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT chk_patient_invoices_amounts
CHECK (total_amount >= 0 AND paid_amount >= 0 AND balance >= 0),
CONSTRAINT chk_patient_invoices_status
CHECK (status IN ('open','paid','partial','cancelled','written_off')),
CONSTRAINT chk_patient_invoices_delivery
CHECK (delivery_method IS NULL OR delivery_method IN ('PORTAL','EMAIL','SMS','PRINT'))
);
CREATE INDEX idx_patient_invoices_patient_status
ON patient_invoices(patient_id, status);
CREATE INDEX idx_patient_invoices_due_date
ON patient_invoices(due_date);
Terminology Bindings
| Field |
Terminology |
Example Value |
delivery_method |
Local value set |
PORTAL |
status |
Local value set |
partial |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
invoice_id |
Invoice |
Invoice.id |
patient_id |
Invoice |
Invoice.subject.reference (Patient/{id}) |
invoice_date |
Invoice |
Invoice.date |
total_amount |
Invoice |
Invoice.totalGross.value |
balance |
Invoice |
Invoice.totalNet.value |
status |
Invoice |
Invoice.status |
10. patient_payments
Purpose: Records patient payments (co-pays, self-pay, POS collections) including method, location, and receipt details.
Field Specification
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
payment_id |
BIGINT |
NO |
IDENTITY |
PK |
Unique patient payment |
patient_id |
BIGINT |
NO |
— |
FK → patients.patient_id |
Patient |
invoice_id |
BIGINT |
YES |
— |
FK → patient_invoices.invoice_id |
Related invoice |
encounter_id |
BIGINT |
YES |
— |
FK → encounters.encounter_id |
Encounter (for co-pay) |
payment_amount |
NUMERIC(14,2) |
NO |
0.00 |
≥ 0 |
Amount collected |
payment_method |
VARCHAR(20) |
NO |
— |
Payment Method Types |
Method |
payment_datetime |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
When collected |
receipt_number |
VARCHAR(30) |
NO |
— |
UNIQUE |
Receipt ID |
collected_by |
BIGINT |
NO |
— |
FK → users.user_id |
Cashier/user |
payment_location |
VARCHAR(20) |
NO |
— |
e.g. 'POS','ONLINE','KIOSK' |
Location/channel |
emirate_code |
VARCHAR(3) |
NO |
— |
UAE emirate |
Location emirate |
is_refundable |
BOOLEAN |
NO |
TRUE |
— |
Whether refund allowed |
SQL DDL
SQLCREATE TABLE patient_payments (
payment_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
patient_id BIGINT NOT NULL,
invoice_id BIGINT,
encounter_id BIGINT,
payment_amount NUMERIC(14,2) NOT NULL DEFAULT 0.00,
payment_method VARCHAR(20) NOT NULL,
payment_datetime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
receipt_number VARCHAR(30) NOT NULL,
collected_by BIGINT NOT NULL,
payment_location VARCHAR(20) NOT NULL,
emirate_code VARCHAR(3) NOT NULL,
is_refundable BOOLEAN NOT NULL DEFAULT TRUE,
CONSTRAINT uq_patient_payments_receipt UNIQUE (receipt_number),
CONSTRAINT fk_patient_payments_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_patient_payments_invoice
FOREIGN KEY (invoice_id) REFERENCES patient_invoices(invoice_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT fk_patient_payments_encounter
FOREIGN KEY (encounter_id) REFERENCES encounters(encounter_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT fk_patient_payments_collected_by
FOREIGN KEY (collected_by) REFERENCES users(user_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT chk_patient_payments_amount
CHECK (payment_amount >= 0),
CONSTRAINT chk_patient_payments_emirate
CHECK (emirate_code IN ('DXB','AUH','SHJ','AJM','RAK','UAQ','FJR'))
);
CREATE INDEX idx_patient_payments_patient_date
ON patient_payments(patient_id, payment_datetime);
CREATE INDEX idx_patient_payments_location
ON patient_payments(payment_location);
CREATE INDEX idx_patient_payments_encounter
ON patient_payments(encounter_id);
Terminology Bindings
| Field |
Terminology |
Example Value |
payment_method |
Payment Method Types |
CREDIT_CARD |
payment_location |
Local value set |
POS |
emirate_code |
UAE Emirate Codes |
DXB |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
payment_id |
PaymentNotice |
PaymentNotice.id |
patient_id |
PaymentNotice |
PaymentNotice.recipient.reference (Patient/{id}) |
payment_amount |
PaymentNotice |
PaymentNotice.amount.value |
payment_datetime |
PaymentNotice |
PaymentNotice.paymentDate |
invoice_id |
Invoice |
Invoice.paymentDetail.identifier (link) |
11. refunds
Purpose: Tracks refunds issued to patients or payers, including linkage to original payment and approval workflow.
Field Specification
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
refund_id |
BIGINT |
NO |
IDENTITY |
PK |
Unique refund |
patient_id |
BIGINT |
YES |
— |
FK → patients.patient_id |
Refunded patient (if patient refund) |
payer_id |
BIGINT |
YES |
— |
FK → payers.payer_id |
Refunded payer (if payer refund) |
original_payment_id |
BIGINT |
NO |
— |
FK → payments.payment_id OR patient_payments.payment_id (see note) |
Original payment reference (payer or patient) |
refund_amount |
NUMERIC(14,2) |
NO |
0.00 |
≥ 0 |
Amount refunded |
refund_reason |
VARCHAR(255) |
NO |
— |
— |
Reason |
refund_method |
VARCHAR(20) |
NO |
— |
Payment Method Types |
Method |
approved_by |
BIGINT |
NO |
— |
FK → users.user_id |
Approver |
refund_datetime |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
When processed |
status |
VARCHAR(30) |
NO |
'pending' |
e.g. 'pending','approved','processed','cancelled' |
Status |
Implementation note: original_payment_id may reference either payer payments or patient_payments. This can be implemented via two nullable fields (payer_payment_id, patient_payment_id) if strict FK enforcement is required; for brevity we keep a single FK to payments here and store patient refunds via a separate mapping table if needed.
SQL DDL
SQLCREATE TABLE refunds (
refund_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
patient_id BIGINT,
payer_id BIGINT,
original_payment_id BIGINT NOT NULL,
refund_amount NUMERIC(14,2) NOT NULL DEFAULT 0.00,
refund_reason VARCHAR(255) NOT NULL,
refund_method VARCHAR(20) NOT NULL,
approved_by BIGINT NOT NULL,
refund_datetime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
status VARCHAR(30) NOT NULL DEFAULT 'pending',
CONSTRAINT fk_refunds_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT fk_refunds_payer
FOREIGN KEY (payer_id) REFERENCES payers(payer_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT fk_refunds_original_payment
FOREIGN KEY (original_payment_id) REFERENCES payments(payment_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_refunds_approved_by
FOREIGN KEY (approved_by) REFERENCES users(user_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT chk_refunds_amount
CHECK (refund_amount >= 0),
CONSTRAINT chk_refunds_status
CHECK (status IN ('pending','approved','processed','cancelled'))
);
CREATE INDEX idx_refunds_patient ON refunds(patient_id);
CREATE INDEX idx_refunds_payer ON refunds(payer_id);
CREATE INDEX idx_refunds_status ON refunds(status);
Terminology Bindings
| Field |
Terminology |
Example Value |
refund_method |
Payment Method Types |
BANK_TRANSFER |
status |
Local value set |
approved |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
refund_id |
PaymentReconciliation |
PaymentReconciliation.id |
refund_amount |
PaymentReconciliation |
PaymentReconciliation.amount.value |
refund_datetime |
PaymentReconciliation |
PaymentReconciliation.created |
12. ar_aging
Purpose: Accounts receivable aging snapshots by claim, patient, payer, and emirate, used for AR dashboards and KPIs.
Field Specification
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
aging_id |
BIGINT |
NO |
IDENTITY |
PK |
Unique snapshot row |
snapshot_date |
DATE |
NO |
— |
— |
Date of AR snapshot |
payer_id |
BIGINT |
YES |
— |
FK → payers.payer_id |
Payer (nullable for self-pay) |
patient_id |
BIGINT |
YES |
— |
FK → patients.patient_id |
Patient |
claim_id |
BIGINT |
YES |
— |
FK → claims.claim_id |
Claim |
total_outstanding |
NUMERIC(14,2) |
NO |
0.00 |
≥ 0 |
Total outstanding |
current_0_30 |
NUMERIC(14,2) |
NO |
0.00 |
≥ 0 |
0–30 days |
days_31_60 |
NUMERIC(14,2) |
NO |
0.00 |
≥ 0 |
31–60 days |
days_61_90 |
NUMERIC(14,2) |
NO |
0.00 |
≥ 0 |
61–90 days |
days_91_120 |
NUMERIC(14,2) |
NO |
0.00 |
≥ 0 |
91–120 days |
days_over_120 |
NUMERIC(14,2) |
NO |
0.00 |
≥ 0 |
>120 days |
emirate_code |
VARCHAR(3) |
NO |
— |
UAE emirate |
Emirate for reporting |
SQL DDL
SQLCREATE TABLE ar_aging (
aging_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
snapshot_date DATE NOT NULL,
payer_id BIGINT,
patient_id BIGINT,
claim_id BIGINT,
total_outstanding NUMERIC(14,2) NOT NULL DEFAULT 0.00,
current_0_30 NUMERIC(14,2) NOT NULL DEFAULT 0.00,
days_31_60 NUMERIC(14,2) NOT NULL DEFAULT 0.00,
days_61_90 NUMERIC(14,2) NOT NULL DEFAULT 0.00,
days_91_120 NUMERIC(14,2) NOT NULL DEFAULT 0.00,
days_over_120 NUMERIC(14,2) NOT NULL DEFAULT 0.00,
emirate_code VARCHAR(3) NOT NULL,
CONSTRAINT fk_ar_aging_payer
FOREIGN KEY (payer_id) REFERENCES payers(payer_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT fk_ar_aging_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT fk_ar_aging_claim
FOREIGN KEY (claim_id) REFERENCES claims(claim_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT chk_ar_aging_amounts
CHECK (
total_outstanding >= 0 AND
current_0_30 >= 0 AND
days_31_60 >= 0 AND
days_61_90 >= 0 AND
days_91_120 >= 0 AND
days_over_120 >= 0
),
CONSTRAINT chk_ar_aging_emirate
CHECK (emirate_code IN ('DXB','AUH','SHJ','AJM','RAK','UAQ','FJR'))
);
CREATE INDEX idx_ar_aging_snapshot_payer
ON ar_aging(snapshot_date, payer_id);
CREATE INDEX idx_ar_aging_snapshot_emirate
ON ar_aging(snapshot_date, emirate_code);
Terminology Bindings
| Field |
Terminology |
Example Value |
emirate_code |
UAE Emirate Codes |
AUH |
FHIR Resource Mapping
AR aging is primarily financial reporting and does not map directly to a core FHIR resource; it can be exposed via a custom MeasureReport if needed.
13. charge_capture_rules
Purpose: Rules for automated charge capture from clinical events, including CPT mapping, modifier logic, and bundling/unbundling rules.
Field Specification
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
rule_id |
BIGINT |
NO |
IDENTITY |
PK |
Unique rule |
source_module |
VARCHAR(50) |
NO |
— |
— |
e.g. cpoe,ris,lis,pis |
source_event_type |
VARCHAR(50) |
NO |
— |
— |
e.g. lab_result_finalized |
cpt_code |
VARCHAR(10) |
YES |
— |
CPT |
Target CPT |
modifier_logic |
VARCHAR(255) |
YES |
— |
— |
Human-readable modifier rules |
bundling_rules |
JSONB |
YES |
— |
— |
Machine-readable bundling/unbundling logic |
effective_date |
DATE |
NO |
— |
— |
Start date |
expiry_date |
DATE |
YES |
— |
— |
End date |
is_active |
BOOLEAN |
NO |
TRUE |
— |
Active flag |
SQL DDL
SQLCREATE TABLE charge_capture_rules (
rule_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
source_module VARCHAR(50) NOT NULL,
source_event_type VARCHAR(50) NOT NULL,
cpt_code VARCHAR(10),
modifier_logic VARCHAR(255),
bundling_rules JSONB,
effective_date DATE NOT NULL,
expiry_date DATE,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
CONSTRAINT chk_charge_capture_rules_dates
CHECK (expiry_date IS NULL OR expiry_date >= effective_date)
);
CREATE INDEX idx_charge_capture_rules_source
ON charge_capture_rules(source_module, source_event_type)
WHERE is_active = TRUE;
CREATE INDEX idx_charge_capture_rules_cpt
ON charge_capture_rules(cpt_code);
Terminology Bindings
| Field |
Terminology |
Example Value |
cpt_code |
CPT |
71045 |
FHIR Resource Mapping
Rules are configuration and do not map directly to FHIR; they influence how ChargeItem and Claim resources are generated.
14. claim_edits
Purpose: Claim edit rules used during scrubbing (CCI edits, payer-specific rules, facility rules). Determines whether a claim is clean or requires correction.
Field Specification
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
edit_id |
BIGINT |
NO |
IDENTITY |
PK |
Unique edit rule |
edit_type |
VARCHAR(30) |
NO |
— |
e.g. 'CCI','PAYER','FACILITY' |
Type |
edit_name |
VARCHAR(100) |
NO |
— |
— |
Human-readable name |
rule_logic_json |
JSONB |
NO |
— |
— |
Machine-readable rule logic |
severity |
VARCHAR(10) |
NO |
— |
e.g. 'error','warning' |
Severity |
action |
VARCHAR(20) |
NO |
— |
e.g. 'reject','pend','informational' |
System action |
payer_id |
BIGINT |
YES |
— |
FK → payers.payer_id |
Payer-specific (nullable) |
payer_specific |
BOOLEAN |
NO |
FALSE |
— |
Flag |
is_active |
BOOLEAN |
NO |
TRUE |
— |
Active flag |
SQL DDL
SQLCREATE TABLE claim_edits (
edit_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_type VARCHAR(30) NOT NULL,
edit_name VARCHAR(100) NOT NULL,
rule_logic_json JSONB NOT NULL,
severity VARCHAR(10) NOT NULL,
action VARCHAR(20) NOT NULL,
payer_id BIGINT,
payer_specific BOOLEAN NOT NULL DEFAULT FALSE,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
CONSTRAINT fk_claim_edits_payer
FOREIGN KEY (payer_id) REFERENCES payers(payer_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT chk_claim_edits_severity
CHECK (severity IN ('error','warning')),
CONSTRAINT chk_claim_edits_action
CHECK (action IN ('reject','pend','informational')),
CONSTRAINT chk_claim_edits_payer_flag
CHECK ((payer_specific = TRUE AND payer_id IS NOT NULL)
OR (payer_specific = FALSE))
);
CREATE INDEX idx_claim_edits_type_active
ON claim_edits(edit_type)
WHERE is_active = TRUE;
CREATE INDEX idx_claim_edits_payer
ON claim_edits(payer_id)
WHERE payer_specific = TRUE AND is_active = TRUE;
Terminology Bindings
| Field |
Terminology |
Example Value |
edit_type |
Local value set |
CCI |
severity |
Local value set |
error |
FHIR Resource Mapping
Claim edits are validation rules and do not map directly to FHIR; they govern validation of Claim resources before submission.
15. remittance_advice
Purpose: Electronic remittance advice (ERA) records summarizing payments from payers, typically imported from DHA eClaimLink/DOH eClaims or other ERA formats.
Field Specification
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
remittance_id |
BIGINT |
NO |
IDENTITY |
PK |
Unique ERA |
payer_id |
BIGINT |
NO |
— |
FK → payers.payer_id |
Payer |
remittance_date |
DATE |
NO |
— |
— |
Date on ERA |
total_paid |
NUMERIC(16,2) |
NO |
0.00 |
≥ 0 |
Total paid |
total_claims |
INTEGER |
NO |
0 |
≥ 0 |
Number of claims |
file_reference |
VARCHAR(100) |
YES |
— |
— |
File name/ID |
import_datetime |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
When imported |
status |
VARCHAR(30) |
NO |
'imported' |
e.g. 'imported','processed','error' |
Processing status |
bank_account_reference |
VARCHAR(50) |
YES |
— |
— |
Bank account identifier |
SQL DDL
SQLCREATE TABLE remittance_advice (
remittance_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
payer_id BIGINT NOT NULL,
remittance_date DATE NOT NULL,
total_paid NUMERIC(16,2) NOT NULL DEFAULT 0.00,
total_claims INTEGER NOT NULL DEFAULT 0,
file_reference VARCHAR(100),
import_datetime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
status VARCHAR(30) NOT NULL DEFAULT 'imported',
bank_account_reference VARCHAR(50),
CONSTRAINT fk_remittance_advice_payer
FOREIGN KEY (payer_id) REFERENCES payers(payer_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT chk_remittance_advice_amounts
CHECK (total_paid >= 0 AND total_claims >= 0),
CONSTRAINT chk_remittance_advice_status
CHECK (status IN ('imported','processed','error'))
);
CREATE INDEX idx_remittance_advice_payer_date
ON remittance_advice(payer_id, remittance_date);
CREATE INDEX idx_remittance_advice_status
ON remittance_advice(status);
Terminology Bindings
| Field |
Terminology |
Example Value |
status |
Local value set |
processed |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
remittance_id |
PaymentReconciliation |
PaymentReconciliation.id |
payer_id |
PaymentReconciliation |
PaymentReconciliation.requestor.reference (Organization/{id}) |
remittance_date |
PaymentReconciliation |
PaymentReconciliation.period.end |
total_paid |
PaymentReconciliation |
PaymentReconciliation.total.value |
Data Volume Estimates
Approximate volumes for a 300-bed UAE hospital with high outpatient throughput.
| Table |
Initial Rows (Year 1) |
Annual Growth |
Notes |
charges |
5–7 million |
+10–15% |
Multiple charges per encounter; high volume from labs/imaging/pharmacy |
charge_details |
5–7 million |
+10–15% |
Typically 1:1 with charges |
claims |
800k–1.2 million |
+10% |
One per encounter per payer; includes resubmissions as same claim_id |
claim_lines |
4–6 million |
+10–15% |
3–5 lines per claim on average |
claim_submissions |
1–1.5 million |
+10% |
Multiple submissions per claim possible |
claim_responses |
1–1.5 million |
+10% |
Initial + adjustment/appeal responses |
remittance_advice |
20k–40k |
+10% |
One per ERA file/batch |
payments |
150k–250k |
+10% |
Payer payment batches |
payment_allocations |
3–5 million |
+10–15% |
Allocations per claim/line |
patient_invoices |
400k–600k |
+10% |
Statements per patient episode |
patient_payments |
600k–900k |
+10% |
POS + online payments |
refunds |
5k–15k |
+5–10% |
Relatively low volume |
ar_aging |
3–5 million |
Depends on snapshot frequency |
Daily snapshots × active claims |
charge_capture_rules |
5k–10k |
Low |
Updated with coding changes |
claim_edits |
5k–7k |
Low–medium |
Grows with payer rules |
Capacity planning should consider partitioning large tables (charges, claim_lines, payment_allocations, ar_aging) by year or month and indexing for common filters (date, payer, emirate, status).
Data Retention Policy (UAE Context)
Retention must comply with UAE federal and emirate-level regulations (MOH, DOH, DHA) and UAE PDPL for personal data. Financial and clinical billing records are typically retained for at least 10 years from last encounter or financial activity, unless stricter internal policies apply.
| Table |
Recommended Retention |
Rationale / Notes |
charges |
≥ 10 years from service_date |
Supports audits, re-billing, and medico-legal reviews. Archive (read-only) after 7 years. |
charge_details |
Same as charges |
Needed to interpret historical charges and cost structures. |
claims |
≥ 10 years from adjudication_date or last activity |
Required for payer audits, disputes, and regulatory inspections (DHA/DOH). |
claim_lines |
Same as claims |
Line-level detail required for coding audits and denial appeals. |
claim_submissions |
≥ 7 years |
Evidence of timely filing and submission history. |
claim_responses |
≥ 10 years |
Proof of payer adjudication, used in disputes and appeals. |
remittance_advice |
≥ 10 years |
Financial reconciliation and audit trail. |
payments |
≥ 10 years from payment_date |
Financial records; align with UAE commercial and tax record retention. |
payment_allocations |
≥ 10 years |
Required to reconstruct claim-level payment history. |
patient_invoices |
≥ 10 years from invoice_date |
Patient billing history; supports complaints and legal inquiries. |
patient_payments |
≥ 10 years |
Financial and PDPL-compliant audit trail of patient transactions. |
refunds |
≥ 10 years |
Financial and compliance evidence for refunds. |
ar_aging |
5–7 years |
Reporting snapshots; older data can be aggregated or anonymised. |
charge_capture_rules |
Active + 5 years after deactivation |
Needed to interpret historical billing behaviour; store inactive rules in archive. |
claim_edits |
Active + 5 years after deactivation |
For explaining historical claim scrubbing decisions. |
PDPL considerations:
- All tables contain personal data (direct or indirect) and must support:
- Data subject access requests (export of patient financial history).
- Rectification (corrections via new transactions; original records preserved for audit).
- Restriction of processing where legally required.
- When retention periods expire, data should be:
- Irreversibly anonymised for statistical use, or
- Securely deleted, ensuring referential integrity and audit requirements are not breached.
- Cross-border transfers (e.g., cloud hosting) must comply with UAE PDPL and any DOH/DHA data residency requirements; billing data linked to clinical encounters may be subject to Malaffi/NABIDH policies when exchanged.