project initialization
Some checks failed
System Monitoring / Health Checks (push) Has been cancelled
System Monitoring / Performance Monitoring (push) Has been cancelled
System Monitoring / Database Monitoring (push) Has been cancelled
System Monitoring / Cache Monitoring (push) Has been cancelled
System Monitoring / Log Monitoring (push) Has been cancelled
System Monitoring / Resource Monitoring (push) Has been cancelled
System Monitoring / Uptime Monitoring (push) Has been cancelled
System Monitoring / Backup Monitoring (push) Has been cancelled
System Monitoring / Security Monitoring (push) Has been cancelled
System Monitoring / Monitoring Dashboard (push) Has been cancelled
System Monitoring / Alerting (push) Has been cancelled
Security Scanning / Dependency Scanning (push) Has been cancelled
Security Scanning / Code Security Scanning (push) Has been cancelled
Security Scanning / Secrets Scanning (push) Has been cancelled
Security Scanning / Container Security Scanning (push) Has been cancelled
Security Scanning / Compliance Checking (push) Has been cancelled
Security Scanning / Security Dashboard (push) Has been cancelled
Security Scanning / Security Remediation (push) Has been cancelled

This commit is contained in:
2025-10-05 02:37:33 +08:00
parent 2cbb6d5fa1
commit b3fff546e9
226 changed files with 97805 additions and 35 deletions

View File

@@ -0,0 +1,388 @@
"""
Contract test for POST /retail/sales endpoint.
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
class RetailSalesPostContractTest(TestCase):
def setUp(self):
self.client = APIClient()
self.sales_url = '/api/v1/retail/sales/'
# Tenant authentication header
self.tenant_auth = {'HTTP_AUTHORIZATION': 'Bearer tenant_token'}
# Valid sale data
self.sale_data = {
'customer': {
'name': 'John Doe',
'email': 'john.doe@example.com',
'phone': '+60123456789',
'address': {
'street': '123 Customer Street',
'city': 'Kuala Lumpur',
'state': 'Wilayah Persekutuan',
'postal_code': '50000',
'country': 'Malaysia'
}
},
'items': [
{
'product_id': 'product-001',
'sku': 'LPT-001',
'quantity': 2,
'unit_price': 2499.99,
'discount_percentage': 5.0,
'tax_rate': 6.0
},
{
'product_id': 'product-002',
'sku': 'MOU-001',
'quantity': 1,
'unit_price': 99.99,
'discount_percentage': 0.0,
'tax_rate': 6.0
}
],
'payment': {
'method': 'CASH',
'amount_paid': 5300.00,
'reference_number': 'CASH-001'
},
'discount': {
'type': 'percentage',
'value': 2.0,
'reason': 'Loyalty discount'
},
'notes': 'Customer requested expedited delivery',
'sales_channel': 'IN_STORE',
'staff_id': 'staff-001'
}
def test_create_sale_success(self):
"""Test successful sale creation."""
response = self.client.post(
self.sales_url,
data=json.dumps(self.sale_data),
content_type='application/json',
**self.tenant_auth
)
# This should fail before implementation
assert response.status_code == status.HTTP_201_CREATED
data = response.json()
assert 'id' in data
assert data['status'] == 'COMPLETED'
assert 'customer' in data
assert 'items' in data
assert 'payment' in data
assert 'totals' in data
# Check customer information
customer = data['customer']
assert customer['name'] == self.sale_data['customer']['name']
assert customer['email'] == self.sale_data['customer']['email']
# Check items
items = data['items']
assert len(items) == 2
assert items[0]['product_id'] == self.sale_data['items'][0]['product_id']
assert items[0]['quantity'] == self.sale_data['items'][0]['quantity']
# Check payment
payment = data['payment']
assert payment['method'] == self.sale_data['payment']['method']
assert payment['amount_paid'] == self.sale_data['payment']['amount_paid']
# Check totals
totals = data['totals']
assert 'subtotal' in totals
assert 'discount_amount' in totals
assert 'tax_amount' in totals
assert 'total_amount' in totals
# Should have timestamps
assert 'created_at' in data
assert 'updated_at' in data
# Should have tenant_id from context
assert 'tenant_id' in data
def test_create_sale_unauthorized(self):
"""Test sale creation without authentication."""
response = self.client.post(
self.sales_url,
data=json.dumps(self.sale_data),
content_type='application/json'
)
assert response.status_code == status.HTTP_401_UNAUTHORIZED
def test_create_sale_missing_required_fields(self):
"""Test sale creation with missing required fields."""
incomplete_data = self.sale_data.copy()
del incomplete_data['customer']
response = self.client.post(
self.sales_url,
data=json.dumps(incomplete_data),
content_type='application/json',
**self.tenant_auth
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
data = response.json()
assert 'customer' in data.get('errors', {})
def test_create_sale_empty_items(self):
"""Test sale creation with empty items list."""
invalid_data = self.sale_data.copy()
invalid_data['items'] = []
response = self.client.post(
self.sales_url,
data=json.dumps(invalid_data),
content_type='application/json',
**self.tenant_auth
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
def test_create_sale_invalid_payment_method(self):
"""Test sale creation with invalid payment method."""
invalid_data = self.sale_data.copy()
invalid_data['payment']['method'] = 'INVALID_METHOD'
response = self.client.post(
self.sales_url,
data=json.dumps(invalid_data),
content_type='application/json',
**self.tenant_auth
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
def test_create_sale_insufficient_payment(self):
"""Test sale creation with insufficient payment amount."""
invalid_data = self.sale_data.copy()
invalid_data['payment']['amount_paid'] = 100.00 # Much less than total
response = self.client.post(
self.sales_url,
data=json.dumps(invalid_data),
content_type='application/json',
**self.tenant_auth
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
def test_create_sale_negative_quantity(self):
"""Test sale creation with negative quantity."""
invalid_data = self.sale_data.copy()
invalid_data['items'][0]['quantity'] = -1
response = self.client.post(
self.sales_url,
data=json.dumps(invalid_data),
content_type='application/json',
**self.tenant_auth
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
def test_create_sale_invalid_discount_percentage(self):
"""Test sale creation with invalid discount percentage."""
invalid_data = self.sale_data.copy()
invalid_data['items'][0]['discount_percentage'] = 150.0 # Over 100%
response = self.client.post(
self.sales_url,
data=json.dumps(invalid_data),
content_type='application/json',
**self.tenant_auth
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
def test_create_sale_with_installment(self):
"""Test sale creation with installment payment."""
installment_data = self.sale_data.copy()
installment_data['payment']['method'] = 'INSTALLMENT'
installment_data['payment']['installment_plan'] = {
'down_payment': 1000.00,
'number_of_installments': 12,
'installment_amount': 358.33,
'first_installment_date': '2024-02-01'
}
response = self.client.post(
self.sales_url,
data=json.dumps(installment_data),
content_type='application/json',
**self.tenant_auth
)
if response.status_code == status.HTTP_201_CREATED:
data = response.json()
assert data['status'] == 'COMPLETED'
assert 'installment_plan' in data['payment']
def test_create_sale_with_multiple_payments(self):
"""Test sale creation with multiple payment methods."""
multi_payment_data = self.sale_data.copy()
multi_payment_data['payment'] = [
{
'method': 'CASH',
'amount_paid': 2000.00,
'reference_number': 'CASH-001'
},
{
'method': 'CARD',
'amount_paid': 3300.00,
'reference_number': 'CARD-001',
'card_last4': '4242'
}
]
response = self.client.post(
self.sales_url,
data=json.dumps(multi_payment_data),
content_type='application/json',
**self.tenant_auth
)
if response.status_code == status.HTTP_201_CREATED:
data = response.json()
assert len(data['payment']) == 2
assert data['payment'][0]['method'] == 'CASH'
def test_create_sale_with_loyalty_points(self):
"""Test sale creation with loyalty points redemption."""
loyalty_data = self.sale_data.copy()
loyalty_data['loyalty'] = {
'points_used': 1000,
'points_value': 100.00,
'customer_id': 'customer-001'
}
response = self.client.post(
self.sales_url,
data=json.dumps(loyalty_data),
content_type='application/json',
**self.tenant_auth
)
if response.status_code == status.HTTP_201_CREATED:
data = response.json()
assert 'loyalty' in data
assert data['loyalty']['points_used'] == 1000
def test_create_sale_with_delivery_info(self):
"""Test sale creation with delivery information."""
delivery_data = self.sale_data.copy()
delivery_data['delivery'] = {
'method': 'DELIVERY',
'address': self.sale_data['customer']['address'],
'scheduled_date': '2024-01-20',
'delivery_fee': 50.00,
'notes': 'Leave at front door'
}
response = self.client.post(
self.sales_url,
data=json.dumps(delivery_data),
content_type='application/json',
**self.tenant_auth
)
if response.status_code == status.HTTP_201_CREATED:
data = response.json()
assert 'delivery' in data
assert data['delivery']['method'] == 'DELIVERY'
def test_create_sale_with_exchange_items(self):
"""Test sale creation with item exchange."""
exchange_data = self.sale_data.copy()
exchange_data['exchange'] = {
'items': [
{
'product_id': 'old-product-001',
'sku': 'OLD-001',
'condition': 'GOOD',
'exchange_value': 500.00
}
],
'total_exchange_value': 500.00
}
response = self.client.post(
self.sales_url,
data=json.dumps(exchange_data),
content_type='application/json',
**self.tenant_auth
)
if response.status_code == status.HTTP_201_CREATED:
data = response.json()
assert 'exchange' in data
assert len(data['exchange']['items']) == 1
def test_create_sale_tax_calculation(self):
"""Test that tax calculation is correct."""
response = self.client.post(
self.sales_url,
data=json.dumps(self.sale_data),
content_type='application/json',
**self.tenant_auth
)
if response.status_code == status.HTTP_201_CREATED:
data = response.json()
totals = data['totals']
# Verify tax calculation (6% GST on subtotal after discounts)
# This will be validated once implementation exists
assert 'tax_amount' in totals
assert totals['tax_amount'] >= 0
def test_create_sale_tenant_isolation(self):
"""Test that sale creation respects tenant isolation."""
response = self.client.post(
self.sales_url,
data=json.dumps(self.sale_data),
content_type='application/json',
**self.tenant_auth
)
if response.status_code == status.HTTP_201_CREATED:
data = response.json()
# Sale should be created in the authenticated tenant's context
assert 'tenant_id' in data
# This will be validated once implementation exists
def test_create_sale_inventory_validation(self):
"""Test that sale creation validates inventory availability."""
response = self.client.post(
self.sales_url,
data=json.dumps(self.sale_data),
content_type='application/json',
**self.tenant_auth
)
# This test will ensure that the system checks if sufficient stock is available
# The test should pass if implementation exists and validates inventory
if response.status_code == status.HTTP_201_CREATED:
# Success means inventory was available
pass
elif response.status_code == status.HTTP_400_BAD_REQUEST:
# This could also be valid if inventory validation fails
pass