FEAT: re-integracion de tax-api y simulador F29
This commit is contained in:
@@ -205,12 +205,44 @@ class SIIService:
|
||||
return {
|
||||
'tax_type': 'EXENTO',
|
||||
'net_amount': net_amount_clp,
|
||||
'tax_rate_percent': Decimal('0.0'),
|
||||
'tax_rate_percent': Decimal('0'),
|
||||
'tax_amount': Decimal('0'),
|
||||
'total_amount': net_amount_clp,
|
||||
'description': 'Exento / No Afecto a Impuestos'
|
||||
'description': 'Exento de Impuestos'
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def simulate_f29(cls, f29_data: dict) -> dict:
|
||||
"""
|
||||
Llama al endpoint /api/f29/simular del tax-api.
|
||||
f29_data debe contener: ventas_netas, compras_netas, etc.
|
||||
"""
|
||||
api_res = cls._call_api("/api/f29/simular", f29_data)
|
||||
if api_res:
|
||||
return api_res
|
||||
|
||||
# Fallback local simplificado si la API no está disponible
|
||||
ventas_netas = Decimal(str(f29_data.get('ventas_netas', 0)))
|
||||
compras_netas = Decimal(str(f29_data.get('compras_netas', 0)))
|
||||
retenciones = Decimal(str(f29_data.get('retenciones_honorarios', 0)))
|
||||
tasa_ppm = Decimal(str(f29_data.get('tasa_ppm', 0.01)))
|
||||
|
||||
debito = ventas_netas * Decimal('0.19')
|
||||
credito = compras_netas * Decimal('0.19')
|
||||
ppm = ventas_netas * tasa_ppm
|
||||
iva_pagar = max(Decimal('0'), debito - credito)
|
||||
|
||||
return {
|
||||
"iva_debito": float(debito),
|
||||
"iva_credito": float(credito),
|
||||
"iva_a_pagar": float(iva_pagar),
|
||||
"ppm_a_pagar": float(ppm),
|
||||
"total_retenciones_honorarios": float(retenciones),
|
||||
"total_a_pagar": float(iva_pagar + ppm + retenciones),
|
||||
"remanente_siguiente_mes": float(max(Decimal('0'), credito - debito)),
|
||||
"nota": "Simulación local de respaldo. API no disponible."
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def check_duplicate_expense(
|
||||
expense_model,
|
||||
|
||||
@@ -26,6 +26,7 @@ urlpatterns = [
|
||||
path('gastos/exportar/csv/', views.export_expenses_csv, name='export_csv'),
|
||||
path('gastos/exportar/transferencias/', views.export_bank_transfer_csv, name='export_bank_transfer_csv'),
|
||||
|
||||
path('f29/simulador/', views.f29_simulator, name='f29_simulator'),
|
||||
path('proyectos/', views.project_list, name='project_list'),
|
||||
path('proyectos/<int:pk>/', views.project_detail, name='project_detail'),
|
||||
path('proyectos/<int:pk>/fase/nueva/', views.project_add_phase, name='project_add_phase'),
|
||||
|
||||
@@ -1014,3 +1014,77 @@ def api_sii_calculate_taxes(request):
|
||||
'description': res['description']
|
||||
})
|
||||
|
||||
|
||||
from datetime import datetime, date
|
||||
import calendar
|
||||
from django.db.models import Sum
|
||||
from procurement.models import PurchaseOrder
|
||||
|
||||
@login_required
|
||||
def f29_simulator(request):
|
||||
year = request.GET.get('year', date.today().year)
|
||||
month = request.GET.get('month', date.today().month)
|
||||
ventas_netas = request.GET.get('ventas_netas', 0)
|
||||
ventas_exentas = request.GET.get('ventas_exentas', 0)
|
||||
tasa_ppm = request.GET.get('tasa_ppm', 0.01)
|
||||
|
||||
try:
|
||||
year, month = int(year), int(month)
|
||||
ventas_netas = float(ventas_netas)
|
||||
ventas_exentas = float(ventas_exentas)
|
||||
tasa_ppm = float(tasa_ppm)
|
||||
except ValueError:
|
||||
year, month = date.today().year, date.today().month
|
||||
ventas_netas, ventas_exentas, tasa_ppm = 0, 0, 0.01
|
||||
|
||||
_, last_day = calendar.monthrange(year, month)
|
||||
start_date = date(year, month, 1)
|
||||
end_date = date(year, month, last_day)
|
||||
|
||||
# 1. Compras Netas (Facturas con IVA 19%)
|
||||
compras_expenses = Expense.objects.filter(
|
||||
date__range=(start_date, end_date),
|
||||
tax_type='IVA_19',
|
||||
approval_status='LIBERADO_PAGADO'
|
||||
).aggregate(total=Sum('amount_clp'))['total'] or 0
|
||||
|
||||
compras_po = PurchaseOrder.objects.filter(
|
||||
order_date__range=(start_date, end_date),
|
||||
status='LIBERADA_PAGADA'
|
||||
).aggregate(total=Sum('net_amount_clp'))['total'] or 0
|
||||
|
||||
compras_netas = float(compras_expenses) + float(compras_po)
|
||||
|
||||
# 2. Honorarios
|
||||
honorarios_expenses = Expense.objects.filter(
|
||||
date__range=(start_date, end_date),
|
||||
tax_type='HONORARIOS_13_75',
|
||||
approval_status='LIBERADO_PAGADO'
|
||||
).aggregate(total=Sum('tax_amount_clp'))['total'] or 0
|
||||
retenciones_honorarios = float(honorarios_expenses)
|
||||
|
||||
f29_data = {
|
||||
'ventas_netas': ventas_netas,
|
||||
'compras_netas': compras_netas,
|
||||
'ventas_exentas': ventas_exentas,
|
||||
'compras_activo_fijo': 0,
|
||||
'remanente_anterior': 0,
|
||||
'tasa_ppm': tasa_ppm,
|
||||
'retenciones_honorarios': retenciones_honorarios
|
||||
}
|
||||
|
||||
simulation = SIIService.simulate_f29(f29_data)
|
||||
|
||||
context = {
|
||||
'year': year,
|
||||
'month': month,
|
||||
'months': range(1, 13),
|
||||
'years': range(date.today().year - 2, date.today().year + 2),
|
||||
'ventas_netas': ventas_netas,
|
||||
'ventas_exentas': ventas_exentas,
|
||||
'tasa_ppm': tasa_ppm,
|
||||
'simulation': simulation,
|
||||
'compras_netas': compras_netas,
|
||||
'retenciones_honorarios': retenciones_honorarios
|
||||
}
|
||||
return render(request, 'expenses/f29_simulator.html', context)
|
||||
|
||||
Reference in New Issue
Block a user