diff --git a/database/docker-compose.yml b/database/docker-compose.yml new file mode 100644 index 0000000..3fe99c3 --- /dev/null +++ b/database/docker-compose.yml @@ -0,0 +1,34 @@ +services: + # database: + # image: 'postgres:latest' + # ports: + # - 5432:5432 + # env_file: + # - .env + # networks: + # - postgres-network + # volumes: + # - ${PWD}/db-data/:/var/lib/postgresql/data/ + + pgadmin: + image: dpage/pgadmin4 + ports: + - 15433:80 + env_file: + - .env + networks: + - postgres-network + volumes: + - ${PWD}/pgadmin-data/:/var/lib/pgadmin/ + + psql-client: + image: postgres:latest + entrypoint: ["bash"] + tty: true + stdin_open: true + networks: + - postgres-network + +networks: + postgres-network: + driver: bridge \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 5436200..900ba69 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ psycopg2-binary python-dotenv dj-database-url requests +django-cors-headers \ No newline at end of file diff --git a/warehouse_managment/product/management/commands/populate_all_data.py b/warehouse_managment/product/management/commands/populate_all_data.py index 66367f1..b79146d 100644 --- a/warehouse_managment/product/management/commands/populate_all_data.py +++ b/warehouse_managment/product/management/commands/populate_all_data.py @@ -1,20 +1,26 @@ from django.core.management.base import BaseCommand from product.models import ProductCategory, Product, SupplierProduct -from warehouse.models import Warehouse, WarehouseInventory,InventoryTransaction +from warehouse.models import Warehouse, WarehouseInventory, InventoryTransaction from decimal import Decimal +from django.utils import timezone import random +from datetime import timedelta + class Command(BaseCommand): help = 'Delete old data and populate categories, products, suppliers, warehouses, and inventory' def handle(self, *args, **kwargs): self.stdout.write("🧹 Deleting existing data...") - # SupplierProduct.objects.all().delete() - # Product.objects.all().delete() - # ProductCategory.objects.all().delete() - # WarehouseInventory.objects.all().delete() - # Warehouse.objects.all().delete() - # InventoryTransaction.objects.all().delete() + + InventoryTransaction.objects.all().delete() + WarehouseInventory.objects.all().delete() + SupplierProduct.objects.all().delete() + Product.objects.all().delete() + ProductCategory.objects.all().delete() + Warehouse.objects.all().delete() + + self.stdout.write("🧪 Populating new data...") # Product categories category_names = ['Cinnamon', 'Pepper', 'Cardamom', 'Chili'] @@ -28,9 +34,10 @@ def handle(self, *args, **kwargs): # Products products = [] - for i in range(10): + for i in range(10): # more products category = random.choice(categories) product = Product.objects.create( + product_SKU=f"SKU{i + 1:03}", product_name=f"{category.category_name} Product {i + 1}", unit_price=round(random.uniform(100, 2000), 2), category=category @@ -38,35 +45,70 @@ def handle(self, *args, **kwargs): products.append(product) # Warehouses - warehouse1 = Warehouse.objects.create( - warehouse_name="Colombo Central", location_x="6.9271° N", location_y="79.8612° E" - ) - warehouse2 = Warehouse.objects.create( - warehouse_name="Kandy Depot", location_x="7.2906° N", location_y="80.6337° E" - ) - warehouse3 = Warehouse.objects.create( - warehouse_name="Kurunegala Rock", location_x="7.0032° N", location_y="80.1102° E" - ) - warehouses = [warehouse1, warehouse2, warehouse3] + warehouse_data = [ + ("Colombo Central", "6.9271° N", "79.8612° E"), + ("Kandy Depot", "7.2906° N", "80.6337° E"), + ("Kurunegala Rock", "7.0032° N", "80.1102° E"), + ] + warehouses = [] + for name, x, y in warehouse_data: + warehouse = Warehouse.objects.create( + warehouse_name=name, + location_x=x, + location_y=y, + capacity=Decimal("1000000.00") + ) + warehouses.append(warehouse) - # Supplier products & warehouse inventory + # SupplierProduct, WarehouseInventory, and InventoryTransaction for product in products: - for supplier_id in [101, 102]: + for supplier_id in [101, 102, 103]: + # SupplierProduct + max_capacity = random.randint(300000, 600000) + lead_time = random.randint(3, 10) SupplierProduct.objects.create( supplier_id=supplier_id, product=product, - maximum_capacity=random.randint(300000, 600000), + maximum_capacity=max_capacity, supplier_price=round(random.uniform(80, 1500), 2), - lead_time_days=random.randint(3, 10) + lead_time_days=lead_time ) + # For each warehouse, create inventory & transactions for warehouse in warehouses: - WarehouseInventory.objects.create( + quantity = Decimal(random.uniform(100000, 400000)) + last_restocked = timezone.now() - timedelta(days=random.randint(1, 60)) + + inventory = WarehouseInventory.objects.create( warehouse=warehouse, product=product, supplier_id=supplier_id, - quantity=Decimal(random.uniform(100000, 400000)), + quantity=quantity, + last_restocked=last_restocked, minimum_stock_level=Decimal("100000.00") ) - self.stdout.write(self.style.SUCCESS("✅ Successfully seeded all data!")) + # Create 1-2 incoming and 1 outgoing transactions per inventory + for _ in range(random.randint(1, 2)): + qty_in = Decimal(random.uniform(10000, 50000)) + InventoryTransaction.objects.create( + inventory=inventory, + transaction_type='INCOMING', + quantity_change=qty_in, + reference_number=f"REF-{random.randint(1000,9999)}", + notes="Initial delivery", + created_by=f"Supplier {supplier_id}" + ) + + if random.choice([True, False]): + qty_out = Decimal(random.uniform(5000, 20000)) + InventoryTransaction.objects.create( + inventory=inventory, + transaction_type='OUTGOING', + quantity_change=qty_out, + reference_number=f"OUT-{random.randint(1000,9999)}", + notes="Customer shipment", + created_by="System" + ) + + self.stdout.write(self.style.SUCCESS("✅ Successfully seeded all data including inventory and transactions!")) diff --git a/warehouse_managment/product/migrations/0002_product_product_sku.py b/warehouse_managment/product/migrations/0002_product_product_sku.py new file mode 100644 index 0000000..01ce84b --- /dev/null +++ b/warehouse_managment/product/migrations/0002_product_product_sku.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2 on 2025-05-07 14:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='product', + name='product_SKU', + field=models.CharField(default='SKU000', max_length=30, unique=True), + ), + ] diff --git a/warehouse_managment/product/models.py b/warehouse_managment/product/models.py index 3bebe68..38f0a3d 100644 --- a/warehouse_managment/product/models.py +++ b/warehouse_managment/product/models.py @@ -7,6 +7,7 @@ class Meta: db_table = 'product_category' class Product(models.Model): + product_SKU = models.CharField(max_length=30, unique=True, default='SKU000') product_name = models.CharField(max_length=60) unit_price = models.DecimalField(max_digits=10, decimal_places=2) category = models.ForeignKey(ProductCategory, on_delete=models.CASCADE) diff --git a/warehouse_managment/product/views.py b/warehouse_managment/product/views.py index 0a179fe..434ddfc 100644 --- a/warehouse_managment/product/views.py +++ b/warehouse_managment/product/views.py @@ -75,26 +75,23 @@ def update_supplier_product(request): except SupplierProduct.DoesNotExist: return Response({"error": "SupplierProduct not found"}, status=404) - @api_view(['GET']) def product_stock_summary(request, sku_code): - - # Ensure SKU format is valid (e.g., SKU001) + # Optional: Validate SKU format if needed if not sku_code.startswith("SKU") or not sku_code[3:].isdigit(): return Response({'error': 'Invalid SKU format'}, status=400) - product_id = int(sku_code[3:]) # SKU001 → 1 - try: - product = Product.objects.get(id=product_id) + product = Product.objects.get(product_SKU=sku_code) except Product.DoesNotExist: return Response({'error': 'Product not found'}, status=status.HTTP_404_NOT_FOUND) - total_stock = WarehouseInventory.objects.filter(product_id=product_id).aggregate( + total_stock = WarehouseInventory.objects.filter(product=product).aggregate( total=Sum('quantity') )['total'] or 0 return Response({ "product_name": product.product_name, + "product_SKU": product.product_SKU, "current_stock": float(total_stock) - }, status=status.HTTP_200_OK) \ No newline at end of file + }, status=status.HTTP_200_OK) diff --git a/warehouse_managment/warehouse/migrations/0002_warehouse_capacity.py b/warehouse_managment/warehouse/migrations/0002_warehouse_capacity.py new file mode 100644 index 0000000..c268077 --- /dev/null +++ b/warehouse_managment/warehouse/migrations/0002_warehouse_capacity.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2 on 2025-05-07 14:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('warehouse', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='warehouse', + name='capacity', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True), + ), + ] diff --git a/warehouse_managment/warehouse/models.py b/warehouse_managment/warehouse/models.py index dde014f..2fbd761 100644 --- a/warehouse_managment/warehouse/models.py +++ b/warehouse_managment/warehouse/models.py @@ -7,6 +7,7 @@ class Warehouse(models.Model): location_x = models.CharField(max_length=64) location_y = models.CharField(max_length=64) warehouse_name = models.CharField(max_length=100, blank=True) + capacity = models.DecimalField(max_digits=12, decimal_places=2, blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: diff --git a/warehouse_managment/warehouse/supplier_names.py b/warehouse_managment/warehouse/supplier_names.py new file mode 100644 index 0000000..c9fd6a1 --- /dev/null +++ b/warehouse_managment/warehouse/supplier_names.py @@ -0,0 +1,9 @@ +# supplier_names.py + +SUPPLIER_NAME_MAP = { + 101: "Supplier A", + 102: "Supplier B", + 103: "Supplier C", + 104: "Supplier D", + # Add more supplier IDs as needed +} diff --git a/warehouse_managment/warehouse/views.py b/warehouse_managment/warehouse/views.py index 71cbdb3..d19e6ec 100644 --- a/warehouse_managment/warehouse/views.py +++ b/warehouse_managment/warehouse/views.py @@ -7,6 +7,7 @@ from product.models import Product, SupplierProduct, ProductCategory from django.db.models import Sum from .models import Warehouse, WarehouseInventory, InventoryTransaction +from .supplier_names import SUPPLIER_NAME_MAP from .serializers import ( WarehouseSerializer, WarehouseInventorySerializer, @@ -23,13 +24,41 @@ def warehouse_list(request): def warehouse_inventory_list(request): warehouse_id = request.query_params.get('warehouse_id') - if warehouse_id: - inventory = WarehouseInventory.objects.filter(warehouse_id=warehouse_id) - else: - inventory = WarehouseInventory.objects.all() + if not warehouse_id: + return Response({"error": "warehouse_id is required"}, status=400) + + try: + warehouse = Warehouse.objects.get(id=warehouse_id) + except Warehouse.DoesNotExist: + return Response({"error": "Warehouse not found"}, status=404) + + inventory = WarehouseInventory.objects.filter(warehouse_id=warehouse_id) + + current_stock_level = inventory.aggregate(total=Sum('quantity'))['total'] or 0 + minimum_stock_level = inventory.aggregate(min_level=Sum('minimum_stock_level'))['min_level'] or 0 + last_restocked = inventory.order_by('-last_restocked').values_list('last_restocked', flat=True).first() + + inventory_product_details = [] + for item in inventory: + inventory_product_details.append({ + "product_name": item.product.product_name, + "category": item.product.category.category_name, + "supplied_by": SUPPLIER_NAME_MAP.get(item.supplier_id, f"Supplier {item.supplier_id}"), + "supplied_date": item.created_at.date() if item.created_at else None, + "product_count": int(item.quantity), + }) + + result = { + "warehouse_city": warehouse.warehouse_name, + "warehouse_capacity": float(warehouse.capacity), + "minimum_stock_level": float(minimum_stock_level), + "last_restocked": last_restocked.date() if last_restocked else None, + "current_stock_level": float(current_stock_level), + "inventory_product_details": inventory_product_details + } + + return Response(result, status=200) - serializer = WarehouseInventorySerializer(inventory, many=True) - return Response(serializer.data) @api_view(['GET']) diff --git a/warehouse_managment/warehouse_managment/settings.py b/warehouse_managment/warehouse_managment/settings.py index 43f79ed..3c09ada 100644 --- a/warehouse_managment/warehouse_managment/settings.py +++ b/warehouse_managment/warehouse_managment/settings.py @@ -48,6 +48,7 @@ 'rest_framework', # Django REST framework 'django.contrib.postgres', + 'corsheaders', # CORS headers ] MIDDLEWARE = [ @@ -58,8 +59,11 @@ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'corsheaders.middleware.CorsMiddleware', # CORS middleware ] +CORS_ALLOW_ALL_ORIGINS = True + ROOT_URLCONF = 'warehouse_managment.urls' TEMPLATES = [