blessedpug commited on
Commit
964980b
·
1 Parent(s): 98b2898

Added Dynamic HTML form filling - Implemented batch upload and processing with debug output

Browse files
Files changed (3) hide show
  1. app.py +10 -11
  2. pipeline.py +222 -43
  3. templates/medical_form.html +112 -10
app.py CHANGED
@@ -1,5 +1,5 @@
1
  import gradio as gr
2
- from pipeline import extract_info_batch, extract_child_fee_info,extract_medical_info
3
  from PIL import Image
4
 
5
 
@@ -71,13 +71,12 @@ with gr.Blocks() as demo:
71
  with gr.Tab("Medical Reimbursement Form"):
72
  with gr.Row():
73
  with gr.Column(scale=2):
74
- medical_img_input = gr.Image(
75
- type="pil",
76
- label="Image Upload",
77
- elem_id="upload-img",
78
- show_label=False,
79
- height=512,
80
- width=512
81
  )
82
  with gr.Column(scale=2):
83
  with gr.Row():
@@ -88,12 +87,12 @@ with gr.Blocks() as demo:
88
  med_designation = gr.Textbox(label="Designation")
89
  med_ext_code = gr.Textbox(label="Extention No.")
90
  med_emp_code = gr.Textbox(label="Employee Code")
91
- medical_upload_btn = gr.Button("Upload and Process")
92
- preview_medical_output = gr.File(label="Download Filled Form")
93
 
94
 
95
  medical_upload_btn.click(
96
- fn=extract_medical_info,
97
  inputs=[medical_img_input,med_emp_name,med_emp_code,med_department,med_designation,med_company_name,med_ext_code],
98
  outputs=preview_medical_output
99
  )
 
1
  import gradio as gr
2
+ from pipeline import extract_info_batch, extract_child_fee_info, extract_medical_info, extract_medical_info_batch
3
  from PIL import Image
4
 
5
 
 
71
  with gr.Tab("Medical Reimbursement Form"):
72
  with gr.Row():
73
  with gr.Column(scale=2):
74
+ medical_img_input = gr.File(
75
+ label="Upload Medical Form Images (Multiple Allowed)",
76
+ file_count="multiple",
77
+ file_types=["image"],
78
+ elem_id="medical-upload-img-batch",
79
+ show_label=True
 
80
  )
81
  with gr.Column(scale=2):
82
  with gr.Row():
 
87
  med_designation = gr.Textbox(label="Designation")
88
  med_ext_code = gr.Textbox(label="Extention No.")
89
  med_emp_code = gr.Textbox(label="Employee Code")
90
+ medical_upload_btn = gr.Button("Upload and Process Batch")
91
+ preview_medical_output = gr.File(label="Download Consolidated Form (HTML)")
92
 
93
 
94
  medical_upload_btn.click(
95
+ fn=extract_medical_info_batch,
96
  inputs=[medical_img_input,med_emp_name,med_emp_code,med_department,med_designation,med_company_name,med_ext_code],
97
  outputs=preview_medical_output
98
  )
pipeline.py CHANGED
@@ -9,6 +9,7 @@ from models import ReceiptData, ChildFeeForm
9
  from form_fill import fill_child_fee_pdf, fill_medical_pdf
10
  from fraud import process_receipt
11
  from datetime import datetime
 
12
 
13
 
14
  load_dotenv()
@@ -68,9 +69,9 @@ medical_form_system_prompt = (
68
  " name: str #the patient name\n"
69
  " relationship: # self, spouse, parent, child\n"
70
  " category: # in-patient, out-patient, maternity(cesarean), maternity(normal)\n"
71
- " detail: # doctor's fee, diagnostic tests, medicines, other hospitalization\n"
72
- " bill_month: Optional[str] = None # Bill Month Field, if not directly stated, find the date and infer the month from that, if not found return null\n"
73
- " amount: float\n"
74
  "class Form(BaseModel):\n"
75
  " claims: List[Item]\n"
76
  " total: float\n"
@@ -80,6 +81,7 @@ medical_form_system_prompt = (
80
  "- Only return a valid JSON object matching the model above.\n"
81
  "- Do not add any explanation or extra text—only the JSON."
82
  "- Try your very best to extract this information as it is very important that you do so\n"
 
83
  "- If you are unable to extract information, return an empty json in the format requested above, never give a response other than a json"
84
  )
85
 
@@ -129,7 +131,6 @@ def extract_info(pil_img):
129
  data = json.loads(raw_output)
130
  # print(data)
131
  validated = ReceiptData(**data)
132
- # json_block = json.dumps(validated.dict(), indent=2, ensure_ascii=False)
133
 
134
  validated_dict = validated.dict() # This is a Python dict, perfect for fraud check
135
  print(validated_dict)
@@ -221,7 +222,7 @@ def extract_medical_info(pil_img, emp_name, emp_code, department, designation, c
221
  {"role": "system", "content": medical_form_system_prompt},
222
  {"role": "user",
223
  "content": [
224
- {"type": "text", "text": "Here is a child fee bill image:"},
225
  {"type": "image_url", "image_url": {"url": "data:image/png;base64," + img_base64}}
226
  ]}
227
  ]
@@ -230,45 +231,223 @@ def extract_medical_info(pil_img, emp_name, emp_code, department, designation, c
230
  print(raw_output)
231
  try:
232
  if raw_output.startswith("```"):
233
- raw_output = raw_output.strip("` \n")
234
  if raw_output.startswith("json"):
235
  raw_output = raw_output[4:].strip()
236
- data = json.loads(raw_output)
237
- print(data)
238
- # Validate if needed:
239
- # ChildFeeForm(**data)
240
-
241
- claims = data.get("claims", [])
242
- bill_month = ""
243
- if claims and "bill_month" in claims[0]:
244
- bill_month = claims[0]["bill_month"]
245
-
246
- date = datetime.now().strftime("%d-%b-%Y") # e.g., "10-Jun-2024"
247
- total = data.get("total", 0)
248
-
249
- print("bill month:",bill_month)
250
-
251
- print("total:",total)
252
- os.makedirs("outputs", exist_ok=True)
253
- output_pdf_path = f"outputs/filled_medical_form_{uuid.uuid4().hex}.pdf"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
 
255
-
256
- filled_pdf_path = fill_medical_pdf(
257
- template_pdf_path="Medical Reim. Form.pdf",
258
- output_pdf_path=output_pdf_path,
259
- company=company,
260
- employee_name=emp_name,
261
- employee_code=emp_code,
262
- department=department,
263
- designation=designation,
264
- extension_no=extension_no,
265
- billing_month=bill_month,
266
- claims=claims,
267
- date= date,
268
- total=total
269
- )
270
-
271
- return filled_pdf_path # Return path to Gradio for download
272
  except Exception as e:
273
- print("ERROR:", e)
274
- return None # or f"Error: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  from form_fill import fill_child_fee_pdf, fill_medical_pdf
10
  from fraud import process_receipt
11
  from datetime import datetime
12
+ import html
13
 
14
 
15
  load_dotenv()
 
69
  " name: str #the patient name\n"
70
  " relationship: # self, spouse, parent, child\n"
71
  " category: # in-patient, out-patient, maternity(cesarean), maternity(normal)\n"
72
+ " detail: # doctor's fee, diagnostic tests, medicines, other hospitalization - only chose from these options, infer from the image which one it is\n"
73
+ " bill_month: Optional[str] = None # Bill Month Field, if not directly stated, find the date and infer the month from that, format should be month - year (mm/yy), if not found return null\n"
74
+ " amount: float - try your best to extract the exact amount present in the image, sometimes there will be discounts applied, look for the total amount paid\n"
75
  "class Form(BaseModel):\n"
76
  " claims: List[Item]\n"
77
  " total: float\n"
 
81
  "- Only return a valid JSON object matching the model above.\n"
82
  "- Do not add any explanation or extra text—only the JSON."
83
  "- Try your very best to extract this information as it is very important that you do so\n"
84
+ "- Only extract claim items that have an explicitly stated amount next to a discernible service or in an itemized list. Do not infer multiple items if only one amount is clearly listed as a charge.\n"
85
  "- If you are unable to extract information, return an empty json in the format requested above, never give a response other than a json"
86
  )
87
 
 
131
  data = json.loads(raw_output)
132
  # print(data)
133
  validated = ReceiptData(**data)
 
134
 
135
  validated_dict = validated.dict() # This is a Python dict, perfect for fraud check
136
  print(validated_dict)
 
222
  {"role": "system", "content": medical_form_system_prompt},
223
  {"role": "user",
224
  "content": [
225
+ {"type": "text", "text": "Here is a medical form image:"},
226
  {"type": "image_url", "image_url": {"url": "data:image/png;base64," + img_base64}}
227
  ]}
228
  ]
 
231
  print(raw_output)
232
  try:
233
  if raw_output.startswith("```"):
234
+ raw_output = raw_output.strip("` \\n")
235
  if raw_output.startswith("json"):
236
  raw_output = raw_output[4:].strip()
237
+ data_from_llm = json.loads(raw_output)
238
+ print("Data from LLM:", data_from_llm)
239
+
240
+ # Extract bill_month from LLM data
241
+ claims_from_llm = data_from_llm.get("claims", [])
242
+ bill_month_from_llm = "" # Default to empty string
243
+ if claims_from_llm and isinstance(claims_from_llm, list) and len(claims_from_llm) > 0:
244
+ first_claim = claims_from_llm[0]
245
+ if isinstance(first_claim, dict) and "bill_month" in first_claim:
246
+ bill_month_from_llm = first_claim.get("bill_month", "")
247
+ print(f"Extracted billing month from LLM: '{bill_month_from_llm}'")
248
+
249
+ # Get total from LLM as well
250
+ total_from_llm = data_from_llm.get("total", 0) # Default to 0 if not found
251
+ print(f"Extracted total from LLM: {total_from_llm}")
252
+
253
+ form_header_data = {
254
+ "company": company,
255
+ "employee_name": emp_name,
256
+ "department": department,
257
+ "designation": designation,
258
+ "extension_no": extension_no,
259
+ "employee_code": emp_code,
260
+ "date": datetime.now().strftime("%Y-%m-%d"),
261
+ "billing_month": bill_month_from_llm,
262
+ "claims": claims_from_llm, # Pass the full claims array
263
+ "total_amount": total_from_llm # Pass the LLM's total (JS will also calculate)
264
+ }
265
+ json_data_for_script = json.dumps(form_header_data)
266
+
267
+ html_template_path = os.path.join("templates", "medical_form.html")
268
+ with open(html_template_path, "r", encoding="utf-8") as f:
269
+ html_content = f.read()
270
+
271
+ # Correctly formatted script to inject
272
+ script_to_inject = f'''
273
+ <script>
274
+ document.addEventListener('DOMContentLoaded', function() {{
275
+ const dataToLoad = {json_data_for_script};
276
+ if (typeof populateMedicalForm === 'function') {{
277
+ populateMedicalForm(dataToLoad);
278
+ }} else {{
279
+ console.error('populateMedicalForm function not defined when trying to load data.');
280
+ }}
281
+ }});
282
+ </script>
283
+ </body>'''
284
+ # Ensure we are replacing the </body> tag correctly
285
+ if "</body>" in html_content:
286
+ html_content = html_content.replace("</body>", script_to_inject, 1)
287
+ else:
288
+ # Fallback if no </body> tag, append at the end (less ideal)
289
+ html_content += script_to_inject.replace("</body>","") # remove the body tag we added if appending
290
+
291
+
292
+ output_dir = "outputs"
293
+ os.makedirs(output_dir, exist_ok=True)
294
+ output_html_filename = f"filled_medical_form_{uuid.uuid4().hex}.html"
295
+ output_html_path = os.path.join(output_dir, output_html_filename)
296
+
297
+ with open(output_html_path, "w", encoding="utf-8") as f:
298
+ f.write(html_content)
299
+
300
+ print(f"Populated HTML form saved to: {output_html_path}")
301
+ return output_html_path
302
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  except Exception as e:
304
+ print(f"ERROR in extract_medical_info: {e}")
305
+ raw_output_escaped = html.escape(raw_output) # Escape raw_output for safe HTML display
306
+ error_html_content = f"<html><body><h1>Error</h1><p>{html.escape(str(e))}</p><p>Raw LLM Output:</p><pre>{raw_output_escaped}</pre></body></html>"
307
+ output_dir = "outputs"
308
+ os.makedirs(output_dir, exist_ok=True)
309
+ error_html_filename = f"error_medical_form_{uuid.uuid4().hex}.html"
310
+ error_html_path = os.path.join(output_dir, error_html_filename)
311
+ with open(error_html_path, "w", encoding="utf-8") as f:
312
+ f.write(error_html_content)
313
+ return error_html_path
314
+
315
+
316
+ def extract_medical_info_batch(image_file_list, emp_name, emp_code, department, designation, company, extension_no):
317
+ """
318
+ Processes a batch of medical form images, consolidates all claims,
319
+ generates a single populated HTML file, and returns its path.
320
+ """
321
+ if not image_file_list:
322
+ # Return an error HTML or an empty/default HTML path if no files are provided
323
+ error_content = "<html><body><h1>No Images Provided</h1><p>Please upload at least one medical form image.</p></body></html>"
324
+ output_dir = "outputs"
325
+ os.makedirs(output_dir, exist_ok=True)
326
+ error_filename = f"error_no_medical_form_images_{uuid.uuid4().hex[:8]}.html"
327
+ error_path = os.path.join(output_dir, error_filename)
328
+ with open(error_path, "w", encoding="utf-8") as f:
329
+ f.write(error_content)
330
+ return error_path # Gradio expects a list, so we might need to adjust app.py output handling or return [error_path]
331
+
332
+ consolidated_claims = []
333
+ first_billing_month_found = "" # To store the billing month from the first processed image that has one
334
+ grand_total_from_llm = 0.0
335
+ processed_file_names = []
336
+
337
+ print(f"DEBUG: Starting batch processing for {len(image_file_list)} images.") # DEBUG
338
+
339
+ for i, image_file_path_obj in enumerate(image_file_list): # DEBUG: Added enumerate
340
+ image_name_for_log = image_file_path_obj.name if hasattr(image_file_path_obj, 'name') else str(image_file_path_obj)
341
+ processed_file_names.append(os.path.basename(image_name_for_log))
342
+ print(f"DEBUG: --- Iteration {i+1} for image: {image_name_for_log} ---") # DEBUG
343
+ try:
344
+ print(f"Processing medical form for consolidation: {image_name_for_log}")
345
+ pil_img = Image.open(image_file_path_obj.name if hasattr(image_file_path_obj, 'name') else image_file_path_obj)
346
+
347
+ processed_image = preprocess_image(pil_img)
348
+ img_bytes = pil_to_bytes(processed_image)
349
+ img_base64 = base64.b64encode(img_bytes.getvalue()).decode("utf-8")
350
+
351
+ response = openai.chat.completions.create(
352
+ model="gpt-4o",
353
+ messages=[
354
+ {"role": "system", "content": medical_form_system_prompt},
355
+ {"role": "user",
356
+ "content": [
357
+ {"type": "text", "text": f"Extract claims from this medical form image ({image_name_for_log}):"},
358
+ {"type": "image_url", "image_url": {"url": "data:image/png;base64," + img_base64}}
359
+ ]}
360
+ ]
361
+ )
362
+ raw_output = response.choices[0].message.content
363
+ print(f"DEBUG: Raw LLM output for {image_name_for_log}:\n{raw_output}\n") # DEBUG
364
+
365
+ if raw_output.startswith("```"):
366
+ raw_output = raw_output.strip("` \\n")
367
+ if raw_output.startswith("json"):
368
+ raw_output = raw_output[4:].strip()
369
+ data_from_llm = json.loads(raw_output)
370
+ print(f"DEBUG: Parsed LLM data for {image_name_for_log}:\n{json.dumps(data_from_llm, indent=2)}\n") # DEBUG
371
+
372
+ current_claims = data_from_llm.get("claims", [])
373
+ print(f"DEBUG: Current claims extracted for {image_name_for_log}: {len(current_claims)} items") # DEBUG
374
+ # print(f"DEBUG: Current claims content for {image_name_for_log}: {json.dumps(current_claims, indent=2)}") # DEBUG - Can be very verbose
375
+
376
+ if current_claims and isinstance(current_claims, list):
377
+ consolidated_claims.extend(current_claims)
378
+ print(f"DEBUG: Consolidated claims after {image_name_for_log}: {len(consolidated_claims)} items total") # DEBUG
379
+ # print(f"DEBUG: Consolidated claims content after {image_name_for_log}: {json.dumps(consolidated_claims, indent=2)}") # DEBUG - Can be very verbose
380
+
381
+ # Get billing month from the first item of the current form's claims, if not already found
382
+ if not first_billing_month_found and current_claims and isinstance(current_claims, list) and len(current_claims) > 0:
383
+ first_claim_current_img = current_claims[0]
384
+ if isinstance(first_claim_current_img, dict) and "bill_month" in first_claim_current_img:
385
+ first_billing_month_found = first_claim_current_img.get("bill_month", "")
386
+
387
+ grand_total_from_llm += float(data_from_llm.get("total", 0) or 0) # Ensure float and handle None
388
+
389
+ except Exception as e:
390
+ print(f"ERROR processing medical form image '{image_name_for_log}' for consolidation: {e}")
391
+ # We can decide to stop or continue. For now, let's log and continue,
392
+ # but not add its claims. The final HTML will be generated with claims from successful ones.
393
+ # Optionally, add an error marker to the final HTML or a separate error report.
394
+ # For simplicity here, we just skip this file's claims on error.
395
+
396
+ print(f"DEBUG: --- End of loop for consolidating claims ---") # DEBUG
397
+ print(f"DEBUG: Final consolidated_claims before HTML generation: {len(consolidated_claims)} items") # DEBUG
398
+ # print(f"DEBUG: Final consolidated_claims content: {json.dumps(consolidated_claims, indent=2)}") # DEBUG - Can be very verbose
399
+
400
+ # Now, prepare and save the single consolidated HTML file
401
+ form_header_data = {
402
+ "company": company,
403
+ "employee_name": emp_name,
404
+ "department": department,
405
+ "designation": designation,
406
+ "extension_no": extension_no,
407
+ "employee_code": emp_code,
408
+ "date": datetime.now().strftime("%Y-%m-%d"),
409
+ "billing_month": first_billing_month_found,
410
+ "claims": consolidated_claims, # All claims from all images
411
+ "total_amount": grand_total_from_llm # Sum of totals from LLM (JS will also recalc)
412
+ }
413
+ json_data_for_script = json.dumps(form_header_data)
414
+
415
+ html_template_path = os.path.join("templates", "medical_form.html")
416
+ with open(html_template_path, "r", encoding="utf-8") as f:
417
+ html_content_template = f.read()
418
+
419
+ current_html_content = str(html_content_template)
420
+
421
+ script_to_inject = f'''
422
+ <script>
423
+ document.addEventListener('DOMContentLoaded', function() {{
424
+ const dataToLoad = {json_data_for_script};
425
+ if (typeof populateMedicalForm === 'function') {{
426
+ populateMedicalForm(dataToLoad);
427
+ }} else {{
428
+ console.error('populateMedicalForm function not defined when trying to load data.');
429
+ }}
430
+ }});
431
+ </script>
432
+ </body>'''
433
+ if "</body>" in current_html_content:
434
+ current_html_content = current_html_content.replace("</body>", script_to_inject, 1)
435
+ else:
436
+ current_html_content += script_to_inject.replace("</body>","")
437
+
438
+ output_dir = "outputs"
439
+ os.makedirs(output_dir, exist_ok=True)
440
+
441
+ # Create a filename for the consolidated report
442
+ # You could include employee name or a timestamp if these are consistent per batch.
443
+ consolidated_filename_भाग = "_-".join(filter(None, processed_file_names))
444
+ if not consolidated_filename_भाग:
445
+ consolidated_filename_भाग = "batch"
446
+ output_html_filename = f"consolidated_medical_form_{consolidated_filename_भाग[:50]}_{uuid.uuid4().hex[:8]}.html"
447
+ output_html_path = os.path.join(output_dir, output_html_filename)
448
+
449
+ with open(output_html_path, "w", encoding="utf-8") as f:
450
+ f.write(current_html_content)
451
+
452
+ print(f"Consolidated HTML form saved to: {output_html_path}")
453
+ return output_html_path # Return path to the single consolidated HTML
templates/medical_form.html CHANGED
@@ -218,8 +218,8 @@
218
  <th class="table-claim-col">Detail</th>
219
  <th class="table-claim-col">Amount</th>
220
  </tr>
221
- <tbody>
222
- {{claims_rows}}
223
  </tbody>
224
  </table>
225
  <table style="margin-top: 0;">
@@ -263,7 +263,104 @@
263
  </div>
264
  </div>
265
  <script>
266
- // Auto sum the Amount columns
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
  const amountFields = [
268
  document.querySelector('input[name="claim_amount1"]'),
269
  document.querySelector('input[name="claim_amount2"]'),
@@ -272,15 +369,20 @@
272
  ];
273
  const totalField = document.getElementById('total_amount');
274
  amountFields.forEach(f => {
275
- f.addEventListener('input', () => {
276
- let total = 0;
277
- amountFields.forEach(ff => {
278
- let val = parseFloat(ff.value);
279
- if (!isNaN(val)) total += val;
 
 
 
 
 
280
  });
281
- totalField.value = total > 0 ? total.toFixed(2) : '';
282
- });
283
  });
 
284
  </script>
285
  </body>
286
  </html>
 
218
  <th class="table-claim-col">Detail</th>
219
  <th class="table-claim-col">Amount</th>
220
  </tr>
221
+ <tbody id="claims_table_body">
222
+ <!-- Claim rows will be dynamically inserted here by JavaScript -->
223
  </tbody>
224
  </table>
225
  <table style="margin-top: 0;">
 
263
  </div>
264
  </div>
265
  <script>
266
+ // Function to populate the main form fields from a JSON object
267
+ function populateMedicalForm(jsonData) {
268
+ if (!jsonData) {
269
+ console.error("No JSON data provided to populateMedicalForm");
270
+ return;
271
+ }
272
+
273
+ // Helper to safely set value
274
+ function setValue(name, value) {
275
+ const element = document.querySelector(`input[name="${name}"]`);
276
+ if (element) {
277
+ element.value = value !== undefined && value !== null ? value : '';
278
+ } else {
279
+ console.warn(`Element with name "${name}" not found.`);
280
+ }
281
+ }
282
+
283
+ setValue("company", jsonData.company);
284
+ setValue("name", jsonData.employee_name); // Assuming JSON key is employee_name for HTML field 'name'
285
+ setValue("department", jsonData.department);
286
+ setValue("designation", jsonData.designation);
287
+ setValue("extension", jsonData.extension_no); // Assuming JSON key is extension_no for HTML field 'extension'
288
+ setValue("employee_code", jsonData.employee_code);
289
+ setValue("date", jsonData.date);
290
+ // billing_month is now being sent, so we uncomment and use it.
291
+ setValue("billing_month", jsonData.billing_month);
292
+
293
+ // Populate the claims table
294
+ populateClaimsTable(jsonData.claims);
295
+ // Setup listeners for dynamic amount fields *after* table is populated
296
+ setupAmountListeners();
297
+ }
298
+
299
+ // Function to populate the claims table dynamically
300
+ function populateClaimsTable(claimsArray) {
301
+ const tableBody = document.getElementById('claims_table_body');
302
+ tableBody.innerHTML = ''; // Clear existing rows
303
+
304
+ if (!claimsArray || !Array.isArray(claimsArray)) {
305
+ console.warn("No claims data provided or data is not an array.");
306
+ // Add a default empty row if you always want at least one row, or leave empty
307
+ // const row = tableBody.insertRow();
308
+ // row.insertCell().innerHTML = '<input type="text" class="table-input" name="claim_name_1">';
309
+ // row.insertCell().innerHTML = '<input type="text" class="table-input" name="claim_relationship_1">';
310
+ // row.insertCell().innerHTML = '<input type="text" class="table-input" name="claim_category_1">';
311
+ // row.insertCell().innerHTML = '<input type="text" class="table-input" name="claim_detail_1">';
312
+ // row.insertCell().innerHTML = '<input type="number" class="table-input claim-amount" name="claim_amount_1" step="0.01" min="0">';
313
+ return;
314
+ }
315
+
316
+ claimsArray.forEach((claim, index) => {
317
+ const row = tableBody.insertRow();
318
+ const i = index + 1; // For 1-based indexing in names if needed by backend/PDF
319
+
320
+ row.insertCell().innerHTML = `<input type="text" class="table-input" name="claim_name_${i}" value="${claim.name || ''}">`;
321
+ row.insertCell().innerHTML = `<input type="text" class="table-input" name="claim_relationship_${i}" value="${claim.relationship || ''}">`;
322
+ row.insertCell().innerHTML = `<input type="text" class="table-input" name="claim_category_${i}" value="${claim.category || ''}">`;
323
+ row.insertCell().innerHTML = `<input type="text" class="table-input" name="claim_detail_${i}" value="${claim.detail || ''}">`;
324
+ row.insertCell().innerHTML = `<input type="number" class="table-input claim-amount" name="claim_amount_${i}" step="0.01" min="0" value="${claim.amount || ''}">`;
325
+ });
326
+ // If no claims, you might want to add a few empty rows for manual input
327
+ if (claimsArray.length === 0) {
328
+ for (let i = 1; i <= 4; i++) { // Add 4 empty rows for example
329
+ const row = tableBody.insertRow();
330
+ row.insertCell().innerHTML = `<input type="text" class="table-input" name="claim_name_${i}">`;
331
+ row.insertCell().innerHTML = `<input type="text" class="table-input" name="claim_relationship_${i}">`;
332
+ row.insertCell().innerHTML = `<input type="text" class="table-input" name="claim_category_${i}">`;
333
+ row.insertCell().innerHTML = `<input type="text" class="table-input" name="claim_detail_${i}">`;
334
+ row.insertCell().innerHTML = `<input type="number" class="table-input claim-amount" name="claim_amount_${i}" step="0.01" min="0">`;
335
+ }
336
+ }
337
+ }
338
+
339
+ // Function to setup amount listeners and calculate total
340
+ function setupAmountListeners() {
341
+ const amountFields = document.querySelectorAll('.claim-amount'); // Select all claim amount fields
342
+ const totalField = document.getElementById('total_amount');
343
+
344
+ amountFields.forEach(f => {
345
+ f.addEventListener('input', () => {
346
+ let total = 0;
347
+ amountFields.forEach(ff => {
348
+ let val = parseFloat(ff.value);
349
+ if (!isNaN(val)) total += val;
350
+ });
351
+ totalField.value = total > 0 ? total.toFixed(2) : '';
352
+ });
353
+ // Trigger input event once to calculate initial total if fields are pre-filled
354
+ f.dispatchEvent(new Event('input'));
355
+ });
356
+ // If there are no amount fields initially, ensure total is 0 or empty
357
+ if (amountFields.length === 0) {
358
+ totalField.value = '';
359
+ }
360
+ }
361
+
362
+ // Auto sum the Amount columns for claims (OLD STATIC SCRIPT - REMOVE OR COMMENT OUT)
363
+ /*
364
  const amountFields = [
365
  document.querySelector('input[name="claim_amount1"]'),
366
  document.querySelector('input[name="claim_amount2"]'),
 
369
  ];
370
  const totalField = document.getElementById('total_amount');
371
  amountFields.forEach(f => {
372
+ if(f) { // Add a check if the field exists
373
+ f.addEventListener('input', () => {
374
+ let total = 0;
375
+ amountFields.forEach(ff => {
376
+ if(ff) { // Add a check if the field exists
377
+ let val = parseFloat(ff.value);
378
+ if (!isNaN(val)) total += val;
379
+ }
380
+ });
381
+ totalField.value = total > 0 ? total.toFixed(2) : '';
382
  });
383
+ }
 
384
  });
385
+ */
386
  </script>
387
  </body>
388
  </html>