FEAT: re-integracion de tax-api y simulador F29
This commit is contained in:
+23
-21
@@ -1,5 +1,4 @@
|
|||||||
version: "3.8"
|
version: '3.8'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
build:
|
build:
|
||||||
@@ -7,37 +6,40 @@ services:
|
|||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
command: python manage.py runserver 0.0.0.0:8000
|
command: python manage.py runserver 0.0.0.0:8000
|
||||||
volumes:
|
volumes:
|
||||||
- .:/app
|
- .:/app
|
||||||
- local_media:/app/media
|
- local_media:/app/media
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- 8000:8000
|
||||||
environment:
|
environment:
|
||||||
- DEBUG=1
|
- DEBUG=1
|
||||||
- USE_S3=False
|
- USE_S3=False
|
||||||
- REDIS_URL=redis://redis:6379/0
|
- REDIS_URL=redis://redis:6379/0
|
||||||
# Forzamos SQLite local para que ignore el .env de producción si existe
|
- DATABASE_URL=sqlite:////app/db.sqlite3
|
||||||
- DATABASE_URL=sqlite:////app/db.sqlite3
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
|
|
||||||
celery:
|
celery:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
command: celery -A config worker -l info
|
command: celery -A config worker -l info
|
||||||
volumes:
|
volumes:
|
||||||
- .:/app
|
- .:/app
|
||||||
environment:
|
environment:
|
||||||
- DEBUG=1
|
- DEBUG=1
|
||||||
- USE_S3=False
|
- USE_S3=False
|
||||||
- REDIS_URL=redis://redis:6379/0
|
- REDIS_URL=redis://redis:6379/0
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
ports:
|
ports:
|
||||||
- "6379:6379"
|
- 6379:6379
|
||||||
|
tax-api:
|
||||||
|
build:
|
||||||
|
context: ./tax_api
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- 8001:8000
|
||||||
|
mem_limit: 256m
|
||||||
volumes:
|
volumes:
|
||||||
local_media:
|
local_media: null
|
||||||
|
|||||||
+34
-29
@@ -1,43 +1,41 @@
|
|||||||
version: "3.8"
|
version: '3.8'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
command: gunicorn config.asgi:application -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000 --workers 3
|
command: gunicorn config.asgi:application -k uvicorn.workers.UvicornWorker --bind
|
||||||
|
0.0.0.0:8000 --workers 3
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
REDIS_URL: redis://redis:6379/0
|
REDIS_URL: redis://redis:6379/0
|
||||||
CSRF_TRUSTED_ORIGINS: "https://${APP_DOMAIN}"
|
CSRF_TRUSTED_ORIGINS: https://${APP_DOMAIN}
|
||||||
volumes:
|
volumes:
|
||||||
- erp_media:/app/media
|
- erp_media:/app/media
|
||||||
- erp_static:/app/static
|
- erp_static:/app/static
|
||||||
networks:
|
networks:
|
||||||
- controlmc_proxy
|
- controlmc_proxy
|
||||||
- internal
|
- internal
|
||||||
labels:
|
labels:
|
||||||
- "controlmc.public=true"
|
- controlmc.public=true
|
||||||
- "traefik.enable=true"
|
- traefik.enable=true
|
||||||
- "traefik.docker.network=controlmc_proxy"
|
- traefik.docker.network=controlmc_proxy
|
||||||
- "traefik.http.routers.${ROUTER_NAME}.rule=Host(`${APP_DOMAIN}`)"
|
- traefik.http.routers.${ROUTER_NAME}.rule=Host(`${APP_DOMAIN}`)
|
||||||
- "traefik.http.routers.${ROUTER_NAME}.entrypoints=websecure"
|
- traefik.http.routers.${ROUTER_NAME}.entrypoints=websecure
|
||||||
- "traefik.http.routers.${ROUTER_NAME}.tls=true"
|
- traefik.http.routers.${ROUTER_NAME}.tls=true
|
||||||
- "traefik.http.routers.${ROUTER_NAME}.tls.certresolver=le"
|
- traefik.http.routers.${ROUTER_NAME}.tls.certresolver=le
|
||||||
- "traefik.http.services.${ROUTER_NAME}.loadbalancer.server.port=8000"
|
- traefik.http.services.${ROUTER_NAME}.loadbalancer.server.port=8000
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
mem_limit: 1g
|
mem_limit: 1g
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- erp_redis_data:/data
|
- erp_redis_data:/data
|
||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
mem_limit: 512m
|
mem_limit: 512m
|
||||||
|
|
||||||
celery:
|
celery:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
@@ -47,17 +45,24 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
REDIS_URL: redis://redis:6379/0
|
REDIS_URL: redis://redis:6379/0
|
||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
mem_limit: 512m
|
mem_limit: 512m
|
||||||
|
tax-api:
|
||||||
|
build:
|
||||||
|
context: ./tax_api
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- controlmc_proxy
|
||||||
|
- internal
|
||||||
|
mem_limit: 256m
|
||||||
networks:
|
networks:
|
||||||
controlmc_proxy:
|
controlmc_proxy:
|
||||||
external: true
|
external: true
|
||||||
internal:
|
internal: null
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
erp_media:
|
erp_media: null
|
||||||
erp_static:
|
erp_static: null
|
||||||
erp_redis_data:
|
erp_redis_data: null
|
||||||
|
|||||||
@@ -205,12 +205,44 @@ class SIIService:
|
|||||||
return {
|
return {
|
||||||
'tax_type': 'EXENTO',
|
'tax_type': 'EXENTO',
|
||||||
'net_amount': net_amount_clp,
|
'net_amount': net_amount_clp,
|
||||||
'tax_rate_percent': Decimal('0.0'),
|
'tax_rate_percent': Decimal('0'),
|
||||||
'tax_amount': Decimal('0'),
|
'tax_amount': Decimal('0'),
|
||||||
'total_amount': net_amount_clp,
|
'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
|
@staticmethod
|
||||||
def check_duplicate_expense(
|
def check_duplicate_expense(
|
||||||
expense_model,
|
expense_model,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ urlpatterns = [
|
|||||||
path('gastos/exportar/csv/', views.export_expenses_csv, name='export_csv'),
|
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('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/', views.project_list, name='project_list'),
|
||||||
path('proyectos/<int:pk>/', views.project_detail, name='project_detail'),
|
path('proyectos/<int:pk>/', views.project_detail, name='project_detail'),
|
||||||
path('proyectos/<int:pk>/fase/nueva/', views.project_add_phase, name='project_add_phase'),
|
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']
|
'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)
|
||||||
|
|||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
with open('templates/base.html', 'r') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
new_lines = []
|
||||||
|
skip = False
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
if 'url \'accounts_payable\'' in line:
|
||||||
|
new_lines.append(line)
|
||||||
|
new_lines.append(' <i data-lucide="dollar-sign" style="width:16px;"></i> Cuentas por Pagar (CXP)\n </a>\n')
|
||||||
|
new_lines.append(' <a href="{% url \'f29_simulator\' %}" class="nav-tab {% if request.resolver_match.url_name == \'f29_simulator\' %}active{% endif %}">\n <i data-lucide="calculator"></i> Simulador F29\n </a>\n')
|
||||||
|
skip = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if skip:
|
||||||
|
if 'Cuentas por Pagar (CXP)' in line:
|
||||||
|
pass
|
||||||
|
elif '</a>' in line:
|
||||||
|
skip = False
|
||||||
|
continue
|
||||||
|
|
||||||
|
new_lines.append(line)
|
||||||
|
|
||||||
|
with open('templates/base.html', 'w') as f:
|
||||||
|
f.writelines(new_lines)
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import yaml
|
||||||
|
|
||||||
|
with open('docker-compose.yml', 'r') as f:
|
||||||
|
data = yaml.safe_load(f)
|
||||||
|
|
||||||
|
data['services']['tax-api'] = {
|
||||||
|
'build': {
|
||||||
|
'context': './tax_api',
|
||||||
|
'dockerfile': 'Dockerfile'
|
||||||
|
},
|
||||||
|
'restart': 'unless-stopped',
|
||||||
|
'networks': ['controlmc_proxy', 'internal'],
|
||||||
|
'mem_limit': '256m'
|
||||||
|
}
|
||||||
|
|
||||||
|
with open('docker-compose.yml', 'w') as f:
|
||||||
|
yaml.dump(data, f, sort_keys=False, default_flow_style=False)
|
||||||
|
|
||||||
|
with open('docker-compose.local.yml', 'r') as f:
|
||||||
|
data_local = yaml.safe_load(f)
|
||||||
|
|
||||||
|
data_local['services']['tax-api'] = {
|
||||||
|
'build': {
|
||||||
|
'context': './tax_api',
|
||||||
|
'dockerfile': 'Dockerfile'
|
||||||
|
},
|
||||||
|
'ports': ['8001:8000'],
|
||||||
|
'mem_limit': '256m'
|
||||||
|
}
|
||||||
|
|
||||||
|
with open('docker-compose.local.yml', 'w') as f:
|
||||||
|
yaml.dump(data_local, f, sort_keys=False, default_flow_style=False)
|
||||||
@@ -69,6 +69,9 @@
|
|||||||
<i data-lucide="award" style="width:16px;"></i> Cartera Talento
|
<i data-lucide="award" style="width:16px;"></i> Cartera Talento
|
||||||
</a>
|
</a>
|
||||||
{% if 'Administrador Finanzas' in request.user.groups.all.0.name or 'Administrador' in request.user.groups.all.0.name or request.user.is_superuser %}
|
{% if 'Administrador Finanzas' in request.user.groups.all.0.name or 'Administrador' in request.user.groups.all.0.name or request.user.is_superuser %}
|
||||||
|
<a href="{% url 'f29_simulator' %}" class="nav-tab {% if request.resolver_match.url_name == 'f29_simulator' %}active{% endif %}">
|
||||||
|
<i data-lucide="calculator"></i> Simulador F29
|
||||||
|
</a>
|
||||||
<a href="{% url 'accounts_payable' %}" class="nav-tab {% if request.resolver_match.url_name == 'accounts_payable' %}active{% endif %}">
|
<a href="{% url 'accounts_payable' %}" class="nav-tab {% if request.resolver_match.url_name == 'accounts_payable' %}active{% endif %}">
|
||||||
<i data-lucide="calculator" style="width:16px;"></i> Cuentas por Pagar (CXP)
|
<i data-lucide="calculator" style="width:16px;"></i> Cuentas por Pagar (CXP)
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load humanize %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<section class="page-view active">
|
||||||
|
<div class="view-header" style="margin-bottom: 2rem;">
|
||||||
|
<div class="view-title">
|
||||||
|
<h2 style="margin:0; color:var(--efintel-petroleum); display:flex; align-items:center; gap:0.5rem;"><i data-lucide="calculator" style="color:var(--efintel-blue);"></i> Simulador F29 (IVA y Retenciones)</h2>
|
||||||
|
<p style="margin-top:0.25rem; font-size:0.9rem; color:var(--efintel-gray);">Calcula la declaración mensual conectándose a la Tax API, utilizando gastos y compras pagadas del mes.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="data-card" style="margin-bottom: 2rem;">
|
||||||
|
<form method="get" id="f29-form" style="display: flex; gap: 1rem; align-items: flex-end; flex-wrap: wrap;">
|
||||||
|
<div class="form-group" style="margin-bottom: 0;">
|
||||||
|
<label style="font-size: 0.8rem; font-weight: 600;">Mes</label>
|
||||||
|
<select name="month" class="form-control" onchange="this.form.submit()">
|
||||||
|
{% for m in months %}
|
||||||
|
<option value="{{ m }}" {% if m == month %}selected{% endif %}>{{ m }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" style="margin-bottom: 0;">
|
||||||
|
<label style="font-size: 0.8rem; font-weight: 600;">Año</label>
|
||||||
|
<select name="year" class="form-control" onchange="this.form.submit()">
|
||||||
|
{% for y in years %}
|
||||||
|
<option value="{{ y }}" {% if y == year %}selected{% endif %}>{{ y }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" style="margin-bottom: 0;">
|
||||||
|
<label style="font-size: 0.8rem; font-weight: 600;">Ventas Netas Proyectadas (CLP)</label>
|
||||||
|
<input type="number" name="ventas_netas" class="form-control" value="{{ ventas_netas|stringformat:"i" }}" style="width: 200px;">
|
||||||
|
</div>
|
||||||
|
<div class="form-group" style="margin-bottom: 0;">
|
||||||
|
<label style="font-size: 0.8rem; font-weight: 600;">Ventas Exentas (CLP)</label>
|
||||||
|
<input type="number" name="ventas_exentas" class="form-control" value="{{ ventas_exentas|stringformat:"i" }}" style="width: 150px;">
|
||||||
|
</div>
|
||||||
|
<div class="form-group" style="margin-bottom: 0;">
|
||||||
|
<label style="font-size: 0.8rem; font-weight: 600;">Tasa PPM (ej: 0.01 = 1%)</label>
|
||||||
|
<input type="number" step="0.001" name="tasa_ppm" class="form-control" value="{{ tasa_ppm }}" style="width: 100px;">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn-primary" style="height: 42px;"><i data-lucide="refresh-cw" style="width:16px;"></i> Simular</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 2rem;">
|
||||||
|
<div class="data-card">
|
||||||
|
<h3 style="margin-top:0; color:var(--efintel-petroleum); font-size:1.1rem; border-bottom: 1px solid var(--efintel-border); padding-bottom: 0.5rem;"><i data-lucide="database"></i> Datos Obtenidos del ERP</h3>
|
||||||
|
<ul style="list-style: none; padding: 0; margin-top: 1rem;">
|
||||||
|
<li style="display:flex; justify-content:space-between; padding: 0.5rem 0; border-bottom: 1px dashed var(--efintel-border);">
|
||||||
|
<span style="color:var(--efintel-gray);">Total Compras Netas (Pagadas):</span>
|
||||||
|
<strong style="font-family:var(--font-mono);">${{ compras_netas|floatformat:"0"|intcomma }}</strong>
|
||||||
|
</li>
|
||||||
|
<li style="display:flex; justify-content:space-between; padding: 0.5rem 0; border-bottom: 1px dashed var(--efintel-border);">
|
||||||
|
<span style="color:var(--efintel-gray);">Total Retenciones Boletas (Pagadas):</span>
|
||||||
|
<strong style="font-family:var(--font-mono);">${{ retenciones_honorarios|floatformat:"0"|intcomma }}</strong>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div style="margin-top: 1rem; font-size: 0.8rem; color:var(--efintel-gray); background: #f8fafc; padding: 0.8rem; border-radius: 4px;">
|
||||||
|
<i data-lucide="info" style="width:14px; vertical-align:middle;"></i> Sólo se incluyen Órdenes de Compra y Erogaciones en estado <strong>LIBERADO_PAGADO</strong>.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="data-card" style="background: #f0fdf4; border: 1px solid #bbf7d0;">
|
||||||
|
<h3 style="margin-top:0; color:var(--efintel-success); font-size:1.1rem; border-bottom: 1px solid #bbf7d0; padding-bottom: 0.5rem;"><i data-lucide="file-check-2"></i> Resultado Simulación F29</h3>
|
||||||
|
<ul style="list-style: none; padding: 0; margin-top: 1rem;">
|
||||||
|
<li style="display:flex; justify-content:space-between; padding: 0.5rem 0; border-bottom: 1px dashed #bbf7d0;">
|
||||||
|
<span>Débito Fiscal (IVA Ventas):</span>
|
||||||
|
<strong style="font-family:var(--font-mono);">${{ simulation.iva_debito|floatformat:"0"|intcomma }}</strong>
|
||||||
|
</li>
|
||||||
|
<li style="display:flex; justify-content:space-between; padding: 0.5rem 0; border-bottom: 1px dashed #bbf7d0;">
|
||||||
|
<span>Crédito Fiscal (IVA Compras):</span>
|
||||||
|
<strong style="font-family:var(--font-mono);">${{ simulation.iva_credito|floatformat:"0"|intcomma }}</strong>
|
||||||
|
</li>
|
||||||
|
<li style="display:flex; justify-content:space-between; padding: 0.5rem 0; border-bottom: 1px dashed #bbf7d0;">
|
||||||
|
<span style="color:var(--efintel-petroleum);">PPM a Pagar:</span>
|
||||||
|
<strong style="font-family:var(--font-mono);">${{ simulation.ppm_a_pagar|floatformat:"0"|intcomma }}</strong>
|
||||||
|
</li>
|
||||||
|
<li style="display:flex; justify-content:space-between; padding: 0.5rem 0; border-bottom: 1px dashed #bbf7d0;">
|
||||||
|
<span style="color:var(--efintel-petroleum);">Retenciones a Pagar:</span>
|
||||||
|
<strong style="font-family:var(--font-mono);">${{ simulation.total_retenciones_honorarios|floatformat:"0"|intcomma }}</strong>
|
||||||
|
</li>
|
||||||
|
<li style="display:flex; justify-content:space-between; padding: 0.5rem 0; border-bottom: 1px dashed #bbf7d0;">
|
||||||
|
<span style="color:var(--efintel-gray);">Remanente Siguiente Mes:</span>
|
||||||
|
<strong style="font-family:var(--font-mono);">${{ simulation.remanente_siguiente_mes|floatformat:"0"|intcomma }}</strong>
|
||||||
|
</li>
|
||||||
|
<li style="display:flex; justify-content:space-between; padding: 1rem 0 0 0; margin-top: 0.5rem; font-size: 1.2rem;">
|
||||||
|
<strong style="color:var(--efintel-petroleum);">Total A Pagar F29:</strong>
|
||||||
|
<strong style="font-family:var(--font-mono); color:var(--efintel-success);">${{ simulation.total_a_pagar|floatformat:"0"|intcomma }}</strong>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div style="margin-top: 1rem; font-size: 0.75rem; color:var(--efintel-gray);">
|
||||||
|
<i data-lucide="check-circle" style="width:12px; vertical-align:middle;"></i> {{ simulation.nota|default:"Cálculo validado correctamente." }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user