Files
WYGIWYH/app/apps/bank_sync/migrations/0001_initial.py
T
Herculino Trotta 9238c48bb5 feat(bank-sync): add SimpleFIN provider
Users paste a one-time setup token. On save it's claimed and the access
URL it returns is stored; credentials go in a header, never the URL, so
errors don't leak them.

- Providers can validate or transform their settings (clean_settings),
  run by the service form once every other field is valid, so a token
  isn't spent on a form that fails anyway.
- Providers can declare a minimum sync interval. SimpleFIN Bridge
  disables tokens that go much past ~24 requests a day, so it's 6 hours.
- Tokens and access URLs are user input pointing the server at a host:
  only SimpleFIN Bridge is allowed unless SIMPLEFIN_ALLOWED_HOSTS says
  otherwise. Checked again on every request, for restored settings.
- Transactions are fetched per account in 90-day windows, Bridge's
  limit per request. Pending ones are skipped.
2026-11-08 16:08:37 -03:00

62 lines
3.9 KiB
Python

# Generated by Django 5.2.17 on 2026-11-08 15:27
import django.core.validators
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('accounts', '0016_account_untracked_by'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='BankSyncService',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name='Name')),
('service_type', models.CharField(choices=[('lunchflow', 'Lunch Flow'), ('simplefin', 'SimpleFIN')], max_length=255, verbose_name='Service Type')),
('is_active', models.BooleanField(default=True, verbose_name='Active')),
('settings', models.JSONField(blank=True, default=dict, verbose_name='Settings')),
('sync_interval', models.PositiveIntegerField(default=24, help_text='Most banks only update once a day.', validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(168)], verbose_name='Sync every X hours')),
('trigger_rules', models.BooleanField(default=True, help_text='Run your transaction rules on synced transactions.', verbose_name='Trigger transaction rules')),
('match_existing', models.BooleanField(default=False, help_text='Instead of creating a synced transaction, link it to one you already added on the same account, with the same type and amount, dated up to 3 days apart.', verbose_name='Match existing transactions')),
('last_sync', models.DateTimeField(blank=True, null=True, verbose_name='Last Successful Sync')),
('failure_count', models.PositiveIntegerField(default=0)),
('last_error', models.TextField(blank=True, default='')),
('owner', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_owned', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Bank Sync Service',
'verbose_name_plural': 'Bank Sync Services',
'ordering': ['name'],
},
),
migrations.CreateModel(
name='BankSyncAccount',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('external_id', models.CharField(max_length=255)),
('external_name', models.CharField(blank=True, max_length=255)),
('available_currencies', models.JSONField(blank=True, default=list)),
('currencies', models.JSONField(blank=True, default=list, help_text='Only sync transactions in these currencies. Empty syncs all.', verbose_name='Currencies')),
('remote_error', models.TextField(blank=True, default='')),
('last_error', models.TextField(blank=True, default='')),
('start_date', models.DateField(blank=True, help_text='Transactions before this date are never imported.', null=True, verbose_name='Sync from')),
('last_synced_date', models.DateField(blank=True, null=True)),
('account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bank_sync_accounts', to='accounts.account', verbose_name='Account')),
('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accounts', to='bank_sync.banksyncservice')),
],
options={
'ordering': ['external_name'],
'unique_together': {('service', 'external_id')},
},
),
]