mirror of
https://github.com/Zproger/Aurum.git
synced 2026-10-13 11:06:40 +07:00
fix: don't enforce write-time transfer rules when reading transactions (#7)
TransactionRead inherited TransactionBase's model_validator, so the rules a new row must satisfy were also applied on the way out, to rows already in the database. A row can stop satisfying them through no fault of its own: transactions.transfer_account_id is ON DELETE SET NULL, so deleting an account leaves every transfer that pointed at it without a destination. The result was that deleting an account could 500 the entire transactions list — permanently, and with the list gone there was no way in the UI to find and delete the offending row. The fields move to TransactionFields, which reading uses; TransactionBase keeps the invariants for the write path. The frontend already tolerates a missing destination (TransactionsTable renders the transfer suffix only when transfer_account_id is set, and the edit modal makes you pick a destination before saving), so such a row is now visible, editable and deletable instead of fatal.
This commit is contained in:
@@ -39,7 +39,15 @@ def transfer_rule_violation(
|
||||
return None
|
||||
|
||||
|
||||
class TransactionBase(BaseModel):
|
||||
class TransactionFields(BaseModel):
|
||||
"""A transaction's shape, without the rules that only make sense while
|
||||
writing one. Reading goes through this: rows already in the database can
|
||||
stop satisfying a write-time rule through no fault of their own — the
|
||||
transfer_account_id FK is ON DELETE SET NULL, so deleting an account
|
||||
leaves the transfers that pointed at it with no destination — and
|
||||
refusing to serialize such a row would take the whole transactions list
|
||||
down with it, leaving no way in the UI to find and delete the row."""
|
||||
|
||||
account_id: int
|
||||
category_id: int | None = None
|
||||
transfer_account_id: int | None = None
|
||||
@@ -57,6 +65,11 @@ class TransactionBase(BaseModel):
|
||||
def _capitalize_description(cls, value: str) -> str:
|
||||
return capitalize_first_letter(value)
|
||||
|
||||
|
||||
class TransactionBase(TransactionFields):
|
||||
"""The write-side shape: the fields plus the invariants a new or edited
|
||||
row has to satisfy."""
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _validate_type_specific_fields(self) -> "TransactionBase":
|
||||
violation = transfer_rule_violation(
|
||||
@@ -93,7 +106,7 @@ class TransactionUpdate(BaseModel):
|
||||
return capitalize_first_letter(value) if value is not None else None
|
||||
|
||||
|
||||
class TransactionRead(TransactionBase):
|
||||
class TransactionRead(TransactionFields):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
|
||||
Reference in New Issue
Block a user