You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

230 lines
15 KiB

# Generated by Django 5.2.17 on 2026-09-06 19:02
import django.core.validators
import django.db.models.deletion
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Account',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=255, unique=True)),
('kind', models.CharField(choices=[('BANK', 'Bank'), ('CREDIT_CARD', 'Credit card')], max_length=16)),
('currency', models.CharField(default='BRL', max_length=3, validators=[django.core.validators.RegexValidator('\\A[A-Z]{3}\\Z', 'Use three uppercase ASCII letters.')])),
('archived', models.BooleanField(default=False)),
],
options={
'constraints': [models.CheckConstraint(condition=models.Q(('kind__in', ['BANK', 'CREDIT_CARD'])), name='account_valid_kind'), models.CheckConstraint(condition=models.Q(('currency__regex', '^[A-Z]{3}$')), name='account_valid_currency'), models.CheckConstraint(condition=models.Q(('name__regex', '\\S')), name='account_nonblank_name')],
},
),
migrations.CreateModel(
name='AuditEvent',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('actor_kind', models.CharField(choices=[('HUMAN', 'Human'), ('APPLICATION', 'Application'), ('AGENT', 'Agent')], max_length=16)),
('actor_identifier', models.CharField(max_length=255)),
('operation', models.CharField(max_length=128)),
('target_id', models.UUIDField(blank=True, null=True)),
('outcome', models.CharField(choices=[('SUCCESS', 'Success'), ('FAILURE', 'Failure')], max_length=16)),
('changed_fields', models.JSONField(blank=True, default=list)),
('error_code', models.CharField(blank=True, max_length=128)),
],
options={
'constraints': [models.CheckConstraint(condition=models.Q(('actor_kind__in', ['HUMAN', 'APPLICATION', 'AGENT'])), name='audit_valid_actor'), models.CheckConstraint(condition=models.Q(('outcome__in', ['SUCCESS', 'FAILURE'])), name='audit_valid_outcome'), models.CheckConstraint(condition=models.Q(('actor_identifier__regex', '\\S')), name='audit_nonblank_actor'), models.CheckConstraint(condition=models.Q(('operation__regex', '\\S')), name='audit_nonblank_operation')],
},
),
migrations.CreateModel(
name='CardStatement',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('period_start', models.DateField()),
('period_end', models.DateField()),
('due_date', models.DateField()),
('statement_balance', models.DecimalField(blank=True, decimal_places=2, max_digits=18, null=True)),
('account', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='statements', to='finance.account')),
],
),
migrations.CreateModel(
name='Category',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=255, unique=True)),
('kind', models.CharField(choices=[('INCOME', 'Income'), ('EXPENSE', 'Expense')], max_length=16)),
('archived', models.BooleanField(default=False)),
],
options={
'constraints': [models.CheckConstraint(condition=models.Q(('kind__in', ['INCOME', 'EXPENSE'])), name='category_valid_kind'), models.CheckConstraint(condition=models.Q(('name__regex', '\\S')), name='category_nonblank_name')],
},
),
migrations.CreateModel(
name='InstallmentPlan',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('description', models.TextField(blank=True)),
('total_installments', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1)])),
('account', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='installment_plans', to='finance.account')),
],
),
migrations.CreateModel(
name='Payee',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=255, unique=True)),
('archived', models.BooleanField(default=False)),
],
options={
'constraints': [models.CheckConstraint(condition=models.Q(('name__regex', '\\S')), name='payee_nonblank_name')],
},
),
migrations.CreateModel(
name='Transaction',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('amount', models.DecimalField(decimal_places=2, max_digits=18)),
('posting_date', models.DateField()),
('occurrence_date', models.DateField(blank=True, null=True)),
('description', models.TextField(blank=True)),
('kind', models.CharField(choices=[('INCOME', 'Income'), ('EXPENSE', 'Expense'), ('TRANSFER', 'Transfer'), ('REFUND', 'Refund'), ('OPENING', 'Opening')], max_length=16)),
('installment_number', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])),
('account', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='transactions', to='finance.account')),
('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='transactions', to='finance.category')),
('installment_plan', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='transactions', to='finance.installmentplan')),
('payee', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='transactions', to='finance.payee')),
('refund_of', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='refunds', to='finance.transaction')),
('statement', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='transactions', to='finance.cardstatement')),
],
),
migrations.CreateModel(
name='SourceRecord',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('document_hash', models.CharField(max_length=64, validators=[django.core.validators.RegexValidator('\\A[0-9a-f]{64}\\Z', 'Use a lowercase SHA-256 hexadecimal digest.')])),
('source_format', models.CharField(choices=[('BANK_CSV', 'Bank CSV'), ('OUROCARD_TXT', 'Ourocard TXT')], max_length=16)),
('parser_version', models.CharField(max_length=128)),
('item_locator', models.CharField(max_length=255)),
('transaction', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='source_records', to='finance.transaction')),
],
),
migrations.CreateModel(
name='Transfer',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('incoming', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='incoming_transfer', to='finance.transaction')),
('outgoing', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='outgoing_transfer', to='finance.transaction')),
],
),
migrations.AddConstraint(
model_name='cardstatement',
constraint=models.CheckConstraint(condition=models.Q(('period_end__gte', models.F('period_start'))), name='statement_period_order'),
),
migrations.AddConstraint(
model_name='cardstatement',
constraint=models.CheckConstraint(condition=models.Q(('due_date__gte', models.F('period_end'))), name='statement_due_order'),
),
migrations.AddConstraint(
model_name='cardstatement',
constraint=models.UniqueConstraint(fields=('account', 'period_end'), name='statement_account_period_unique'),
),
migrations.AddConstraint(
model_name='installmentplan',
constraint=models.CheckConstraint(condition=models.Q(('total_installments__gt', 0)), name='plan_positive_total'),
),
migrations.AddConstraint(
model_name='transaction',
constraint=models.CheckConstraint(condition=models.Q(('kind__in', ['INCOME', 'EXPENSE', 'TRANSFER', 'REFUND', 'OPENING'])), name='transaction_valid_kind'),
),
migrations.AddConstraint(
model_name='transaction',
constraint=models.CheckConstraint(condition=models.Q(('amount', 0), _negated=True), name='transaction_nonzero_amount'),
),
migrations.AddConstraint(
model_name='transaction',
constraint=models.CheckConstraint(condition=models.Q(models.Q(('amount__gt', 0), ('kind__in', ['INCOME', 'REFUND'])), models.Q(('amount__lt', 0), ('kind', 'EXPENSE')), ('kind__in', ['TRANSFER', 'OPENING']), _connector='OR'), name='transaction_kind_sign'),
),
migrations.AddConstraint(
model_name='transaction',
constraint=models.UniqueConstraint(condition=models.Q(('kind', 'OPENING')), fields=('account',), name='transaction_one_opening'),
),
migrations.AddConstraint(
model_name='transaction',
constraint=models.CheckConstraint(condition=models.Q(models.Q(('kind', 'OPENING'), _negated=True), models.Q(('category__isnull', True), ('installment_number__isnull', True), ('installment_plan__isnull', True), ('payee__isnull', True), ('refund_of__isnull', True), ('statement__isnull', True)), _connector='OR'), name='transaction_opening_unassociated'),
),
migrations.AddConstraint(
model_name='transaction',
constraint=models.CheckConstraint(condition=models.Q(models.Q(('kind', 'TRANSFER'), _negated=True), models.Q(('category__isnull', True), ('installment_number__isnull', True), ('installment_plan__isnull', True), ('refund_of__isnull', True)), _connector='OR'), name='transaction_transfer_unclassified'),
),
migrations.AddConstraint(
model_name='transaction',
constraint=models.CheckConstraint(condition=models.Q(('refund_of__isnull', True), ('kind', 'REFUND'), _connector='OR'), name='transaction_refund_link_kind'),
),
migrations.AddConstraint(
model_name='transaction',
constraint=models.CheckConstraint(condition=models.Q(('refund_of__isnull', True), models.Q(('refund_of', models.F('id')), _negated=True), _connector='OR'), name='transaction_refund_not_self'),
),
migrations.AddConstraint(
model_name='transaction',
constraint=models.CheckConstraint(condition=models.Q(models.Q(('installment_number__isnull', True), ('installment_plan__isnull', True)), models.Q(('installment_number__isnull', False), ('installment_plan__isnull', False)), _connector='OR'), name='transaction_installment_pair'),
),
migrations.AddConstraint(
model_name='transaction',
constraint=models.CheckConstraint(condition=models.Q(('installment_number__isnull', True), ('installment_number__gt', 0), _connector='OR'), name='transaction_installment_positive'),
),
migrations.AddConstraint(
model_name='transaction',
constraint=models.CheckConstraint(condition=models.Q(('installment_plan__isnull', True), ('kind', 'EXPENSE'), _connector='OR'), name='transaction_installment_expense'),
),
migrations.AddConstraint(
model_name='transaction',
constraint=models.UniqueConstraint(condition=models.Q(('installment_plan__isnull', False)), fields=('installment_plan', 'installment_number'), name='transaction_installment_unique'),
),
migrations.AddConstraint(
model_name='sourcerecord',
constraint=models.CheckConstraint(condition=models.Q(('source_format__in', ['BANK_CSV', 'OUROCARD_TXT'])), name='source_valid_format'),
),
migrations.AddConstraint(
model_name='sourcerecord',
constraint=models.CheckConstraint(condition=models.Q(('document_hash__regex', '^[0-9a-f]{64}$')), name='source_valid_hash'),
),
migrations.AddConstraint(
model_name='sourcerecord',
constraint=models.CheckConstraint(condition=models.Q(('parser_version__regex', '\\S')), name='source_nonblank_parser'),
),
migrations.AddConstraint(
model_name='sourcerecord',
constraint=models.CheckConstraint(condition=models.Q(('item_locator__regex', '\\S')), name='source_nonblank_locator'),
),
migrations.AddConstraint(
model_name='sourcerecord',
constraint=models.UniqueConstraint(fields=('document_hash', 'item_locator'), name='source_document_item_unique'),
),
migrations.AddConstraint(
model_name='transfer',
constraint=models.CheckConstraint(condition=models.Q(('outgoing', models.F('incoming')), _negated=True), name='transfer_distinct_legs'),
),
]