Marwan Alwali 4d06ca4b5e update
2025-09-20 14:26:19 +03:00

1568 lines
58 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Generated by Django 5.2.6 on 2025-09-19 10:58
import django.core.validators
import django.db.models.deletion
import django.utils.timezone
import uuid
from decimal import Decimal
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
("core", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="InventoryItem",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"item_id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="Unique item identifier",
unique=True,
),
),
(
"item_code",
models.CharField(help_text="Internal item code", max_length=50),
),
("item_name", models.CharField(help_text="Item name", max_length=200)),
(
"description",
models.TextField(
blank=True, help_text="Item description", null=True
),
),
(
"category",
models.CharField(
choices=[
("MEDICAL_SUPPLIES", "Medical Supplies"),
("PHARMACEUTICALS", "Pharmaceuticals"),
("SURGICAL_INSTRUMENTS", "Surgical Instruments"),
("DIAGNOSTIC_EQUIPMENT", "Diagnostic Equipment"),
("PATIENT_CARE", "Patient Care Equipment"),
("LABORATORY_SUPPLIES", "Laboratory Supplies"),
("RADIOLOGY_SUPPLIES", "Radiology Supplies"),
("OFFICE_SUPPLIES", "Office Supplies"),
("MAINTENANCE_SUPPLIES", "Maintenance Supplies"),
("FOOD_NUTRITION", "Food & Nutrition"),
("LINENS_TEXTILES", "Linens & Textiles"),
("CLEANING_SUPPLIES", "Cleaning Supplies"),
("SAFETY_EQUIPMENT", "Safety Equipment"),
("IT_EQUIPMENT", "IT Equipment"),
("FURNITURE", "Furniture"),
("OTHER", "Other"),
],
help_text="Item category",
max_length=30,
),
),
(
"subcategory",
models.CharField(
blank=True,
help_text="Item subcategory",
max_length=50,
null=True,
),
),
(
"item_type",
models.CharField(
choices=[
("CONSUMABLE", "Consumable"),
("REUSABLE", "Reusable"),
("EQUIPMENT", "Equipment"),
("MEDICATION", "Medication"),
("IMPLANT", "Implant"),
("DEVICE", "Medical Device"),
("SUPPLY", "Supply"),
("ASSET", "Asset"),
],
help_text="Item type",
max_length=20,
),
),
(
"manufacturer",
models.CharField(
blank=True,
help_text="Manufacturer name",
max_length=100,
null=True,
),
),
(
"model_number",
models.CharField(
blank=True, help_text="Model number", max_length=50, null=True
),
),
(
"part_number",
models.CharField(
blank=True, help_text="Part number", max_length=50, null=True
),
),
(
"upc_code",
models.CharField(
blank=True, help_text="UPC barcode", max_length=20, null=True
),
),
(
"ndc_code",
models.CharField(
blank=True,
help_text="National Drug Code (for medications)",
max_length=20,
null=True,
),
),
(
"gtin_code",
models.CharField(
blank=True,
help_text="Global Trade Item Number",
max_length=20,
null=True,
),
),
(
"unit_of_measure",
models.CharField(
choices=[
("EACH", "Each"),
("BOX", "Box"),
("CASE", "Case"),
("BOTTLE", "Bottle"),
("VIAL", "Vial"),
("TUBE", "Tube"),
("PACK", "Pack"),
("KIT", "Kit"),
("ROLL", "Roll"),
("SHEET", "Sheet"),
("POUND", "Pound"),
("KILOGRAM", "Kilogram"),
("LITER", "Liter"),
("MILLILITER", "Milliliter"),
("METER", "Meter"),
("FOOT", "Foot"),
],
default="EACH",
help_text="Unit of measure",
max_length=20,
),
),
(
"package_size",
models.PositiveIntegerField(
default=1, help_text="Number of units per package"
),
),
(
"package_type",
models.CharField(
choices=[
("INDIVIDUAL", "Individual"),
("BULK", "Bulk"),
("STERILE", "Sterile Package"),
("NON_STERILE", "Non-Sterile Package"),
],
default="INDIVIDUAL",
help_text="Package type",
max_length=20,
),
),
(
"unit_cost",
models.DecimalField(
decimal_places=2,
default=Decimal("0.00"),
help_text="Unit cost",
max_digits=10,
),
),
(
"list_price",
models.DecimalField(
decimal_places=2,
default=Decimal("0.00"),
help_text="List price",
max_digits=10,
),
),
(
"storage_temperature_min",
models.DecimalField(
blank=True,
decimal_places=1,
help_text="Minimum storage temperature (Celsius)",
max_digits=5,
null=True,
),
),
(
"storage_temperature_max",
models.DecimalField(
blank=True,
decimal_places=1,
help_text="Maximum storage temperature (Celsius)",
max_digits=5,
null=True,
),
),
(
"storage_humidity_min",
models.PositiveIntegerField(
blank=True,
help_text="Minimum storage humidity (%)",
null=True,
validators=[django.core.validators.MaxValueValidator(100)],
),
),
(
"storage_humidity_max",
models.PositiveIntegerField(
blank=True,
help_text="Maximum storage humidity (%)",
null=True,
validators=[django.core.validators.MaxValueValidator(100)],
),
),
(
"storage_requirements",
models.TextField(
blank=True, help_text="Special storage requirements", null=True
),
),
(
"has_expiration",
models.BooleanField(
default=False, help_text="Item has expiration date"
),
),
(
"shelf_life_days",
models.PositiveIntegerField(
blank=True, help_text="Shelf life in days", null=True
),
),
(
"fda_approved",
models.BooleanField(default=False, help_text="FDA approved"),
),
(
"controlled_substance",
models.BooleanField(
default=False, help_text="Controlled substance"
),
),
(
"dea_schedule",
models.CharField(
blank=True,
choices=[
("CI", "Schedule I"),
("CII", "Schedule II"),
("CIII", "Schedule III"),
("CIV", "Schedule IV"),
("CV", "Schedule V"),
],
help_text="DEA schedule (for controlled substances)",
max_length=5,
null=True,
),
),
(
"is_active",
models.BooleanField(default=True, help_text="Item is active"),
),
(
"is_tracked",
models.BooleanField(
default=True, help_text="Track inventory levels"
),
),
(
"is_serialized",
models.BooleanField(
default=False, help_text="Track by serial number"
),
),
(
"is_lot_tracked",
models.BooleanField(default=False, help_text="Track by lot number"),
),
(
"reorder_point",
models.PositiveIntegerField(
default=0, help_text="Reorder point quantity"
),
),
(
"reorder_quantity",
models.PositiveIntegerField(
default=0, help_text="Reorder quantity"
),
),
(
"min_stock_level",
models.PositiveIntegerField(
blank=True, help_text="Minimum stock level", null=True
),
),
(
"max_stock_level",
models.PositiveIntegerField(
blank=True, help_text="Maximum stock level", null=True
),
),
(
"clinical_use",
models.TextField(
blank=True, help_text="Clinical use and indications", null=True
),
),
(
"contraindications",
models.TextField(
blank=True,
help_text="Contraindications and warnings",
null=True,
),
),
(
"notes",
models.TextField(
blank=True, help_text="Additional notes", null=True
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"created_by",
models.ForeignKey(
blank=True,
help_text="User who created the item",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="created_inventory_items",
to=settings.AUTH_USER_MODEL,
),
),
(
"tenant",
models.ForeignKey(
help_text="Organization tenant",
on_delete=django.db.models.deletion.CASCADE,
related_name="inventory_items",
to="core.tenant",
),
),
],
options={
"verbose_name": "Inventory Item",
"verbose_name_plural": "Inventory Items",
"db_table": "inventory_item",
"ordering": ["item_name"],
},
),
migrations.CreateModel(
name="InventoryLocation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"location_id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="Unique location identifier",
unique=True,
),
),
(
"location_code",
models.CharField(help_text="Location code", max_length=20),
),
("name", models.CharField(help_text="Location name", max_length=100)),
(
"description",
models.TextField(
blank=True, help_text="Location description", null=True
),
),
(
"location_type",
models.CharField(
choices=[
("WAREHOUSE", "Warehouse"),
("STOREROOM", "Storeroom"),
("PHARMACY", "Pharmacy"),
("NURSING_UNIT", "Nursing Unit"),
("OR_STORAGE", "OR Storage"),
("LAB_STORAGE", "Lab Storage"),
("RADIOLOGY", "Radiology Storage"),
("CENTRAL_SUPPLY", "Central Supply"),
("REFRIGERATOR", "Refrigerator"),
("FREEZER", "Freezer"),
("CONTROLLED", "Controlled Substance"),
("QUARANTINE", "Quarantine"),
("RECEIVING", "Receiving"),
("SHIPPING", "Shipping"),
("OTHER", "Other"),
],
help_text="Location type",
max_length=20,
),
),
(
"building",
models.CharField(
blank=True,
help_text="Building name/number",
max_length=50,
null=True,
),
),
(
"floor",
models.CharField(
blank=True, help_text="Floor", max_length=10, null=True
),
),
(
"room",
models.CharField(
blank=True, help_text="Room number", max_length=20, null=True
),
),
(
"zone",
models.CharField(
blank=True, help_text="Zone or area", max_length=20, null=True
),
),
(
"aisle",
models.CharField(
blank=True, help_text="Aisle", max_length=10, null=True
),
),
(
"shelf",
models.CharField(
blank=True, help_text="Shelf", max_length=10, null=True
),
),
(
"bin",
models.CharField(
blank=True, help_text="Bin location", max_length=10, null=True
),
),
(
"capacity_cubic_feet",
models.DecimalField(
blank=True,
decimal_places=2,
help_text="Capacity in cubic feet",
max_digits=10,
null=True,
),
),
(
"max_weight_pounds",
models.DecimalField(
blank=True,
decimal_places=2,
help_text="Maximum weight in pounds",
max_digits=10,
null=True,
),
),
(
"temperature_controlled",
models.BooleanField(
default=False, help_text="Temperature controlled"
),
),
(
"temperature_min",
models.DecimalField(
blank=True,
decimal_places=1,
help_text="Minimum temperature (Celsius)",
max_digits=5,
null=True,
),
),
(
"temperature_max",
models.DecimalField(
blank=True,
decimal_places=1,
help_text="Maximum temperature (Celsius)",
max_digits=5,
null=True,
),
),
(
"humidity_controlled",
models.BooleanField(default=False, help_text="Humidity controlled"),
),
(
"humidity_min",
models.PositiveIntegerField(
blank=True,
help_text="Minimum humidity (%)",
null=True,
validators=[django.core.validators.MaxValueValidator(100)],
),
),
(
"humidity_max",
models.PositiveIntegerField(
blank=True,
help_text="Maximum humidity (%)",
null=True,
validators=[django.core.validators.MaxValueValidator(100)],
),
),
(
"secure_location",
models.BooleanField(
default=False, help_text="Secure/restricted access location"
),
),
(
"access_control",
models.CharField(
choices=[
("OPEN", "Open Access"),
("BADGE", "Badge Access"),
("KEY", "Key Access"),
("BIOMETRIC", "Biometric Access"),
("DUAL_CONTROL", "Dual Control"),
],
default="OPEN",
help_text="Access control method",
max_length=20,
),
),
(
"is_active",
models.BooleanField(default=True, help_text="Location is active"),
),
(
"notes",
models.TextField(blank=True, help_text="Location notes", null=True),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"created_by",
models.ForeignKey(
blank=True,
help_text="User who created the location",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="created_inventory_locations",
to=settings.AUTH_USER_MODEL,
),
),
(
"location_manager",
models.ForeignKey(
blank=True,
help_text="Location manager",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="managed_locations",
to=settings.AUTH_USER_MODEL,
),
),
(
"parent_location",
models.ForeignKey(
blank=True,
help_text="Parent location",
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="child_locations",
to="inventory.inventorylocation",
),
),
(
"tenant",
models.ForeignKey(
help_text="Organization tenant",
on_delete=django.db.models.deletion.CASCADE,
related_name="inventory_locations",
to="core.tenant",
),
),
],
options={
"verbose_name": "Inventory Location",
"verbose_name_plural": "Inventory Locations",
"db_table": "inventory_location",
"ordering": ["location_code"],
},
),
migrations.CreateModel(
name="PurchaseOrder",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"po_id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="Unique purchase order identifier",
unique=True,
),
),
(
"po_number",
models.CharField(
help_text="Purchase order number", max_length=20, unique=True
),
),
(
"order_date",
models.DateField(
default=django.utils.timezone.now, help_text="Order date"
),
),
(
"requested_delivery_date",
models.DateField(
blank=True, help_text="Requested delivery date", null=True
),
),
(
"promised_delivery_date",
models.DateField(
blank=True, help_text="Promised delivery date", null=True
),
),
(
"actual_delivery_date",
models.DateField(
blank=True, help_text="Actual delivery date", null=True
),
),
(
"order_type",
models.CharField(
choices=[
("STANDARD", "Standard Order"),
("RUSH", "Rush Order"),
("EMERGENCY", "Emergency Order"),
("BLANKET", "Blanket Order"),
("CONTRACT", "Contract Order"),
("CONSIGNMENT", "Consignment"),
],
default="STANDARD",
help_text="Order type",
max_length=20,
),
),
(
"priority",
models.CharField(
choices=[
("LOW", "Low"),
("NORMAL", "Normal"),
("HIGH", "High"),
("URGENT", "Urgent"),
],
default="NORMAL",
help_text="Order priority",
max_length=10,
),
),
(
"subtotal",
models.DecimalField(
decimal_places=2,
default=Decimal("0.00"),
help_text="Subtotal amount",
max_digits=12,
),
),
(
"tax_amount",
models.DecimalField(
decimal_places=2,
default=Decimal("0.00"),
help_text="Tax amount",
max_digits=12,
),
),
(
"shipping_amount",
models.DecimalField(
decimal_places=2,
default=Decimal("0.00"),
help_text="Shipping amount",
max_digits=12,
),
),
(
"total_amount",
models.DecimalField(
decimal_places=2,
default=Decimal("0.00"),
help_text="Total order amount",
max_digits=12,
),
),
(
"status",
models.CharField(
choices=[
("DRAFT", "Draft"),
("PENDING_APPROVAL", "Pending Approval"),
("APPROVED", "Approved"),
("SENT", "Sent to Supplier"),
("ACKNOWLEDGED", "Acknowledged"),
("PARTIAL_RECEIVED", "Partially Received"),
("RECEIVED", "Received"),
("INVOICED", "Invoiced"),
("PAID", "Paid"),
("CANCELLED", "Cancelled"),
("CLOSED", "Closed"),
],
default="DRAFT",
help_text="Order status",
max_length=20,
),
),
(
"delivery_instructions",
models.TextField(
blank=True, help_text="Delivery instructions", null=True
),
),
(
"payment_terms",
models.CharField(
choices=[
("NET_30", "Net 30 Days"),
("NET_60", "Net 60 Days"),
("NET_90", "Net 90 Days"),
("COD", "Cash on Delivery"),
("PREPAID", "Prepaid"),
("CREDIT_CARD", "Credit Card"),
],
default="NET_30",
help_text="Payment terms",
max_length=20,
),
),
(
"approval_date",
models.DateTimeField(
blank=True, help_text="Approval date and time", null=True
),
),
(
"notes",
models.TextField(
blank=True, help_text="Order notes and comments", null=True
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"approved_by",
models.ForeignKey(
blank=True,
help_text="User who approved the order",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="approved_purchase_orders",
to=settings.AUTH_USER_MODEL,
),
),
(
"created_by",
models.ForeignKey(
blank=True,
help_text="User who created the order",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="created_purchase_orders",
to=settings.AUTH_USER_MODEL,
),
),
(
"delivery_location",
models.ForeignKey(
blank=True,
help_text="Delivery location",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="purchase_orders",
to="inventory.inventorylocation",
),
),
(
"requested_by",
models.ForeignKey(
blank=True,
help_text="User who requested the order",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="requested_purchase_orders",
to=settings.AUTH_USER_MODEL,
),
),
(
"tenant",
models.ForeignKey(
help_text="Organization tenant",
on_delete=django.db.models.deletion.CASCADE,
related_name="purchase_orders",
to="core.tenant",
),
),
],
options={
"verbose_name": "Purchase Order",
"verbose_name_plural": "Purchase Orders",
"db_table": "inventory_purchase_order",
"ordering": ["-order_date"],
},
),
migrations.CreateModel(
name="PurchaseOrderItem",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"item_id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="Unique item identifier",
unique=True,
),
),
(
"line_number",
models.PositiveIntegerField(help_text="Line item number"),
),
(
"quantity_ordered",
models.PositiveIntegerField(help_text="Quantity ordered"),
),
(
"quantity_received",
models.PositiveIntegerField(
default=0, help_text="Quantity received"
),
),
(
"quantity_remaining",
models.PositiveIntegerField(
default=0, help_text="Quantity remaining to receive"
),
),
(
"unit_price",
models.DecimalField(
decimal_places=2, help_text="Unit price", max_digits=10
),
),
(
"total_price",
models.DecimalField(
decimal_places=2,
help_text="Total price (quantity × unit price)",
max_digits=12,
),
),
(
"requested_delivery_date",
models.DateField(
blank=True,
help_text="Requested delivery date for this item",
null=True,
),
),
(
"status",
models.CharField(
choices=[
("PENDING", "Pending"),
("ORDERED", "Ordered"),
("PARTIAL_RECEIVED", "Partially Received"),
("RECEIVED", "Received"),
("CANCELLED", "Cancelled"),
],
default="PENDING",
help_text="Item status",
max_length=20,
),
),
(
"notes",
models.TextField(blank=True, help_text="Item notes", null=True),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"inventory_item",
models.ForeignKey(
help_text="Inventory item",
on_delete=django.db.models.deletion.CASCADE,
related_name="purchase_order_items",
to="inventory.inventoryitem",
),
),
(
"purchase_order",
models.ForeignKey(
help_text="Purchase order",
on_delete=django.db.models.deletion.CASCADE,
related_name="line_items",
to="inventory.purchaseorder",
),
),
],
options={
"verbose_name": "Purchase Order Item",
"verbose_name_plural": "Purchase Order Items",
"db_table": "inventory_purchase_order_item",
"ordering": ["line_number"],
},
),
migrations.CreateModel(
name="Supplier",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"supplier_id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="Unique supplier identifier",
unique=True,
),
),
(
"supplier_code",
models.CharField(help_text="Supplier code", max_length=20),
),
("name", models.CharField(help_text="Supplier name", max_length=200)),
(
"supplier_type",
models.CharField(
choices=[
("MANUFACTURER", "Manufacturer"),
("DISTRIBUTOR", "Distributor"),
("WHOLESALER", "Wholesaler"),
("RETAILER", "Retailer"),
("SERVICE", "Service Provider"),
("CONSULTANT", "Consultant"),
("OTHER", "Other"),
],
help_text="Supplier type",
max_length=20,
),
),
(
"contact_person",
models.CharField(
blank=True,
help_text="Primary contact person",
max_length=100,
null=True,
),
),
(
"phone",
models.CharField(
blank=True, help_text="Phone number", max_length=20, null=True
),
),
(
"email",
models.EmailField(
blank=True, help_text="Email address", max_length=254, null=True
),
),
(
"website",
models.URLField(blank=True, help_text="Website URL", null=True),
),
(
"address_line_1",
models.CharField(
blank=True,
help_text="Address line 1",
max_length=100,
null=True,
),
),
(
"address_line_2",
models.CharField(
blank=True,
help_text="Address line 2",
max_length=100,
null=True,
),
),
(
"city",
models.CharField(
blank=True, help_text="City", max_length=50, null=True
),
),
(
"state",
models.CharField(
blank=True, help_text="State/Province", max_length=50, null=True
),
),
(
"postal_code",
models.CharField(
blank=True,
help_text="Postal/ZIP code",
max_length=20,
null=True,
),
),
(
"country",
models.CharField(
blank=True, help_text="Country", max_length=50, null=True
),
),
(
"tax_id",
models.CharField(
blank=True, help_text="Tax ID/EIN", max_length=20, null=True
),
),
(
"duns_number",
models.CharField(
blank=True, help_text="DUNS number", max_length=20, null=True
),
),
(
"payment_terms",
models.CharField(
choices=[
("NET_30", "Net 30 Days"),
("NET_60", "Net 60 Days"),
("NET_90", "Net 90 Days"),
("COD", "Cash on Delivery"),
("PREPAID", "Prepaid"),
("CREDIT_CARD", "Credit Card"),
],
default="NET_30",
help_text="Default payment terms",
max_length=20,
),
),
(
"performance_rating",
models.DecimalField(
decimal_places=1,
default=Decimal("0.0"),
help_text="Performance rating (0-5)",
max_digits=3,
validators=[
django.core.validators.MinValueValidator(0),
django.core.validators.MaxValueValidator(5),
],
),
),
(
"on_time_delivery_rate",
models.DecimalField(
decimal_places=2,
default=Decimal("0.00"),
help_text="On-time delivery rate (%)",
max_digits=5,
validators=[
django.core.validators.MinValueValidator(0),
django.core.validators.MaxValueValidator(100),
],
),
),
(
"quality_rating",
models.DecimalField(
decimal_places=1,
default=Decimal("0.0"),
help_text="Quality rating (0-5)",
max_digits=3,
validators=[
django.core.validators.MinValueValidator(0),
django.core.validators.MaxValueValidator(5),
],
),
),
(
"is_active",
models.BooleanField(default=True, help_text="Supplier is active"),
),
(
"is_preferred",
models.BooleanField(default=False, help_text="Preferred supplier"),
),
(
"certifications",
models.JSONField(default=list, help_text="Supplier certifications"),
),
(
"contract_start_date",
models.DateField(
blank=True, help_text="Contract start date", null=True
),
),
(
"contract_end_date",
models.DateField(
blank=True, help_text="Contract end date", null=True
),
),
(
"notes",
models.TextField(blank=True, help_text="Supplier notes", null=True),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"created_by",
models.ForeignKey(
blank=True,
help_text="User who created the supplier",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="created_suppliers",
to=settings.AUTH_USER_MODEL,
),
),
(
"tenant",
models.ForeignKey(
help_text="Organization tenant",
on_delete=django.db.models.deletion.CASCADE,
related_name="suppliers",
to="core.tenant",
),
),
],
options={
"verbose_name": "Supplier",
"verbose_name_plural": "Suppliers",
"db_table": "inventory_supplier",
"ordering": ["name"],
},
),
migrations.AddField(
model_name="purchaseorder",
name="supplier",
field=models.ForeignKey(
help_text="Supplier",
on_delete=django.db.models.deletion.CASCADE,
related_name="purchase_orders",
to="inventory.supplier",
),
),
migrations.CreateModel(
name="InventoryStock",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"stock_id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="Unique stock identifier",
unique=True,
),
),
(
"lot_number",
models.CharField(
blank=True,
help_text="Lot/batch number",
max_length=50,
null=True,
),
),
(
"serial_number",
models.CharField(
blank=True, help_text="Serial number", max_length=50, null=True
),
),
(
"quantity_on_hand",
models.PositiveIntegerField(
default=0, help_text="Quantity on hand"
),
),
(
"quantity_reserved",
models.PositiveIntegerField(
default=0, help_text="Quantity reserved"
),
),
(
"quantity_available",
models.PositiveIntegerField(
default=0, help_text="Quantity available (on hand - reserved)"
),
),
(
"received_date",
models.DateField(blank=True, help_text="Date received", null=True),
),
(
"expiration_date",
models.DateField(
blank=True, help_text="Expiration date", null=True
),
),
(
"unit_cost",
models.DecimalField(
decimal_places=2,
default=Decimal("0.00"),
help_text="Unit cost for this lot",
max_digits=10,
),
),
(
"total_cost",
models.DecimalField(
decimal_places=2,
default=Decimal("0.00"),
help_text="Total cost (quantity × unit cost)",
max_digits=12,
),
),
(
"quality_status",
models.CharField(
choices=[
("GOOD", "Good"),
("QUARANTINE", "Quarantine"),
("EXPIRED", "Expired"),
("DAMAGED", "Damaged"),
("RECALLED", "Recalled"),
("REJECTED", "Rejected"),
],
default="GOOD",
help_text="Quality status",
max_length=20,
),
),
(
"notes",
models.TextField(blank=True, help_text="Stock notes", null=True),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"inventory_item",
models.ForeignKey(
help_text="Inventory item",
on_delete=django.db.models.deletion.CASCADE,
related_name="inventory_stocks",
to="inventory.inventoryitem",
),
),
(
"location",
models.ForeignKey(
help_text="Storage location",
on_delete=django.db.models.deletion.CASCADE,
related_name="inventory_stocks",
to="inventory.inventorylocation",
),
),
(
"purchase_order",
models.ForeignKey(
blank=True,
help_text="Related purchase order",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="inventory_stocks",
to="inventory.purchaseorder",
),
),
(
"supplier",
models.ForeignKey(
blank=True,
help_text="Supplier for this stock",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="inventory_stocks",
to="inventory.supplier",
),
),
],
options={
"verbose_name": "Inventory Stock",
"verbose_name_plural": "Inventory Stocks",
"db_table": "inventory_stock",
"ordering": ["expiration_date"],
},
),
migrations.AddField(
model_name="inventoryitem",
name="primary_supplier",
field=models.ForeignKey(
blank=True,
help_text="Primary supplier",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="primary_items",
to="inventory.supplier",
),
),
migrations.AddIndex(
model_name="inventorylocation",
index=models.Index(
fields=["tenant", "location_type"],
name="inventory_l_tenant__ed8a91_idx",
),
),
migrations.AddIndex(
model_name="inventorylocation",
index=models.Index(
fields=["location_code"], name="inventory_l_locatio_cac77e_idx"
),
),
migrations.AddIndex(
model_name="inventorylocation",
index=models.Index(fields=["name"], name="inventory_l_name_93296b_idx"),
),
migrations.AddIndex(
model_name="inventorylocation",
index=models.Index(
fields=["is_active"], name="inventory_l_is_acti_a5da3a_idx"
),
),
migrations.AlterUniqueTogether(
name="inventorylocation",
unique_together={("tenant", "location_code")},
),
migrations.AddIndex(
model_name="purchaseorderitem",
index=models.Index(
fields=["purchase_order", "line_number"],
name="inventory_p_purchas_6822eb_idx",
),
),
migrations.AddIndex(
model_name="purchaseorderitem",
index=models.Index(
fields=["inventory_item"], name="inventory_p_invento_29a3e0_idx"
),
),
migrations.AddIndex(
model_name="purchaseorderitem",
index=models.Index(fields=["status"], name="inventory_p_status_5c95f6_idx"),
),
migrations.AlterUniqueTogether(
name="purchaseorderitem",
unique_together={("purchase_order", "line_number")},
),
migrations.AddIndex(
model_name="supplier",
index=models.Index(
fields=["tenant", "supplier_type"],
name="inventory_s_tenant__6e7a3e_idx",
),
),
migrations.AddIndex(
model_name="supplier",
index=models.Index(
fields=["supplier_code"], name="inventory_s_supplie_ad4a4a_idx"
),
),
migrations.AddIndex(
model_name="supplier",
index=models.Index(fields=["name"], name="inventory_s_name_d435cf_idx"),
),
migrations.AddIndex(
model_name="supplier",
index=models.Index(
fields=["is_active"], name="inventory_s_is_acti_3743da_idx"
),
),
migrations.AlterUniqueTogether(
name="supplier",
unique_together={("tenant", "supplier_code")},
),
migrations.AddIndex(
model_name="purchaseorder",
index=models.Index(
fields=["tenant", "status"], name="inventory_p_tenant__664733_idx"
),
),
migrations.AddIndex(
model_name="purchaseorder",
index=models.Index(
fields=["po_number"], name="inventory_p_po_numb_12ed00_idx"
),
),
migrations.AddIndex(
model_name="purchaseorder",
index=models.Index(
fields=["supplier"], name="inventory_p_supplie_d056c0_idx"
),
),
migrations.AddIndex(
model_name="purchaseorder",
index=models.Index(
fields=["order_date"], name="inventory_p_order_d_c8909e_idx"
),
),
migrations.AddIndex(
model_name="purchaseorder",
index=models.Index(
fields=["requested_delivery_date"],
name="inventory_p_request_6ccfe8_idx",
),
),
migrations.AddIndex(
model_name="inventorystock",
index=models.Index(
fields=["inventory_item", "location"],
name="inventory_s_invento_2c87e7_idx",
),
),
migrations.AddIndex(
model_name="inventorystock",
index=models.Index(
fields=["lot_number"], name="inventory_s_lot_num_6fbc51_idx"
),
),
migrations.AddIndex(
model_name="inventorystock",
index=models.Index(
fields=["serial_number"], name="inventory_s_serial__78281b_idx"
),
),
migrations.AddIndex(
model_name="inventorystock",
index=models.Index(
fields=["expiration_date"], name="inventory_s_expirat_4a74b1_idx"
),
),
migrations.AddIndex(
model_name="inventorystock",
index=models.Index(
fields=["quality_status"], name="inventory_s_quality_3cbb6d_idx"
),
),
migrations.AddIndex(
model_name="inventoryitem",
index=models.Index(
fields=["tenant", "category"], name="inventory_i_tenant__160581_idx"
),
),
migrations.AddIndex(
model_name="inventoryitem",
index=models.Index(
fields=["item_code"], name="inventory_i_item_co_e61695_idx"
),
),
migrations.AddIndex(
model_name="inventoryitem",
index=models.Index(
fields=["item_name"], name="inventory_i_item_na_83e4bd_idx"
),
),
migrations.AddIndex(
model_name="inventoryitem",
index=models.Index(
fields=["manufacturer"], name="inventory_i_manufac_3c7e24_idx"
),
),
migrations.AddIndex(
model_name="inventoryitem",
index=models.Index(
fields=["is_active"], name="inventory_i_is_acti_fd4174_idx"
),
),
migrations.AlterUniqueTogether(
name="inventoryitem",
unique_together={("tenant", "item_code")},
),
]