NutriTrack SaaS
Motor de Facturación Automatizada
v2.4.1 — billing-engine 16 jun 2026 · 09:14 CET
Métricas en tiempo real
MRR
6.847€
↑ +12,3% vs mayo
ARR proyectado
82.164€
↑ +8,1% YoY
Suscripciones activas
47
+3 este mes
En dunning
2
Requieren atención
Churn rate
1,8%
Objetivo <2%
Suscripciones activas — Junio 2026 47 total
Cliente Plan Estado Próxima factura Importe
TechCorp Madrid
cus_TC2024001
Growth Anual Activo 01/01/2027 1.490,00€
Rest. La Huerta
cus_LH2025047
Starter Mensual Dunning #2 Pendiente cobro 59,29€ (IVA)
Grupo Altur
cus_GA2025019
Enterprise Anual Migrando 15/06/2026 4.990,00€
Startup Edtech XYZ
cus_ET2026003
Starter Trial Trial 28/06/2026 0,00€
Clínica Salud360
cus_CS2025088
Growth Mensual Activo 01/07/2026 180,29€ (IVA)
MiGym Pro
cus_MG2024055
Enterprise Mensual Activo 01/07/2026 603,79€ (IVA)
Ciclo de vida
TRIAL
14-30 días
ACTIVO
Pagando
↙    ↘
PAUSADO
Suspendido
VENCIDO
Pago fallido
CANCELADO
Baja definitiva
Distribución actual
● Activos42 (89%)
● Trial3 (6%)
● Dunning2 (4%)
● Pausados0 (0%)
Ciclos activos
TechCorp — Anual
Ene–Dic 2026 75% completado
167 días
Salud360 — Mensual
Jun 2026
15 días
Edtech XYZ — Trial
Trial 14d
12 días
Distribución por ciclo
28
Mensuales
16
Anuales
3
Trials
Dunning — Rest. La Huerta Intento 2/3
Pago fallido el 10/06/2026 · Tarjeta terminada en •••• 4521 · Motivo: insufficient_funds
Intento 1
10/06
2
Intento 2
13/06
3
Intento 3
17/06
Cancelación
24/06
Emails enviados
✉ Notif. pago fallido — 10/06
✉ Recordatorio urgente — 13/06
⏳ Último aviso — 17/06
⏳ Cuenta suspendida — 24/06
Acción automática
↓ Restringir export
↓ Bloquear nuevos usuarios
⏳ Suspender acceso
⏳ Archivar datos 30d
Próxima acción automática
17/06/2026 a las 09:00 UTC — Reintento de cargo (49 € + 10,29 € IVA = 59,29 €)
Prorrateo — Grupo Altur (Growth → Enterprise) Calculado
Cambio de plan el 15/06/2026 a mitad del ciclo anual (180 días restantes de 365)
Plan actual
Growth
1.490€ / año

Días usados185 días
Crédito restante180 días
Tarifa diaria4,08€/día
Crédito a aplicar-734,52€
Nuevo plan
Enterprise
4.990€ / año

Enterprise (180d)2.460,82€
Crédito Growth-734,52€
Base imponible1.726,30€
IVA (21%)362,52€
Cobro HOY2.088,82€
Fórmula de prorrateo
credit = (remaining_days / cycle_days) × old_plan_price
charge = (remaining_days / cycle_days) × new_plan_price
net = charge − credit + IVA(21%)
Factura generada automáticamente PAGADA
FACTURA #NT-2026-0847
NutriTrack SaaS S.L.
CIF: B-12345678
Calle Gran Vía 28, 28013 Madrid
Fecha emisión: 01/06/2026
Vencimiento: 01/06/2026
Cliente: Clínica Salud360
CIF: B-87654321

CONCEPTO UNID. PRECIO IMPORTE

NutriTrack Growth — Jun 2026 1 149,00€ 149,00€
Periodo: 01/06/2026 – 30/06/2026
Ciclo mensual · 25 seats incluidos · Auto-renovación

Subtotal 149,00€
IVA 21% 31,29€
TOTAL 180,29€

Cobrado automáticamente el 01/06/2026 · Tarjeta •••• 7832
Referencia de pago: pi_3QhTwLKZ0pB82UkM1s5j9cDf · Estado: succeeded
BillingEngine — Core Python billing_engine.py
from datetime import datetime, timedelta from decimal import Decimal from enum import Enum # Configuración NutriTrack España VAT_RATE = Decimal('0.21') # IVA 21% DUNNING_SCHEDULE = [0, 3, 7] # días tras fallo class BillingEngine: def process_billing_cycle(self, sub_id): sub = self.get_subscription(sub_id) if datetime.now() < sub.period_end: return # Aún no vence invoice = self.generate_invoice(sub) result = self.charge_customer(sub, invoice.total) if result.success: invoice.mark_paid() sub.advance_period() self.send_invoice_email(invoice) else: sub.mark_past_due() self.start_dunning(sub, invoice) def calculate_proration(self, sub, new_plan): cycle_days = (sub.period_end - sub.period_start).days remaining = (sub.period_end - datetime.now()).days ratio = Decimal(remaining) / Decimal(cycle_days) credit = ratio * sub.plan.price charge = ratio * new_plan.price net = charge - credit return { 'credit': credit, 'charge': charge, 'net_excl_vat': net, 'vat': net * VAT_RATE, 'total': net * (1 + VAT_RATE), } def start_dunning(self, sub, invoice): for day_offset in DUNNING_SCHEDULE: retry_at = datetime.now() + timedelta(days=day_offset) self.schedule_retry(sub, invoice, retry_at) # Cancelar si todos fallan (+7d) cancel_at = datetime.now() + timedelta(days=14) self.schedule_cancellation(sub, cancel_at)