import os import django # Set up Django environment os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hospital_management.settings') django.setup() import uuid import random from datetime import datetime, timedelta, date from decimal import Decimal from django.utils import timezone as django_timezone from django.contrib.auth import get_user_model from core.models import Tenant from patients.models import PatientProfile, InsuranceInfo from emr.models import Encounter from inpatients.models import Admission from billing.models import ( MedicalBill, BillLineItem, InsuranceClaim, Payment, ClaimStatusUpdate, BillingConfiguration ) User = get_user_model() from django.db import IntegrityError, transaction from django.db.models import DecimalField, CharField, IntegerField from django.db.models.fields import NOT_PROVIDED def _model_fields(Model): return {f.name for f in Model._meta.get_fields() if getattr(f, "concrete", False) and not f.auto_created} def _filter_kwargs(Model, data: dict): allowed = _model_fields(Model) return {k: v for k, v in data.items() if k in allowed} def _next_seq_number(prefix, Model, field): today = django_timezone.now().date().strftime("%Y%m%d") i = 1 while True: candidate = f"{prefix}-{today}-{i:04d}" if not Model.objects.filter(**{field: candidate}).exists(): return candidate i += 1 def _quantize_for_field(value: Decimal, f: DecimalField) -> Decimal: """Quantize a Decimal to a field's decimal_places and clamp to max_digits.""" # Quantize to required decimal_places q = Decimal(1).scaleb(-f.decimal_places) # 10^(-decimal_places) v = Decimal(value).quantize(q) # Ensure total digits <= max_digits (digits before + decimal_places) # Count digits before decimal: sign, digits, exp = v.as_tuple() digits_str_len = len(digits) # number of digits after decimal is decimal_places digits_before = digits_str_len - f.decimal_places if f.decimal_places else digits_str_len # if v is 0.x and decimal_places > digits, digits_before can be negative; normalize if digits_before < 0: digits_before = 0 max_before = f.max_digits - f.decimal_places if max_before < 0: max_before = 0 # If too many digits before decimal, clamp to the largest representable value if digits_before > max_before: # Largest integer part we can store is 10^max_before - 1 max_int = (10 ** max_before) - 1 if max_before > 0 else 0 v = Decimal(max_int).quantize(q) return v def _default_decimal_for(BillingConfiguration, f: DecimalField) -> Decimal: """Return a safe default decimal per field name & precision.""" name = f.name.lower() if any(k in name for k in ["tax", "vat", "rate"]): # Prefer 15% if it's a rate; use 15 if integer percent base = Decimal("15") if f.decimal_places == 0 else Decimal("0.15") else: base = Decimal("0") return _quantize_for_field(base, f) # Saudi billing specific data SAUDI_MEDICAL_SERVICES = [ # Consultation Services ('99213', 'Office/Outpatient Visit, Established Patient (Low Complexity)', 'CONSULTATION', 150.00), ('99214', 'Office/Outpatient Visit, Established Patient (Moderate Complexity)', 'CONSULTATION', 200.00), ('99215', 'Office/Outpatient Visit, Established Patient (High Complexity)', 'CONSULTATION', 280.00), ('99203', 'Office/Outpatient Visit, New Patient (Low Complexity)', 'CONSULTATION', 180.00), ('99204', 'Office/Outpatient Visit, New Patient (Moderate Complexity)', 'CONSULTATION', 250.00), ('99205', 'Office/Outpatient Visit, New Patient (High Complexity)', 'CONSULTATION', 350.00), # Emergency Services ('99281', 'Emergency Department Visit (Low Complexity)', 'EMERGENCY', 300.00), ('99282', 'Emergency Department Visit (Low-Moderate Complexity)', 'EMERGENCY', 450.00), ('99283', 'Emergency Department Visit (Moderate Complexity)', 'EMERGENCY', 650.00), ('99284', 'Emergency Department Visit (Moderate-High Complexity)', 'EMERGENCY', 850.00), ('99285', 'Emergency Department Visit (High Complexity)', 'EMERGENCY', 1200.00), # Laboratory Services ('80053', 'Comprehensive Metabolic Panel', 'LABORATORY', 85.00), ('85025', 'Blood Count; Complete (CBC)', 'LABORATORY', 45.00), ('80061', 'Lipid Panel', 'LABORATORY', 65.00), ('85610', 'Prothrombin Time (PT)', 'LABORATORY', 35.00), ('84443', 'Thyroid Stimulating Hormone (TSH)', 'LABORATORY', 75.00), ('82947', 'Glucose; Quantitative, Blood', 'LABORATORY', 25.00), ('83036', 'Hemoglobin A1C', 'LABORATORY', 55.00), # Radiology Services ('71020', 'Chest X-ray, Two Views', 'RADIOLOGY', 120.00), ('73060', 'Knee X-ray, Two Views', 'RADIOLOGY', 100.00), ('72148', 'MRI Lumbar Spine', 'RADIOLOGY', 1800.00), ('70553', 'MRI Brain with Contrast', 'RADIOLOGY', 2200.00), ('76700', 'Abdominal Ultrasound', 'RADIOLOGY', 350.00), ('93000', 'Electrocardiogram (ECG)', 'RADIOLOGY', 80.00), ('93307', 'Echocardiography', 'RADIOLOGY', 450.00), # Surgical Procedures ('44970', 'Laparoscopic Appendectomy', 'SURGERY', 8500.00), ('47563', 'Laparoscopic Cholecystectomy', 'SURGERY', 12000.00), ('29881', 'Arthroscopy, Knee, Surgical', 'SURGERY', 6500.00), ('64483', 'Epidural Injection, Lumbar', 'SURGERY', 1200.00), ('19307', 'Mastectomy, Modified Radical', 'SURGERY', 15000.00), # Pharmacy Services ('J1050', 'Injection, Medroxyprogesterone Acetate', 'PHARMACY', 45.00), ('J3420', 'Injection, Vitamin B-12', 'PHARMACY', 25.00), ('J7050', 'Infusion, Normal Saline Solution', 'PHARMACY', 35.00), ('90788', 'Intramuscular Injection', 'PHARMACY', 15.00), # Room and Board ('R0001', 'Private Room, Per Day', 'ACCOMMODATION', 800.00), ('R0002', 'Semi-Private Room, Per Day', 'ACCOMMODATION', 600.00), ('R0003', 'ICU Bed, Per Day', 'ACCOMMODATION', 2500.00), ('R0004', 'CCU Bed, Per Day', 'ACCOMMODATION', 2800.00), ('R0005', 'Emergency Department Bed, Per Day', 'ACCOMMODATION', 1200.00), ] SAUDI_DIAGNOSIS_CODES = [ ('Z00.00', 'Encounter for general adult medical examination without abnormal findings'), ('I10', 'Essential (primary) hypertension'), ('E11.9', 'Type 2 diabetes mellitus without complications'), ('J44.1', 'Chronic obstructive pulmonary disease with acute exacerbation'), ('M79.3', 'Panniculitis, unsystematic'), ('K21.9', 'Gastro-esophageal reflux disease without esophagitis'), ('R06.02', 'Shortness of breath'), ('M25.511', 'Pain in right shoulder'), ('R50.9', 'Fever, unspecified'), ('N39.0', 'Urinary tract infection, site not specified'), ('J06.9', 'Acute upper respiratory infection, unspecified'), ('K59.00', 'Constipation, unspecified'), ('R51', 'Headache'), ('M54.5', 'Low back pain'), ('F32.9', 'Major depressive disorder, single episode, unspecified'), ] SAUDI_PROVIDERS = [ 'Dr. Mohammed Al-Rashid', 'Dr. Fatima Al-Zahra', 'Dr. Abdullah Al-Mutairi', 'Dr. Aisha Al-Qarni', 'Dr. Ahmad Al-Harbi', 'Dr. Nora Al-Dawsari', 'Dr. Khalid Al-Subai', 'Dr. Maryam Al-Sharani', 'Dr. Omar Al-Ghamdi', 'Dr. Layla Al-Zahrani', 'Dr. Faisal Al-Maliki', 'Dr. Hala Al-Shehri' ] SAUDI_CLEARINGHOUSES = ['Availity', 'Change Healthcare', 'Trizetto', 'Saudi Health Exchange'] SAUDI_PAYMENT_METHODS = ['CASH', 'CREDIT_CARD', 'DEBIT_CARD', 'BANK_TRANSFER', 'CHECK', 'INSURANCE'] SAUDI_PAYMENT_SOURCES = ['PATIENT', 'INSURANCE', 'GOVERNMENT', 'EMPLOYER'] def _next_seq_number(prefix, Model, field): today = django_timezone.now().date().strftime("%Y%m%d") i = 1 while True: candidate = f"{prefix}-{today}-{i:04d}" if not Model.objects.filter(**{field: candidate}).exists(): return candidate i += 1 def create_saudi_medical_bills(): """Create medical bills for Saudi patients""" print("Creating Saudi medical bills...") # Get patients with encounters or admissions patients_with_encounters = list(PatientProfile.objects.filter( encounters__isnull=False ).distinct()[:100]) patients_with_admissions = list(PatientProfile.objects.filter( admissions__isnull=False ).distinct()[:50]) all_patients = list(set(patients_with_encounters + patients_with_admissions)) if not all_patients: print("No patients with encounters or admissions found. Creating bills for random patients...") all_patients = list(PatientProfile.objects.all()[:100]) # Get actual User instances for providers users = list(User.objects.filter(is_active=True)) provider_users = list(User.objects.filter( is_active=True, role__in=['PHYSICIAN', 'NURSE_PRACTITIONER', 'PHYSICIAN_ASSISTANT', 'RADIOLOGIST'] )) if not provider_users: provider_users = users bills_created = 0 created_bills = [] # Keep track of bill numbers to avoid duplicates used_bill_numbers = set() for patient in all_patients: num_bills = random.randint(1, 3) for i in range(num_bills): try: service_date_from = django_timezone.now().date() - timedelta(days=random.randint(1, 180)) service_date_to = service_date_from + timedelta(days=random.randint(0, 7)) bill_date = service_date_from + timedelta(days=random.randint(0, 30)) due_date = bill_date + timedelta(days=30) encounter = patient.encounters.first() if patient.encounters.exists() else None admission = patient.admissions.first() if patient.admissions.exists() else None primary_insurance = patient.insurance_info.filter(insurance_type='PRIMARY').first() secondary_insurance = patient.insurance_info.filter(insurance_type='SECONDARY').first() # Generate unique bill number today = django_timezone.now().date() bill_number = None counter = 1 # while bill_number is None or bill_number in used_bill_numbers: # bill_number = f"BILL-{today.strftime('%Y%m%d')}-{bills_created + counter:04d}" # counter += 1 used_bill_numbers.add(bill_number) bill_number = _next_seq_number("BILL", MedicalBill, "bill_number") bill = MedicalBill.objects.create( tenant=patient.tenant, patient=patient, bill_number=bill_number, # Explicitly set bill number bill_type=random.choice(['INPATIENT', 'OUTPATIENT', 'EMERGENCY', 'PROFESSIONAL']), service_date_from=service_date_from, service_date_to=service_date_to, bill_date=bill_date, due_date=due_date, subtotal=Decimal('0.00'), tax_amount=Decimal('0.00'), discount_amount=Decimal('0.00'), adjustment_amount=Decimal('0.00'), total_amount=Decimal('0.00'), paid_amount=Decimal('0.00'), balance_amount=Decimal('0.00'), primary_insurance=primary_insurance, secondary_insurance=secondary_insurance, status=random.choice(['DRAFT', 'PENDING', 'SENT', 'PARTIAL_PAYMENT', 'PAID']), attending_provider=random.choice(provider_users) if provider_users else None, billing_provider=random.choice(provider_users) if provider_users else None, encounter=encounter, admission=admission, notes=random.choice([ 'Standard billing procedure completed', 'Insurance verification pending', 'Patient requires payment plan', 'Emergency services provided' ]) if random.choice([True, False]) else None, payment_terms=30, collection_status=random.choice(['ACTIVE', 'COLLECTIONS', 'WRITE_OFF', 'PAID_IN_FULL']), last_statement_date=bill_date + timedelta(days=random.randint(1, 15)) if random.choice( [True, False]) else None, created_by=random.choice(users) if users else None, ) created_bills.append(bill) bills_created += 1 if bills_created % 10 == 0: print(f"Created {bills_created} medical bills...") except Exception as e: print(f"Error creating medical bill for patient {patient.mrn}: {str(e)}") continue print(f"Successfully created {bills_created} Saudi medical bills.") return created_bills def create_saudi_bill_line_items(medical_bills): """Create bill line items for medical bills""" print("Creating Saudi bill line items...") if not medical_bills: print("No medical bills provided for line items.") return [] provider_users = list(User.objects.filter( is_active=True, role__in=['PHYSICIAN', 'NURSE_PRACTITIONER', 'PHYSICIAN_ASSISTANT', 'RADIOLOGIST'] )) or list(User.objects.filter(is_active=True)) created_line_items = [] for bill in medical_bills: num_items = random.randint(2, 8) bill_subtotal = Decimal('0.00') for i in range(num_items): try: # Unpack tuple: (code, description, category, base_price) code, description, category, base_price = random.choice(SAUDI_MEDICAL_SERVICES) # Price/qty math unit_price = (Decimal(str(base_price)) * Decimal(str(random.uniform(0.85, 1.2)))).quantize(Decimal('0.01')) quantity = random.randint(1, 5) line_total = (unit_price * quantity).quantize(Decimal('0.01')) discount_percentage = random.choice([0, 5, 10, 15, 20]) if random.choice([True, False]) else 0 discount_amount = (line_total * (Decimal(discount_percentage) / 100)).quantize(Decimal('0.01')) net_amount = (line_total - discount_amount).quantize(Decimal('0.01')) # Diagnosis tuple: (icd_code, description) diag_code, _ = random.choice(SAUDI_DIAGNOSIS_CODES) # Safe service_date delta_days = max(0, (bill.service_date_to - bill.service_date_from).days if bill.service_date_to else 0) service_date = bill.service_date_from + timedelta(days=random.randint(0, delta_days)) item_kwargs = { "medical_bill": bill, "tenant": getattr(bill, "tenant", None), "line_number": i + 1, "service_code": code, "service_description": description, "service_category": category, "cpt_code": code, # if your model has cpt_code "icd_code": diag_code, # if your model has icd_code "quantity": quantity, "unit_price": unit_price, "line_total": line_total, "discount_amount": discount_amount, "net_amount": net_amount, "rendering_provider": random.choice(provider_users) if provider_users else None, "service_date": service_date, } # Only pass fields that exist on the model item = BillLineItem.objects.create(**_filter_kwargs(BillLineItem, item_kwargs)) created_line_items.append(item) bill_subtotal += net_amount except Exception as e: print(f"Error creating line item for bill {bill.bill_number}: {e}") # Update bill totals (and paid amount based on status) try: bill.subtotal = bill_subtotal.quantize(Decimal('0.01')) bill.tax_amount = (bill.subtotal * Decimal('0.15')).quantize(Decimal('0.01')) bill.total_amount = (bill.subtotal + bill.tax_amount).quantize(Decimal('0.01')) if bill.status == 'PAID': bill.paid_amount = bill.total_amount elif bill.status == 'PARTIAL_PAYMENT': bill.paid_amount = (bill.total_amount * Decimal(str(random.uniform(0.2, 0.8)))).quantize(Decimal('0.01')) else: bill.paid_amount = Decimal('0.00') bill.balance_amount = (bill.total_amount - bill.paid_amount).quantize(Decimal('0.01')) bill.save(update_fields=['subtotal', 'tax_amount', 'total_amount', 'paid_amount', 'balance_amount']) except Exception as e: print(f"Error updating bill totals for {bill.bill_number}: {e}") print(f"Successfully created {len(created_line_items)} bill line items.") return created_line_items from billing.models import InsuranceClaim # ensure import def _next_claim_number(): return _next_seq_number("CLM", InsuranceClaim, "claim_number") def create_saudi_insurance_claims(medical_bills): print("Creating Saudi insurance claims...") if not medical_bills: print("No medical bills provided for claims.") return [] created = [] for bill in medical_bills: if not bill.primary_insurance and not bill.secondary_insurance: continue def _base_claim_data(): return { "medical_bill": bill, "claim_number": _next_claim_number(), "billed_amount": bill.total_amount, "submission_date": bill.bill_date + timedelta(days=random.randint(1, 7)), "service_date_from": bill.service_date_from, # <-- REQUIRED "service_date_to": bill.service_date_to, # <-- REQUIRED (if your model is NOT NULL) "status": random.choice(['SUBMITTED', 'RECEIVED', 'UNDER_REVIEW', 'APPROVED', 'DENIED']), } if bill.primary_insurance: try: data = _base_claim_data() data["claim_type"] = "PRIMARY" data["insurance_info"] = bill.primary_insurance claim = InsuranceClaim.objects.create(**_filter_kwargs(InsuranceClaim, data)) created.append(claim) except Exception as e: print(f"Error creating primary insurance claim for bill {bill.bill_number}: {e}") if bill.secondary_insurance: try: data = _base_claim_data() data["claim_type"] = "SECONDARY" data["insurance_info"] = bill.secondary_insurance data["billed_amount"] = (bill.total_amount * Decimal('0.2')).quantize(Decimal('0.01')) data["submission_date"] = bill.bill_date + timedelta(days=random.randint(7, 14)) claim = InsuranceClaim.objects.create(**_filter_kwargs(InsuranceClaim, data)) created.append(claim) except Exception as e: print(f"Error creating secondary insurance claim for bill {bill.bill_number}: {e}") print(f"Successfully created {len(created)} insurance claims.") return created from billing.models import Payment # ensure this import is present def _next_payment_number(): return _next_seq_number("PMT", Payment, "payment_number") def create_saudi_payments(bills): """Create payments for medical bills""" print("Creating Saudi payments...") payments_created = 0 users = list(User.objects.filter(is_active=True)) bills_with_payments = [bill for bill in bills if bill.paid_amount and bill.paid_amount > 0] for bill in bills_with_payments: num_payments = random.randint(1, min(3, int(bill.paid_amount / 100) + 1)) remaining_payment = bill.paid_amount for i in range(num_payments): try: payment_amount = remaining_payment if i == num_payments - 1 else (remaining_payment * Decimal(str(random.uniform(0.2, 0.7)))) remaining_payment -= payment_amount if payment_amount <= 0: continue payment_date = bill.bill_date + timedelta(days=random.randint(1, 60)) payment_method = random.choice(SAUDI_PAYMENT_METHODS) payment_source = random.choice(SAUDI_PAYMENT_SOURCES) check_number = f"CHK{random.randint(100000, 999999)}" if payment_method == 'CHECK' else None bank_name = random.choice(['Al Rajhi Bank','Saudi National Bank','Riyad Bank','Arab National Bank','Banque Saudi Fransi','Saudi Investment Bank']) if payment_method in ['CHECK','BANK_TRANSFER'] else None card_type = random.choice(['VISA','MASTERCARD','AMEX']) if payment_method in ['CREDIT_CARD','DEBIT_CARD'] else None card_last_four = f"{random.randint(1000, 9999)}" if payment_method in ['CREDIT_CARD','DEBIT_CARD'] else None authorization_code = f"AUTH{random.randint(100000, 999999)}" if payment_method in ['CREDIT_CARD','DEBIT_CARD'] else None transaction_id = f"TXN{random.randint(1000000, 9999999)}" if payment_method in ['CREDIT_CARD','DEBIT_CARD','BANK_TRANSFER'] else None insurance_claim = random.choice(list(bill.insurance_claims.all())) if payment_source == 'INSURANCE' and bill.insurance_claims.exists() else None # Build kwargs, include payment_number if field exists kwargs = { "medical_bill": bill, "payment_date": payment_date, "payment_amount": payment_amount.quantize(Decimal('0.01')), "payment_method": payment_method, "payment_source": payment_source, "check_number": check_number, "bank_name": bank_name, "routing_number": f"{random.randint(100000000, 999999999)}" if payment_method in ['CHECK','BANK_TRANSFER'] else None, "card_type": card_type, "card_last_four": card_last_four, "authorization_code": authorization_code, "transaction_id": transaction_id, "insurance_claim": insurance_claim, "eob_number": f"EOB{random.randint(100000, 999999)}" if payment_source == 'INSURANCE' else None, "status": random.choice(['PENDING','PROCESSED','CLEARED','RETURNED']), "deposit_date": payment_date + timedelta(days=random.randint(0, 3)), "deposit_slip": f"DEP{random.randint(100000, 999999)}" if random.choice([True, False]) else None, "notes": random.choice(['Payment processed successfully','Partial payment received','Insurance payment - EOB attached','Patient payment plan installment']) if random.choice([True, False]) else None, "refund_amount": Decimal('0.00'), "received_by": random.choice(users) if users else None, "processed_by": random.choice(users) if users else None, } if "payment_number" in _model_fields(Payment): # retry loop for rare uniqueness races or model-level generators attempts = 0 while True: attempts += 1 kwargs["payment_number"] = _next_payment_number() try: Payment.objects.create(**_filter_kwargs(Payment, kwargs)) payments_created += 1 break except IntegrityError: if attempts >= 5: raise # try another number continue else: Payment.objects.create(**_filter_kwargs(Payment, kwargs)) payments_created += 1 except Exception as e: print(f"Error creating payment for bill {bill.bill_number}: {e}") continue print(f"Successfully created {payments_created} payments.") return payments_created def create_saudi_claim_status_updates(insurance_claims): """Create claim status updates with non-null previous_status.""" print("Creating Saudi claim status updates...") if not insurance_claims: print("No insurance claims provided for status updates.") return [] users = list(User.objects.filter(is_active=True)) created_updates = [] for claim in insurance_claims: num_updates = random.randint(1, 4) # Define a sensible progression starting from the claim's current status all_steps = ['SUBMITTED', 'RECEIVED', 'UNDER_REVIEW', 'APPROVED', 'PAID', 'DENIED'] try: start_idx = all_steps.index(claim.status) if claim.status in all_steps else 0 except ValueError: start_idx = 0 # ensure we have forward steps; if already terminal, just repeat last/nearby statuses forward = all_steps[start_idx+1:] or [all_steps[min(start_idx, len(all_steps)-1)]] previous_status = claim.status # NOT NULL for first update for i in range(num_updates): try: new_status = forward[min(i, len(forward)-1)] if forward else previous_status base_date = claim.submission_date + timedelta(days=(i+1) * random.randint(3, 14)) status_date = django_timezone.make_aware( datetime.combine(base_date, datetime.min.time()), timezone=django_timezone.get_current_timezone() ) update_kwargs = { "insurance_claim": claim, "previous_status": previous_status, # never None "new_status": new_status, "status_date": status_date, "update_source": random.choice(['EDI','PORTAL','PHONE','FAX','EMAIL']), "response_code": f"R{random.randint(100, 999)}" if random.choice([True, False]) else None, "response_message": random.choice([ 'Claim processed successfully', 'Additional information required', 'Claim approved for payment', 'Claim denied - insufficient documentation' ]) if random.choice([True, False]) else None, } # Optional financials only for approved/paid if new_status in ['APPROVED', 'PAID']: allowed = (claim.billed_amount * Decimal(str(random.uniform(0.7, 1.0)))).quantize(Decimal('0.01')) patient_resp = (claim.billed_amount * Decimal(str(random.uniform(0.1, 0.3)))).quantize(Decimal('0.01')) update_kwargs["allowed_amount"] = allowed update_kwargs["patient_responsibility"] = patient_resp if new_status == 'PAID': update_kwargs["paid_amount"] = (allowed - patient_resp).quantize(Decimal('0.01')) update = ClaimStatusUpdate.objects.create(**_filter_kwargs(ClaimStatusUpdate, update_kwargs)) created_updates.append(update) previous_status = new_status # move the chain forward except Exception as e: print(f"Error creating status update for claim {claim.claim_number}: {e}") continue print(f"Successfully created {len(created_updates)} claim status updates.") return created_updates def create_saudi_billing_configurations(): """Create billing configurations with per-field-precision-safe decimals.""" print("Creating Saudi billing configurations...") tenants = list(Tenant.objects.all()) if not tenants: print("No tenants found for billing configurations.") return [] from billing.models import BillingConfiguration created = [] cfg_fields = _model_fields(BillingConfiguration) for tenant in tenants: try: data = {} # Currency-like field for f in BillingConfiguration._meta.get_fields(): if isinstance(f, CharField) and f.name in {"currency", "currency_code", "base_currency"}: data[f.name] = "SAR" # Payment terms-like field for f in BillingConfiguration._meta.get_fields(): if isinstance(f, IntegerField) and f.name in {"default_payment_terms", "payment_terms", "net_terms"}: data[f.name] = 30 # Decimal fields: generate a safe value per field precision for f in BillingConfiguration._meta.get_fields(): if isinstance(f, DecimalField): has_default = getattr(f, "default", NOT_PROVIDED) is not NOT_PROVIDED if not f.null and not has_default: data[f.name] = _default_decimal_for(BillingConfiguration, f) # Include tenant if the model has it if "tenant" in cfg_fields: obj, is_new = BillingConfiguration.objects.get_or_create( tenant=tenant, defaults=_filter_kwargs(BillingConfiguration, data) ) if is_new: created.append(obj) print(f"Successfully created billing configuration for {tenant.name}") else: print(f"Billing configuration already exists for {tenant.name}") else: obj = BillingConfiguration.objects.create(**_filter_kwargs(BillingConfiguration, data)) created.append(obj) print("Successfully created billing configuration (no tenant FK)") except Exception as e: print(f"Error creating billing configuration for tenant {tenant.name}: {e}") print(f"Successfully created {len(created)} billing configurations.") return created def main(): """Main function to create all Saudi billing data""" print("šŸ„ Creating Saudi Billing Data") try: # Check if tenants exist tenants = list(Tenant.objects.all()) if not tenants: print("āŒ No tenants found. Please run core_data.py first.") return # Check if patients exist patients = list(PatientProfile.objects.all()) if not patients: print("āŒ No patients found. Please run patients_data.py first.") return print(f"šŸ“‹ Found {len(tenants)} tenants and {len(patients)} patients") # Create billing configurations first print("\n1ļøāƒ£ Creating Billing Configurations...") configs_count = create_saudi_billing_configurations() # Create medical bills print("\n2ļøāƒ£ Creating Medical Bills...") bills = create_saudi_medical_bills() if not bills: print("āŒ No bills created. Stopping.") return # Create bill line items print("\n3ļøāƒ£ Creating Bill Line Items...") line_items_count = create_saudi_bill_line_items(bills) # Create insurance claims print("\n4ļøāƒ£ Creating Insurance Claims...") claims = create_saudi_insurance_claims(bills) # Create payments print("\n5ļøāƒ£ Creating Payments...") payments_count = create_saudi_payments(bills) # Create claim status updates print("\n6ļøāƒ£ Creating Claim Status Updates...") updates_count = create_saudi_claim_status_updates(claims) print("\nšŸŽ‰ Saudi Billing Data Creation Complete!") print("šŸ“Š Summary:") print(f" - Billing Configurations: {len(configs_count)}") print(f" - Medical Bills: {len(bills)}") print(f" - Bill Line Items: {len(line_items_count)}") print(f" - Insurance Claims: {len(claims)}") print(f" - Payments: {payments_count}") print(f" - Claim Status Updates: {len(updates_count)}") except Exception as e: print(f"āŒ Error in main execution: {str(e)}") if __name__ == '__main__': main()