1078 lines
44 KiB
Python
1078 lines
44 KiB
Python
import os
|
||
import django
|
||
|
||
# Set up Django environment
|
||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hospital_management.settings')
|
||
django.setup()
|
||
|
||
import random
|
||
import uuid
|
||
from datetime import datetime, timedelta
|
||
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 inventory.models import InventoryItem, InventoryStock, InventoryLocation, PurchaseOrder, PurchaseOrderItem, \
|
||
Supplier
|
||
|
||
User = get_user_model()
|
||
|
||
# Saudi Arabian Inventory Data
|
||
SAUDI_MEDICAL_CATEGORIES = [
|
||
'Pharmaceuticals',
|
||
'Medical Devices',
|
||
'Surgical Instruments',
|
||
'Laboratory Supplies',
|
||
'PPE & Safety',
|
||
'IV Therapy',
|
||
'Emergency Supplies'
|
||
]
|
||
|
||
SAUDI_SUPPLIERS = [
|
||
'Saudi Medical Supply Co.',
|
||
'Gulf Medical Equipment',
|
||
'Arabian Healthcare Supplies',
|
||
'Riyadh Medical Trading',
|
||
'Al-Dawaa Medical',
|
||
'Nahdi Medical Company',
|
||
'United Pharmaceuticals'
|
||
]
|
||
|
||
SAUDI_CITIES = ['Riyadh', 'Jeddah', 'Dammam', 'Medina', 'Taif', 'Khobar']
|
||
|
||
MEDICAL_ITEMS = [
|
||
{'name': 'Paracetamol 500mg', 'category': 'Pharmaceuticals', 'unit': 'TAB'},
|
||
{'name': 'Disposable Syringe 5ml', 'category': 'Medical Devices', 'unit': 'PCS'},
|
||
{'name': 'Surgical Gloves Size M', 'category': 'PPE & Safety', 'unit': 'PAIR'},
|
||
{'name': 'Blood Collection Tube', 'category': 'Laboratory Supplies', 'unit': 'PCS'},
|
||
{'name': 'IV Bag Normal Saline', 'category': 'IV Therapy', 'unit': 'BAG'},
|
||
{'name': 'Emergency Oxygen Mask', 'category': 'Emergency Supplies', 'unit': 'PCS'}
|
||
]
|
||
|
||
|
||
def create_saudi_suppliers(tenants):
|
||
"""Create Saudi suppliers"""
|
||
suppliers = []
|
||
|
||
for tenant in tenants:
|
||
print(f"Creating suppliers for {tenant.name}...")
|
||
|
||
for i, supplier_name in enumerate(SAUDI_SUPPLIERS):
|
||
supplier_code = f"SUP-{tenant.id}-{i + 1:03d}"
|
||
|
||
try:
|
||
supplier = Supplier.objects.create(
|
||
tenant=tenant,
|
||
supplier_code=supplier_code,
|
||
name=supplier_name,
|
||
supplier_type='DISTRIBUTOR',
|
||
city=random.choice(SAUDI_CITIES),
|
||
country='Saudi Arabia',
|
||
is_active=True
|
||
)
|
||
suppliers.append(supplier)
|
||
print(f" ✓ Created supplier: {supplier_name}")
|
||
|
||
except Exception as e:
|
||
print(f" ✗ Error creating supplier {supplier_name}: {e}")
|
||
continue
|
||
|
||
print(f"Created {len(suppliers)} suppliers")
|
||
return suppliers
|
||
|
||
|
||
def create_saudi_inventory_locations(tenants):
|
||
"""Create Saudi inventory locations"""
|
||
locations = []
|
||
|
||
storage_rooms = ['Pharmacy', 'Central Supply', 'OR Storage', 'ICU Supply', 'Ward Storage']
|
||
|
||
for tenant in tenants:
|
||
print(f"Creating locations for {tenant.name}...")
|
||
|
||
for i, room in enumerate(storage_rooms):
|
||
location_code = f"LOC-{tenant.id}-{i + 1:03d}"
|
||
|
||
try:
|
||
location = InventoryLocation.objects.create(
|
||
tenant=tenant,
|
||
location_code=location_code,
|
||
name=f"{room} - {tenant.city}",
|
||
description=f"Storage location in {room}",
|
||
location_type='WAREHOUSE',
|
||
building='Main Hospital',
|
||
floor='Ground Floor',
|
||
room=room,
|
||
is_active=True
|
||
)
|
||
locations.append(location)
|
||
print(f" ✓ Created location: {location.name}")
|
||
|
||
except Exception as e:
|
||
print(f" ✗ Error creating location {room}: {e}")
|
||
continue
|
||
|
||
print(f"Created {len(locations)} locations")
|
||
return locations
|
||
|
||
|
||
def create_saudi_inventory_items(tenants):
|
||
"""Create Saudi inventory items"""
|
||
items = []
|
||
|
||
for tenant in tenants:
|
||
print(f"Creating items for {tenant.name}...")
|
||
|
||
for i, item_data in enumerate(MEDICAL_ITEMS):
|
||
item_code = f"ITM-{tenant.id}-{i + 1:03d}"
|
||
|
||
try:
|
||
item = InventoryItem.objects.create(
|
||
tenant=tenant,
|
||
item_code=item_code,
|
||
item_name=item_data['name'],
|
||
description=f"Medical item: {item_data['name']}",
|
||
category=item_data['category'],
|
||
subcategory=item_data['category'],
|
||
item_type='STOCK',
|
||
manufacturer='Saudi Medical Industries',
|
||
unit_of_measure=item_data['unit'],
|
||
package_size=1,
|
||
unit_cost=Decimal(str(random.uniform(10, 100))),
|
||
list_price=Decimal(str(random.uniform(15, 150))),
|
||
has_expiration=item_data['category'] == 'Pharmaceuticals',
|
||
is_active=True,
|
||
is_tracked=True,
|
||
reorder_point=random.randint(10, 50),
|
||
reorder_quantity=random.randint(100, 500),
|
||
max_stock_level=random.randint(500, 1000)
|
||
)
|
||
items.append(item)
|
||
print(f" ✓ Created item: {item.item_name}")
|
||
|
||
except Exception as e:
|
||
print(f" ✗ Error creating item {item_data['name']}: {e}")
|
||
continue
|
||
|
||
print(f"Created {len(items)} items")
|
||
return items
|
||
|
||
|
||
def create_saudi_inventory_stock(items, locations):
|
||
"""Create Saudi inventory stock entries"""
|
||
stocks = []
|
||
|
||
for item in items:
|
||
print(f"Creating stock for {item.item_name}...")
|
||
|
||
# Get locations for this tenant
|
||
tenant_locations = [loc for loc in locations if loc.tenant == item.tenant]
|
||
if not tenant_locations:
|
||
continue
|
||
|
||
location = random.choice(tenant_locations)
|
||
|
||
try:
|
||
stock = InventoryStock.objects.create(
|
||
inventory_item=item,
|
||
location=location,
|
||
quantity_on_hand=random.randint(50, 500),
|
||
quantity_reserved=random.randint(0, 20),
|
||
received_date=django_timezone.now().date() - timedelta(days=random.randint(1, 90)),
|
||
expiration_date=django_timezone.now().date() + timedelta(days=365) if item.has_expiration else None,
|
||
unit_cost=item.unit_cost,
|
||
quality_status='AVAILABLE'
|
||
)
|
||
stocks.append(stock)
|
||
print(f" ✓ Created stock for: {item.item_name}")
|
||
|
||
except Exception as e:
|
||
print(f" ✗ Error creating stock for {item.item_name}: {e}")
|
||
continue
|
||
|
||
print(f"Created {len(stocks)} stock entries")
|
||
return stocks
|
||
|
||
|
||
def create_saudi_purchase_orders(tenants, suppliers):
|
||
"""Create Saudi purchase orders"""
|
||
orders = []
|
||
|
||
for tenant in tenants:
|
||
print(f"Creating purchase orders for {tenant.name}...")
|
||
|
||
# Get suppliers for this tenant
|
||
tenant_suppliers = [supplier for supplier in suppliers if supplier.tenant == tenant]
|
||
if not tenant_suppliers:
|
||
print(f" No suppliers found for {tenant.name}, skipping...")
|
||
continue
|
||
|
||
# Get delivery locations
|
||
try:
|
||
locations = InventoryLocation.objects.filter(tenant=tenant)
|
||
delivery_location = locations.first() if locations.exists() else None
|
||
except:
|
||
delivery_location = None
|
||
|
||
for i in range(3): # Create 3 orders per tenant
|
||
po_number = f"PO-{tenant.id}-{django_timezone.now().year}-{i + 1:04d}"
|
||
supplier = random.choice(tenant_suppliers)
|
||
|
||
try:
|
||
order = PurchaseOrder.objects.create(
|
||
tenant=tenant,
|
||
po_number=po_number,
|
||
supplier=supplier,
|
||
order_date=django_timezone.now().date() - timedelta(days=random.randint(1, 30)),
|
||
requested_delivery_date=django_timezone.now().date() + timedelta(days=random.randint(7, 30)),
|
||
order_type='STANDARD',
|
||
priority='NORMAL',
|
||
subtotal=Decimal(str(random.uniform(1000, 10000))),
|
||
tax_amount=Decimal('0.00'),
|
||
shipping_amount=Decimal('0.00'),
|
||
total_amount=Decimal(str(random.uniform(1000, 10000))),
|
||
status='DRAFT',
|
||
delivery_location=delivery_location,
|
||
payment_terms='NET_30'
|
||
)
|
||
orders.append(order)
|
||
print(f" ✓ Created PO: {po_number}")
|
||
|
||
except Exception as e:
|
||
print(f" ✗ Error creating PO {po_number}: {e}")
|
||
continue
|
||
|
||
print(f"Created {len(orders)} purchase orders")
|
||
return orders
|
||
|
||
|
||
def create_saudi_purchase_order_items(orders, items):
|
||
"""Create Saudi purchase order items"""
|
||
po_items = []
|
||
|
||
for order in orders:
|
||
print(f"Creating items for PO {order.po_number}...")
|
||
|
||
# Get items for this tenant
|
||
tenant_items = [item for item in items if item.tenant == order.tenant]
|
||
if not tenant_items:
|
||
continue
|
||
|
||
# Create 2-3 items per order
|
||
num_items = min(3, len(tenant_items))
|
||
selected_items = random.sample(tenant_items, num_items)
|
||
|
||
for line_num, item in enumerate(selected_items, 1):
|
||
quantity_ordered = random.randint(10, 100)
|
||
unit_price = item.unit_cost * Decimal(str(random.uniform(0.9, 1.1)))
|
||
total_price = unit_price * quantity_ordered
|
||
|
||
try:
|
||
po_item = PurchaseOrderItem.objects.create(
|
||
purchase_order=order,
|
||
line_number=line_num,
|
||
inventory_item=item,
|
||
quantity_ordered=quantity_ordered,
|
||
quantity_received=0,
|
||
unit_price=unit_price,
|
||
total_price=total_price,
|
||
requested_delivery_date=order.requested_delivery_date,
|
||
status='PENDING'
|
||
)
|
||
po_items.append(po_item)
|
||
print(f" ✓ Created PO item: {item.item_name}")
|
||
|
||
except Exception as e:
|
||
print(f" ✗ Error creating PO item for {item.item_name}: {e}")
|
||
continue
|
||
|
||
print(f"Created {len(po_items)} purchase order items")
|
||
return po_items
|
||
|
||
|
||
def main():
|
||
"""Main function to create all Saudi inventory data"""
|
||
print("🏥 Starting Saudi Inventory Data Generation...")
|
||
|
||
# Get tenants
|
||
try:
|
||
tenants = list(Tenant.objects.filter(is_active=True)[:5]) # Limit to first 5 tenants
|
||
if not tenants:
|
||
print("❌ No active tenants found. Please run core_data.py first.")
|
||
return
|
||
|
||
print(f"📋 Found {len(tenants)} active tenants")
|
||
except Exception as e:
|
||
print(f"❌ Error getting tenants: {e}")
|
||
return
|
||
|
||
# Create data step by step
|
||
print("\n1️⃣ Creating Suppliers...")
|
||
suppliers = create_saudi_suppliers(tenants)
|
||
if not suppliers:
|
||
print("❌ No suppliers created. Stopping.")
|
||
return
|
||
|
||
print("\n2️⃣ Creating Locations...")
|
||
locations = create_saudi_inventory_locations(tenants)
|
||
if not locations:
|
||
print("❌ No locations created. Stopping.")
|
||
return
|
||
|
||
print("\n3️⃣ Creating Items...")
|
||
items = create_saudi_inventory_items(tenants)
|
||
if not items:
|
||
print("❌ No items created. Stopping.")
|
||
return
|
||
|
||
print("\n4️⃣ Creating Stock...")
|
||
stocks = create_saudi_inventory_stock(items, locations)
|
||
|
||
print("\n5️⃣ Creating Purchase Orders...")
|
||
orders = create_saudi_purchase_orders(tenants, suppliers)
|
||
|
||
print("\n6️⃣ Creating Purchase Order Items...")
|
||
po_items = create_saudi_purchase_order_items(orders, items)
|
||
|
||
print("\n🎉 Saudi Inventory Data Generation Complete!")
|
||
print(f"📊 Summary:")
|
||
print(f" - Suppliers: {len(suppliers)}")
|
||
print(f" - Locations: {len(locations)}")
|
||
print(f" - Items: {len(items)}")
|
||
print(f" - Stock Entries: {len(stocks)}")
|
||
print(f" - Purchase Orders: {len(orders)}")
|
||
print(f" - PO Items: {len(po_items)}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|
||
|
||
|
||
# import random
|
||
# import uuid
|
||
# from datetime import datetime, timedelta
|
||
# from decimal import Decimal
|
||
# from django.utils import timezone as django_timezone
|
||
# from django.db import transaction
|
||
#
|
||
# from core.models import Tenant
|
||
# from accounts.models import User
|
||
# from inventory.models import InventoryItem, InventoryStock, InventoryLocation, PurchaseOrder, PurchaseOrderItem, \
|
||
# Supplier
|
||
#
|
||
# # Saudi Arabian Inventory Data
|
||
# SAUDI_MEDICAL_CATEGORIES = [
|
||
# 'Pharmaceuticals',
|
||
# 'Medical Devices',
|
||
# 'Surgical Instruments',
|
||
# 'Laboratory Supplies',
|
||
# 'Radiology Supplies',
|
||
# 'PPE & Safety',
|
||
# 'Wound Care',
|
||
# 'IV Therapy',
|
||
# 'Respiratory Care',
|
||
# 'Cardiology Equipment',
|
||
# 'Orthopedic Supplies',
|
||
# 'Emergency Supplies',
|
||
# 'Cleaning & Disinfection',
|
||
# 'Office Supplies',
|
||
# 'Food & Nutrition'
|
||
# ]
|
||
#
|
||
# SAUDI_PHARMACEUTICAL_SUBCATEGORIES = {
|
||
# 'Pharmaceuticals': [
|
||
# 'Antibiotics', 'Analgesics', 'Cardiovascular', 'Diabetes Care',
|
||
# 'Respiratory', 'Oncology', 'Psychiatric', 'Emergency Drugs',
|
||
# 'Anesthetics', 'Vaccines', 'IV Solutions', 'Blood Products'
|
||
# ]
|
||
# }
|
||
#
|
||
# SAUDI_MEDICAL_MANUFACTURERS = [
|
||
# 'Saudi Pharmaceutical Industries (SPIMACO)',
|
||
# 'Tabuk Pharmaceuticals',
|
||
# 'Jamjoom Pharmaceuticals',
|
||
# 'Al-Jazeera Pharmaceutical Industries',
|
||
# 'Medical Supplies Company',
|
||
# 'Saudi Medical Supplies',
|
||
# 'Gulf Pharmaceutical Industries',
|
||
# 'Middle East Healthcare',
|
||
# 'Arabian Medical Equipment',
|
||
# 'Riyadh Pharma',
|
||
# 'Johnson & Johnson Saudi',
|
||
# 'Pfizer Saudi Arabia',
|
||
# 'Novartis Saudi',
|
||
# 'Roche Saudi Arabia',
|
||
# 'Abbott Saudi Arabia'
|
||
# ]
|
||
#
|
||
# SAUDI_STORAGE_LOCATIONS = {
|
||
# 'buildings': ['Main Hospital', 'Outpatient Clinic', 'Emergency Wing', 'Research Center', 'Administrative Building'],
|
||
# 'floors': ['Ground Floor', 'First Floor', 'Second Floor', 'Third Floor', 'Basement'],
|
||
# 'rooms': ['Pharmacy', 'Central Supply', 'OR Storage', 'ICU Supply', 'Ward Storage', 'Emergency Supply'],
|
||
# 'zones': ['Zone A', 'Zone B', 'Zone C', 'Zone D'],
|
||
# 'aisles': ['Aisle 1', 'Aisle 2', 'Aisle 3', 'Aisle 4', 'Aisle 5'],
|
||
# 'shelves': ['Shelf A', 'Shelf B', 'Shelf C', 'Shelf D', 'Shelf E'],
|
||
# 'bins': ['Bin 1', 'Bin 2', 'Bin 3', 'Bin 4', 'Bin 5']
|
||
# }
|
||
#
|
||
# SAUDI_SUPPLIER_DATA = [
|
||
# {
|
||
# 'name': 'Saudi Medical Supply Co.',
|
||
# 'type': 'DISTRIBUTOR',
|
||
# 'city': 'Riyadh',
|
||
# 'phone': '+966-11-234-5678'
|
||
# },
|
||
# {
|
||
# 'name': 'Gulf Medical Equipment',
|
||
# 'type': 'MANUFACTURER',
|
||
# 'city': 'Dammam',
|
||
# 'phone': '+966-13-345-6789'
|
||
# },
|
||
# {
|
||
# 'name': 'Arabian Healthcare Supplies',
|
||
# 'type': 'WHOLESALER',
|
||
# 'city': 'Jeddah',
|
||
# 'phone': '+966-12-456-7890'
|
||
# },
|
||
# {
|
||
# 'name': 'Riyadh Medical Trading',
|
||
# 'type': 'DISTRIBUTOR',
|
||
# 'city': 'Riyadh',
|
||
# 'phone': '+966-11-567-8901'
|
||
# },
|
||
# {
|
||
# 'name': 'Al-Dawaa Medical',
|
||
# 'type': 'MANUFACTURER',
|
||
# 'city': 'Medina',
|
||
# 'phone': '+966-14-678-9012'
|
||
# },
|
||
# {
|
||
# 'name': 'Nahdi Medical Company',
|
||
# 'type': 'RETAILER',
|
||
# 'city': 'Jeddah',
|
||
# 'phone': '+966-12-789-0123'
|
||
# },
|
||
# {
|
||
# 'name': 'United Pharmaceuticals',
|
||
# 'type': 'MANUFACTURER',
|
||
# 'city': 'Khobar',
|
||
# 'phone': '+966-13-890-1234'
|
||
# },
|
||
# {
|
||
# 'name': 'Middle East Medical',
|
||
# 'type': 'DISTRIBUTOR',
|
||
# 'city': 'Taif',
|
||
# 'phone': '+966-12-901-2345'
|
||
# },
|
||
# {
|
||
# 'name': 'Kingdom Medical Supplies',
|
||
# 'type': 'WHOLESALER',
|
||
# 'city': 'Buraidah',
|
||
# 'phone': '+966-16-012-3456'
|
||
# },
|
||
# {
|
||
# 'name': 'Eastern Province Medical Co.',
|
||
# 'type': 'DISTRIBUTOR',
|
||
# 'city': 'Dhahran',
|
||
# 'phone': '+966-13-123-4567'
|
||
# }
|
||
# ]
|
||
#
|
||
# SAUDI_CITIES = [
|
||
# 'Riyadh', 'Jeddah', 'Mecca', 'Medina', 'Dammam', 'Khobar', 'Dhahran',
|
||
# 'Taif', 'Buraidah', 'Tabuk', 'Hail', 'Khamis Mushait', 'Hofuf', 'Jubail',
|
||
# 'Hafar Al-Batin', 'Yanbu', 'Abha', 'Najran', 'Qatif', 'Sakaka'
|
||
# ]
|
||
#
|
||
# COMMON_MEDICAL_ITEMS = [
|
||
# # Pharmaceuticals
|
||
# {'name': 'Paracetamol 500mg', 'category': 'Pharmaceuticals', 'subcategory': 'Analgesics', 'unit': 'TAB',
|
||
# 'controlled': False},
|
||
# {'name': 'Amoxicillin 250mg', 'category': 'Pharmaceuticals', 'subcategory': 'Antibiotics', 'unit': 'CAP',
|
||
# 'controlled': False},
|
||
# {'name': 'Insulin Regular', 'category': 'Pharmaceuticals', 'subcategory': 'Diabetes Care', 'unit': 'VIAL',
|
||
# 'controlled': False},
|
||
# {'name': 'Morphine 10mg/ml', 'category': 'Pharmaceuticals', 'subcategory': 'Analgesics', 'unit': 'AMP',
|
||
# 'controlled': True},
|
||
# {'name': 'Diazepam 5mg', 'category': 'Pharmaceuticals', 'subcategory': 'Psychiatric', 'unit': 'TAB',
|
||
# 'controlled': True},
|
||
# {'name': 'Normal Saline 0.9%', 'category': 'Pharmaceuticals', 'subcategory': 'IV Solutions', 'unit': 'BAG',
|
||
# 'controlled': False},
|
||
#
|
||
# # Medical Devices
|
||
# {'name': 'Disposable Syringe 5ml', 'category': 'Medical Devices', 'subcategory': 'Injection Equipment',
|
||
# 'unit': 'PCS', 'controlled': False},
|
||
# {'name': 'Blood Pressure Cuff', 'category': 'Medical Devices', 'subcategory': 'Monitoring Equipment', 'unit': 'PCS',
|
||
# 'controlled': False},
|
||
# {'name': 'ECG Electrodes', 'category': 'Medical Devices', 'subcategory': 'Cardiology Equipment', 'unit': 'PCS',
|
||
# 'controlled': False},
|
||
# {'name': 'Surgical Gloves Size M', 'category': 'PPE & Safety', 'subcategory': 'Protective Equipment',
|
||
# 'unit': 'PAIR', 'controlled': False},
|
||
#
|
||
# # Surgical Instruments
|
||
# {'name': 'Scalpel Blade #15', 'category': 'Surgical Instruments', 'subcategory': 'Cutting Instruments',
|
||
# 'unit': 'PCS', 'controlled': False},
|
||
# {'name': 'Forceps Straight', 'category': 'Surgical Instruments', 'subcategory': 'Grasping Instruments',
|
||
# 'unit': 'PCS', 'controlled': False},
|
||
#
|
||
# # Laboratory Supplies
|
||
# {'name': 'Blood Collection Tube EDTA', 'category': 'Laboratory Supplies', 'subcategory': 'Collection Tubes',
|
||
# 'unit': 'PCS', 'controlled': False},
|
||
# {'name': 'Urine Container', 'category': 'Laboratory Supplies', 'subcategory': 'Collection Containers',
|
||
# 'unit': 'PCS', 'controlled': False},
|
||
# ]
|
||
#
|
||
#
|
||
# def generate_saudi_item_code():
|
||
# """Generate Saudi medical item code"""
|
||
# return f"SAU-{random.randint(100000, 999999)}"
|
||
#
|
||
#
|
||
# def generate_saudi_lot_number():
|
||
# """Generate Saudi lot number"""
|
||
# return f"LOT{random.randint(2024, 2025)}{random.randint(100, 999)}"
|
||
#
|
||
#
|
||
# def generate_saudi_po_number():
|
||
# """Generate Saudi purchase order number"""
|
||
# return f"PO-{random.randint(2024, 2025)}-{random.randint(1000, 9999)}"
|
||
#
|
||
#
|
||
# def create_saudi_suppliers(tenants):
|
||
# """Create Saudi suppliers"""
|
||
# suppliers = []
|
||
#
|
||
# for tenant in tenants:
|
||
# print(f"Creating suppliers for {tenant.name}...")
|
||
#
|
||
# # Get or create creator
|
||
# try:
|
||
# creators = User.objects.filter(tenant=tenant)
|
||
# creator = random.choice(creators) if creators.exists() else None
|
||
# except:
|
||
# creator = None
|
||
#
|
||
# for i, supplier_data in enumerate(SAUDI_SUPPLIER_DATA):
|
||
# supplier_code = f"SUP-{i + 1:03d}"
|
||
#
|
||
# # Ensure unique supplier code
|
||
# counter = 1
|
||
# original_code = supplier_code
|
||
# try:
|
||
# while Supplier.objects.filter(tenant=tenant, supplier_code=supplier_code).exists():
|
||
# supplier_code = f"{original_code}-{counter}"
|
||
# counter += 1
|
||
# except:
|
||
# pass
|
||
#
|
||
# try:
|
||
# with transaction.savepoint():
|
||
# supplier = Supplier.objects.create(
|
||
# tenant=tenant,
|
||
# supplier_code=supplier_code,
|
||
# name=supplier_data['name'],
|
||
# supplier_type=supplier_data['type'],
|
||
# contact_person=f"Manager - {supplier_data['name']}",
|
||
# phone=supplier_data['phone'],
|
||
# email=f"contact@{supplier_data['name'].lower().replace(' ', '').replace('.', '')}.sa",
|
||
# website=f"https://www.{supplier_data['name'].lower().replace(' ', '').replace('.', '')}.sa",
|
||
# address_line_1=f"{random.randint(1, 999)} King {random.choice(['Fahd', 'Abdullah', 'Saud'])} Road",
|
||
# city=supplier_data['city'],
|
||
# state=f"{supplier_data['city']} Province",
|
||
# postal_code=f"{random.randint(10000, 99999)}",
|
||
# country='Saudi Arabia',
|
||
# tax_id=f"TAX-{random.randint(100000000, 999999999)}",
|
||
# payment_terms=random.choice(['NET_30', 'NET_60', 'COD']),
|
||
# performance_rating=Decimal(str(random.uniform(3.5, 5.0))),
|
||
# on_time_delivery_rate=Decimal(str(random.uniform(85, 98))),
|
||
# quality_rating=Decimal(str(random.uniform(3.5, 5.0))),
|
||
# is_active=True,
|
||
# is_preferred=random.choice([True, False]),
|
||
# certifications=[
|
||
# {'name': 'ISO 13485', 'number': f"ISO-{random.randint(10000, 99999)}",
|
||
# 'expiry': '2025-12-31'},
|
||
# {'name': 'Saudi FDA', 'number': f"SFDA-{random.randint(10000, 99999)}",
|
||
# 'expiry': '2025-06-30'}
|
||
# ],
|
||
# notes=f"Saudi medical supplier - {supplier_data['name']}",
|
||
# created_by=creator
|
||
# )
|
||
# suppliers.append(supplier)
|
||
#
|
||
# except Exception as e:
|
||
# print(f"Error creating supplier {supplier_code}: {e}")
|
||
# continue
|
||
#
|
||
# print(f"Created {len(suppliers)} suppliers")
|
||
# return suppliers
|
||
#
|
||
#
|
||
# def create_saudi_inventory_locations(tenants, locations_per_tenant=20):
|
||
# """Create Saudi inventory locations"""
|
||
# locations = []
|
||
#
|
||
# for tenant in tenants:
|
||
# print(f"Creating inventory locations for {tenant.name}...")
|
||
#
|
||
# for i in range(locations_per_tenant):
|
||
# location_code = f"LOC-{i + 1:03d}"
|
||
# building = random.choice(SAUDI_STORAGE_LOCATIONS['buildings'])
|
||
# floor = random.choice(SAUDI_STORAGE_LOCATIONS['floors'])
|
||
# room = random.choice(SAUDI_STORAGE_LOCATIONS['rooms'])
|
||
# zone = random.choice(SAUDI_STORAGE_LOCATIONS['zones']) if random.choice([True, False]) else None
|
||
# aisle = random.choice(SAUDI_STORAGE_LOCATIONS['aisles']) if random.choice([True, False]) else None
|
||
# shelf = random.choice(SAUDI_STORAGE_LOCATIONS['shelves']) if random.choice([True, False]) else None
|
||
# bin_location = random.choice(SAUDI_STORAGE_LOCATIONS['bins']) if random.choice([True, False]) else None
|
||
#
|
||
# location_type = random.choice(['WAREHOUSE', 'PHARMACY', 'DEPARTMENT', 'MOBILE', 'EXTERNAL'])
|
||
#
|
||
# # Temperature controlled for pharmaceuticals
|
||
# temp_controlled = random.choice([True, False])
|
||
# temp_min = random.choice([2, 15, 20]) if temp_controlled else None
|
||
# temp_max = random.choice([8, 25, 30]) if temp_controlled else None
|
||
#
|
||
# # Humidity controlled
|
||
# humidity_controlled = random.choice([True, False])
|
||
# humidity_min = random.randint(30, 45) if humidity_controlled else None
|
||
# humidity_max = random.randint(55, 70) if humidity_controlled else None
|
||
#
|
||
# # Secure location for controlled substances
|
||
# secure_location = random.choice([True, False])
|
||
#
|
||
# # Get or create manager
|
||
# try:
|
||
# managers = User.objects.filter(tenant=tenant)
|
||
# manager = random.choice(managers) if managers.exists() else None
|
||
# except:
|
||
# manager = None
|
||
#
|
||
# try:
|
||
# with transaction.savepoint():
|
||
# location = InventoryLocation.objects.create(
|
||
# tenant=tenant,
|
||
# location_code=location_code,
|
||
# name=f"{building} - {room}",
|
||
# description=f"Storage location in {building}, {floor}, {room}",
|
||
# location_type=location_type,
|
||
# building=building,
|
||
# floor=floor,
|
||
# room=room,
|
||
# zone=zone,
|
||
# aisle=aisle,
|
||
# shelf=shelf,
|
||
# bin=bin_location,
|
||
# capacity_cubic_feet=Decimal(str(random.randint(100, 1000))),
|
||
# max_weight_pounds=Decimal(str(random.randint(500, 5000))),
|
||
# temperature_controlled=temp_controlled,
|
||
# temperature_min=temp_min,
|
||
# temperature_max=temp_max,
|
||
# humidity_controlled=humidity_controlled,
|
||
# humidity_min=humidity_min,
|
||
# humidity_max=humidity_max,
|
||
# secure_location=secure_location,
|
||
# access_control=random.choice(
|
||
# ['KEYCARD', 'PIN', 'BIOMETRIC', 'KEY']) if secure_location else None,
|
||
# is_active=True,
|
||
# location_manager=manager,
|
||
# notes=f"Created for {tenant.name} inventory management",
|
||
# created_by=manager
|
||
# )
|
||
# locations.append(location)
|
||
#
|
||
# except Exception as e:
|
||
# print(f"Error creating location {location_code}: {e}")
|
||
# continue
|
||
#
|
||
# print(f"Created {len(locations)} inventory locations")
|
||
# return locations
|
||
#
|
||
#
|
||
# def create_saudi_inventory_items(tenants, items_per_tenant=100):
|
||
# """Create Saudi inventory items"""
|
||
# items = []
|
||
#
|
||
# for tenant in tenants:
|
||
# print(f"Creating inventory items for {tenant.name}...")
|
||
#
|
||
# # Get or create creator
|
||
# try:
|
||
# creators = User.objects.filter(tenant=tenant)
|
||
# creator = random.choice(creators) if creators.exists() else None
|
||
# except:
|
||
# creator = None
|
||
#
|
||
# # Create mix of common items and random items
|
||
# items_to_create = COMMON_MEDICAL_ITEMS.copy()
|
||
#
|
||
# # Add random items
|
||
# for i in range(items_per_tenant - len(COMMON_MEDICAL_ITEMS)):
|
||
# category = random.choice(SAUDI_MEDICAL_CATEGORIES)
|
||
# subcategory = SAUDI_PHARMACEUTICAL_SUBCATEGORIES.get(category, [f"{category} Supplies"])[
|
||
# 0] if category in SAUDI_PHARMACEUTICAL_SUBCATEGORIES else f"{category} Supplies"
|
||
#
|
||
# items_to_create.append({
|
||
# 'name': f"{category} Item {i + 1}",
|
||
# 'category': category,
|
||
# 'subcategory': subcategory,
|
||
# 'unit': random.choice(['PCS', 'BOX', 'VIAL', 'TAB', 'CAP', 'ML', 'GM']),
|
||
# 'controlled': random.choice([True, False]) if category == 'Pharmaceuticals' else False
|
||
# })
|
||
#
|
||
# for item_data in items_to_create[:items_per_tenant]:
|
||
# item_code = generate_saudi_item_code()
|
||
#
|
||
# # Ensure unique item code
|
||
# counter = 1
|
||
# original_code = item_code
|
||
# try:
|
||
# while InventoryItem.objects.filter(tenant=tenant, item_code=item_code).exists():
|
||
# item_code = f"{original_code}-{counter}"
|
||
# counter += 1
|
||
# except:
|
||
# pass
|
||
#
|
||
# manufacturer = random.choice(SAUDI_MEDICAL_MANUFACTURERS)
|
||
#
|
||
# # Generate costs in SAR
|
||
# unit_cost = Decimal(str(random.uniform(10, 1000)))
|
||
# list_price = unit_cost * Decimal(str(random.uniform(1.2, 3.0)))
|
||
#
|
||
# # Storage requirements
|
||
# storage_temp_min = random.choice([2, 15, 20]) if random.choice([True, False]) else None
|
||
# storage_temp_max = random.choice([8, 25, 30]) if storage_temp_min else None
|
||
#
|
||
# # Controlled substance info
|
||
# controlled = item_data.get('controlled', False)
|
||
# dea_schedule = random.choice(['II', 'III', 'IV', 'V']) if controlled else None
|
||
#
|
||
# try:
|
||
# with transaction.savepoint():
|
||
# item = InventoryItem.objects.create(
|
||
# tenant=tenant,
|
||
# item_code=item_code,
|
||
# item_name=item_data['name'],
|
||
# description=f"Medical supply item for {tenant.name}",
|
||
# category=item_data['category'],
|
||
# subcategory=item_data['subcategory'],
|
||
# item_type=random.choice(['STOCK', 'NON_STOCK', 'SERVICE', 'KIT']),
|
||
# manufacturer=manufacturer,
|
||
# model_number=f"MOD-{random.randint(1000, 9999)}" if random.choice([True, False]) else None,
|
||
# part_number=f"PN-{random.randint(100000, 999999)}" if random.choice([True, False]) else None,
|
||
# upc_code=str(random.randint(100000000000, 999999999999)) if random.choice(
|
||
# [True, False]) else None,
|
||
# ndc_code=f"{random.randint(10000, 99999)}-{random.randint(100, 999)}-{random.randint(10, 99)}" if
|
||
# item_data['category'] == 'Pharmaceuticals' else None,
|
||
# unit_of_measure=item_data['unit'],
|
||
# package_size=random.randint(1, 100),
|
||
# package_type=random.choice(['BOTTLE', 'BOX', 'VIAL', 'TUBE', 'POUCH']),
|
||
# unit_cost=unit_cost,
|
||
# list_price=list_price,
|
||
# storage_temperature_min=storage_temp_min,
|
||
# storage_temperature_max=storage_temp_max,
|
||
# storage_humidity_min=random.randint(30, 45) if random.choice([True, False]) else None,
|
||
# storage_humidity_max=random.randint(55, 70) if random.choice([True, False]) else None,
|
||
# storage_requirements="Store in cool, dry place" if random.choice([True, False]) else None,
|
||
# has_expiration=True if item_data['category'] == 'Pharmaceuticals' else random.choice(
|
||
# [True, False]),
|
||
# shelf_life_days=random.randint(365, 1095) if item_data[
|
||
# 'category'] == 'Pharmaceuticals' else None,
|
||
# fda_approved=random.choice([True, False]),
|
||
# controlled_substance=controlled,
|
||
# dea_schedule=dea_schedule,
|
||
# is_active=True,
|
||
# is_tracked=True,
|
||
# is_serialized=random.choice([True, False]),
|
||
# is_lot_tracked=True if item_data['category'] == 'Pharmaceuticals' else random.choice(
|
||
# [True, False]),
|
||
# reorder_point=random.randint(10, 100),
|
||
# reorder_quantity=random.randint(50, 500),
|
||
# max_stock_level=random.randint(200, 1000),
|
||
# clinical_use=f"Used for {item_data['subcategory'].lower()} treatment" if random.choice(
|
||
# [True, False]) else None,
|
||
# notes=f"Saudi medical supply item - {item_data['name']}",
|
||
# created_by=creator
|
||
# )
|
||
# items.append(item)
|
||
#
|
||
# except Exception as e:
|
||
# print(f"Error creating item {item_code}: {e}")
|
||
# continue
|
||
#
|
||
# print(f"Created {len(items)} inventory items")
|
||
# return items
|
||
#
|
||
#
|
||
# def create_saudi_inventory_stock(items, locations, stock_entries_per_item=2):
|
||
# """Create Saudi inventory stock entries"""
|
||
# stocks = []
|
||
#
|
||
# for item in items:
|
||
# print(f"Creating stock for {item.item_name}...")
|
||
#
|
||
# # Get locations for this tenant
|
||
# tenant_locations = [loc for loc in locations if loc.tenant == item.tenant]
|
||
# if not tenant_locations:
|
||
# continue
|
||
#
|
||
# for _ in range(min(stock_entries_per_item, len(tenant_locations))):
|
||
# location = random.choice(tenant_locations)
|
||
#
|
||
# # Generate stock details
|
||
# lot_number = generate_saudi_lot_number() if item.is_lot_tracked else None
|
||
# serial_number = f"SN{random.randint(100000, 999999)}" if item.is_serialized else None
|
||
#
|
||
# quantity_on_hand = random.randint(0, 500)
|
||
# quantity_reserved = random.randint(0, min(quantity_on_hand, 50))
|
||
#
|
||
# # Dates
|
||
# received_date = django_timezone.now().date() - timedelta(days=random.randint(1, 365))
|
||
# expiration_date = received_date + timedelta(
|
||
# days=item.shelf_life_days) if item.has_expiration and item.shelf_life_days else None
|
||
#
|
||
# # Costs
|
||
# unit_cost = item.unit_cost * Decimal(str(random.uniform(0.9, 1.1))) # Some variation
|
||
#
|
||
# # Quality status
|
||
# quality_status = 'AVAILABLE'
|
||
# if expiration_date and expiration_date <= django_timezone.now().date():
|
||
# quality_status = 'EXPIRED'
|
||
# elif expiration_date and expiration_date <= django_timezone.now().date() + timedelta(days=30):
|
||
# quality_status = 'EXPIRING_SOON'
|
||
# elif random.choice([True, False, False, False]): # 25% chance
|
||
# quality_status = random.choice(['QUARANTINED', 'DAMAGED', 'RECALLED'])
|
||
#
|
||
# try:
|
||
# with transaction.savepoint():
|
||
# stock = InventoryStock.objects.create(
|
||
# inventory_item=item,
|
||
# location=location,
|
||
# lot_number=lot_number,
|
||
# serial_number=serial_number,
|
||
# quantity_on_hand=quantity_on_hand,
|
||
# quantity_reserved=quantity_reserved,
|
||
# received_date=received_date,
|
||
# expiration_date=expiration_date,
|
||
# unit_cost=unit_cost,
|
||
# quality_status=quality_status,
|
||
# notes=f"Stock entry for {item.item_name} at {location.name}"
|
||
# )
|
||
# stocks.append(stock)
|
||
#
|
||
# except Exception as e:
|
||
# print(f"Error creating stock for {item.item_code}: {e}")
|
||
# continue
|
||
#
|
||
# print(f"Created {len(stocks)} stock entries")
|
||
# return stocks
|
||
#
|
||
#
|
||
# def create_saudi_purchase_orders(tenants, suppliers, orders_per_tenant=10):
|
||
# """Create Saudi purchase orders"""
|
||
# orders = []
|
||
#
|
||
# for tenant in tenants:
|
||
# print(f"Creating purchase orders for {tenant.name}...")
|
||
#
|
||
# # Get suppliers for this tenant
|
||
# tenant_suppliers = [supplier for supplier in suppliers if supplier.tenant == tenant]
|
||
# if not tenant_suppliers:
|
||
# print(f"No suppliers found for {tenant.name}, skipping purchase orders...")
|
||
# continue
|
||
#
|
||
# # Get users who can create POs
|
||
# try:
|
||
# requesters = User.objects.filter(tenant=tenant)
|
||
# approvers = User.objects.filter(tenant=tenant)
|
||
#
|
||
# requester = random.choice(requesters) if requesters.exists() else None
|
||
# approver = random.choice(approvers) if approvers.exists() else None
|
||
# except:
|
||
# requester = None
|
||
# approver = None
|
||
#
|
||
# # Get delivery locations
|
||
# try:
|
||
# locations = InventoryLocation.objects.filter(tenant=tenant)
|
||
# delivery_location = random.choice(locations) if locations.exists() else None
|
||
# except:
|
||
# delivery_location = None
|
||
#
|
||
# for i in range(orders_per_tenant):
|
||
# po_number = generate_saudi_po_number()
|
||
#
|
||
# # Ensure unique PO number
|
||
# counter = 1
|
||
# original_po = po_number
|
||
# try:
|
||
# while PurchaseOrder.objects.filter(tenant=tenant, po_number=po_number).exists():
|
||
# po_number = f"{original_po}-{counter}"
|
||
# counter += 1
|
||
# except:
|
||
# pass
|
||
#
|
||
# supplier = random.choice(tenant_suppliers)
|
||
#
|
||
# # Generate dates
|
||
# order_date = django_timezone.now().date() - timedelta(days=random.randint(1, 90))
|
||
# requested_delivery = order_date + timedelta(days=random.randint(7, 30))
|
||
#
|
||
# # Determine status
|
||
# status = random.choices(
|
||
# ['DRAFT', 'PENDING_APPROVAL', 'APPROVED', 'SENT', 'PARTIAL_RECEIVED', 'RECEIVED', 'CANCELLED'],
|
||
# weights=[10, 15, 10, 20, 15, 25, 5]
|
||
# )[0]
|
||
#
|
||
# # Generate amounts in SAR
|
||
# subtotal = Decimal(str(random.uniform(1000, 50000)))
|
||
# tax_rate = Decimal('0.15') # 15% VAT in Saudi Arabia
|
||
# tax_amount = subtotal * tax_rate
|
||
# shipping_amount = Decimal(str(random.uniform(100, 1000)))
|
||
# total_amount = subtotal + tax_amount + shipping_amount
|
||
#
|
||
# try:
|
||
# with transaction.savepoint():
|
||
# order = PurchaseOrder.objects.create(
|
||
# tenant=tenant,
|
||
# po_number=po_number,
|
||
# supplier=supplier, # Now using the Supplier object
|
||
# order_date=order_date,
|
||
# requested_delivery_date=requested_delivery,
|
||
# promised_delivery_date=requested_delivery + timedelta(
|
||
# days=random.randint(0, 7)) if status != 'DRAFT' else None,
|
||
# actual_delivery_date=requested_delivery + timedelta(days=random.randint(-3, 10)) if status in [
|
||
# 'RECEIVED', 'PARTIAL_RECEIVED'] else None,
|
||
# order_type=random.choice(['STANDARD', 'RUSH', 'EMERGENCY', 'BLANKET']),
|
||
# priority=random.choice(['LOW', 'NORMAL', 'HIGH', 'URGENT']),
|
||
# subtotal=subtotal,
|
||
# tax_amount=tax_amount,
|
||
# shipping_amount=shipping_amount,
|
||
# total_amount=total_amount,
|
||
# status=status,
|
||
# delivery_location=delivery_location,
|
||
# delivery_instructions=f"Deliver to {delivery_location.name} during business hours" if delivery_location else None,
|
||
# payment_terms=random.choice(['NET_30', 'NET_60', 'COD', 'PREPAID']),
|
||
# requested_by=requester,
|
||
# approved_by=approver if status not in ['DRAFT', 'PENDING_APPROVAL'] else None,
|
||
# approval_date=order_date + timedelta(days=random.randint(1, 5)) if status not in ['DRAFT',
|
||
# 'PENDING_APPROVAL'] else None,
|
||
# notes=f"Purchase order for {tenant.name} medical supplies",
|
||
# created_by=requester
|
||
# )
|
||
# orders.append(order)
|
||
#
|
||
# except Exception as e:
|
||
# print(f"Error creating PO {po_number}: {e}")
|
||
# continue
|
||
#
|
||
# print(f"Created {len(orders)} purchase orders")
|
||
# return orders
|
||
#
|
||
#
|
||
# def create_saudi_purchase_order_items(orders, items):
|
||
# """Create Saudi purchase order items"""
|
||
# po_items = []
|
||
#
|
||
# for order in orders:
|
||
# print(f"Creating items for PO {order.po_number}...")
|
||
#
|
||
# # Get items for this tenant
|
||
# tenant_items = [item for item in items if item.tenant == order.tenant]
|
||
# if not tenant_items:
|
||
# continue
|
||
#
|
||
# # Create 1-10 items per order
|
||
# num_items = random.randint(1, min(10, len(tenant_items)))
|
||
# selected_items = random.sample(tenant_items, num_items)
|
||
#
|
||
# for line_num, item in enumerate(selected_items, 1):
|
||
# quantity_ordered = random.randint(10, 500)
|
||
# quantity_received = 0
|
||
#
|
||
# if order.status in ['PARTIAL_RECEIVED', 'RECEIVED']:
|
||
# if order.status == 'RECEIVED':
|
||
# quantity_received = quantity_ordered
|
||
# else:
|
||
# quantity_received = random.randint(0, quantity_ordered)
|
||
#
|
||
# # Price variations
|
||
# unit_price = item.unit_cost * Decimal(str(random.uniform(0.8, 1.2)))
|
||
# total_price = unit_price * quantity_ordered
|
||
#
|
||
# status = 'PENDING'
|
||
# if order.status == 'RECEIVED':
|
||
# status = 'RECEIVED'
|
||
# elif order.status == 'PARTIAL_RECEIVED':
|
||
# status = 'PARTIAL_RECEIVED' if quantity_received < quantity_ordered else 'RECEIVED'
|
||
# elif order.status == 'CANCELLED':
|
||
# status = 'CANCELLED'
|
||
#
|
||
# try:
|
||
# with transaction.savepoint():
|
||
# po_item = PurchaseOrderItem.objects.create(
|
||
# purchase_order=order,
|
||
# line_number=line_num,
|
||
# inventory_item=item,
|
||
# quantity_ordered=quantity_ordered,
|
||
# quantity_received=quantity_received,
|
||
# unit_price=unit_price,
|
||
# total_price=total_price,
|
||
# requested_delivery_date=order.requested_delivery_date,
|
||
# status=status,
|
||
# notes=f"PO item for {item.item_name}"
|
||
# )
|
||
# po_items.append(po_item)
|
||
#
|
||
# except Exception as e:
|
||
# print(f"Error creating PO item for {item.item_code}: {e}")
|
||
# continue
|
||
#
|
||
# print(f"Created {len(po_items)} purchase order items")
|
||
# return po_items
|
||
#
|
||
#
|
||
# def main():
|
||
# """Main function to create all Saudi inventory data"""
|
||
# print("Starting Saudi inventory data generation...")
|
||
#
|
||
# # Get tenants
|
||
# try:
|
||
# tenants = list(Tenant.objects.filter(is_active=True))
|
||
# if not tenants:
|
||
# print("No active tenants found. Please create tenants first.")
|
||
# return
|
||
#
|
||
# print(f"Found {len(tenants)} active tenants")
|
||
# except Exception as e:
|
||
# print(f"Error getting tenants: {e}")
|
||
# return
|
||
#
|
||
# # Create suppliers first (required for purchase orders)
|
||
# suppliers = create_saudi_suppliers(tenants)
|
||
#
|
||
# # Create inventory locations
|
||
# locations = create_saudi_inventory_locations(tenants, locations_per_tenant=15)
|
||
#
|
||
# # Create inventory items
|
||
# items = create_saudi_inventory_items(tenants, items_per_tenant=50)
|
||
#
|
||
# # Create inventory stock
|
||
# stocks = create_saudi_inventory_stock(items, locations, stock_entries_per_item=2)
|
||
#
|
||
# # Create purchase orders
|
||
# orders = create_saudi_purchase_orders(tenants, suppliers, orders_per_tenant=8)
|
||
#
|
||
# # Create purchase order items
|
||
# po_items = create_saudi_purchase_order_items(orders, items)
|
||
#
|
||
# print("\n=== Saudi Inventory Data Generation Complete ===")
|
||
# print(f"Created {len(suppliers)} suppliers")
|
||
# print(f"Created {len(locations)} inventory locations")
|
||
# print(f"Created {len(items)} inventory items")
|
||
# print(f"Created {len(stocks)} stock entries")
|
||
# print(f"Created {len(orders)} purchase orders")
|
||
# print(f"Created {len(po_items)} purchase order items")
|
||
#
|
||
#
|
||
# if __name__ == "__main__":
|
||
# main() |