import logging from django.utils.timezone import make_aware from decimal import Decimal from django.core.management.base import BaseCommand from datetime import datetime from django.utils.dateparse import parse_datetime import json import os from finanzas.models import Cuenta, Movimiento, Categoria from reymotausers.models import ReyMotaUser logger = logging.getLogger('importar_movimientos_externos') handler = logging.FileHandler('importar_movimientos.log') formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) logger.addHandler(handler) logger.setLevel(logging.INFO) class Command(BaseCommand): help = 'Importa movimientos externos desde un archivo JSON' def add_arguments(self, parser): parser.add_argument('archivo_json', type=str) def handle(self, *args, **options): ruta = options['archivo_json'] if not os.path.exists(ruta): self.stdout.write(self.style.ERROR(f'Archivo no encontrado: {ruta}')) return with open(ruta, 'r', encoding='utf-8') as f: datos = json.load(f) for registro in datos: try: user = ReyMotaUser.objects.get(id=registro['user_id']) except ReyMotaUser.DoesNotExist: logger.warning(f"Usuario con id {registro['user_id']} no encontrado. Movimiento omitido.") continue nombre_origen = registro['source_name'] nombre_destino = registro['destination_name'] monto_raw = str(registro['amount']).replace(',', '.') try: monto = abs(Decimal(monto_raw)) except: logger.warning(f"Monto inválido: {registro['amount']}") continue if nombre_origen == nombre_destino: logger.info(f"Movimiento con cuentas iguales omitido: {nombre_origen}") continue cuenta_origen, _ = Cuenta.objects.get_or_create(nombre=nombre_origen, usuario=user, defaults={'tipo': 'gasto'}) cuenta_destino, _ = Cuenta.objects.get_or_create(nombre=nombre_destino, usuario=user, defaults={'tipo': 'ingreso'}) fecha = make_aware(datetime.fromisoformat(registro['date'][:19])) descripcion = registro.get('description', '') # Detección de duplicado duplicado = Movimiento.objects.filter( usuario=user, cuenta_origen=cuenta_origen, cuenta_destino=cuenta_destino, monto=monto, fecha=fecha, descripcion=descripcion ).exists() if duplicado: logger.info(f"Duplicado omitido: {descripcion} - {monto} ({fecha})") continue mov = Movimiento.objects.create( usuario=user, cuenta_origen=cuenta_origen, cuenta_destino=cuenta_destino, monto=monto, fecha=fecha, descripcion=descripcion ) if cat := registro.get('category'): categoria, _ = Categoria.objects.get_or_create(nombre=cat) mov.categoria = categoria mov.save() try: cuenta_origen.saldo -= monto cuenta_origen.save() except: logger.warning(f"Monto inválido: {registro['amount']} {registro['journal_id']}") continue try: cuenta_destino.saldo += monto cuenta_destino.save() except: logger.warning(f"Monto inválido: {registro['amount']}") continue logger.info(f"Movimiento importado: {descripcion} - {monto} ({fecha})") self.stdout.write(self.style.SUCCESS('Importación finalizada. Ver "importar_movimientos.log" para detalles.'))