Taxes for the whole of Spain (common territory, Navarre, Álava, Bizkaia and Gipuzkoa); flats, garages and land valued on their own; Treasury bills, bonds and deposits priced on BME; broker accounts; a guided start; a page per investment with its chart and the tax of selling today; net worth at the top; undo when deleting; Woodpecker CI for the tests and the Linux build.
171 lines
11 KiB
Python
171 lines
11 KiB
Python
"""
|
|
exportar.py — El informe fiscal de un ejercicio en una hoja de cálculo.
|
|
|
|
Lo mismo que enseña la pestaña «Fiscal», con todo el detalle, para repasarlo
|
|
fuera del programa, guardarlo con la declaración o dárselo a quien te lleve
|
|
los impuestos. Valores, sin fórmulas ni macros: es una foto, no se edita.
|
|
|
|
Resumen las cifras del año y la liquidación de la parte del ahorro
|
|
Ventas cada transmisión: lo cobrado, lo que costó y la ganancia
|
|
Lotes de qué compras sale cada venta (el FIFO, lote a lote)
|
|
Rendimientos dividendos, intereses y rentas, con su retención
|
|
Actividad la minería: ingresos, gastos y amortización de los aparatos
|
|
Recibido cada entrada minada o recibida sin pagar, a su valor del día
|
|
Existencias lo que quedaba a 31 de diciembre
|
|
Criterios con qué criterios está hecho y qué queda por resolver
|
|
|
|
Recibe el informe ya calculado (fiscal.completo): aquí no se calcula nada.
|
|
Necesita openpyxl, como la exportación de la cartera.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from ..exportar_xlsx import EUROS, UNIDADES, _fecha, _Hoja, _openpyxl
|
|
|
|
FECHA = "DD/MM/YYYY"
|
|
CANTIDAD = "#,##0.00000000"
|
|
|
|
|
|
def exportar(r: dict, destino: Path) -> None:
|
|
"""Escribe el libro del informe «r» (lo que devuelve fiscal.completo)."""
|
|
_openpyxl()
|
|
from openpyxl import Workbook
|
|
|
|
wb = Workbook()
|
|
wb.remove(wb.active)
|
|
c, t = r["cuota"], r["total"]
|
|
|
|
h = _Hoja(wb, "Resumen", [("Concepto", 62, None), ("Importe", 16, EUROS), ("Nota", 90, None)])
|
|
h.datos([f"Ejercicio {r['ejercicio']} · {c['nombre_territorio']}", None,
|
|
"Ayuda para preparar la declaración, no asesoría fiscal. Hecho solo con lo que hay en la cartera."], True)
|
|
h.titulo("Ganancias y pérdidas por ventas")
|
|
h.datos(["Ganancias", t["ganancias"]])
|
|
h.datos(["Pérdidas", t["perdidas"]])
|
|
h.datos(["Pérdidas aplazadas por recompra", t["aplazadas"]])
|
|
h.datos(["Pérdidas de años anteriores que se integran", t["integradas"]])
|
|
h.datos(["Lo que cuenta este año", t["computable"]], True)
|
|
for x in r["por_clase"]:
|
|
h.datos([f" {x['clase']} ({x['transmisiones']} operaciones)", x["neto"],
|
|
f"Transmisión {x['valor_transmision']:.2f} · adquisición {x['valor_adquisicion']:.2f}"])
|
|
if r.get("bróker"):
|
|
h.titulo("Cuentas de bróker o trading (del informe fiscal del bróker)")
|
|
for x in r["bróker"]:
|
|
h.datos([x["nombre"], x["resultado"], None if x["resultado"] is not None else "Sin apuntar: no entra en la cuota"])
|
|
if r.get("alquileres") or r.get("imputaciones") or r.get("ventas_inmuebles"):
|
|
h.titulo("Inmuebles")
|
|
for x in r.get("alquileres", []):
|
|
h.datos([f"{x['nombre']}: alquiler ({'base del ahorro' if x['base'] == 'ahorro' else 'parte general'})", x["rendimiento"],
|
|
f"Cobrado {x['ingresos']:.2f} · gastos {x['deducido']:.2f} · bonificación {x['bonificacion']:.2f} · reducción {x['reduccion']:.2f}"
|
|
+ ("" if x["verificado"] else " · REGLAS SIN COMPROBAR")])
|
|
for x in r.get("ventas_inmuebles", []):
|
|
h.datos([f"{x['nombre']}: venta (base del ahorro)", x["ganancia"],
|
|
f"Cobrado {x['valor_transmision']:.2f} · coste {x['valor_adquisicion']:.2f} (pagado {x['pagado']:.2f}, "
|
|
f"actualizado {x['actualizado']:.2f}, amortizado {x['amortizacion']:.2f})"])
|
|
for x in r.get("imputaciones", []):
|
|
h.datos([f"{x['nombre']}: renta imputada (parte general)", x["renta"], x["aviso"] or x["cita"]])
|
|
h.titulo("Dividendos, intereses y rentas")
|
|
for x in r["por_concepto"]:
|
|
h.datos([x["concepto"], x["bruto"], f"Retención {x['retencion']:.2f} · cobrado {x['neto']:.2f}"])
|
|
h.titulo("Actividades económicas (parte general)")
|
|
for a in r["actividades"]:
|
|
h.datos([f"{a['nombre']}: rendimiento neto", a["rendimiento"], "Detalle en la hoja «Actividad»"], True)
|
|
h.datos(["Minado o recibido sin pagar, a su valor del día", r["total_recibido"]])
|
|
h.titulo(f"Parte del ahorro · {c['nombre_territorio']}")
|
|
if not c["verificado"]:
|
|
h.datos(["REGLAS SIN COMPROBAR PARA ESTE AÑO", None, c["nota"]], True)
|
|
h.datos(["Dividendos e intereses (bruto)", c["rendimientos"]])
|
|
h.datos(["Ganancias y pérdidas (ventas y bróker)" if any(x["resultado"] is not None for x in r.get("bróker", [])) else
|
|
"Ganancias y pérdidas por ventas", c["ganancias"]])
|
|
for p in c["pasos"]:
|
|
h.datos([f" ↳ {p['texto']}", None, p["cita"]])
|
|
h.datos(["Base del ahorro", c["base"]], True)
|
|
for x in c["tramos"]:
|
|
h.datos([f" {x['base']:.2f} al {x['tipo']} %", x["cuota"]])
|
|
h.datos(["Cuota del ahorro", c["cuota"], c["citas"]["escala"]], True)
|
|
h.datos(["Retenciones", -c["retenciones"]])
|
|
h.datos(["Resultado (a pagar si es positivo)", c["resultado"]], True)
|
|
for x in c["pendientes"]:
|
|
h.datos([f"Queda para restar: {x['clase']} de {x['ejercicio']}", x["importe"], f"Hasta {x['hasta']}"])
|
|
h.datos([None, None, f"Reglas: {c['norma']} ({c['fuente']}), consultado el {c['consultado']}."])
|
|
|
|
h = _Hoja(wb, "Ventas", [("Fecha", 12, FECHA), ("Activo", 28, None), ("Clase", 20, None), ("Qué", 32, None),
|
|
("Cantidad", 16, CANTIDAD), ("Valor de transmisión", 18, EUROS),
|
|
("Valor de adquisición", 18, EUROS), ("Ganancia", 14, EUROS),
|
|
("Aplazada por recompra", 18, EUROS), ("Cuenta este año", 16, EUROS), ("Avisos", 30, None)])
|
|
lotes = _Hoja(wb, "Lotes", [("Fecha de la venta", 16, FECHA), ("Activo", 28, None), ("Qué", 32, None),
|
|
("Fecha de la compra", 16, FECHA), ("Cantidad", 16, CANTIDAD), ("Coste", 14, EUROS)])
|
|
avisos = {"sin_cubrir": "falta historial", "estimado": "cifras estimadas", "sin_fecha": "compra sin fecha (traspaso de fuera)"}
|
|
for x in r["transmisiones"]:
|
|
h.datos([_fecha(x["fecha"]), x["nombre"], x["clase"], x["concepto"], x["cantidad"], x["valor_transmision"],
|
|
x["valor_adquisicion"], x["ganancia"], x["aplazada"], x["computable"],
|
|
", ".join(avisos.get(a, a) for a in x["avisos"])])
|
|
for lote in x["lotes"]:
|
|
lotes.datos([_fecha(x["fecha"]), x["nombre"], x["concepto"], _fecha(lote["fecha"]), lote["cantidad"], lote["coste"]])
|
|
for x in r["integradas"]:
|
|
h.datos([_fecha(x["fecha"]), x["nombre"], None, f"Pérdida aplazada de la venta del {x['fecha_venta']} que se integra",
|
|
None, None, None, None, None, x["importe"]])
|
|
|
|
h = _Hoja(wb, "Rendimientos", [("Fecha", 12, FECHA), ("Activo", 28, None), ("Concepto", 26, None),
|
|
("Bruto", 14, EUROS), ("Retención", 14, EUROS), ("Cobrado", 14, EUROS)])
|
|
for x in r["rendimientos"]:
|
|
h.datos([_fecha(x["fecha"]), x["nombre"], x["concepto"], x["bruto"], x["retencion"], x["neto"]])
|
|
|
|
h = _Hoja(wb, "Actividad", [("Concepto", 46, None), ("Fecha", 12, FECHA), ("Importe", 16, EUROS),
|
|
("Amortizado antes", 16, EUROS), ("Este año", 14, EUROS), ("Queda", 14, EUROS), ("Nota", 70, None)])
|
|
for a in r["actividades"]:
|
|
h.datos([f"{a['nombre']} · {a['ejercicio']}", None, None, None, None, None, a["citas"]["actividad"]], True)
|
|
h.datos(["Ingresos: lo recibido, a su valor de ese día", None, a["ingresos"], None, None, None, f"{a['entradas']} entradas"])
|
|
h.datos(["Gastos del año", None, -a["gastos"]])
|
|
if a["amortiza"]:
|
|
h.datos(["Amortización de los aparatos", None, -a["amortizacion"], None, None, None,
|
|
f"{a['tabla']['nombre']}: {a['tabla']['coeficiente']} % al año como mucho ({a['citas']['amortizacion']})"])
|
|
else:
|
|
h.datos(["Amortización de los aparatos", None, None, None, None, None,
|
|
f"No se restan aparte: los cubre la reducción ({a['citas']['amortizacion']})"])
|
|
h.datos([a["texto_reduccion"] or "Reducción", None, -a["reduccion"], None, None, None, a["citas"]["reduccion"]])
|
|
h.datos(["Rendimiento neto de la actividad", None, a["rendimiento"], None, None, None,
|
|
a["texto_negativo"] if a["rendimiento"] < 0 and a["texto_negativo"] else a["citas"]["general"]], True)
|
|
if not a["verificado"]:
|
|
h.datos(["REGLAS SIN COMPROBAR", None, None, None, None, None, a["nota"]], True)
|
|
h.titulo("Aparatos")
|
|
for x in a["aparatos"]:
|
|
h.datos([x["notas"] or "Aparato", _fecha(x["fecha"]), x["coste"], x["antes"], x["amortizacion"], x["pendiente"],
|
|
"cifras estimadas" if x["estimado"] else None])
|
|
h.titulo("Gastos del año, uno a uno")
|
|
for x in a["corrientes"]:
|
|
h.datos([x["notas"] or ("Devolución" if x["importe"] < 0 else "Gasto"), _fecha(x["fecha"]), x["importe"],
|
|
None, None, None, "cifras estimadas" if x["estimado"] else None])
|
|
h.fila += 1
|
|
|
|
h = _Hoja(wb, "Recibido", [("Fecha", 12, FECHA), ("Activo", 28, None), ("Qué", 14, None), ("Actividad", 18, None),
|
|
("Cantidad", 16, CANTIDAD), ("Valor al recibirlo", 16, EUROS)])
|
|
for x in r["recibido"]:
|
|
h.datos([_fecha(x["fecha"]), x["nombre"], "Minado" if x["tipo"] == "Mineria" else "Recibido", x["actividad"],
|
|
x["cantidad"], x["valor"]])
|
|
|
|
h = _Hoja(wb, "Existencias", [("Qué", 28, None), ("Clase", 20, None), ("Cantidad", 16, CANTIDAD), ("Coste (FIFO)", 16, EUROS),
|
|
(f"Valor a 31-12-{r['ejercicio']}", 18, EUROS), ("Compras vivas", 14, None),
|
|
("Compra más antigua", 18, FECHA)])
|
|
for x in r["existencias"]:
|
|
h.datos([x["nombre"], x["clase"], x["cantidad"], x["coste"], x["valor"], x["lotes"], _fecha(x["primer_lote"])])
|
|
|
|
h = _Hoja(wb, "Criterios", [("Criterio", 50, None), ("Cómo se aplica", 120, None), ("Fuente", 60, None)])
|
|
for x in r["criterios"]:
|
|
h.datos([x["titulo"], x["texto"], x["fuente"]])
|
|
for clave, o in r["opciones_posibles"].items():
|
|
h.datos([f"Elegido · {o['titulo']}", dict(o["valores"]).get(r["opciones"][clave], r["opciones"][clave])])
|
|
if r["tareas"]:
|
|
h.titulo("Por resolver antes de fiarse de estas cifras")
|
|
for x in r["tareas"]:
|
|
h.datos([f"{x['fecha']} · {x['activo']}", x["texto"]])
|
|
fuera = [x for x in r["fuera"] if x["cobrado"]]
|
|
if fuera:
|
|
h.titulo("No entran en este cálculo")
|
|
for x in fuera:
|
|
h.datos([x["nombre"], f"Cobraste {x['cobrado']:.2f} €; {x['motivo']}"])
|
|
|
|
destino = Path(destino)
|
|
destino.parent.mkdir(parents=True, exist_ok=True)
|
|
wb.save(destino)
|