Resumen financiero — Mayo 2026
Ingresos totales
€18.500
↑ +12% vs abr
Total gastos
€6.890
↑ +3% vs abr
Beneficio neto
€11.610
↑ +18% vs abr
Facturas sincronizadas
37
100% auto-sync Stripe→Xero
Transacciones banco
12
Openbank importadas
Informe P&L — Mayo 2026
Reports/ProfitAndLoss
| Ingresos | |
| Subscripciones Professional | €18.000 |
| Add-ons & módulos extra | €500 |
| Total Ingresos | €18.500 |
| Coste de ventas | |
| AWS / Infraestructura | €820 |
| Pasarela de pago (Stripe 2.9%) | €537 |
| Beneficio bruto | €17.143 |
| Gastos operativos | |
| Nómina equipo técnico | €3.200 |
| Oficina / coworking Málaga | €350 |
| HubSpot CRM | €180 |
| Herramientas SaaS | €203 |
| Asesoría contable | €600 |
| Beneficio neto | €12.610 |
Ingresos
Coste ventas
Op. expenses
Beneficio neto
Facturas sincronizadas
Invoices (ACCREC)
-
NFClínica Nutrifit MadridINV-0142 · 02/05/2026€500,00PAGADO
-
SPHospital Sant Pau DietéticaINV-0143 · 03/05/2026€500,00PAGADO
-
WBCentro Wellness BarcelonaINV-0144 · 05/05/2026€500,00PAGADO
-
NSClínica NutrSalut GironaINV-0145 · 08/05/2026€500,00PAGADO
-
DGDr. García Nutrición SevillaINV-0146 · 10/05/2026€500,00PAGADO
-
32+Otras clínicas suscritasINV-0147 a INV-0178€16.000SINC. AUTO
Flujo Stripe → Xero
Webhook
Webhook
payment_intent.succeeded → create_invoice() + mark_invoice_paid() ejecutados en <2s por evento.
Transacciones bancarias
BankTransactions
-
Ingresos Stripe (batch)RECEIVE · Cuenta 200 · 31/05+€18.500
-
AWS EU-WEST-1SPEND · Cuenta 400 · 01/05−€820
-
Coworking MálagaSPEND · Cuenta 710 · 01/05−€350
-
HubSpot CRMSPEND · Cuenta 400 · 04/05−€180
-
Nómina desarrolladorSPEND · Cuenta 477 · 30/05−€3.200
-
SaaS stack (Notion, Loom…)SPEND · Cuenta 400 · 15/05−€203
-
Asesoría contableSPEND · Cuenta 721 · 28/05−€600
Importadas via bulk_import_transactions() · 7 transacciones · 1 llamada API
Autenticación OAuth2 + Sync Log
xero_auth.py
✓
App registrada
developer.xero.com
developer.xero.com
→
✓
Authorize URL
+ PKCE state
+ PKCE state
→
✓
exchange_code
_for_tokens()
_for_tokens()
→
✓
Auto-refresh
cada 30 min
cada 30 min
→
⚡
API calls
activas
activas
Pipeline Stripe → Xero (tiempo real)
stripe
→ webhook→
xero
Nutrifit Madrid · €500 · 02/05/2026
✓ Sincronizado
stripe
→ webhook→
xero
Hospital Sant Pau · €500 · 03/05/2026
✓ Sincronizado
stripe
→ webhook→
xero
Centro Wellness BCN · €500 · 05/05/2026
✓ Sincronizado
09:02:14[OK]OAuth2 token válido · expira en 28m
09:02:14[INFO]Tenant conectado: NutriSaaS Pro SL (ES-B12345678)
09:02:15[INFO]Stripe webhook recibido: payment_intent.succeeded
09:02:15[OK]Contacto upsertado: Clínica Nutrifit Madrid
09:02:16[OK]Factura creada: INV-0142 · €500,00 · AUTHORISED
09:02:16[OK]Pago registrado → INV-0142 marcada PAID
09:03:41[INFO]Importando extracto bancario Openbank (CSV)
09:03:42[OK]bulk_import_transactions(): 7 transacciones · 1 llamada API
09:04:00[INFO]Generando P&L report: 2026-05-01 → 2026-05-31
09:04:01[OK]P&L report generado · Beneficio neto: €12.610
09:04:01[OK]Email enviado a CFO: cfo@nutrisaas.pro ✉️
09:30:00[WARN]Token cerca de expirar — refrescando...
09:30:01[OK]Token refrescado via refresh_access_token()
Código de integración principal
nutrisaas_xero_sync.py
# nutrisaas_xero_sync.py
# Sync automático Stripe → Xero para NutriSaaS Pro
from xero_client import XeroClient
from xero_auth import get_valid_token, get_tenant_id
from datetime import datetime
client = XeroClient()
# 1. Webhook Stripe → factura Xero
def on_stripe_payment(event):
p = event["data"]["object"]
contact = upsert_contact(
name=p["metadata"]["clinic_name"],
email=p["receipt_email"],
is_customer=True,
)
invoice = create_invoice(
contact_name=contact["Name"],
line_items=[{
"description": "NutriSaaS Pro · Plan Professional",
"quantity": 1,
"unitAmount": p["amount"] / 100,
"accountCode": "200",
}],
due_date=today(),
currency="EUR",
)
mark_invoice_paid(
invoice["InvoiceID"],
amount=p["amount"] / 100,
)
print(f"✓ {invoice['InvoiceNumber']} sincronizada")
# 2. Importar extracto bancario (CSV Openbank)
def import_bank_csv(csv_path, account_id):
import csv
transactions = []
with open(csv_path) as f:
for row in csv.DictReader(f):
transactions.append({
"date": row["fecha"],
"contact": row["concepto"],
"amount": float(row["importe"]),
"description": row["concepto"],
"account_code": "400",
})
bulk_import_transactions(account_id, transactions)
# 3. Generar P&L y notificar al CFO
def monthly_pnl_email(year, month):
pnl = get_profit_and_loss(
f"{year}-{month:02d}-01",
f"{year}-{month:02d}-31",
)
send_email(
to="cfo@nutrisaas.pro",
subject=f"P&L {month}/{year}",
body=format_pnl(pnl["summary"]),
)
Variables de entorno
| XERO_CLIENT_ID | ID app OAuth2 · developer.xero.com | SET ✓ |
| XERO_CLIENT_SECRET | Secret OAuth2 (cifrado en Vault) | SET ✓ |
| XERO_REDIRECT_URI | https://app.nutrisaas.pro/xero/cb | SET ✓ |
Rate limit: 60 llamadas/min por tenant · Batch writes activadas ·
If-Modified-Since en todos los GET para sync incremental.