Achint Aggarwal commited on
Commit
4ebefef
Β·
1 Parent(s): 05fc7bd

Update SOP guidelines to explicitly require formal appeal letters

Browse files
generate_sop_docs.py ADDED
@@ -0,0 +1,593 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Generate synthetic SOP (Standard Operating Procedure) PDFs for each payer.
3
+ Replaces old BCBS/Aetna/Medicare SOPs with synthetic payer names.
4
+ Each SOP contains resolution workflows for CO-252 and CO-16 denial codes,
5
+ with specific guidance for M127 remark code (missing medical records).
6
+
7
+ The SOPs are structured to guide the RCA and Execution agents through:
8
+ RCA: Identify denial β†’ Analyse EOB β†’ Map to SOP steps
9
+ Execution: Retrieve docs β†’ Submit to payer β†’ Case summary (human review) β†’ Update provider DB
10
+ """
11
+
12
+ from reportlab.lib.pagesizes import letter
13
+ from reportlab.lib.units import inch
14
+ from reportlab.lib import colors
15
+ from reportlab.pdfgen import canvas
16
+ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
17
+ from reportlab.platypus import (
18
+ SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
19
+ PageBreak, HRFlowable
20
+ )
21
+ from reportlab.lib.enums import TA_LEFT, TA_CENTER
22
+ import os
23
+
24
+ SOP_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "sop")
25
+
26
+ # ═══════════════════════════════════════════════
27
+ # Payer configurations (all synthetic)
28
+ # ═══════════════════════════════════════════════
29
+ PAYERS = {
30
+ "Summit_Health": {
31
+ "name": "Summit Health Insurance of Texas",
32
+ "short": "Summit Health",
33
+ "portal": "provider.summithealth.example",
34
+ "filing_deadline": "180 days from denial date",
35
+ "filename": "Summit_Health.pdf",
36
+ },
37
+ "Crestview_Health": {
38
+ "name": "Crestview Health Plans",
39
+ "short": "Crestview Health",
40
+ "portal": "provider.crestviewhealth.example",
41
+ "filing_deadline": "180 days from denial date",
42
+ "filename": "Crestview_Health.pdf",
43
+ },
44
+ "NationalCare": {
45
+ "name": "NationalCare",
46
+ "short": "NationalCare",
47
+ "portal": "portal.nationalcare.example",
48
+ "filing_deadline": "120 days from denial date",
49
+ "filename": "NationalCare.pdf",
50
+ },
51
+ }
52
+
53
+
54
+ def build_styles():
55
+ """Create reusable paragraph styles."""
56
+ styles = getSampleStyleSheet()
57
+
58
+ styles.add(ParagraphStyle(
59
+ name='SOPTitle',
60
+ parent=styles['Title'],
61
+ fontSize=18,
62
+ spaceAfter=6,
63
+ textColor=colors.HexColor('#1a1a2e'),
64
+ ))
65
+ styles.add(ParagraphStyle(
66
+ name='SOPSubtitle',
67
+ parent=styles['Heading2'],
68
+ fontSize=13,
69
+ spaceAfter=4,
70
+ textColor=colors.HexColor('#16213e'),
71
+ ))
72
+ styles.add(ParagraphStyle(
73
+ name='SOPHeading',
74
+ parent=styles['Heading2'],
75
+ fontSize=12,
76
+ spaceBefore=14,
77
+ spaceAfter=6,
78
+ textColor=colors.HexColor('#0f3460'),
79
+ ))
80
+ styles.add(ParagraphStyle(
81
+ name='SOPStepHeading',
82
+ parent=styles['Heading3'],
83
+ fontSize=11,
84
+ spaceBefore=10,
85
+ spaceAfter=4,
86
+ textColor=colors.HexColor('#1a1a2e'),
87
+ leftIndent=10,
88
+ ))
89
+ styles.add(ParagraphStyle(
90
+ name='SOPBody',
91
+ parent=styles['Normal'],
92
+ fontSize=9.5,
93
+ leading=13,
94
+ spaceAfter=4,
95
+ ))
96
+ styles.add(ParagraphStyle(
97
+ name='SOPBullet',
98
+ parent=styles['Normal'],
99
+ fontSize=9.5,
100
+ leading=13,
101
+ leftIndent=24,
102
+ spaceAfter=2,
103
+ bulletIndent=12,
104
+ ))
105
+ styles.add(ParagraphStyle(
106
+ name='SOPSubBullet',
107
+ parent=styles['Normal'],
108
+ fontSize=9,
109
+ leading=12,
110
+ leftIndent=40,
111
+ spaceAfter=2,
112
+ bulletIndent=28,
113
+ ))
114
+ styles.add(ParagraphStyle(
115
+ name='SOPMeta',
116
+ parent=styles['Normal'],
117
+ fontSize=9,
118
+ leading=12,
119
+ textColor=colors.HexColor('#444444'),
120
+ ))
121
+ return styles
122
+
123
+
124
+ def add_metadata_table(story, payer, denial_code, title, styles):
125
+ """Add the header metadata table for an SOP section."""
126
+ story.append(Paragraph(payer["short"], styles['SOPTitle']))
127
+ story.append(Paragraph(title, styles['SOPSubtitle']))
128
+ story.append(Spacer(1, 8))
129
+
130
+ meta_data = [
131
+ ["Payer", payer["name"], "Version", "2.0"],
132
+ ["Effective", "January 2025", "Department", "Revenue Cycle Management"],
133
+ ["Portal", payer["portal"], "Filing Deadline", payer["filing_deadline"]],
134
+ ]
135
+ meta_table = Table(meta_data, colWidths=[70, 190, 90, 170])
136
+ meta_table.setStyle(TableStyle([
137
+ ('FONTNAME', (0, 0), (-1, -1), 'Helvetica'),
138
+ ('FONTSIZE', (0, 0), (-1, -1), 9),
139
+ ('FONTNAME', (0, 0), (0, -1), 'Helvetica-Bold'),
140
+ ('FONTNAME', (2, 0), (2, -1), 'Helvetica-Bold'),
141
+ ('TEXTCOLOR', (0, 0), (0, -1), colors.HexColor('#0f3460')),
142
+ ('TEXTCOLOR', (2, 0), (2, -1), colors.HexColor('#0f3460')),
143
+ ('BACKGROUND', (0, 0), (-1, -1), colors.HexColor('#f0f4f8')),
144
+ ('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor('#cccccc')),
145
+ ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
146
+ ('TOPPADDING', (0, 0), (-1, -1), 4),
147
+ ('BOTTOMPADDING', (0, 0), (-1, -1), 4),
148
+ ('LEFTPADDING', (0, 0), (-1, -1), 6),
149
+ ]))
150
+ story.append(meta_table)
151
+ story.append(Spacer(1, 12))
152
+
153
+
154
+ def add_co252_section(story, payer, styles):
155
+ """Add the CO-252 denial resolution SOP section."""
156
+
157
+ add_metadata_table(
158
+ story, payer, "CO-252",
159
+ "CO-252 Denial Resolution β€” Additional Documentation Required",
160
+ styles
161
+ )
162
+
163
+ # ── Overview ──
164
+ story.append(Paragraph("Overview", styles['SOPHeading']))
165
+ story.append(Paragraph(
166
+ "CO-252 is a Claim Adjustment Reason Code (CARC) indicating that additional documentation or "
167
+ "attachments are required by the payer to process or adjudicate the claim. The claim cannot be "
168
+ "processed until the missing supporting information is submitted.",
169
+ styles['SOPBody']
170
+ ))
171
+ story.append(Paragraph(
172
+ "CO-252 denials are <b>NOT</b> a medical necessity denial β€” they are a <b>documentation deficiency</b>. "
173
+ "The service may be covered, but the claim cannot be adjudicated without the required supporting documentation.",
174
+ styles['SOPBody']
175
+ ))
176
+ story.append(Spacer(1, 4))
177
+
178
+ # ── Remark Codes ──
179
+ story.append(Paragraph("Associated Remark Codes", styles['SOPHeading']))
180
+ story.append(Paragraph(
181
+ "CO-252 denials are always accompanied by one or more Remittance Advice Remark Codes (RARCs) "
182
+ "that specify exactly what documentation is missing. Common remark codes include:",
183
+ styles['SOPBody']
184
+ ))
185
+
186
+ remark_data = [
187
+ ["Remark Code", "Description", "Required Action"],
188
+ ["M127", "Missing patient medical record for this service",
189
+ "Retrieve and submit the operative report, chart notes, or clinical documentation for the date of service"],
190
+ ["M15", "Missing/incomplete/invalid clinical documentation",
191
+ "Obtain complete clinical notes from the provider and resubmit"],
192
+ ["MA130", "Paper claim/attachment may be needed",
193
+ "Submit documentation via payer portal or as paper attachment"],
194
+ ["N479", "Missing operative report",
195
+ "Retrieve operative report from the surgical facility and submit with appeal"],
196
+ ["N519", "Missing pathology report",
197
+ "Retrieve pathology report and submit with the claim resubmission"],
198
+ ]
199
+ remark_table = Table(remark_data, colWidths=[70, 200, 250])
200
+ remark_table.setStyle(TableStyle([
201
+ ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
202
+ ('FONTSIZE', (0, 0), (-1, -1), 8),
203
+ ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#1a1a2e')),
204
+ ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
205
+ ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor('#fafafa')),
206
+ ('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor('#cccccc')),
207
+ ('VALIGN', (0, 0), (-1, -1), 'TOP'),
208
+ ('TOPPADDING', (0, 0), (-1, -1), 4),
209
+ ('BOTTOMPADDING', (0, 0), (-1, -1), 4),
210
+ ('LEFTPADDING', (0, 0), (-1, -1), 4),
211
+ ]))
212
+ story.append(remark_table)
213
+ story.append(Spacer(1, 8))
214
+
215
+ # ── Common Missing Documentation Types ──
216
+ story.append(Paragraph("Common Missing Documentation Types", styles['SOPHeading']))
217
+ doc_types = [
218
+ "<b>Operative Report:</b> Required for surgical procedures (CPT 10000–69999). Must include surgeon name, pre/post-operative diagnosis, procedure details, findings, anesthesia type, and signature.",
219
+ "<b>Certificate of Medical Necessity (CMN):</b> Required for DME equipment (HCPCS E-codes). Must include physician signature, diagnosis, prognosis, and functional limitations.",
220
+ "<b>Delivery Ticket:</b> Required for DME to confirm patient received equipment. Must include patient signature, date of delivery, equipment description, and serial/lot number.",
221
+ "<b>Chart Notes / Clinical Documentation:</b> Required to support diagnosis and level of service. Must be from the date of service and signed by the rendering provider.",
222
+ "<b>Pathology Report:</b> Required when biopsy was performed. Must include specimen description, findings, and pathologist signature.",
223
+ "<b>Prescription / Written Order:</b> Required for DME, orthotics, and prosthetics. Must include prescribing physician signature, NPI, date, and equipment description.",
224
+ ]
225
+ for item in doc_types:
226
+ story.append(Paragraph(f"β€’ {item}", styles['SOPBullet']))
227
+ story.append(Spacer(1, 6))
228
+
229
+ # ══════════════════════════════════════════════
230
+ # Resolution Workflow (maps to agent actions)
231
+ # ══════════════════════════════════════════════
232
+ story.append(Paragraph("Resolution Workflow", styles['SOPHeading']))
233
+ story.append(HRFlowable(width="100%", thickness=1, color=colors.HexColor('#0f3460')))
234
+ story.append(Spacer(1, 4))
235
+
236
+ # STEP 1: Identify the Denial (RCA Agent β€” Step 2,3,4)
237
+ story.append(Paragraph(
238
+ "STEP 1: Identify the Denial and Missing Documentation",
239
+ styles['SOPStepHeading']
240
+ ))
241
+ story.append(Paragraph(
242
+ "Review the Explanation of Benefits (EOB) to identify the exact denial code, remark codes, and what documentation is missing.",
243
+ styles['SOPBody']
244
+ ))
245
+ step1_bullets = [
246
+ "Retrieve the denied claim data from the claims database using the claim ID (rec_id)",
247
+ "Locate and read the EOB PDF file referenced in the claim record",
248
+ "Identify the primary denial code (CO-252) and associated remark code (e.g., M127)",
249
+ "Cross-reference the remark code with the table above to determine which document is needed",
250
+ "Note the CPT/HCPCS code to determine the document category (surgical β†’ operative report, DME β†’ CMN/delivery ticket)",
251
+ "Record the service date β€” all retrieved documents must correspond to this date",
252
+ ]
253
+ for b in step1_bullets:
254
+ story.append(Paragraph(f"β€’ {b}", styles['SOPBullet']))
255
+ story.append(Spacer(1, 4))
256
+
257
+ # STEP 1A: Denial Summary
258
+ story.append(Paragraph(
259
+ "STEP 1A: Prepare Denial Summary",
260
+ styles['SOPStepHeading']
261
+ ))
262
+ story.append(Paragraph(
263
+ "Compile a denial summary (Intelligence Summary) that includes all relevant claim metadata, "
264
+ "the root cause analysis, and recommended resolution steps. This summary will be used by the "
265
+ "execution workflow to resolve the denial.",
266
+ styles['SOPBody']
267
+ ))
268
+ step1a_bullets = [
269
+ "Include: Claim ID, Invoice Number, Patient Name, Payer, Denial Code, Remark Codes, Service Date, Billed Amount",
270
+ "Include: Denial analysis explaining why the claim was denied and what is needed",
271
+ "Include: Specific SOP steps from this document that apply to the denial scenario",
272
+ "Include: Recommended Next Best Actions for the execution team",
273
+ ]
274
+ for b in step1a_bullets:
275
+ story.append(Paragraph(f"β€’ {b}", styles['SOPBullet']))
276
+ story.append(Spacer(1, 6))
277
+
278
+ # STEP 2: Retrieve Documentation (Execution Agent β€” Step 2)
279
+ story.append(Paragraph(
280
+ "STEP 2: Retrieve Required Documentation from Provider / EHR System",
281
+ styles['SOPStepHeading']
282
+ ))
283
+ story.append(Paragraph(
284
+ "Query the provider portal or EHR (Electronic Health Record) system to retrieve the medical "
285
+ "documents needed to support the claim. Use the invoice number to look up available records.",
286
+ styles['SOPBody']
287
+ ))
288
+ step2_bullets = [
289
+ "Query the provider documentation system using the invoice number from the claim data",
290
+ "Retrieve all available documents associated with the claim (operative reports, chart notes, delivery tickets, prescriptions, etc.)",
291
+ "Verify that the retrieved documents match the date of service on the denied claim",
292
+ "Verify that the documents contain the required elements:",
293
+ ]
294
+ for b in step2_bullets:
295
+ story.append(Paragraph(f"β€’ {b}", styles['SOPBullet']))
296
+
297
+ # Sub-bullets for document verification
298
+ verification_items = [
299
+ "Operative Report: Surgeon name, procedure performed, pre/post diagnosis, anesthesia, complications, date, and signature",
300
+ "Chart Notes: Provider name, date of service, assessment/plan, diagnosis codes, and signature",
301
+ "Delivery Ticket: Patient name, equipment description, serial/lot number, date of delivery, and patient signature",
302
+ "Prescription: Prescribing physician name, NPI, equipment ordered, diagnosis, date, and signature",
303
+ "CMN: Physician signature, diagnosis, prognosis, estimated length of need, and functional limitations",
304
+ ]
305
+ for v in verification_items:
306
+ story.append(Paragraph(f" β—¦ {v}", styles['SOPSubBullet']))
307
+ story.append(Spacer(1, 4))
308
+
309
+ story.append(Paragraph(
310
+ "<b>For CO-252 with M127 specifically:</b> The M127 remark code means \"Missing patient medical record "
311
+ "for this service.\" You must retrieve the operative report or chart notes from the provider that "
312
+ "document what was performed on the date of service. The document must include the surgeon's name, "
313
+ "procedure details, and be signed and dated.",
314
+ styles['SOPBody']
315
+ ))
316
+ story.append(Spacer(1, 6))
317
+
318
+ # STEP 3: Submit to Payer (Execution Agent)
319
+ story.append(Paragraph(
320
+ f"STEP 3: Submit Appeal Package to {payer['short']} Payer Portal",
321
+ styles['SOPStepHeading']
322
+ ))
323
+ story.append(Paragraph(
324
+ f"After retrieving the required documents, compile and submit the appeal package "
325
+ f"to the {payer['short']} payer portal.",
326
+ styles['SOPBody']
327
+ ))
328
+ step3_bullets = [
329
+ f"Generate a formal appeal letter indicating the denial code (CO-252), remark code, and list of documents retrieved",
330
+ f"Compile the appeal package: include the original claim reference (invoice number), the appeal letter, and all supporting documents retrieved from the provider/EHR system",
331
+ f"Submit via the {payer['short']} provider portal at {payer['portal']}",
332
+ "Include appeal notes that reference the denial code (CO-252), remark code (e.g., M127), and list the documents being submitted",
333
+ "Record the confirmation number returned by the payer portal β€” this is your proof of submission",
334
+ f"Submit within {payer['filing_deadline']} to avoid timely filing rejections",
335
+ "For package_path, use the format: docs/{{invoice_number}}/appeal_package.pdf",
336
+ ]
337
+ for b in step3_bullets:
338
+ story.append(Paragraph(f"β€’ {b}", styles['SOPBullet']))
339
+ story.append(Spacer(1, 6))
340
+
341
+ # STEP 4: Update Case Notes (Execution Agent)
342
+ story.append(Paragraph(
343
+ "STEP 4: Create Case Summary and Update Provider Records",
344
+ styles['SOPStepHeading']
345
+ ))
346
+ story.append(Paragraph(
347
+ "After successful submission, create a comprehensive case summary and update the provider "
348
+ "database with all actions taken, confirmation numbers, and resolution status.",
349
+ styles['SOPBody']
350
+ ))
351
+ step4_bullets = [
352
+ "Draft a case summary that includes:",
353
+ ]
354
+ for b in step4_bullets:
355
+ story.append(Paragraph(f"β€’ {b}", styles['SOPBullet']))
356
+
357
+ summary_items = [
358
+ "Original denial code and remark code (e.g., CO-252 / M127)",
359
+ "List of documents retrieved from the provider portal (type and file path)",
360
+ "Appeal submission confirmation number from the payer portal",
361
+ "Date and time of submission",
362
+ "Current status (e.g., Appeal Submitted, Pending Review)",
363
+ ]
364
+ for s in summary_items:
365
+ story.append(Paragraph(f" β—¦ {s}", styles['SOPSubBullet']))
366
+
367
+ step4_bullets_cont = [
368
+ "Present the draft case notes to the human reviewer for approval before updating the database",
369
+ "Update the provider database with the case notes, confirmation number, and updated status",
370
+ "Set the claim status to 'Submitted' after successful appeal submission",
371
+ "If the appeal is denied again, escalate to a formal Level 2 appeal or peer-to-peer review",
372
+ ]
373
+ for b in step4_bullets_cont:
374
+ story.append(Paragraph(f"β€’ {b}", styles['SOPBullet']))
375
+ story.append(Spacer(1, 8))
376
+
377
+ # ── Specific Scenario: CO-252 + M127 ──
378
+ story.append(Paragraph("Specific Scenario: CO-252 with Remark Code M127", styles['SOPHeading']))
379
+ story.append(HRFlowable(width="100%", thickness=1, color=colors.HexColor('#e94560')))
380
+ story.append(Spacer(1, 4))
381
+
382
+ story.append(Paragraph(
383
+ "<b>Scenario:</b> A claim is denied with CO-252 and remark code M127 (\"Missing patient medical record "
384
+ "for this service\"). This is one of the most common CO-252 denial scenarios.",
385
+ styles['SOPBody']
386
+ ))
387
+ story.append(Paragraph(
388
+ "<b>Root Cause:</b> The payer requires the patient's medical record (operative report, chart notes, "
389
+ "or clinical documentation) for the date of service to adjudicate the claim, but this documentation "
390
+ "was not included with the original submission.",
391
+ styles['SOPBody']
392
+ ))
393
+
394
+ story.append(Paragraph("Resolution Steps for CO-252 / M127:", styles['SOPStepHeading']))
395
+ m127_steps = [
396
+ "<b>1. Identify the missing record type:</b> Check the CPT/HCPCS code on the denied claim. "
397
+ "Surgical codes (10000-69999) require an operative report. DME codes (E-codes) require a CMN and/or delivery ticket. "
398
+ "E&M codes (99201-99499) require chart notes.",
399
+ "<b>2. Query the provider portal:</b> Use the invoice number to retrieve all available medical records "
400
+ "from the provider/EHR system. Look specifically for operative reports, chart notes, and any other "
401
+ "clinical documentation matching the date of service.",
402
+ "<b>3. Verify document completeness:</b> Ensure the retrieved operative report or chart notes contain: "
403
+ "patient name, date of service, provider/surgeon name, procedure performed, pre/post-operative diagnosis, "
404
+ "and provider signature. If any element is missing, contact the provider to obtain a complete document.",
405
+ "<b>4. Generate appeal letter:</b> Generate a formal appeal letter indicating the denial code (CO-252), remark code (M127), and list the documents retrieved.",
406
+ f"<b>5. Submit to payer:</b> Package the retrieved documents along with the appeal letter and submit to {payer['short']} portal. "
407
+ "Include notes referencing denial code CO-252, remark code M127, and the documents being attached. "
408
+ "Record the confirmation number.",
409
+ "<b>6. Update provider records:</b> After submission, create a case summary with: the confirmation number, "
410
+ "list of documents submitted, submission date, and change the status to 'Submitted'. "
411
+ "Draft case notes and get human approval before writing to the provider database.",
412
+ ]
413
+ for step in m127_steps:
414
+ story.append(Paragraph(f" {step}", styles['SOPBullet']))
415
+ story.append(Spacer(1, 8))
416
+
417
+ # ── Follow-up & Prevention ──
418
+ story.append(Paragraph("Follow-Up Timeline", styles['SOPHeading']))
419
+ followup = [
420
+ f"Allow 30 days for {payer['short']} to process the resubmitted claim with documentation",
421
+ "If no response after 30 days, contact the payer to check claim status",
422
+ "If denied again, review the new denial reason and escalate to formal appeal if needed",
423
+ f"All appeals must be filed within {payer['filing_deadline']}",
424
+ ]
425
+ for f_item in followup:
426
+ story.append(Paragraph(f"β€’ {f_item}", styles['SOPBullet']))
427
+ story.append(Spacer(1, 6))
428
+
429
+ story.append(Paragraph("Prevention Tips", styles['SOPHeading']))
430
+ prevention = [
431
+ "Collect operative reports at time of claim submission for all surgical procedures",
432
+ "Obtain CMN before delivering any DME equipment",
433
+ "Always get signed delivery ticket with lot/serial number at time of DME delivery",
434
+ "Attach chart notes proactively for high-value claims or complex procedures",
435
+ "Verify payer-specific documentation requirements before initial billing",
436
+ "Use a claim scrubber tool to flag claims likely to receive CO-252 denials",
437
+ ]
438
+ for p in prevention:
439
+ story.append(Paragraph(f"β€’ {p}", styles['SOPBullet']))
440
+
441
+
442
+ def add_co16_section(story, payer, styles):
443
+ """Add the CO-16 denial resolution SOP section."""
444
+
445
+ add_metadata_table(
446
+ story, payer, "CO-16",
447
+ "CO-16 Denial Resolution β€” Claim Lacks Information / Billing Error",
448
+ styles
449
+ )
450
+
451
+ # ── Overview ──
452
+ story.append(Paragraph("Overview", styles['SOPHeading']))
453
+ story.append(Paragraph(
454
+ "CO-16 is a Claim Adjustment Reason Code (CARC) indicating that the claim lacks information or "
455
+ "has a billing/submission error that prevents adjudication. Unlike CO-252 (missing attachments), "
456
+ "CO-16 typically requires correcting information ON the claim form itself.",
457
+ styles['SOPBody']
458
+ ))
459
+ story.append(Paragraph(
460
+ "Common CO-16 issues include: invalid or non-specific diagnosis codes, missing modifiers, "
461
+ "missing referring provider NPI, missing ABN, or incorrect procedure code combinations.",
462
+ styles['SOPBody']
463
+ ))
464
+ story.append(Spacer(1, 4))
465
+
466
+ # ── Common CO-16 Reasons ──
467
+ story.append(Paragraph("Common CO-16 Denial Reasons", styles['SOPHeading']))
468
+ reasons = [
469
+ "<b>Invalid/Non-specific Diagnosis Code:</b> ICD-10 code is too general or not supported by documentation",
470
+ "<b>Missing Modifier:</b> Required modifier absent (e.g., telehealth modifier 95 or GT)",
471
+ "<b>Missing Referring Provider NPI:</b> Box 17b not populated when required",
472
+ "<b>Missing ABN:</b> Advance Beneficiary Notice not obtained for potentially non-covered service",
473
+ "<b>Invalid Procedure Code Combination:</b> CPT codes billed together that are not compatible",
474
+ "<b>Missing Secondary Diagnosis:</b> Additional codes required to support medical necessity",
475
+ ]
476
+ for r in reasons:
477
+ story.append(Paragraph(f"β€’ {r}", styles['SOPBullet']))
478
+ story.append(Spacer(1, 6))
479
+
480
+ # ── Resolution Workflow ──
481
+ story.append(Paragraph("Resolution Workflow", styles['SOPHeading']))
482
+ story.append(HRFlowable(width="100%", thickness=1, color=colors.HexColor('#0f3460')))
483
+ story.append(Spacer(1, 4))
484
+
485
+ # Step 1
486
+ story.append(Paragraph("STEP 1: Identify the Billing Error", styles['SOPStepHeading']))
487
+ story.append(Paragraph(
488
+ "Review the EOB denial reason carefully to identify the specific information that is missing or incorrect.",
489
+ styles['SOPBody']
490
+ ))
491
+ s1_bullets = [
492
+ "Retrieve the denied claim data and EOB content",
493
+ "Identify the specific field or code that needs correction (diagnosis, modifier, NPI, etc.)",
494
+ "Cross-reference with the claim form (CMS-1500) to locate the field in question",
495
+ ]
496
+ for b in s1_bullets:
497
+ story.append(Paragraph(f"β€’ {b}", styles['SOPBullet']))
498
+ story.append(Spacer(1, 4))
499
+
500
+ # Step 2
501
+ story.append(Paragraph("STEP 2: Review Supporting Documentation", styles['SOPStepHeading']))
502
+ s2_bullets = [
503
+ "Pull chart notes from the date of service",
504
+ "Identify the correct information to populate on the claim",
505
+ "Verify with the billing department or provider if needed",
506
+ ]
507
+ for b in s2_bullets:
508
+ story.append(Paragraph(f"β€’ {b}", styles['SOPBullet']))
509
+ story.append(Spacer(1, 4))
510
+
511
+ # Step 3
512
+ story.append(Paragraph("STEP 3: Correct and Resubmit Claim", styles['SOPStepHeading']))
513
+ s3_bullets = [
514
+ "Correct the identified error on the CMS-1500 form",
515
+ "Generate a formal appeal letter indicating the denial code (CO-16), remark code, and list of supported documents",
516
+ "Submit as a corrected claim (not a new claim) and include the appeal letter",
517
+ "Include a brief explanation of the correction if required by the payer",
518
+ f"Submit via {payer['portal']}",
519
+ ]
520
+ for b in s3_bullets:
521
+ story.append(Paragraph(f"β€’ {b}", styles['SOPBullet']))
522
+ story.append(Spacer(1, 4))
523
+
524
+ # Step 4
525
+ story.append(Paragraph("STEP 4: Document and Follow Up", styles['SOPStepHeading']))
526
+ s4_bullets = [
527
+ "Record all corrections made and the confirmation number",
528
+ "Update the internal claim notes with the correction details",
529
+ "Follow up in 30 days if no response received",
530
+ f"All corrections must be filed within {payer['filing_deadline']}",
531
+ ]
532
+ for b in s4_bullets:
533
+ story.append(Paragraph(f"β€’ {b}", styles['SOPBullet']))
534
+ story.append(Spacer(1, 6))
535
+
536
+ # ── Prevention ──
537
+ story.append(Paragraph("Prevention Tips", styles['SOPHeading']))
538
+ prevention = [
539
+ "Use diagnosis codes at highest level of specificity available",
540
+ "Verify modifier requirements for telehealth, bilateral, and multiple procedure codes",
541
+ "Always populate referring provider NPI when service was referred",
542
+ "Obtain signed ABN before rendering services that may not be covered",
543
+ "Use a claim scrubber tool to catch errors before submission",
544
+ ]
545
+ for p in prevention:
546
+ story.append(Paragraph(f"β€’ {p}", styles['SOPBullet']))
547
+
548
+
549
+ def create_sop_pdf(payer_key, payer_config):
550
+ """Generate a complete SOP PDF for one payer."""
551
+ output_path = os.path.join(SOP_DIR, payer_config["filename"])
552
+ styles = build_styles()
553
+
554
+ doc = SimpleDocTemplate(
555
+ output_path,
556
+ pagesize=letter,
557
+ topMargin=0.6 * inch,
558
+ bottomMargin=0.6 * inch,
559
+ leftMargin=0.6 * inch,
560
+ rightMargin=0.6 * inch,
561
+ )
562
+
563
+ story = []
564
+
565
+ # Page 1-2: CO-16 SOP
566
+ add_co16_section(story, payer_config, styles)
567
+
568
+ # Page break
569
+ story.append(PageBreak())
570
+
571
+ # Page 3-4+: CO-252 SOP (the main one for our pipeline)
572
+ add_co252_section(story, payer_config, styles)
573
+
574
+ doc.build(story)
575
+ print(f" βœ“ Created: {output_path}")
576
+
577
+
578
+ # ──────────────────────────────────────────────
579
+ # Main
580
+ # ──────────────────────────────────────────────
581
+ if __name__ == "__main__":
582
+ print("Generating synthetic SOP PDFs for all payers...\n")
583
+
584
+ for payer_key, payer_config in PAYERS.items():
585
+ create_sop_pdf(payer_key, payer_config)
586
+
587
+ print(f"\nβœ… {len(PAYERS)} SOP PDFs generated successfully!")
588
+ print(" Payers:")
589
+ for k, v in PAYERS.items():
590
+ print(f" β€’ {v['name']} β†’ sop/{v['filename']}")
591
+ print(" ALL payer names are 100% fictitious.")
592
+ print(" Each SOP contains CO-16 and CO-252 resolution workflows.")
593
+ print(" CO-252 section includes specific M127 remark code scenario.")
sop/Crestview_Health.pdf CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:5796c9627924cd5e3844b48218912371b5f4e2d66815aa856f3d7f749d51b140
3
- size 14114
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e3970362573a402dc856f4045e661ea016b8f5a99b6282a2ccae703b4baa51ea
3
+ size 14407
sop/NationalCare.pdf CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:33585c4745c1474d4cc9612d3e1bca11a3eeaae7707c8d400797d05323eb9a3b
3
- size 14041
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:61e14815716f12a2001bd8db8869e20860996420f19610034898b4b23793c3d8
3
+ size 14370
sop/Summit_Health.pdf CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:8f8146a25c07894c5f66fcab1dfc3ced1266d75faf7ce05968d4fa4a4801def1
3
- size 14122
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:22369f5def366ce4cb67932e0a74dae5b8164243ca4a89a06bcb3e2ddd7cbd83
3
+ size 14413