feat: auto-capitalize the first letter of transaction descriptions

Transactions/recurring templates entered with mixed casing ("траты на
продукты") are now capitalized on create/update via a Pydantic validator,
so the fix applies to every client, not just the web UI. Adds a one-off
backfill script for existing rows.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
ZProger
2026-08-24 23:10:22 +03:00
parent 2b2e01a8e2
commit 6cc532378b
6 changed files with 106 additions and 5 deletions

View File

@@ -1,8 +1,9 @@
from datetime import date as date_
from decimal import Decimal
from pydantic import BaseModel, ConfigDict, Field, model_validator
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
from app.core.text import capitalize_first_letter
from app.models.enums import TransactionType
from app.schemas.account import AccountRead
from app.schemas.category import CategoryRead
@@ -19,6 +20,13 @@ class TransactionBase(BaseModel):
notes: str | None = None
date: date_
# Auto-capitalizes "траты на продукты" -> "Траты на продукты" so mixed
# casing from quick manual entry doesn't need fixing by hand later.
@field_validator("description")
@classmethod
def _capitalize_description(cls, value: str) -> str:
return capitalize_first_letter(value)
@model_validator(mode="after")
def _validate_type_specific_fields(self) -> "TransactionBase":
if self.type == TransactionType.TRANSFER and not self.transfer_account_id:
@@ -47,6 +55,11 @@ class TransactionUpdate(BaseModel):
notes: str | None = None
date: date_ | None = None
@field_validator("description")
@classmethod
def _capitalize_description(cls, value: str | None) -> str | None:
return capitalize_first_letter(value) if value is not None else None
class TransactionRead(TransactionBase):
model_config = ConfigDict(from_attributes=True)