caudal/programa/cartera/deshacer_btc.py
2026-10-13 08:01:06 -05:00

84 lines
3.8 KiB
Python

"""
deshacer_btc.py — Volver a empezar el bitcoin: quita lo que metieron el CSV de
un extracto de exchanges y el historial de los monederos, sin tocar nada más.
Qué se quita:
• las operaciones que creó el CSV de exchanges (su línea empieza por «ct:»);
• todo lo que guardaste desde el nodo, sea revisando todo el historial o
en la comprobación diaria (cada importación se deshace entera: lo creado
se borra y a lo enlazado se le quita el txid);
• las decisiones sin guardar (borradores) y lo recordado de cada exchange;
• los activos «BTC en …» que creó el CSV, si se quedan sin operaciones.
Qué se queda: los monederos, lo que tenías apuntado antes (aunque el historial
lo hubiera enlazado) y cualquier operación hecha a mano. Se borra marcando, como todo en la app, así que la
sincronización lo lleva también a la copia de tu servidor git.
Primero se simula (qué y cuánto) y solo se aplica si se confirma.
"""
from __future__ import annotations
from . import historial_btc
PREFIJOS_CSV = ("ct:", "ct-fee:")
def _del_csv(m: dict) -> bool:
return str(m.get("extracto") or "").startswith(PREFIJOS_CSV)
def plan(cartera, config_btc: dict) -> dict:
"""Lo que se quitaría, sin tocar nada."""
movs = cartera.datos.vivos("movimientos")
todos = cartera.datos.movimientos
imps = config_btc.get("importaciones") or []
vivos = {m["id"] for m in movs}
creados_hist = {i for imp in imps for i in imp.get("creados") or [] if i in vivos}
enlazados = {i for imp in imps for i in imp.get("enlazados") or [] if i in vivos}
del_csv = {m["id"] for m in movs if _del_csv(m)}
quitar = del_csv | creados_hist
# Activos que creó el CSV: tienen (o tuvieron) alguna operación suya y no
# les queda ninguna que no se vaya a quitar
con_csv = {m["activo"] for m in todos if _del_csv(m)}
activos, se_quedan = [], []
for a in cartera.datos.vivos("activos"):
if a["id"] not in con_csv:
continue
restantes = [m for m in movs if m["activo"] == a["id"] and m["id"] not in quitar]
if restantes:
se_quedan.append({"id": a["id"], "nombre": a["nombre"], "operaciones": len(restantes)})
else:
activos.append({"id": a["id"], "nombre": a["nombre"]})
en_uso = {m.get("activo") for m in config_btc.get("monederos") or []}
for a in list(activos):
if a["id"] in en_uso: # un monedero apunta ahí: no se borra
activos.remove(a)
se_quedan.append({**a, "operaciones": 0, "monedero": True})
return {"del_csv": len(del_csv), "del_historial": len(creados_hist - del_csv),
"desenlazar": len(enlazados - quitar), "importaciones": len(imps),
"decisiones": len(config_btc.get("resueltas") or {}),
"borradores": len(config_btc.get("borradores") or {}),
"activos": activos, "activos_que_se_quedan": se_quedan}
def aplicar(cartera, config_btc: dict, ajustes_extractos: dict) -> dict:
"""Quita lo del plan. Cambia config_btc y ajustes_extractos en el sitio;
quien llama los guarda."""
p = plan(cartera, config_btc)
# 1. El historial, importación a importación (borra lo creado, quita enlaces)
for imp in list(config_btc.get("importaciones") or []):
historial_btc.deshacer(cartera, config_btc, imp["id"])
config_btc["resueltas"] = {}
config_btc["borradores"] = {}
config_btc["importaciones"] = []
# 2. Lo que metió el CSV
ids = [m["id"] for m in cartera.datos.vivos("movimientos") if _del_csv(m)]
if ids:
cartera.borrar_movimientos(ids)
# 3. Los activos «BTC en …» que se han quedado vacíos
if p["activos"]:
cartera.borrar_activos_vacios([a["id"] for a in p["activos"]])
ajustes_extractos.pop("destinos_exchange", None)
return p