medicafe 0.240419.2__py3-none-any.whl → 0.240613.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.

Files changed (38) hide show
  1. MediBot/MediBot.bat +174 -38
  2. MediBot/MediBot.py +80 -77
  3. MediBot/MediBot_Charges.py +0 -28
  4. MediBot/MediBot_Crosswalk_Library.py +281 -0
  5. MediBot/MediBot_Post.py +0 -0
  6. MediBot/MediBot_Preprocessor.py +138 -211
  7. MediBot/MediBot_Preprocessor_lib.py +496 -0
  8. MediBot/MediBot_UI.py +80 -35
  9. MediBot/MediBot_dataformat_library.py +79 -35
  10. MediBot/MediBot_docx_decoder.py +295 -0
  11. MediBot/update_medicafe.py +46 -8
  12. MediLink/MediLink.py +207 -108
  13. MediLink/MediLink_837p_encoder.py +299 -214
  14. MediLink/MediLink_837p_encoder_library.py +445 -245
  15. MediLink/MediLink_API_v2.py +174 -0
  16. MediLink/MediLink_APIs.py +139 -0
  17. MediLink/MediLink_ConfigLoader.py +44 -32
  18. MediLink/MediLink_DataMgmt.py +297 -89
  19. MediLink/MediLink_Decoder.py +63 -0
  20. MediLink/MediLink_Down.py +73 -102
  21. MediLink/MediLink_ERA_decoder.py +4 -4
  22. MediLink/MediLink_Gmail.py +479 -4
  23. MediLink/MediLink_Mailer.py +0 -0
  24. MediLink/MediLink_Parser.py +111 -0
  25. MediLink/MediLink_Scan.py +0 -0
  26. MediLink/MediLink_Scheduler.py +2 -131
  27. MediLink/MediLink_StatusCheck.py +0 -4
  28. MediLink/MediLink_UI.py +87 -27
  29. MediLink/MediLink_Up.py +301 -45
  30. MediLink/MediLink_batch.bat +1 -1
  31. MediLink/test.py +74 -0
  32. medicafe-0.240613.0.dist-info/METADATA +55 -0
  33. medicafe-0.240613.0.dist-info/RECORD +43 -0
  34. {medicafe-0.240419.2.dist-info → medicafe-0.240613.0.dist-info}/WHEEL +5 -5
  35. medicafe-0.240419.2.dist-info/METADATA +0 -19
  36. medicafe-0.240419.2.dist-info/RECORD +0 -32
  37. {medicafe-0.240419.2.dist-info → medicafe-0.240613.0.dist-info}/LICENSE +0 -0
  38. {medicafe-0.240419.2.dist-info → medicafe-0.240613.0.dist-info}/top_level.txt +0 -0
MediLink/MediLink.py CHANGED
@@ -1,38 +1,51 @@
1
1
  import os
2
- import MediLink_ConfigLoader
3
- import MediLink_837p_encoder
4
- import logging
5
2
  import MediLink_Down
6
3
  import MediLink_Up
4
+ import MediLink_ConfigLoader
5
+ import MediLink_837p_encoder
6
+ import MediLink_DataMgmt
7
7
 
8
8
  # For UI Functions
9
9
  import os
10
10
  import MediLink_UI # Import UI module for handling all user interfaces
11
-
12
- """
13
- Development Tasks for Backend Enhancement in MediSoft Claims Submittal (MediLink) Script:
14
-
15
- Implement dynamic configurations for multiple endpoints (Availity, Optum, PNT Data) with environmental settings support.
16
- Enhance file detection with detailed logging and introduce integrity checks for pre-processing validation.
17
- Verify file transmissions via WinSCP log analysis for successful endpoint acknowledgments and secure data transfer.
18
- Automate response file handling from endpoints and integrate feedback into MediSoft with exception alerts.
19
- De-persisting Intermediate Files.
20
- When transmissions fail, there is some retaining of patient data in memory or something that seems to default
21
- any new endpoint changes to Optum. May need to "de-confirm" patients, but leave the suggested endpoints as the previously
22
- confirmed endpoints. This should be similar logic to if the user made a mistake and wants to go back and fix it.
23
- These tasks involve backend enhancements such as dynamic configurations, file detection improvements, file transmission verification, automation of response file handling, and management of intermediate files and transmission failures.
24
-
25
- TODO Crosswalk should be to PayerID key vs Medisoft:Endpoint.
26
- TODO Availity has a response file that says "File was received at TIME. File was sent for processing." as a confirmation
27
- that sits in the SendFiles folder after a submittal.
28
-
29
- BUG Suggested Endpoint when you say 'n' to proceed with transmission is not getting updated with the endpoint
30
- that was selected previously by the user. However, when we go back to the confirmation list, we do have a persist of the assignment.
31
- This can be confusing for the user.
32
- """
33
-
34
- # Setup basic logging
35
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s\n')
11
+ from tqdm import tqdm
12
+
13
+ # Add parent directory of the project to the Python path
14
+ import sys
15
+ project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
16
+ sys.path.append(project_dir)
17
+
18
+ from MediBot import MediBot_Preprocessor_lib
19
+ load_insurance_data_from_mains = MediBot_Preprocessor_lib.load_insurance_data_from_mains
20
+ from MediBot import MediBot_Crosswalk_Library
21
+
22
+ # Define insurance options with codes and descriptions.
23
+ # TODO This needs to move to the config file
24
+ insurance_options = {
25
+ "11": "Other Non-Federal Programs",
26
+ "12": "Preferred Provider Organization (PPO)",
27
+ "13": "Point of Service (POS)",
28
+ "14": "Exclusive Provider Organization (EPO)",
29
+ "15": "Indemnity Insurance",
30
+ "16": "Health Maintenance Organization (HMO) Medicare Risk",
31
+ "17": "Dental Maintenance Organization",
32
+ "AM": "Automobile Medical",
33
+ "BL": "Blue Cross/Blue Shield",
34
+ "CH": "Champus",
35
+ "CI": "Commercial Insurance Co.",
36
+ "DS": "Disability",
37
+ "FI": "Federal Employees Program",
38
+ "HM": "Health Maintenance Organization",
39
+ "LM": "Liability Medical",
40
+ "MA": "Medicare Part A",
41
+ "MB": "Medicare Part B",
42
+ "MC": "Medicaid",
43
+ "OF": "Other Federal Program",
44
+ "TV": "Title V",
45
+ "VA": "Veterans Affairs Plan",
46
+ "WC": "Workers Compensation Health Claim",
47
+ "ZZ": "Mutually Defined"
48
+ }
36
49
 
37
50
  def detect_and_display_file_summaries(directory_path, config, crosswalk):
38
51
  """
@@ -41,35 +54,61 @@ def detect_and_display_file_summaries(directory_path, config, crosswalk):
41
54
 
42
55
  :param directory_path: Path to the directory containing files to be detected.
43
56
  :param config: Configuration settings loaded from a JSON file.
57
+ :param crosswalk: Crosswalk data for mapping purposes.
44
58
  :return: A tuple containing a list of new file paths and the detailed patient data.
45
59
  """
46
- new_files = detect_new_files(directory_path)
47
- if not new_files:
48
- print(" No new claims detected. Check Medisoft claims output.\n")
60
+ try:
61
+ new_files, file_flagged = MediLink_DataMgmt.detect_new_files(directory_path)
62
+ if not new_files:
63
+ print("No new claims detected. Check Medisoft claims output.")
64
+ MediLink_ConfigLoader.log("No new claims detected. Check Medisoft claims output.")
65
+ return False, []
66
+
67
+ if not file_flagged:
68
+ selected_files = MediLink_UI.user_select_files(new_files)
69
+ else:
70
+ # Extract the newest single latest file from the list
71
+ selected_files = [max(new_files, key=os.path.getctime)]
72
+
73
+ detailed_patient_data = [] # Initialize list for detailed patient data
74
+ for file_path in selected_files:
75
+ detailed_data = extract_and_suggest_endpoint(file_path, config, crosswalk)
76
+ detailed_patient_data.extend(detailed_data) # Accumulate detailed data for processing
77
+
78
+ # Enrich the detailed patient data with insurance type
79
+ detailed_patient_data = enrich_with_insurance_type(detailed_patient_data, insurance_options)
80
+
81
+ # Display summaries and provide an option for bulk edit
82
+ MediLink_UI.display_patient_summaries(detailed_patient_data)
83
+
84
+ # Return the list of new files and the enriched detailed patient data
85
+ return selected_files, detailed_patient_data
86
+ except Exception as e:
87
+ MediLink_ConfigLoader.log("Error in detect_and_display_file_summaries: {}".format(e))
49
88
  return False, []
50
89
 
51
- detailed_patient_data = [] # Initialize list for detailed patient data
52
- for file_path in new_files:
53
- detailed_data = extract_and_suggest_endpoint(file_path, config, crosswalk)
54
- detailed_patient_data.extend(detailed_data) # Accumulate detailed data for processing
90
+ def enrich_with_insurance_type(detailed_patient_data, patient_insurance_type_mapping=None):
91
+ """
92
+ Enriches the detailed patient data with insurance type based on patient ID.
55
93
 
56
- # Return just the list of new files and the enriched detailed patient data
57
- return new_files, detailed_patient_data
94
+ Parameters:
95
+ - detailed_patient_data: List of dictionaries containing detailed patient data.
96
+ - patient_insurance_mapping: Dictionary mapping patient IDs to their insurance types.
58
97
 
59
- def detect_new_files(directory_path, file_extension='.DAT'):
60
- """
61
- Scans the specified directory for new files with a given extension.
98
+ Returns:
99
+ - Enriched detailed patient data with insurance type added.
62
100
 
63
- :param directory_path: Path to the directory containing files to be detected.
64
- :param file_extension: Extension of the files to detect. Defaults to '.csv'.
65
- :return: A list of paths to new files detected in the directory.
101
+ TODO: Implement a function to provide `patient_insurance_mapping` from a reliable source.
66
102
  """
67
- detected_file_paths = []
68
- for filename in os.listdir(directory_path):
69
- if filename.endswith(file_extension):
70
- file_path = os.path.join(directory_path, filename)
71
- detected_file_paths.append(file_path)
72
- return detected_file_paths
103
+ if patient_insurance_type_mapping is None:
104
+ MediLink_ConfigLoader.log("No Patient:Insurance-Type mapping available.")
105
+ patient_insurance_type_mapping = {}
106
+
107
+ for data in detailed_patient_data:
108
+ patient_id = data.get('PATID') # I think this is the right name?
109
+ insurance_type = patient_insurance_type_mapping.get(patient_id, '12') # Default to '12' (PPO)
110
+ data['insurance_type'] = insurance_type
111
+ return detailed_patient_data
73
112
 
74
113
  def extract_and_suggest_endpoint(file_path, config, crosswalk):
75
114
  """
@@ -88,15 +127,42 @@ def extract_and_suggest_endpoint(file_path, config, crosswalk):
88
127
  """
89
128
  detailed_patient_data = []
90
129
 
91
- for personal_info, insurance_info, service_info in MediLink_837p_encoder.read_fixed_width_data(file_path, config.get('MediLink_Config', {})):
92
- parsed_data = MediLink_837p_encoder.parse_fixed_width_data(personal_info, insurance_info, service_info, config.get('MediLink_Config', {}))
130
+ # Load insurance data from MAINS to create a mapping from insurance names to their respective IDs
131
+ insurance_to_id = load_insurance_data_from_mains(config)
132
+ MediLink_ConfigLoader.log("Insurance data loaded from MAINS. {} insurance providers found.".format(len(insurance_to_id)))
133
+
134
+ for personal_info, insurance_info, service_info, service_info_2, service_info_3 in MediLink_DataMgmt.read_fixed_width_data(file_path):
135
+ parsed_data = MediLink_DataMgmt.parse_fixed_width_data(personal_info, insurance_info, service_info, service_info_2, service_info_3, config.get('MediLink_Config', config))
93
136
 
94
137
  primary_insurance = parsed_data.get('INAME')
95
138
 
96
- # TODO This suggested endpoint should be a payerid_to_endpoint_mapping.
97
- suggested_endpoint = crosswalk['insurance_to_endpoint_mapping'].get(primary_insurance, 'AVAILITY')
139
+ # Retrieve the insurance ID associated with the primary insurance
140
+ insurance_id = insurance_to_id.get(primary_insurance)
141
+ MediLink_ConfigLoader.log("Primary insurance ID retrieved for '{}': {}".format(primary_insurance, insurance_id))
142
+
143
+ # Use insurance ID to retrieve the payer ID(s) associated with the insurance
144
+ payer_ids = []
145
+ if insurance_id:
146
+ for payer_id, payer_data in crosswalk.get('payer_id', {}).items():
147
+ medisoft_ids = [str(id) for id in payer_data.get('medisoft_id', [])]
148
+ # MediLink_ConfigLoader.log("Payer ID: {}, Medisoft IDs: {}".format(payer_id, medisoft_ids))
149
+ if str(insurance_id) in medisoft_ids:
150
+ payer_ids.append(payer_id)
151
+ if payer_ids:
152
+ MediLink_ConfigLoader.log("Payer IDs retrieved for insurance '{}': {}".format(primary_insurance, payer_ids))
153
+ else:
154
+ MediLink_ConfigLoader.log("No payer IDs found for insurance '{}'".format(primary_insurance))
98
155
 
99
- # Directly enrich detailed patient data with additional information and suggested endpoint
156
+ # Find the suggested endpoint from the crosswalk based on the payer IDs
157
+ suggested_endpoint = 'AVAILITY' # Default endpoint if no matching payer IDs found
158
+ if payer_ids:
159
+ payer_id = payer_ids[0] # Select the first payer ID
160
+ suggested_endpoint = crosswalk['payer_id'].get(payer_id, {}).get('endpoint', 'AVAILITY')
161
+ MediLink_ConfigLoader.log("Suggested endpoint for payer ID '{}': {}".format(payer_id, suggested_endpoint))
162
+ else:
163
+ MediLink_ConfigLoader.log("No suggested endpoint found for payer IDs: {}".format(payer_ids))
164
+
165
+ # Enrich detailed patient data with additional information and suggested endpoint
100
166
  detailed_data = parsed_data.copy() # Copy parsed_data to avoid modifying the original dictionary
101
167
  detailed_data.update({
102
168
  'file_path': file_path,
@@ -112,42 +178,53 @@ def extract_and_suggest_endpoint(file_path, config, crosswalk):
112
178
  # Return only the enriched detailed patient data, eliminating the need for a separate summary list
113
179
  return detailed_patient_data
114
180
 
115
- def organize_patient_data_by_endpoint(detailed_patient_data):
116
- """
117
- Organizes detailed patient data by their confirmed endpoints.
118
- This simplifies processing and conversion per endpoint basis, ensuring that claims are generated and submitted
119
- according to the endpoint-specific requirements.
120
-
121
- :param detailed_patient_data: A list of dictionaries, each containing detailed patient data including confirmed endpoint.
122
- :return: A dictionary with endpoints as keys and lists of detailed patient data as values for processing.
123
- """
124
- organized = {}
125
- for data in detailed_patient_data:
126
- # Retrieve confirmed endpoint from each patient's data
127
- endpoint = data['confirmed_endpoint'] if 'confirmed_endpoint' in data else data['suggested_endpoint']
128
- # Initialize a list for the endpoint if it doesn't exist
129
- if endpoint not in organized:
130
- organized[endpoint] = []
131
- organized[endpoint].append(data)
132
- return organized
133
-
134
181
  def check_for_new_remittances(config):
135
182
  print("\nChecking for new files across all endpoints...")
136
183
  endpoints = config['MediLink_Config']['endpoints']
137
- for endpoint_key, endpoint_info in endpoints.items():
138
- try:
139
- # Pass the endpoint key to MediLink_Down.main() as an argument
140
- ERA_path = MediLink_Down.main(desired_endpoint=endpoint_key)
141
- # BUG This needs to check to see if this actually worked maybe winscplog before saying it completed successfully
142
- print("New remittances for {} completed successfully.".format(endpoint_info['name']))
143
- print("Results saved to: {}\n".format(ERA_path))
144
- except Exception as e:
145
- print("An error occurred while checking remittances for {}: {}".format(endpoint_info['name'], e))
184
+ processed_endpoints = []
185
+
186
+ if isinstance(endpoints, dict): # BUG This check can probably be removed later.
187
+ for endpoint_key, endpoint_info in tqdm(endpoints.items(), desc="Processing endpoints"):
188
+ if 'remote_directory_down' in endpoint_info: # Check if the 'remote_directory_down' key exists
189
+ #print("Processing endpoint: ", endpoint_info['name'])
190
+ # BUG (Debug and verbosity removal) this is really for debug only. Positive statements can be muted.
191
+ try:
192
+ ERA_path = MediLink_Down.main(desired_endpoint=endpoint_key)
193
+ processed_endpoints.append((endpoint_info['name'], ERA_path))
194
+ MediLink_ConfigLoader.log("Results for {} saved to: {}".format(endpoint_info['name'], ERA_path))
195
+ # TODO (Low SFTP - Download side) This needs to check to see if this actually worked maybe winscplog before saying it completed successfully
196
+ # Check if there is commonality with the upload side so we can use the same validation function.
197
+ except Exception as e:
198
+ print("An error occurred while checking remittances for {}: {}".format(endpoint_info['name'], e))
199
+ else:
200
+ MediLink_ConfigLoader.log("Skipping endpoint '{}' as it does not have 'remote_directory_down' configured.".format(endpoint_info['name']))
201
+ else:
202
+ print("Error: Endpoint config is not a 'dictionary' as expected.")
203
+ # Check if all ERA paths are the same
204
+ unique_era_paths = set(path for _, path in processed_endpoints)
205
+ if len(unique_era_paths) == 1:
206
+ common_era_path = unique_era_paths.pop() # Get the common ERA path
207
+ endpoints_list = ", ".join(endpoint for endpoint, _ in processed_endpoints)
208
+ print("\nProcessed Endpoints: {}".format(endpoints_list))
209
+ print("File located at: {}\n".format(common_era_path))
210
+ # TODO (MediPost) These prints will eventually be logs when MediPost is made.
211
+
212
+ else:
213
+ if processed_endpoints:
214
+ print("\nProcessed Endpoints:")
215
+ for endpoint, path in processed_endpoints:
216
+ print("Endpoint: {}, ERA Path: {}".format(endpoint, path))
217
+ else:
218
+ print("No endpoints were processed.")
146
219
 
147
220
  def user_decision_on_suggestions(detailed_patient_data, config):
148
221
  """
149
222
  Presents the user with all patient summaries and suggested endpoints,
150
223
  then asks for confirmation to proceed with all or specify adjustments manually.
224
+
225
+ BUG (Med suggested_endpoint) The display summary suggested_endpoint key isn't updating per the user's decision
226
+ although the user decision is persisting. Possibly consider making the current/suggested/confirmed endpoint
227
+ part of a class that the user can interact with via these menus? Probably better handling that way.
151
228
  """
152
229
  # Display summaries of patient details and endpoints.
153
230
  MediLink_UI.display_patient_summaries(detailed_patient_data)
@@ -157,32 +234,27 @@ def user_decision_on_suggestions(detailed_patient_data, config):
157
234
 
158
235
  # If the user agrees to proceed with all suggested endpoints, confirm them.
159
236
  if proceed:
160
- return confirm_all_suggested_endpoints(detailed_patient_data)
237
+ return MediLink_DataMgmt.confirm_all_suggested_endpoints(detailed_patient_data)
161
238
  # Otherwise, allow the user to adjust the endpoints manually.
162
239
  else:
163
240
  return select_and_adjust_files(detailed_patient_data, config)
164
-
165
- def confirm_all_suggested_endpoints(detailed_patient_data):
166
- """
167
- Confirms all suggested endpoints for each patient's detailed data.
168
- """
169
- for data in detailed_patient_data:
170
- if 'confirmed_endpoint' not in data:
171
- data['confirmed_endpoint'] = data['suggested_endpoint']
172
- return detailed_patient_data
173
-
241
+
174
242
  def select_and_adjust_files(detailed_patient_data, config):
175
243
  """
176
244
  Allows users to select patients and adjust their endpoints by interfacing with UI functions.
245
+
246
+ BUG (Med suggested_endpoint) After the user is done making their selection (probably via a class?),
247
+ Then suggested_endpoint should update to persist the user selection as priority over its original suggestion.
248
+ Which means the crosswalk should persist the change in the endpoint as well.
177
249
  """
178
250
  # Display options for patients
179
251
  MediLink_UI.display_patient_options(detailed_patient_data)
180
252
 
181
253
  # Get user-selected indices for adjustment
182
254
  selected_indices = MediLink_UI.get_selected_indices(len(detailed_patient_data))
183
-
184
- # Fetch endpoint names dynamically from the JSON config
185
- endpoint_mapping = {str(i + 1): config['MediLink_Config']['endpoints'][endpoint]['name'] for i, endpoint in enumerate(config['MediLink_Config']['endpoints'])}
255
+
256
+ # Get an ordered list of endpoint keys
257
+ endpoint_keys = list(config['MediLink_Config']['endpoints'].keys())
186
258
 
187
259
  # Iterate over each selected index and process endpoint changes
188
260
  for i in selected_indices:
@@ -190,20 +262,20 @@ def select_and_adjust_files(detailed_patient_data, config):
190
262
  MediLink_UI.display_patient_for_adjustment(data['patient_name'], data.get('suggested_endpoint', 'N/A'))
191
263
 
192
264
  endpoint_change = MediLink_UI.get_endpoint_decision()
193
-
194
265
  if endpoint_change == 'y':
195
- MediLink_UI.display_endpoint_options(endpoint_mapping)
196
- new_endpoint_choice = MediLink_UI.get_new_endpoint_choice()
266
+ MediLink_UI.display_endpoint_options(config['MediLink_Config']['endpoints'])
267
+ endpoint_index = int(MediLink_UI.get_new_endpoint_choice()) - 1 # Adjusting for zero-based index
197
268
 
198
- if new_endpoint_choice in endpoint_mapping:
199
- data['confirmed_endpoint'] = endpoint_mapping[new_endpoint_choice]
200
- print("Endpoint changed to {0} for patient {1}.".format(data['confirmed_endpoint'], data['patient_name']))
269
+ if 0 <= endpoint_index < len(endpoint_keys):
270
+ selected_endpoint_key = endpoint_keys[endpoint_index]
271
+ data['confirmed_endpoint'] = selected_endpoint_key
272
+ print("Endpoint changed to {0} for patient {1}.".format(config['MediLink_Config']['endpoints'][selected_endpoint_key]['name'], data['patient_name']))
273
+ # BUG (Med, Crosswalk & suggested_endpoint) Probably update crosswalk and suggested endpoint here???
201
274
  else:
202
275
  print("Invalid selection. Keeping the suggested endpoint.")
203
276
  else:
204
277
  data['confirmed_endpoint'] = data.get('suggested_endpoint', 'N/A')
205
278
 
206
- # Return the updated data
207
279
  return detailed_patient_data
208
280
 
209
281
  def main_menu():
@@ -212,7 +284,14 @@ def main_menu():
212
284
  including loading configurations and managing user input for menu selections.
213
285
  """
214
286
  # Load configuration settings and display the initial welcome message.
215
- config, crosswalk = MediLink_ConfigLoader.load_configuration() # BUG does this need an argument?
287
+ config, crosswalk = MediLink_ConfigLoader.load_configuration()
288
+
289
+ # Check to make sure payer_id key is available in crosswalk, otherwise, go through that crosswalk initialization flow
290
+ MediBot_Crosswalk_Library.check_and_initialize_crosswalk(config)
291
+
292
+ # Check if the application is in test mode
293
+ if config.get("MediLink_Config", {}).get("TestMode", False):
294
+ print("\n--- MEDILINK TEST MODE --- \nTo enable full functionality, please update the config file \nand set 'TestMode' to 'false'.")
216
295
 
217
296
  # Display Welcome Message
218
297
  MediLink_UI.display_welcome()
@@ -222,6 +301,9 @@ def main_menu():
222
301
 
223
302
  # Detect new files and collect detailed patient data if available.
224
303
  new_files, detailed_patient_data = detect_and_display_file_summaries(directory_path, config, crosswalk)
304
+
305
+ if new_files:
306
+ handle_submission(detailed_patient_data, config)
225
307
 
226
308
  while True:
227
309
  # Define the menu options. Base options include checking remittances and exiting the program.
@@ -240,7 +322,7 @@ def main_menu():
240
322
  check_for_new_remittances(config)
241
323
  elif choice == '2' and new_files:
242
324
  # Handle the claims submission flow if new files are present.
243
- handle_submission(detailed_patient_data, config)
325
+ handle_submission(detailed_patient_data, config) # Since we have the bulk edit, we should actually go there first
244
326
  elif choice == '3' or (choice == '2' and not new_files):
245
327
  # Exit the program if the user chooses to exit or if no new files are present.
246
328
  MediLink_UI.display_exit_message()
@@ -254,18 +336,35 @@ def handle_submission(detailed_patient_data, config):
254
336
  Handles the submission process for claims based on detailed patient data.
255
337
  This function orchestrates the flow from user decision on endpoint suggestions to the actual submission of claims.
256
338
  """
339
+ # TODO If we get here via a user decline we end up not displaying the patient summary data, but this doesn't happen in the first round. Can be de-tangled later.
340
+
341
+ # Ask the user if they want to edit insurance types
342
+ edit_insurance = input("Do you want to edit insurance types? (y/n): ").strip().lower()
343
+ if edit_insurance in ['y', 'yes', '']:
344
+ while True:
345
+ # Bulk edit insurance types
346
+ MediLink_DataMgmt.bulk_edit_insurance_types(detailed_patient_data, insurance_options)
347
+
348
+ # Review and confirm changes
349
+ if MediLink_DataMgmt.review_and_confirm_changes(detailed_patient_data, insurance_options):
350
+ break # Exit the loop if changes are confirmed
351
+ else:
352
+ print("Returning to bulk edit insurance types.")
353
+
257
354
  # Initiate user interaction to confirm or adjust suggested endpoints.
258
355
  adjusted_data = user_decision_on_suggestions(detailed_patient_data, config)
356
+
259
357
  # Confirm all remaining suggested endpoints.
260
- confirmed_data = confirm_all_suggested_endpoints(adjusted_data)
358
+ confirmed_data = MediLink_DataMgmt.confirm_all_suggested_endpoints(adjusted_data)
261
359
  if confirmed_data: # Proceed if there are confirmed data entries.
262
360
  # Organize data by confirmed endpoints for submission.
263
- organized_data = organize_patient_data_by_endpoint(confirmed_data)
361
+ organized_data = MediLink_DataMgmt.organize_patient_data_by_endpoint(confirmed_data)
264
362
  # Confirm transmission with the user and check for internet connectivity.
265
363
  if MediLink_Up.confirm_transmission(organized_data):
266
364
  if MediLink_Up.check_internet_connection():
267
365
  # Submit claims if internet connectivity is confirmed.
268
- MediLink_Up.submit_claims(organized_data, config)
366
+ _ = MediLink_Up.submit_claims(organized_data, config)
367
+ # TODO submit_claims will have a receipt return in the future.
269
368
  else:
270
369
  # Notify the user of an internet connection error.
271
370
  print("Internet connection error. Please ensure you're connected and try again.")