Files
erp-efintel-2026-portainer/expenses/tests.py
T
2026-09-07 18:44:32 -03:00

177 lines
7.9 KiB
Python

from django.test import TestCase, Client
from django.contrib.auth.models import User, Group
from decimal import Decimal
from datetime import date
from expenses.models import (
Expense, Project, Category, SystemSetting, Vendor, ProjectPhase,
ProjectStatusEnum, TaxTypeEnum, ExpenseTypeEnum, VehicleLog
)
from expenses.services.sii import SIIService
from talent.models import SpecialistCategoryEnum, SpecialistCandidate
from procurement.models import ItemCatalog, Category as ProcurementCategory
class ERPRequirementsTestCase(TestCase):
def setUp(self):
self.client = Client()
self.admin = User.objects.create_superuser('admin_user', 'admin@efintel.cl', 'password123')
self.pm_group, _ = Group.objects.get_or_create(name='Director de Proyecto')
self.pm_user = User.objects.create_user('pm_user', 'pm@efintel.cl', 'password123')
self.pm_user.groups.add(self.pm_group)
self.setting = SystemSetting.objects.create(id=1, usd_exchange_rate=950.0, iva_rate=19.00)
self.category_combustible = Category.objects.create(code='3101', name='Combustibles / Bencina')
self.category_office = Category.objects.create(code='2301', name='Arriendo de Oficinas y Espacios de Trabajo')
self.category_courses = Category.objects.create(code='4201', name='Cursos, Capacitación y Certificaciones Técnicas para Especialistas')
self.category_ai = Category.objects.create(code='7000', name='Software, Datos, Inteligencia Artificial y Cloud')
self.project_idi = Project.objects.create(
code='CC-IDI-01',
name='Centro I+D+i Central',
assigned_budget_clp=10000000,
payment_method='Mensual',
total_income_clp=15000000,
type=ExpenseTypeEnum.COSTO_DIRECTO,
status=ProjectStatusEnum.ACTIVO,
manager=self.pm_user
)
def test_req_1_and_5_dynamic_iva_and_settings(self):
"""Test Requerimiento 1 (No global_budget in dashboard) & Requerimiento 5 (Dynamic IVA)."""
self.client.login(username='admin_user', password='password123')
response = self.client.get('/')
self.assertEqual(response.status_code, 200)
self.assertNotIn('PRESUPUESTO GENERAL I+D+i', response.content.decode())
# Test Dynamic IVA (Change to 21%)
self.setting.iva_rate = Decimal('21.00')
self.setting.save()
calc = SIIService.calculate_taxes(Decimal('100000'), 'IVA_19')
self.assertEqual(calc['tax_amount'], Decimal('21000'))
self.assertEqual(calc['total_amount'], Decimal('121000'))
def test_req_2_exact_project_count(self):
"""Test Requerimiento 2: Exact project count by status."""
Project.objects.create(code='PROY-P1', name='Pausado', status=ProjectStatusEnum.PAUSADA)
Project.objects.create(code='PROY-T1', name='Terminado', status=ProjectStatusEnum.TERMINADO)
Project.objects.create(code='PROY-D1', name='Borrador', status=ProjectStatusEnum.BORRADOR)
self.client.login(username='admin_user', password='password123')
res = self.client.get('/proyectos/')
self.assertEqual(res.status_code, 200)
self.assertEqual(res.context['project_count_active'], 1)
self.assertEqual(res.context['project_count_paused'], 1)
self.assertEqual(res.context['project_count_completed'], 1)
self.assertEqual(res.context['project_count_draft'], 1)
self.assertEqual(res.context['project_count_total'], 4)
def test_req_4_sii_integration(self):
"""Test Requerimiento 4: SII API service and taxpayer info query."""
is_valid, formatted = SIIService.validate_rut('76543210-3')
self.assertTrue(is_valid)
self.assertEqual(formatted, '76.543.210-3')
info = SIIService.get_taxpayer_info('76.543.210-3')
self.assertTrue(info['valid'])
self.assertEqual(info['rut'], '76.543.210-3')
def test_req_6_vendor_draft_mitigation(self):
"""Test Requerimiento 6: Vendor registration error mitigation and draft in session."""
self.client.login(username='admin_user', password='password123')
# Submit invalid vendor form (RUT invalid - fails Modulo 11 check)
response = self.client.post('/gestion/proveedores/nuevo/', {
'rut': '12345678-0',
'name': 'Vendor Test Draft',
'payment_terms': 30
})
self.assertEqual(response.status_code, 200)
self.assertIn('vendor_draft', self.client.session)
def test_req_7_is_reimbursement_logic(self):
"""Test Requerimiento 7: Erogación rendición vs centro I+D+i."""
expense = Expense.objects.create(
project=self.project_idi,
category=self.category_office,
is_reimbursement=False,
amount_clp=50000,
date=date.today(),
description='Gasto I+D+i directo',
user=self.admin
)
self.assertFalse(expense.is_reimbursement)
# Reimbursement expense
expense_reimb = Expense.objects.create(
project=self.project_idi,
category=self.category_office,
is_reimbursement=True,
responsible_person=self.pm_user,
amount_clp=30000,
date=date.today(),
description='Rendición de pasajes',
user=self.admin
)
self.assertTrue(expense_reimb.is_reimbursement)
self.assertEqual(expense_reimb.responsible_person, self.pm_user)
def test_req_8_9_10_project_detail_and_phase_balances(self):
"""Test Requerimiento 8, 9, 10: Project detail, progressive creation, and phase balances (+ / -)."""
phase1 = ProjectPhase.objects.create(
project=self.project_idi,
phase_number=1,
name='Fase 1: Prototipado',
planned_amount_clp=5000000,
income_amount_clp=6000000,
expense_amount_clp=2000000
)
self.assertEqual(phase1.balance_clp, Decimal('4000000')) # Positive balance +$4.000.000
phase2 = ProjectPhase.objects.create(
project=self.project_idi,
phase_number=2,
name='Fase 2: Pruebas Terreno',
planned_amount_clp=3000000,
income_amount_clp=1000000,
expense_amount_clp=2500000
)
self.assertEqual(phase2.balance_clp, Decimal('-1500000')) # Negative balance -$1.500.000
self.client.login(username='admin_user', password='password123')
res = self.client.get(f'/proyectos/{self.project_idi.id}/')
self.assertEqual(res.status_code, 200)
self.assertIn('Fases de Pago e Hitos del Proyecto', res.content.decode())
def test_req_11_12_13_categories_taxonomy(self):
"""Test Requerimiento 11, 12, 13: Talent category 7, Office Rental, and Specialist Courses."""
# Req 11
self.assertEqual(SpecialistCategoryEnum.CAT_7_SOFTWARE_DATOS_IA_CLOUD, '7')
self.assertEqual(SpecialistCategoryEnum.CAT_7_SOFTWARE_DATOS_IA_CLOUD.label, '7. Software, Datos, IA y Cloud')
# Req 12
self.assertEqual(self.category_office.code, '2301')
# Req 13
self.assertEqual(self.category_courses.code, '4201')
def test_req_14_vehicle_category_strict_combustible(self):
"""Test Requerimiento 14: Vehicle log applies ONLY to combustible category."""
exp_combustible = Expense.objects.create(
project=self.project_idi,
category=self.category_combustible,
amount_clp=40000,
date=date.today(),
description='Carga Combustible Copec',
user=self.admin
)
self.assertTrue(exp_combustible.is_vehicle_category())
exp_office = Expense.objects.create(
project=self.project_idi,
category=self.category_office,
amount_clp=150000,
date=date.today(),
description='Pago Arriendo Oficina',
user=self.admin
)
self.assertFalse(exp_office.is_vehicle_category())