MVP final
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
import os
|
||||
import sys
|
||||
import django
|
||||
from django.test import Client
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from decimal import Decimal
|
||||
|
||||
# Set up Django environment
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
|
||||
django.setup()
|
||||
|
||||
from django.conf import settings
|
||||
if 'testserver' not in settings.ALLOWED_HOSTS:
|
||||
settings.ALLOWED_HOSTS.append('testserver')
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from expenses.models import Project, Category, Vendor, Expense, Document, SystemSetting, CurrencyEnum, TaxTypeEnum, ApprovalStatusEnum
|
||||
|
||||
def run_test():
|
||||
print("=" * 60)
|
||||
print("EFINTEL ERP — Expense Creation Flow Integration Test")
|
||||
print("=" * 60)
|
||||
|
||||
# 1. Setup client and login
|
||||
print("Step 1: Setting up Django test Client & Authenticating...")
|
||||
client = Client()
|
||||
admin_user = User.objects.filter(username='admin').first()
|
||||
if not admin_user:
|
||||
print("Admin user 'admin' not found in database. Creating user...")
|
||||
admin_user = User.objects.create_superuser('admin', 'admin@example.com', 'admin')
|
||||
print("Admin user created successfully.")
|
||||
|
||||
client.force_login(admin_user)
|
||||
print(f"Authenticated successfully as: {admin_user.username} (ID: {admin_user.id})")
|
||||
|
||||
# Fetch required objects
|
||||
print("\nFetching Project (PRY-101) and Category (1201)...")
|
||||
project = Project.objects.filter(code='PRY-101').first()
|
||||
if not project:
|
||||
raise ValueError("Project PRY-101 not found! Please run project seed scripts first.")
|
||||
print(f"Found Project: {project.code} - '{project.name}' (ID: {project.id})")
|
||||
|
||||
category = Category.objects.filter(code='1201').first()
|
||||
if not category:
|
||||
raise ValueError("Category 1201 not found! Please run category seed scripts first.")
|
||||
print(f"Found Category: {category.code} - '{category.name}' (ID: {category.id})")
|
||||
|
||||
# Fetch/create SystemSetting to get exchange rate
|
||||
setting, created = SystemSetting.objects.get_or_create(id=1, defaults={'usd_exchange_rate': 950.0})
|
||||
usd_rate = setting.usd_exchange_rate
|
||||
print(f"System setting exchange rate is: {usd_rate}")
|
||||
|
||||
# Prepare Mock Files
|
||||
print("\nPreparing mock evidence files for upload...")
|
||||
file1 = SimpleUploadedFile("receipt1.pdf", b"pdf content mock", content_type="application/pdf")
|
||||
file2 = SimpleUploadedFile("receipt2.png", b"png content mock", content_type="image/png")
|
||||
|
||||
# 2. Prepare POST data
|
||||
print("\nStep 2: Preparing POST request data for '/gastos/nuevo/'...")
|
||||
post_data = {
|
||||
'project': project.id,
|
||||
'category': category.id,
|
||||
'amount_clp': '2.500.000',
|
||||
'tax_type': TaxTypeEnum.IVA_19,
|
||||
'tax_amount_clp': '475.000', # 19% of 2.500.000
|
||||
'total_amount_clp': '2.975.000', # 2.500.000 + 475.000
|
||||
'currency': CurrencyEnum.CLP,
|
||||
'responsible_person': admin_user.id,
|
||||
'date': '2026-08-21',
|
||||
'description': 'Servicios de consultoría de prototipado rápido y PCBs',
|
||||
'new_vendor_rut': '77.888.999-5',
|
||||
'new_vendor_name': 'CONEXIONES SPA',
|
||||
'toggle-new-vendor': 'on',
|
||||
'region': 'Región Metropolitana',
|
||||
'comuna': 'Santiago',
|
||||
'receipt': [file1, file2],
|
||||
}
|
||||
|
||||
# Clean existing data to avoid conflicts and guarantee clean assertion
|
||||
print("Cleaning any existing vendor/expense with target RUT to avoid duplication conflicts...")
|
||||
Vendor.objects.filter(rut='77.888.999-5').delete()
|
||||
|
||||
# 3. Execute POST request
|
||||
print("\nStep 3: Executing POST request to '/gastos/nuevo/'...")
|
||||
response = client.post('/gastos/nuevo/', post_data, follow=False)
|
||||
|
||||
# 4. Verify results
|
||||
print("\nStep 4: Verifying results...")
|
||||
|
||||
# 4.1 Response Status and Redirect
|
||||
status_code = response.status_code
|
||||
location = response.get('Location', '')
|
||||
print(f" [Assertion 1] Response Status Code: {status_code} (Expected: 302)")
|
||||
print(f" [Assertion 1] Redirect Location: {location} (Expected: /gastos/)")
|
||||
assert status_code == 302, f"Expected status code 302, got {status_code}"
|
||||
assert '/gastos/' in location, f"Expected redirect to '/gastos/', got '{location}'"
|
||||
print(" => Response redirect validation: PASSED")
|
||||
|
||||
# 4.2 New Vendor creation
|
||||
vendor = Vendor.objects.filter(rut='77.888.999-5').first()
|
||||
assert vendor is not None, "Vendor was not created!"
|
||||
print(f" [Assertion 2] Vendor Created: RUT={vendor.rut}, Name='{vendor.name}' (Expected: 'CONEXIONES SPA')")
|
||||
assert vendor.name == 'CONEXIONES SPA', f"Expected name 'CONEXIONES SPA', got '{vendor.name}'"
|
||||
print(" => Vendor DB validation: PASSED")
|
||||
|
||||
# 4.3 New Expense creation
|
||||
expense = Expense.objects.filter(vendor=vendor, date='2026-08-21').first()
|
||||
assert expense is not None, "Expense was not created!"
|
||||
print(f" [Assertion 3] Expense Created with net amount={expense.amount_clp} CLP (Expected: 2500000.00 CLP)")
|
||||
assert expense.amount_clp == Decimal('2500000.00'), f"Expected net amount 2500000.00, got {expense.amount_clp}"
|
||||
print(" => Expense DB amount validation: PASSED")
|
||||
|
||||
# 4.4 Associated Document verification
|
||||
documents = list(expense.documents.all())
|
||||
print(f" [Assertion 4] Associated Documents Count: {len(documents)} (Expected: 2)")
|
||||
assert len(documents) == 2, f"Expected 2 documents, found {len(documents)}"
|
||||
filenames = [os.path.basename(doc.file.name) for doc in documents]
|
||||
print(f" [Assertion 4] Document filenames in DB: {filenames} (Expected containing 'receipt1.pdf' and 'receipt2.png')")
|
||||
assert any("receipt1" in fn and fn.endswith(".pdf") for fn in filenames), "receipt1.pdf not found in documents"
|
||||
assert any("receipt2" in fn and fn.endswith(".png") for fn in filenames), "receipt2.png not found in documents"
|
||||
print(" => Documents validation: PASSED")
|
||||
|
||||
# 4.5 USD conversion calculation
|
||||
expected_usd = Decimal('2500000.00') / usd_rate
|
||||
print(f" [Assertion 5] Expense USD Amount in DB: {expense.amount_usd} (Expected: {expected_usd})")
|
||||
assert abs(expense.amount_usd - expected_usd) < Decimal('0.01'), f"USD conversion mismatch: DB={expense.amount_usd}, Expected={expected_usd}"
|
||||
print(" => USD exchange rate conversion validation: PASSED")
|
||||
|
||||
# 5. Test bank transfer export
|
||||
print("\nStep 5: Testing 'export_bank_transfer_csv' endpoint...")
|
||||
# Change expense status to SOLICITADO_FINANZAS to make sure it is exported
|
||||
expense.approval_status = ApprovalStatusEnum.SOLICITADO_FINANZAS
|
||||
expense.save()
|
||||
|
||||
export_response = client.get('/gastos/exportar/transferencias/')
|
||||
print(f" [Assertion 6] Export Status Code: {export_response.status_code} (Expected: 200)")
|
||||
assert export_response.status_code == 200, f"Expected 200, got {export_response.status_code}"
|
||||
|
||||
# Decode using utf-8-sig to automatically handle BOM if present, or manually assert it
|
||||
content_raw = export_response.content
|
||||
assert content_raw.startswith(b'\xef\xbb\xbf'), "BOM bytes not found at the start of CSV!"
|
||||
content_str = content_raw.decode('utf-8')
|
||||
assert 'RUT Proveedor' in content_str, "CSV header mismatch!"
|
||||
assert 'CONEXIONES SPA' in content_str, "CONEXIONES SPA should be present in the export!"
|
||||
print(" => Bank Transfer CSV validation: PASSED")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("🎉 ALL INTEGRATION VALIDATION STEPS PASSED SUCCESSFULLY! 🎉")
|
||||
print("=" * 60)
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
run_test()
|
||||
except Exception as e:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user