Subclass the Transaction with multiple children
Each children is essentually a type of transaction. We currently have: - bank transactions - money transactions - split transactions The table inheritance is implemented as a single table, with a polymorphic type and Null columns. Adds a IsSplit interface, which will later be used for the category views, so as to not repeat transactions.
This commit is contained in:
parent
0d287624c4
commit
478bd25190
74
alembic/versions/37d80de801a7_inheritance.py
Normal file
74
alembic/versions/37d80de801a7_inheritance.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
"""Inheritance
|
||||||
|
|
||||||
|
Revision ID: 37d80de801a7
|
||||||
|
Revises: 8cc9870b0d74
|
||||||
|
Create Date: 2023-01-10 22:41:03.540108+00:00
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = "37d80de801a7"
|
||||||
|
down_revision = "8cc9870b0d74"
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column(
|
||||||
|
"originals",
|
||||||
|
sa.Column("type", sa.String(), nullable=False),
|
||||||
|
schema="transactions",
|
||||||
|
)
|
||||||
|
op.add_column(
|
||||||
|
"originals",
|
||||||
|
sa.Column("split", sa.Boolean(), nullable=True),
|
||||||
|
schema="transactions",
|
||||||
|
)
|
||||||
|
op.add_column(
|
||||||
|
"originals",
|
||||||
|
sa.Column("original", sa.BigInteger(), nullable=True),
|
||||||
|
schema="transactions",
|
||||||
|
)
|
||||||
|
op.alter_column(
|
||||||
|
"originals",
|
||||||
|
"bank",
|
||||||
|
existing_type=sa.TEXT(),
|
||||||
|
nullable=True,
|
||||||
|
schema="transactions",
|
||||||
|
)
|
||||||
|
op.create_foreign_key(
|
||||||
|
op.f("fk_originals_original_originals"),
|
||||||
|
"originals",
|
||||||
|
"originals",
|
||||||
|
["original"],
|
||||||
|
["id"],
|
||||||
|
source_schema="transactions",
|
||||||
|
referent_schema="transactions",
|
||||||
|
ondelete="CASCADE",
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_constraint(
|
||||||
|
op.f("fk_originals_original_originals"),
|
||||||
|
"originals",
|
||||||
|
schema="transactions",
|
||||||
|
type_="foreignkey",
|
||||||
|
)
|
||||||
|
op.alter_column(
|
||||||
|
"originals",
|
||||||
|
"bank",
|
||||||
|
existing_type=sa.TEXT(),
|
||||||
|
nullable=False,
|
||||||
|
schema="transactions",
|
||||||
|
)
|
||||||
|
op.drop_column("originals", "original", schema="transactions")
|
||||||
|
op.drop_column("originals", "split", schema="transactions")
|
||||||
|
op.drop_column("originals", "type", schema="transactions")
|
||||||
|
# ### end Alembic commands ###
|
||||||
@ -74,14 +74,17 @@ class Transaction(Base):
|
|||||||
id: Mapped[idpk] = mapped_column(init=False)
|
id: Mapped[idpk] = mapped_column(init=False)
|
||||||
date: Mapped[dt.date]
|
date: Mapped[dt.date]
|
||||||
description: Mapped[Optional[str]]
|
description: Mapped[Optional[str]]
|
||||||
bank: Mapped[bankfk]
|
|
||||||
amount: Mapped[money]
|
amount: Mapped[money]
|
||||||
|
|
||||||
|
type: Mapped[str] = mapped_column(init=False)
|
||||||
|
|
||||||
category: Mapped[Optional[TransactionCategory]] = relationship(init=False)
|
category: Mapped[Optional[TransactionCategory]] = relationship(init=False)
|
||||||
note: Mapped[Optional[Note]] = relationship(init=False)
|
note: Mapped[Optional[Note]] = relationship(init=False)
|
||||||
tags: Mapped[Optional[set[TransactionTag]]] = relationship(init=False)
|
tags: Mapped[Optional[set[TransactionTag]]] = relationship(init=False)
|
||||||
|
|
||||||
def __lt__(self, other):
|
__mapper_args__ = {"polymorphic_on": "type", "polymorphic_identity": "transaction"}
|
||||||
|
|
||||||
|
def __lt__(self, other: Transaction):
|
||||||
return self.date < other.date
|
return self.date < other.date
|
||||||
|
|
||||||
|
|
||||||
@ -90,6 +93,26 @@ idfk = Annotated[
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class IsSplit:
|
||||||
|
split: Mapped[bool] = mapped_column(use_existing_column=True, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
class BankTransaction(IsSplit, Transaction):
|
||||||
|
bank: Mapped[bankfk] = mapped_column(nullable=True)
|
||||||
|
|
||||||
|
__mapper_args__ = {"polymorphic_identity": "bank", "polymorphic_load": "inline"}
|
||||||
|
|
||||||
|
|
||||||
|
class MoneyTransaction(IsSplit, Transaction):
|
||||||
|
__mapper_args__ = {"polymorphic_identity": "money"}
|
||||||
|
|
||||||
|
|
||||||
|
class SplitTransaction(Transaction):
|
||||||
|
original: Mapped[idfk] = mapped_column(nullable=True)
|
||||||
|
|
||||||
|
__mapper_args__ = {"polymorphic_identity": "split", "polymorphic_load": "inline"}
|
||||||
|
|
||||||
|
|
||||||
class CategoryGroup(Base):
|
class CategoryGroup(Base):
|
||||||
__tablename__ = "categories_groups"
|
__tablename__ = "categories_groups"
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ from uuid import uuid4
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from pfbudget.db.model import Transaction
|
from pfbudget.db.model import BankTransaction
|
||||||
from pfbudget.utils import convert
|
from pfbudget.utils import convert
|
||||||
|
|
||||||
from .input import Input
|
from .input import Input
|
||||||
@ -29,7 +29,7 @@ class NordigenInput(Input):
|
|||||||
self._start = date.min
|
self._start = date.min
|
||||||
self._end = date.max
|
self._end = date.max
|
||||||
|
|
||||||
def parse(self) -> list[Transaction]:
|
def parse(self) -> list[BankTransaction]:
|
||||||
transactions = []
|
transactions = []
|
||||||
assert len(self._banks) > 0
|
assert len(self._banks) > 0
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
from datetime import date, timedelta
|
from datetime import date
|
||||||
from functools import singledispatch
|
from functools import singledispatch
|
||||||
|
|
||||||
from pfbudget.common.types import TransactionError
|
from pfbudget.common.types import TransactionError
|
||||||
from pfbudget.db.model import Bank, Transaction
|
from pfbudget.db.model import Bank, BankTransaction
|
||||||
from .utils import parse_decimal
|
from .utils import parse_decimal
|
||||||
|
|
||||||
|
|
||||||
@ -13,10 +13,10 @@ def convert(t):
|
|||||||
|
|
||||||
|
|
||||||
@convert.register
|
@convert.register
|
||||||
def _(json: dict, bank: Bank) -> Transaction:
|
def _(json: dict, bank: Bank) -> BankTransaction:
|
||||||
i = -1 if bank.nordigen.invert else 1
|
i = -1 if bank.nordigen.invert else 1
|
||||||
try:
|
try:
|
||||||
transaction = Transaction(
|
transaction = BankTransaction(
|
||||||
date=date.fromisoformat(json["bookingDate"]),
|
date=date.fromisoformat(json["bookingDate"]),
|
||||||
description=json["remittanceInformationUnstructured"],
|
description=json["remittanceInformationUnstructured"],
|
||||||
bank=bank.name,
|
bank=bank.name,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user