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