""" Integration test for retail module operations. This test MUST fail before implementation. """ import pytest from django.test import TestCase from django.urls import reverse from rest_framework.test import APIClient from rest_framework import status import json from datetime import datetime, timedelta class RetailOperationsIntegrationTest(TestCase): def setUp(self): self.client = APIClient() # Tenant authentication header self.tenant_auth = {'HTTP_AUTHORIZATION': 'Bearer tenant_token'} # Test product data self.product_data = { 'sku': 'LPT-PRO-001', 'name': 'Professional Laptop 15"', 'description': 'High-performance laptop for business use', 'category': 'ELECTRONICS', 'price': 3499.99, 'cost': 2800.00, 'stock_quantity': 25, 'barcode': '1234567890123', 'brand': 'TechBrand', 'model': 'PRO-15-2024', 'tax_rate': 6.0 } # Test customer data self.customer_data = { 'name': 'John Customer', 'email': 'john.customer@example.com', 'phone': '+60123456789', 'address': { 'street': '123 Customer Street', 'city': 'Kuala Lumpur', 'state': 'Wilayah Persekutuan', 'postal_code': '50000', 'country': 'Malaysia' } } def test_complete_retail_workflow(self): """Test complete retail workflow from product creation to sales reporting.""" # Step 1: Create product (should fail before implementation) product_response = self.client.post( '/api/v1/retail/products/', data=json.dumps(self.product_data), content_type='application/json', **self.tenant_auth ) assert product_response.status_code == status.HTTP_201_CREATED product_data = product_response.json() # Verify product structure assert 'id' in product_data assert product_data['sku'] == self.product_data['sku'] assert product_data['stock_quantity'] == self.product_data['stock_quantity'] assert product_data['status'] == 'ACTIVE' # Step 2: Create additional products for inventory testing additional_products = [ { 'sku': 'MOU-WRL-001', 'name': 'Wireless Mouse', 'category': 'ELECTRONICS', 'price': 89.99, 'cost': 45.00, 'stock_quantity': 50 }, { 'sku': 'KEY-MEC-001', 'name': 'Mechanical Keyboard', 'category': 'ELECTRONICS', 'price': 299.99, 'cost': 180.00, 'stock_quantity': 30 } ] created_products = [] for prod_data in additional_products: prod_response = self.client.post( '/api/v1/retail/products/', data=json.dumps(prod_data), content_type='application/json', **self.tenant_auth ) assert prod_response.status_code == status.HTTP_201_CREATED created_products.append(prod_response.json()) # Step 3: Process multiple sales transactions sales_transactions = [ { 'customer': self.customer_data, 'items': [ { 'product_id': product_data['id'], 'sku': product_data['sku'], 'quantity': 2, 'unit_price': product_data['price'] }, { 'product_id': created_products[0]['id'], 'sku': created_products[0]['sku'], 'quantity': 1, 'unit_price': created_products[0]['price'] } ], 'payment': { 'method': 'CARD', 'amount_paid': 7290.00, 'reference_number': 'CARD-001' } }, { 'customer': { 'name': 'Jane Buyer', 'email': 'jane@example.com', 'phone': '+60198765432' }, 'items': [ { 'product_id': created_products[1]['id'], 'sku': created_products[1]['sku'], 'quantity': 3, 'unit_price': created_products[1]['price'] } ], 'payment': { 'method': 'CASH', 'amount_paid': 900.00, 'reference_number': 'CASH-001' } } ] created_sales = [] for sale_data in sales_transactions: sale_response = self.client.post( '/api/v1/retail/sales/', data=json.dumps(sale_data), content_type='application/json', **self.tenant_auth ) assert sale_response.status_code == status.HTTP_201_CREATED created_sales.append(sale_response.json()) # Step 4: Verify inventory updates after sales inventory_check_response = self.client.get( f'/api/v1/retail/products/{product_data["id"]}/', **self.tenant_auth ) assert inventory_check_response.status_code == status.HTTP_200_OK updated_product = inventory_check_response.json() # Stock should be reduced by sold quantity expected_stock = self.product_data['stock_quantity'] - 2 # 2 laptops sold assert updated_product['stock_quantity'] == expected_stock # Step 5: Test sales reporting sales_report_response = self.client.get( '/api/v1/retail/reports/sales/', data={ 'start_date': (datetime.now() - timedelta(days=7)).isoformat(), 'end_date': datetime.now().isoformat() }, **self.tenant_auth ) assert sales_report_response.status_code == status.HTTP_200_OK sales_report = sales_report_response.json() assert 'total_sales' in sales_report assert 'total_revenue' in sales_report assert 'transactions_count' in sales_report assert 'top_products' in sales_report # Verify report data assert sales_report['transactions_count'] == len(created_sales) assert sales_report['total_revenue'] > 0 # Step 6: Test inventory reporting inventory_report_response = self.client.get( '/api/v1/retail/reports/inventory/', **self.tenant_auth ) assert inventory_report_response.status_code == status.HTTP_200_OK inventory_report = inventory_report_response.json() assert 'total_products' in inventory_report assert 'low_stock_items' in inventory_report assert 'total_value' in inventory_report # Step 7: Test product search and filtering search_response = self.client.get( '/api/v1/retail/products/', data={'search': 'laptop', 'category': 'ELECTRONICS'}, **self.tenant_auth ) assert search_response.status_code == status.HTTP_200_OK search_results = search_response.json()['products'] # Should find the laptop product assert len(search_results) > 0 assert any(product['id'] == product_data['id'] for product in search_results) def test_inventory_management_operations(self): """Test inventory management operations.""" # Create product first product_response = self.client.post( '/api/v1/retail/products/', data=json.dumps(self.product_data), content_type='application/json', **self.tenant_auth ) assert product_response.status_code == status.HTTP_201_CREATED product_data = product_response.json() # Step 1: Stock adjustment adjustment_data = { 'type': 'ADDITION', 'quantity': 10, 'reason': 'New stock received', 'reference': 'PO-2024-001', 'unit_cost': 2750.00 } adjustment_response = self.client.post( f'/api/v1/retail/products/{product_data["id"]}/inventory/', data=json.dumps(adjustment_data), content_type='application/json', **self.tenant_auth ) assert adjustment_response.status_code == status.HTTP_200_OK # Verify stock was updated updated_product_response = self.client.get( f'/api/v1/retail/products/{product_data["id"]}/', **self.tenant_auth ) assert updated_product_response.status_code == status.HTTP_200_OK updated_product = updated_product_response.json() expected_stock = self.product_data['stock_quantity'] + 10 assert updated_product['stock_quantity'] == expected_stock # Step 2: Stock transfer transfer_data = { 'quantity': 5, 'from_location': 'Warehouse A', 'to_location': 'Store Front', 'reason': 'Restocking store' } transfer_response = self.client.post( f'/api/v1/retail/products/{product_data["id"]}/transfer/', data=json.dumps(transfer_data), content_type='application/json', **self.tenant_auth ) assert transfer_response.status_code == status.HTTP_200_OK # Step 3: Low stock alerts # Create product with low stock low_stock_product = self.product_data.copy() low_stock_product['sku'] = 'LOW-STOCK-001' low_stock_product['stock_quantity'] = 2 low_stock_response = self.client.post( '/api/v1/retail/products/', data=json.dumps(low_stock_product), content_type='application/json', **self.tenant_auth ) assert low_stock_response.status_code == status.HTTP_201_CREATED # Check low stock report low_stock_report_response = self.client.get( '/api/v1/retail/reports/low-stock/', **self.tenant_auth ) assert low_stock_report_response.status_code == status.HTTP_200_OK low_stock_report = low_stock_report_response.json() assert 'low_stock_items' in low_stock_report assert len(low_stock_report['low_stock_items']) > 0 def test_product_variant_management(self): """Test product variant management.""" # Create parent product with variants parent_product = self.product_data.copy() parent_product['variants'] = [ { 'sku': 'LPT-PRO-001-BLK', 'name': 'Professional Laptop 15" - Black', 'attributes': {'color': 'Black', 'storage': '512GB SSD'}, 'price_adjustment': 0, 'stock_quantity': 10 }, { 'sku': 'LPT-PRO-001-SLV', 'name': 'Professional Laptop 15" - Silver', 'attributes': {'color': 'Silver', 'storage': '1TB SSD'}, 'price_adjustment': 200, 'stock_quantity': 8 } ] product_response = self.client.post( '/api/v1/retail/products/', data=json.dumps(parent_product), content_type='application/json', **self.tenant_auth ) assert product_response.status_code == status.HTTP_201_CREATED created_product = product_response.json() # Verify variants were created assert 'variants' in created_product assert len(created_product['variants']) == 2 # Test variant operations variant = created_product['variants'][0] # Update variant stock variant_stock_update = { 'stock_quantity': 15, 'reason': 'New stock received' } variant_update_response = self.client.put( f'/api/v1/retail/products/{created_product["id"]}/variants/{variant["sku"]}/', data=json.dumps(variant_stock_update), content_type='application/json', **self.tenant_auth ) assert variant_update_response.status_code == status.HTTP_200_OK def test_customer_management(self): """Test customer management operations.""" # Create customer customer_response = self.client.post( '/api/v1/retail/customers/', data=json.dumps(self.customer_data), content_type='application/json', **self.tenant_auth ) assert customer_response.status_code == status.HTTP_201_CREATED customer_data = customer_response.json() # Step 1: Customer purchase history # Create a sale for this customer sale_data = { 'customer_id': customer_data['id'], 'items': [ { 'product_id': 'product-001', 'sku': 'TEST-001', 'quantity': 1, 'unit_price': 99.99 } ], 'payment': { 'method': 'CASH', 'amount_paid': 99.99 } } sale_response = self.client.post( '/api/v1/retail/sales/', data=json.dumps(sale_data), content_type='application/json', **self.tenant_auth ) assert sale_response.status_code == status.HTTP_201_CREATED # Get customer purchase history history_response = self.client.get( f'/api/v1/retail/customers/{customer_data["id"]}/history/', **self.tenant_auth ) assert history_response.status_code == status.HTTP_200_OK history_data = history_response.json() assert 'purchases' in history_data assert 'total_spent' in history_data assert 'loyalty_points' in history_data # Step 2: Customer loyalty program loyalty_data = { 'points_earned': 100, 'notes': 'Purchase bonus' } loyalty_response = self.client.post( f'/api/v1/retail/customers/{customer_data["id"]}/loyalty/', data=json.dumps(loyalty_data), content_type='application/json', **self.tenant_auth ) assert loyalty_response.status_code == status.HTTP_200_OK def test_discount_and_promotion_management(self): """Test discount and promotion management.""" # Create promotion promotion_data = { 'name': 'New Year Sale', 'type': 'PERCENTAGE', 'value': 20, 'start_date': (datetime.now() - timedelta(days=1)).isoformat(), 'end_date': (datetime.now() + timedelta(days=30)).isoformat(), 'applicable_products': ['product-001', 'product-002'], 'minimum_purchase': 100, 'usage_limit': 100 } promotion_response = self.client.post( '/api/v1/retail/promotions/', data=json.dumps(promotion_data), content_type='application/json', **self.tenant_auth ) assert promotion_response.status_code == status.HTTP_201_CREATED created_promotion = promotion_response.json() # Test promotion application in sale sale_with_promotion = { 'customer': self.customer_data, 'items': [ { 'product_id': 'product-001', 'sku': 'TEST-001', 'quantity': 2, 'unit_price': 100.00 } ], 'promotion_code': created_promotion['code'], 'payment': { 'method': 'CARD', 'amount_paid': 160.00 # 20% discount on 200 } } sale_response = self.client.post( '/api/v1/retail/sales/', data=json.dumps(sale_with_promotion), content_type='application/json', **self.tenant_auth ) assert sale_response.status_code == status.HTTP_201_CREATED sale_data = sale_response.json() # Verify discount was applied assert 'discount_amount' in sale_data['totals'] assert sale_data['totals']['discount_amount'] == 40.00 def test_return_and_refund_operations(self): """Test return and refund operations.""" # Create a sale first sale_data = { 'customer': self.customer_data, 'items': [ { 'product_id': 'product-001', 'sku': 'TEST-001', 'quantity': 2, 'unit_price': 100.00 } ], 'payment': { 'method': 'CARD', 'amount_paid': 200.00 } } sale_response = self.client.post( '/api/v1/retail/sales/', data=json.dumps(sale_data), content_type='application/json', **self.tenant_auth ) assert sale_response.status_code == status.HTTP_201_CREATED created_sale = sale_response.json() # Process return return_data = { 'sale_id': created_sale['id'], 'items': [ { 'product_id': 'product-001', 'quantity': 1, 'reason': 'Defective product', 'condition': 'DAMAGED' } ], 'refund_method': 'ORIGINAL', 'notes': 'Customer reported defective item' } return_response = self.client.post( '/api/v1/retail/returns/', data=json.dumps(return_data), content_type='application/json', **self.tenant_auth ) assert return_response.status_code == status.HTTP_201_CREATED return_data = return_response.json() # Verify inventory was updated (returned to stock) # Verify refund was processed assert 'refund_amount' in return_data assert return_data['refund_amount'] == 100.00 def test_retail_analytics_and_reporting(self): """Test retail analytics and reporting.""" # Generate some test data first # This would involve creating multiple products and sales # Test sales analytics analytics_response = self.client.get( '/api/v1/retail/analytics/', data={ 'period': 'monthly', 'year': 2024, 'month': 1 }, **self.tenant_auth ) assert analytics_response.status_code == status.HTTP_200_OK analytics_data = analytics_response.json() assert 'revenue' in analytics_data assert 'profit' in analytics_data assert 'top_products' in analytics_data assert 'customer_metrics' in analytics_data # Test product performance performance_response = self.client.get( '/api/v1/retail/reports/product-performance/', **self.tenant_auth ) assert performance_response.status_code == status.HTTP_200_OK performance_data = performance_response.json() assert 'products' in performance_data assert 'best_sellers' in performance_data assert 'low_performers' in performance_data