medicafe 0.250711.1__py3-none-any.whl → 0.250720.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of medicafe might be problematic. Click here for more details.

@@ -0,0 +1,832 @@
1
+ # MediLink_837p_cob_library.py
2
+ """
3
+ 837P Coordination of Benefits (COB) Enhancement Library
4
+
5
+ This module provides enhanced 837P generation capabilities for:
6
+ - Medicare as primary payer (SBR09 = MB/MA)
7
+ - Secondary payer claims with embedded COB adjudication
8
+ - Optional attachment references (PWK)
9
+ - 835-derived adjudication embedding
10
+
11
+ All functions are currently implemented as placeholder comments to preserve
12
+ existing production functionality while providing clear implementation guidance.
13
+
14
+ IMPLEMENTATION STRATEGY:
15
+ 1. All functions are commented placeholders to avoid disrupting production code
16
+ 2. Detailed implementation guidance is provided in comments
17
+ 3. Integration points are marked with TODO comments in the main encoder
18
+ 4. Validation logic is included for claim integrity and compliance
19
+ 5. Medicare-specific payer types (MB/MA) are properly handled
20
+ 6. 835 data extraction and validation is supported
21
+ 7. COB loops (2320, 2330B, 2330C, 2430) are fully specified
22
+
23
+ Key Implementation Notes:
24
+ - Medicare Part B uses SBR09 = "MB"
25
+ - Medicare Advantage uses SBR09 = "MA"
26
+ - Secondary claims require SBR01 = "S" and proper COB loops
27
+ - 835 data integration requires validation of remittance dates and amounts
28
+ - SNIP validation level 3+ recommended for COB claims
29
+ - Total paid validation: AMT*D in Loop 2320 must match sum of SVD02 values
30
+ - CLM05-3 must be "1" (original) for secondary claims unless replacement logic applies
31
+
32
+ SEGMENT ORDER CONSTRAINTS:
33
+ - 1000A: Submitter
34
+ - 1000B: Receiver
35
+ - 2000A–2010AA: Billing provider
36
+ - 2000B–2010BA/BB: Subscriber and payer
37
+ - 2300: Claim
38
+ - 2320: Other subscriber info (COB)
39
+ - 2330B: Prior payer
40
+ - 2400: Service line
41
+ - 2430: Line-level COB (if applicable)
42
+
43
+ VALIDATION REQUIREMENTS:
44
+ - SBR09 required if SBR01 = "S"
45
+ - NM1*PR in 2330B must match Medicare Payer ID when applicable
46
+ - CLM segment must reflect consistent claim frequency and COB type
47
+ - 835-derived data must contain remittance date and paid amount per service
48
+ - Multi-payer COB handling must be configured appropriately
49
+ """
50
+
51
+ from datetime import datetime
52
+ import sys, os
53
+ from MediLink import MediLink_ConfigLoader
54
+
55
+ project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
56
+ if project_dir not in sys.path:
57
+ sys.path.append(project_dir)
58
+
59
+ # Import utility functions from the utilities module
60
+ try:
61
+ from .MediLink_837p_utilities import convert_date_format
62
+ except ImportError as e:
63
+ # Fallback implementation for convert_date_format if utilities module is not available
64
+ MediLink_ConfigLoader.log("Warning: Could not import utilities functions: {}".format(e), level="WARNING")
65
+ def convert_date_format(date_str):
66
+ """Fallback date format conversion function"""
67
+ try:
68
+ # Parse the input date string into a datetime object
69
+ input_format = "%m-%d-%Y" if len(date_str) == 10 else "%m-%d-%y"
70
+ date_obj = datetime.strptime(date_str, input_format)
71
+ # Format the datetime object into the desired output format
72
+ return date_obj.strftime("%Y%m%d")
73
+ except (ValueError, TypeError):
74
+ # Return original string if conversion fails
75
+ return date_str
76
+
77
+ def create_2320_other_subscriber_segments(patient_data, config, crosswalk):
78
+ """
79
+ Creates Loop 2320 segments for other subscriber information in COB scenarios.
80
+
81
+ Required segments:
82
+ - SBR: Secondary payer relationship and responsibility
83
+ - AMT: AMT*D – Total amount paid by Medicare or other primary
84
+ - CAS: Patient liability (CO/PR adjustments)
85
+ - OI: Other insurance coverage indicator
86
+
87
+ Parameters:
88
+ - patient_data: Dictionary containing patient and COB information
89
+ - config: Configuration settings
90
+ - crosswalk: Crosswalk data for payer mapping
91
+
92
+ Returns:
93
+ - List of 2320 loop segments
94
+ """
95
+ segments = []
96
+
97
+ # Determine if this is a secondary claim
98
+ is_secondary = patient_data.get('claim_type', 'primary') == 'secondary'
99
+
100
+ if is_secondary:
101
+ # SBR segment for secondary payer
102
+ responsibility_code = "S" # Secondary
103
+ insurance_type = determine_medicare_payer_type(patient_data, config)
104
+ if not insurance_type:
105
+ insurance_type = patient_data.get('insurance_type', '18') # Default to Medicare
106
+
107
+ sbr_segment = "SBR*{}*18*******{}~".format(responsibility_code, insurance_type)
108
+ segments.append(sbr_segment)
109
+
110
+ # AMT*D segment for total amount paid by primary
111
+ total_paid = patient_data.get('primary_paid_amount', '0.00')
112
+ amt_segment = "AMT*D*{}~".format(total_paid)
113
+ segments.append(amt_segment)
114
+
115
+ # CAS segments for patient liability adjustments
116
+ cas_adjustments = patient_data.get('cas_adjustments', [])
117
+ for adjustment in cas_adjustments:
118
+ cas_segment = "CAS*{}*{}*{}~".format(
119
+ adjustment.get('group', 'CO'),
120
+ adjustment.get('reason', ''),
121
+ adjustment.get('amount', '0.00')
122
+ )
123
+ segments.append(cas_segment)
124
+
125
+ # OI segment for other insurance coverage
126
+ oi_segment = "OI***Y***Y~"
127
+ segments.append(oi_segment)
128
+
129
+ return segments
130
+
131
+ def create_2330B_prior_payer_segments(patient_data, config, crosswalk):
132
+ """
133
+ Creates Loop 2330B segments for prior payer information.
134
+
135
+ Required segments:
136
+ - NM1: Prior payer name and ID (e.g., Medicare = 00850)
137
+ - N3: (optional) Address line 1 and 2
138
+ - N4: (optional) City/State/ZIP
139
+ - REF: (optional) Reference ID (e.g., prior auth)
140
+
141
+ Parameters:
142
+ - patient_data: Dictionary containing prior payer information
143
+ - config: Configuration settings
144
+ - crosswalk: Crosswalk data for payer mapping
145
+
146
+ Returns:
147
+ - List of 2330B loop segments
148
+ """
149
+ segments = []
150
+
151
+ # Get prior payer information
152
+ prior_payer_name = patient_data.get('prior_payer_name', 'MEDICARE')
153
+ prior_payer_id = patient_data.get('prior_payer_id', '00850')
154
+
155
+ # NM1 segment for prior payer
156
+ nm1_segment = "NM1*PR*2*{}*****PI*{}~".format(prior_payer_name, prior_payer_id)
157
+ segments.append(nm1_segment)
158
+
159
+ # Optional N3 segment for address
160
+ if patient_data.get('prior_payer_address'):
161
+ n3_segment = "N3*{}*{}~".format(
162
+ patient_data.get('prior_payer_address', ''),
163
+ patient_data.get('prior_payer_address2', '')
164
+ )
165
+ segments.append(n3_segment)
166
+
167
+ # Optional N4 segment for city/state/zip
168
+ if patient_data.get('prior_payer_city'):
169
+ n4_segment = "N4*{}*{}*{}~".format(
170
+ patient_data.get('prior_payer_city', ''),
171
+ patient_data.get('prior_payer_state', ''),
172
+ patient_data.get('prior_payer_zip', '')
173
+ )
174
+ segments.append(n4_segment)
175
+
176
+ # Optional REF segment for reference ID
177
+ if patient_data.get('prior_auth_number'):
178
+ ref_segment = "REF*G1*{}~".format(patient_data.get('prior_auth_number'))
179
+ segments.append(ref_segment)
180
+
181
+ return segments
182
+
183
+ def create_2430_service_line_cob_segments(patient_data, config, crosswalk):
184
+ """
185
+ Creates Loop 2430 segments for service line COB information.
186
+
187
+ Required segments (when service-level adjudication exists):
188
+ - SVD: Paid amount per service line (from 835 SVC03)
189
+ - CAS: Line-level adjustments (from 835 CAS segments)
190
+ - DTP: DTP*573 – Adjudication date per service
191
+
192
+ Parameters:
193
+ - patient_data: Dictionary containing service line and adjudication data
194
+ - config: Configuration settings
195
+ - crosswalk: Crosswalk data for service mapping
196
+
197
+ Returns:
198
+ - List of 2430 loop segments
199
+ """
200
+ segments = []
201
+
202
+ # Get service line adjudication data
203
+ service_adjudications = patient_data.get('service_adjudications', [])
204
+
205
+ for service in service_adjudications:
206
+ # SVD segment for service line paid amount
207
+ svd_segment = "SVD*{}*{}*{}*{}~".format(
208
+ service.get('payer_id', '00850'),
209
+ service.get('paid_amount', '0.00'),
210
+ service.get('revenue_code', ''),
211
+ service.get('units', '1')
212
+ )
213
+ segments.append(svd_segment)
214
+
215
+ # CAS segments for line-level adjustments
216
+ line_adjustments = service.get('adjustments', [])
217
+ for adjustment in line_adjustments:
218
+ cas_segment = "CAS*{}*{}*{}~".format(
219
+ adjustment.get('group', 'CO'),
220
+ adjustment.get('reason', ''),
221
+ adjustment.get('amount', '0.00')
222
+ )
223
+ segments.append(cas_segment)
224
+
225
+ # DTP*573 segment for adjudication date
226
+ if service.get('adjudication_date'):
227
+ dtp_segment = "DTP*573*D8*{}~".format(
228
+ convert_date_format(service.get('adjudication_date'))
229
+ )
230
+ segments.append(dtp_segment)
231
+
232
+ return segments
233
+
234
+ def create_2330C_other_subscriber_name_segments(patient_data, config, crosswalk):
235
+ """
236
+ Creates Loop 2330C segments when patient is not the subscriber.
237
+
238
+ Required segments:
239
+ - NM1*IL: Other Subscriber Name
240
+ - N3/N4: (optional) Address
241
+ - DMG: (optional) Date of birth / gender
242
+
243
+ Parameters:
244
+ - patient_data: Dictionary containing other subscriber information
245
+ - config: Configuration settings
246
+ - crosswalk: Crosswalk data for subscriber mapping
247
+
248
+ Returns:
249
+ - List of 2330C loop segments
250
+ """
251
+ segments = []
252
+
253
+ # Check if patient is different from subscriber
254
+ if patient_data.get('patient_is_subscriber', True) == False:
255
+ # NM1*IL segment for other subscriber
256
+ nm1_segment = "NM1*IL*1*{}*{}*{}***MI*{}~".format(
257
+ patient_data.get('subscriber_last_name', ''),
258
+ patient_data.get('subscriber_first_name', ''),
259
+ patient_data.get('subscriber_middle_name', ''),
260
+ patient_data.get('subscriber_policy_number', '')
261
+ )
262
+ segments.append(nm1_segment)
263
+
264
+ # Optional N3 segment for address
265
+ if patient_data.get('subscriber_address'):
266
+ n3_segment = "N3*{}*{}~".format(
267
+ patient_data.get('subscriber_address', ''),
268
+ patient_data.get('subscriber_address2', '')
269
+ )
270
+ segments.append(n3_segment)
271
+
272
+ # Optional N4 segment for city/state/zip
273
+ if patient_data.get('subscriber_city'):
274
+ n4_segment = "N4*{}*{}*{}~".format(
275
+ patient_data.get('subscriber_city', ''),
276
+ patient_data.get('subscriber_state', ''),
277
+ patient_data.get('subscriber_zip', '')
278
+ )
279
+ segments.append(n4_segment)
280
+
281
+ # Optional DMG segment for date of birth/gender
282
+ if patient_data.get('subscriber_dob'):
283
+ dmg_segment = "DMG*D8*{}*{}~".format(
284
+ convert_date_format(patient_data.get('subscriber_dob')),
285
+ patient_data.get('subscriber_gender', '')
286
+ )
287
+ segments.append(dmg_segment)
288
+
289
+ return segments
290
+
291
+ def create_pwk_attachment_segment(patient_data, config):
292
+ """
293
+ Creates PWK segment for attachment references (non-electronic EOB handling).
294
+
295
+ Example: PWK*EB*FX*123456~
296
+
297
+ Parameters:
298
+ - patient_data: Dictionary containing attachment information
299
+ - config: Configuration settings
300
+
301
+ Returns:
302
+ - PWK segment string or None if not required
303
+ """
304
+ # Check if attachment is required
305
+ if patient_data.get('requires_attachment', False):
306
+ report_type = patient_data.get('attachment_report_type', 'EB') # EB = Explanation of Benefits
307
+ transmission_code = patient_data.get('attachment_transmission_code', 'FX') # FX = Fax
308
+ control_number = patient_data.get('attachment_control_number', '')
309
+
310
+ if control_number:
311
+ pwk_segment = "PWK*{}*{}*{}~".format(report_type, transmission_code, control_number)
312
+ return pwk_segment
313
+
314
+ return None
315
+
316
+ def validate_cob_claim_integrity(patient_data, config):
317
+ """
318
+ Validates COB claim integrity and compliance.
319
+
320
+ Validation checks:
321
+ - SBR09 required if SBR01 = "S"
322
+ - NM1*PR in 2330B must match Medicare Payer ID when applicable
323
+ - CLM segment must reflect consistent claim frequency and COB type
324
+ - Total paid validation: AMT*D in Loop 2320 matches sum of SVD02 values
325
+
326
+ Parameters:
327
+ - patient_data: Dictionary containing claim data
328
+ - config: Configuration settings
329
+
330
+ Returns:
331
+ - Tuple of (is_valid, error_messages)
332
+ """
333
+ errors = []
334
+
335
+ # Validate SBR09 for secondary claims
336
+ if patient_data.get('claim_type') == 'secondary':
337
+ insurance_type = determine_medicare_payer_type(patient_data, config)
338
+ if not insurance_type:
339
+ errors.append("SBR09 required when SBR01 = S (secondary claim)")
340
+
341
+ # Validate Medicare Payer ID
342
+ if patient_data.get('prior_payer_id') == '00850': # Medicare
343
+ if patient_data.get('prior_payer_name') != 'MEDICARE':
344
+ errors.append("NM1*PR must match Medicare Payer ID 00850")
345
+
346
+ # Validate claim frequency for COB claims
347
+ if patient_data.get('claim_type') == 'secondary':
348
+ claim_frequency = patient_data.get('claim_frequency', '1')
349
+ if claim_frequency != '1':
350
+ errors.append("CLM05-3 must be '1' for original COB claims")
351
+
352
+ # Validate total paid amount
353
+ amt_d_total = float(patient_data.get('primary_paid_amount', '0.00'))
354
+ svd_amounts = [float(s.get('paid_amount', '0.00')) for s in patient_data.get('service_adjudications', [])]
355
+ if abs(amt_d_total - sum(svd_amounts)) > 0.01: # Allow for rounding differences
356
+ errors.append("AMT*D total must match sum of SVD02 amounts")
357
+
358
+ return len(errors) == 0, errors
359
+
360
+ def extract_835_adjudication_data(patient_data, config):
361
+ """
362
+ Extracts and validates 835-derived adjudication data for COB embedding.
363
+
364
+ Extracts:
365
+ - CLP02 → AMT*D (2320)
366
+ - CAS segments → CAS (2320 and/or 2430)
367
+ - SVC03 → SVD02 (2430)
368
+ - DTP segments → DTP*573 (2430)
369
+
370
+ Parameters:
371
+ - patient_data: Dictionary containing 835 data
372
+ - config: Configuration settings
373
+
374
+ Returns:
375
+ - Dictionary of extracted adjudication data or None if invalid
376
+ """
377
+ adjudication_data = {}
378
+
379
+ # Extract total paid amount from CLP02
380
+ adjudication_data['total_paid'] = patient_data.get('clp02_amount', '0.00')
381
+
382
+ # Extract CAS adjustments
383
+ cas_adjustments = patient_data.get('cas_segments', [])
384
+ adjudication_data['cas_adjustments'] = []
385
+ for cas in cas_adjustments:
386
+ adjustment = {
387
+ 'group': cas.get('group', 'CO'),
388
+ 'reason': cas.get('reason', ''),
389
+ 'amount': cas.get('amount', '0.00')
390
+ }
391
+ adjudication_data['cas_adjustments'].append(adjustment)
392
+
393
+ # Extract service line paid amounts from SVC03
394
+ service_adjudications = patient_data.get('svc_segments', [])
395
+ adjudication_data['service_paid_amounts'] = []
396
+ for svc in service_adjudications:
397
+ service = {
398
+ 'payer_id': svc.get('payer_id', '00850'),
399
+ 'paid_amount': svc.get('paid_amount', '0.00'),
400
+ 'revenue_code': svc.get('revenue_code', ''),
401
+ 'units': svc.get('units', '1'),
402
+ 'adjudication_date': svc.get('adjudication_date', ''),
403
+ 'adjustments': svc.get('adjustments', [])
404
+ }
405
+ adjudication_data['service_paid_amounts'].append(service)
406
+
407
+ # Validate 835 structure
408
+ if not validate_835_structure(patient_data):
409
+ return None
410
+
411
+ return adjudication_data
412
+
413
+ def validate_835_structure(patient_data):
414
+ """
415
+ Validates that 835 data has required structure for COB processing.
416
+
417
+ Parameters:
418
+ - patient_data: Dictionary containing 835 data
419
+
420
+ Returns:
421
+ - Boolean indicating if 835 structure is valid
422
+ """
423
+ required_fields = ['clp02_amount', 'cas_segments', 'svc_segments']
424
+
425
+ for field in required_fields:
426
+ if field not in patient_data:
427
+ return False
428
+
429
+ return True
430
+
431
+ def determine_medicare_payer_type(patient_data, config):
432
+ """
433
+ Determines Medicare payer type for proper SBR09 assignment.
434
+
435
+ Returns:
436
+ - "MB" for Medicare Part B
437
+ - "MA" for Medicare Advantage
438
+ - None if not Medicare
439
+ """
440
+ payer_id = patient_data.get('payer_id', '')
441
+
442
+ if payer_id == "00850": # Medicare
443
+ # Check if this is Medicare Advantage
444
+ if patient_data.get('medicare_advantage', False):
445
+ return "MA"
446
+ else:
447
+ return "MB"
448
+
449
+ return None
450
+
451
+ def create_enhanced_sbr_segment(patient_data, config, crosswalk):
452
+ """
453
+ Enhanced SBR segment creation supporting secondary indicators and Medicare payer types.
454
+
455
+ Enhancement:
456
+ - Support secondary indicator (SBR01 = "S")
457
+ - Proper payer type in SBR09 ("MB" for Medicare Part B, "MA" for Medicare Advantage)
458
+
459
+ Parameters:
460
+ - patient_data: Dictionary containing patient and payer information
461
+ - config: Configuration settings
462
+ - crosswalk: Crosswalk data for payer mapping
463
+
464
+ Returns:
465
+ - Enhanced SBR segment string
466
+ """
467
+ # Determine responsibility code
468
+ responsibility_code = "S" if patient_data.get('claim_type') == 'secondary' else "P"
469
+
470
+ # Determine Medicare payer type
471
+ medicare_type = determine_medicare_payer_type(patient_data, config)
472
+ if medicare_type:
473
+ insurance_type = medicare_type
474
+ else:
475
+ insurance_type = patient_data.get('insurance_type', '18')
476
+
477
+ sbr_segment = "SBR*{}*18*******{}~".format(responsibility_code, insurance_type)
478
+
479
+ return sbr_segment
480
+
481
+ def create_enhanced_clm_segment(patient_data, config, crosswalk):
482
+ """
483
+ Enhanced CLM segment creation ensuring proper claim frequency for COB.
484
+
485
+ Enhancement:
486
+ - Ensure CLM05-3 is explicitly set to "1" (original) for secondary claims
487
+ - Unless claim replacement logic applies
488
+
489
+ Parameters:
490
+ - patient_data: Dictionary containing claim information
491
+ - config: Configuration settings
492
+ - crosswalk: Crosswalk data for claim mapping
493
+
494
+ Returns:
495
+ - Enhanced CLM segment string
496
+ """
497
+ # Get claim details
498
+ claim_number = patient_data.get('CHART', '')
499
+ amount = patient_data.get('AMOUNT', '0.00')
500
+ type_of_service = patient_data.get('TOS', '')
501
+
502
+ # Determine claim frequency
503
+ if patient_data.get('claim_type') == 'secondary':
504
+ claim_frequency = "1" # Original for secondary claims
505
+ else:
506
+ claim_frequency = patient_data.get('claim_frequency', '1')
507
+
508
+ clm_segment = "CLM*{}*{}***{}:B:{}*Y*A*Y*Y~".format(
509
+ claim_number, amount, type_of_service, claim_frequency)
510
+
511
+ return clm_segment
512
+
513
+ def validate_cob_multi_payer_handling(patient_data, config):
514
+ """
515
+ Validates COB multi-payer handling configuration.
516
+
517
+ Mode: single_payer_only (Options: single_payer_only, multi_payer_supported)
518
+ Rejection action: raise configuration error if >1 COB payer detected
519
+
520
+ Parameters:
521
+ - patient_data: Dictionary containing COB payer information
522
+ - config: Configuration settings
523
+
524
+ Returns:
525
+ - Tuple of (is_valid, error_messages)
526
+ """
527
+ errors = []
528
+
529
+ # Count COB payers
530
+ cob_payers = len(patient_data.get('cob_payers', []))
531
+ cob_mode = get_cob_configuration(config).get('cob_mode', 'single_payer_only')
532
+
533
+ if cob_payers > 1 and cob_mode == 'single_payer_only':
534
+ errors.append("Multiple COB payers detected but single_payer_only mode enabled")
535
+
536
+ return len(errors) == 0, errors
537
+
538
+ def log_cob_validation_results(validation_results, config):
539
+ """
540
+ Logs COB validation results for audit and debugging.
541
+
542
+ Parameters:
543
+ - validation_results: Dictionary containing validation results
544
+ - config: Configuration settings
545
+ """
546
+ for validation_type, result in validation_results.items():
547
+ if result['is_valid']:
548
+ MediLink_ConfigLoader.log("COB validation passed: {}".format(validation_type), config, level="INFO")
549
+ else:
550
+ MediLink_ConfigLoader.log("COB validation failed: {}".format(validation_type), config, level="ERROR")
551
+ for error in result['errors']:
552
+ MediLink_ConfigLoader.log(" - {}".format(error), config, level="ERROR")
553
+
554
+ # Configuration enhancement functions
555
+
556
+ def get_cob_configuration(config):
557
+ """
558
+ Retrieves COB-specific configuration settings.
559
+
560
+ Parameters:
561
+ - config: Main configuration dictionary
562
+
563
+ Returns:
564
+ - COB configuration dictionary
565
+ """
566
+ cob_config = config.get('MediLink_Config', {}).get('cob_settings', {})
567
+ return cob_config
568
+
569
+ def validate_cob_configuration(config):
570
+ """
571
+ Validates COB configuration completeness and correctness.
572
+
573
+ Parameters:
574
+ - config: Configuration settings
575
+
576
+ Returns:
577
+ - Tuple of (is_valid, error_messages)
578
+ """
579
+ errors = []
580
+
581
+ cob_config = get_cob_configuration(config)
582
+ required_fields = ['medicare_payer_ids', 'cob_mode', 'validation_level']
583
+
584
+ for field in required_fields:
585
+ if field not in cob_config:
586
+ errors.append("Missing required COB configuration field: {}".format(field))
587
+
588
+ return len(errors) == 0, errors
589
+
590
+ # Insurance type code enhancement
591
+
592
+ def get_enhanced_insurance_options(config):
593
+ """
594
+ Retrieves enhanced insurance options including Medicare-specific codes.
595
+
596
+ Parameters:
597
+ - config: Configuration settings
598
+
599
+ Returns:
600
+ - Enhanced insurance options dictionary
601
+ """
602
+ base_options = config.get('MediLink_Config', {}).get('insurance_options', {})
603
+ medicare_options = {
604
+ 'MB': 'Medicare Part B',
605
+ 'MA': 'Medicare Advantage',
606
+ 'MC': 'Medicare Part C'
607
+ }
608
+ enhanced_options = base_options.copy()
609
+ enhanced_options.update(medicare_options)
610
+ return enhanced_options
611
+
612
+ # Main COB processing function
613
+
614
+ def process_cob_claim(patient_data, config, crosswalk, client):
615
+ """
616
+ Main function for processing COB claims with enhanced 837P generation.
617
+
618
+ This function orchestrates the creation of all COB-related segments
619
+ and validates the complete claim for compliance.
620
+
621
+ Parameters:
622
+ - patient_data: Dictionary containing patient and COB information
623
+ - config: Configuration settings
624
+ - crosswalk: Crosswalk data for mapping
625
+ - client: API client for external data retrieval
626
+
627
+ Returns:
628
+ - List of all 837P segments including COB enhancements
629
+ """
630
+ segments = []
631
+
632
+ # 1. Validate COB configuration
633
+ is_valid, errors = validate_cob_configuration(config)
634
+ if not is_valid:
635
+ raise ValueError("Invalid COB configuration: {}".format(errors))
636
+
637
+ # 2. Extract 835 adjudication data if available
638
+ adjudication_data = extract_835_adjudication_data(patient_data, config)
639
+ if adjudication_data:
640
+ # Merge 835 data into patient_data
641
+ patient_data.update(adjudication_data)
642
+
643
+ # 3. Create enhanced SBR segment
644
+ enhanced_sbr = create_enhanced_sbr_segment(patient_data, config, crosswalk)
645
+ if enhanced_sbr:
646
+ segments.append(enhanced_sbr)
647
+
648
+ # 4. Create 2320 other subscriber segments if COB
649
+ if patient_data.get('claim_type') == 'secondary':
650
+ segments.extend(create_2320_other_subscriber_segments(patient_data, config, crosswalk))
651
+
652
+ # 5. Create 2330B prior payer segments
653
+ segments.extend(create_2330B_prior_payer_segments(patient_data, config, crosswalk))
654
+
655
+ # 6. Create 2330C other subscriber segments if patient != subscriber
656
+ if not patient_data.get('patient_is_subscriber', True):
657
+ segments.extend(create_2330C_other_subscriber_name_segments(patient_data, config, crosswalk))
658
+
659
+ # 7. Create enhanced CLM segment
660
+ enhanced_clm = create_enhanced_clm_segment(patient_data, config, crosswalk)
661
+ if enhanced_clm:
662
+ segments.append(enhanced_clm)
663
+
664
+ # 8. Create 2430 service line COB segments if service-level adjudication
665
+ if patient_data.get('service_adjudications'):
666
+ segments.extend(create_2430_service_line_cob_segments(patient_data, config, crosswalk))
667
+
668
+ # 9. Create PWK attachment segment if required
669
+ pwk_segment = create_pwk_attachment_segment(patient_data, config)
670
+ if pwk_segment:
671
+ segments.append(pwk_segment)
672
+
673
+ # 10. Validate final claim integrity
674
+ is_valid, errors = validate_cob_claim_integrity(patient_data, config)
675
+ if not is_valid:
676
+ raise ValueError("COB claim validation failed: {}".format(errors))
677
+
678
+ # 11. Log validation results
679
+ log_cob_validation_results({'integrity': {'is_valid': is_valid, 'errors': errors}}, config)
680
+
681
+ return segments
682
+
683
+ # Configuration Documentation and Examples
684
+
685
+ """
686
+ COB CONFIGURATION STRUCTURE
687
+
688
+ The COB library requires specific configuration settings to be added to the main config.json file.
689
+ Below is the recommended structure for COB configuration:
690
+
691
+ {
692
+ "MediLink_Config": {
693
+ "cob_settings": {
694
+ "medicare_payer_ids": ["00850"],
695
+ "cob_mode": "single_payer_only",
696
+ "validation_level": 3,
697
+ "medicare_advantage_identifiers": ["MA", "MC"],
698
+ "default_medicare_type": "MB",
699
+ "require_835_validation": true,
700
+ "attachment_handling": {
701
+ "enable_pwk_segments": true,
702
+ "default_report_type": "EB",
703
+ "default_transmission_code": "FX"
704
+ },
705
+ "service_level_adjudication": {
706
+ "enable_2430_loop": true,
707
+ "require_adjudication_date": true,
708
+ "validate_paid_amounts": true
709
+ },
710
+ "validation_rules": {
711
+ "require_sbr09_for_secondary": true,
712
+ "validate_medicare_payer_id": true,
713
+ "require_claim_frequency_1": true,
714
+ "validate_total_paid_amount": true
715
+ }
716
+ },
717
+ "insurance_options": {
718
+ "MB": "Medicare Part B",
719
+ "MA": "Medicare Advantage",
720
+ "MC": "Medicare Part C",
721
+ "18": "Medicare",
722
+ "12": "Medicaid",
723
+ "16": "Self Pay"
724
+ }
725
+ }
726
+ }
727
+
728
+ COB INTEGRATION GUIDE
729
+
730
+ 1. DATA STRUCTURE REQUIREMENTS
731
+
732
+ Patient data for COB claims should include:
733
+ - claim_type: "primary" or "secondary"
734
+ - payer_id: Payer identifier (e.g., "00850" for Medicare)
735
+ - medicare_advantage: Boolean indicating if Medicare Advantage
736
+ - primary_paid_amount: Amount paid by primary payer
737
+ - cas_adjustments: List of adjustment objects
738
+ - service_adjudications: List of service-level adjudication data
739
+ - prior_payer_name: Name of prior payer
740
+ - prior_payer_id: ID of prior payer
741
+ - patient_is_subscriber: Boolean indicating if patient is subscriber
742
+ - requires_attachment: Boolean indicating if attachment required
743
+
744
+ 2. 835 DATA INTEGRATION
745
+
746
+ For 835-derived adjudication data, include:
747
+ - clp02_amount: Total amount paid by primary
748
+ - cas_segments: List of CAS adjustment segments
749
+ - svc_segments: List of service line segments with:
750
+ - payer_id: Payer identifier
751
+ - paid_amount: Amount paid for service
752
+ - revenue_code: Revenue code
753
+ - units: Number of units
754
+ - adjudication_date: Date of adjudication
755
+ - adjustments: List of line-level adjustments
756
+
757
+ 3. VALIDATION REQUIREMENTS
758
+
759
+ The library performs comprehensive validation:
760
+ - SBR09 required for secondary claims
761
+ - Medicare payer ID validation
762
+ - Claim frequency validation
763
+ - Total paid amount validation
764
+ - 835 data structure validation
765
+
766
+ 4. INTEGRATION POINTS
767
+
768
+ Key integration points in the main encoder:
769
+ - Enhanced SBR segment creation
770
+ - COB loop processing (2320, 2330B, 2330C, 2430)
771
+ - Enhanced CLM segment creation
772
+ - PWK attachment segment creation
773
+
774
+ 5. ERROR HANDLING
775
+
776
+ The library provides detailed error messages for:
777
+ - Configuration validation failures
778
+ - Claim integrity violations
779
+ - 835 data structure issues
780
+ - Medicare payer type determination failures
781
+
782
+ 6. LOGGING
783
+
784
+ Comprehensive logging is provided for:
785
+ - COB validation results
786
+ - Configuration validation
787
+ - Claim processing steps
788
+ - Error conditions
789
+
790
+ 7. FUTURE ENHANCEMENTS
791
+
792
+ Planned enhancements include:
793
+ - Multi-payer COB support
794
+ - Advanced 835 data extraction
795
+ - Enhanced validation rules
796
+ - Attachment workflow integration
797
+ - Real-time payer verification
798
+
799
+ USAGE EXAMPLE
800
+
801
+ # Basic COB claim processing
802
+ patient_data = {
803
+ 'claim_type': 'secondary',
804
+ 'payer_id': '00850',
805
+ 'medicare_advantage': False,
806
+ 'primary_paid_amount': '150.00',
807
+ 'cas_adjustments': [
808
+ {'group': 'CO', 'reason': '45', 'amount': '25.00'}
809
+ ],
810
+ 'service_adjudications': [
811
+ {
812
+ 'payer_id': '00850',
813
+ 'paid_amount': '125.00',
814
+ 'revenue_code': '0001',
815
+ 'units': '1',
816
+ 'adjudication_date': '01-15-2024',
817
+ 'adjustments': []
818
+ }
819
+ ]
820
+ }
821
+
822
+ # Process COB claim
823
+ cob_segments = process_cob_claim(patient_data, config, crosswalk, client)
824
+
825
+ # Validate configuration
826
+ is_valid, errors = validate_cob_configuration(config)
827
+ if not is_valid:
828
+ print("Configuration errors:", errors)
829
+
830
+ # Get enhanced insurance options
831
+ insurance_options = get_enhanced_insurance_options(config)
832
+ """