hospital-management/inventory_data.py
Marwan Alwali 6b85b05882 update
2025-08-13 19:31:08 +03:00

765 lines
32 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.db import transaction, connection
from core.models import Tenant
from accounts.models import User
from inventory.models import InventoryItem, InventoryStock, InventoryLocation, PurchaseOrder, PurchaseOrderItem, \
Supplier
# Test database connection
def test_database_connection():
"""Test if database connection is working"""
try:
with connection.cursor() as cursor:
cursor.execute("SELECT 1")
result = cursor.fetchone()
print(f"Database connection test: {'SUCCESS' if result else 'FAILED'}")
return result is not None
except Exception as e:
print(f"Database connection error: {e}")
return False
# 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(list(creators)) if creators.exists() else None
except Exception as e:
print(f"Warning: Could not get creator: {e}")
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 Exception as e:
print(f"Warning: Could not check existing suppliers: {e}")
try:
# Use atomic transaction instead of savepoint
with transaction.atomic():
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)
print(f"Created supplier: {supplier_code}")
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', 'NURSING_UNIT', 'OR_STORAGE', 'CENTRAL_SUPPLY'])
# 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(list(managers)) if managers.exists() else None
except Exception as e:
print(f"Warning: Could not get manager: {e}")
manager = None
try:
# Use atomic transaction instead of savepoint
with transaction.atomic():
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)
print(f"Created location: {location_code}")
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(list(creators)) if creators.exists() else None
except Exception as e:
print(f"Warning: Could not get creator: {e}")
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 Exception as e:
print(f"Warning: Could not check existing items: {e}")
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:
# Use atomic transaction instead of savepoint
with transaction.atomic():
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:
# Use atomic transaction instead of savepoint
with transaction.atomic():
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(list(requesters)) if requesters.exists() else None
approver = random.choice(list(approvers)) if approvers.exists() else None
except Exception as e:
print(f"Warning: Could not get users: {e}")
requester = None
approver = None
# Get delivery locations
try:
locations = InventoryLocation.objects.filter(tenant=tenant)
delivery_location = random.choice(list(locations)) if locations.exists() else None
except Exception as e:
print(f"Warning: Could not get locations: {e}")
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 Exception as e:
print(f"Warning: Could not check existing POs: {e}")
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:
# Use atomic transaction instead of savepoint
with transaction.atomic():
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:
# Use atomic transaction instead of savepoint
with transaction.atomic():
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...")
# Test database connection first
if not test_database_connection():
print("Database connection failed. Please check your database configuration.")
return
# 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()