feat: migración de datos de traspasos heredados
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
7fd2f3361d
commit
9af6e749d5
@ -0,0 +1,72 @@
|
||||
"""Convierte en Transfer los traspasos históricos que estaban sueltos.
|
||||
|
||||
Antes de que existiera Transfer, un traspaso eran dos movimientos marcados con
|
||||
is_transfer pero sin nada que los relacionara: un Expense en la cuenta de origen
|
||||
y un Income en la de destino. Aquí se emparejan y se les cuelga un Transfer.
|
||||
|
||||
No se pueden emparejar con certeza: nada en la base de datos dice qué gasto va
|
||||
con qué ingreso. El criterio es mismo owner, mismo importe, misma fecha y cuentas
|
||||
distintas, y solo cuando en ese grupo hay exactamente un gasto y un ingreso. Los
|
||||
ambiguos (varios candidatos con el mismo importe y fecha) y los huérfanos (sin
|
||||
pareja) se dejan tal cual, con su is_transfer=True: siguen contando en los saldos
|
||||
y fuera de los análisis igual que antes, solo que sin Transfer que los orqueste.
|
||||
Emparejarlos a ojo sería inventarse datos del usuario.
|
||||
"""
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def link_legacy_transfers(apps, schema_editor):
|
||||
Expense = apps.get_model("expenses", "Expense")
|
||||
Income = apps.get_model("expenses", "Income")
|
||||
Transfer = apps.get_model("expenses", "Transfer")
|
||||
|
||||
# Solo las patas que aún no pertenecen a ningún Transfer.
|
||||
expenses_by_key = defaultdict(list)
|
||||
for expense in Expense.objects.filter(
|
||||
is_transfer=True, transfer_out__isnull=True, transfer_fee__isnull=True
|
||||
):
|
||||
expenses_by_key[(expense.owner_id, expense.amount, expense.date)].append(
|
||||
expense
|
||||
)
|
||||
|
||||
incomes_by_key = defaultdict(list)
|
||||
for income in Income.objects.filter(is_transfer=True, transfer_in__isnull=True):
|
||||
incomes_by_key[(income.owner_id, income.amount, income.date)].append(income)
|
||||
|
||||
for key, expenses in expenses_by_key.items():
|
||||
incomes = incomes_by_key.get(key, [])
|
||||
|
||||
# Uno y solo uno de cada lado: cualquier otra cosa es ambigua.
|
||||
if len(expenses) != 1 or len(incomes) != 1:
|
||||
continue
|
||||
|
||||
expense, income = expenses[0], incomes[0]
|
||||
|
||||
if expense.account_id == income.account_id:
|
||||
continue
|
||||
|
||||
Transfer.objects.create(
|
||||
owner_id=expense.owner_id,
|
||||
date=expense.date,
|
||||
amount=expense.amount,
|
||||
from_account_id=expense.account_id,
|
||||
to_account_id=income.account_id,
|
||||
expense_leg=expense,
|
||||
income_leg=income,
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("expenses", "0014_category_is_system_transfer"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
# Sin marcha atrás: deshacer esto es deshacer 0014, que se lleva la tabla
|
||||
# Transfer entera y deja las patas intactas, que es el estado de partida.
|
||||
migrations.RunPython(link_legacy_transfers, migrations.RunPython.noop),
|
||||
]
|
||||
Loading…
Reference in New Issue
Block a user