Compare commits

...

2 Commits

Author SHA1 Message Date
f4bf1a2b30 Adding auto creation of opening transaction on create 2025-02-12 23:13:26 +01:00
825fa41bd9 Updating admin_user_create name 2025-02-12 23:11:10 +01:00
9 changed files with 60 additions and 30 deletions

View File

@@ -1,4 +1,5 @@
from datetime import date from datetime import date
from decimal import Decimal
from account.resource import AccountResource from account.resource import AccountResource
from account.schemas import AccountCreate, CategoryCreate from account.schemas import AccountCreate, CategoryCreate
@@ -29,36 +30,42 @@ fixtures_account = [
"parent_path": None, "parent_path": None,
"type": "Asset", "type": "Asset",
"opening_date": date(1970, 1, 1), "opening_date": date(1970, 1, 1),
"opening_balance": Decimal("0.00"),
}, },
{ {
"name": "Cash in Wallet", "name": "Cash in Wallet",
"parent_path": "/Accounts/Asset/Current Assets/", "parent_path": "/Accounts/Asset/Current Assets/",
"type": "Asset", "type": "Asset",
"opening_date": date(1970, 1, 1), "opening_date": date(1970, 1, 1),
"opening_balance": Decimal("0.00"),
}, },
{ {
"name": "Checking Account", "name": "Checking Account",
"parent_path": "/Accounts/Asset/Current Assets/", "parent_path": "/Accounts/Asset/Current Assets/",
"type": "Asset", "type": "Asset",
"opening_date": date(1970, 1, 1), "opening_date": date(1970, 1, 1),
"opening_balance": Decimal("0.00"),
}, },
{ {
"name": "Savings Account", "name": "Savings Account",
"parent_path": "/Accounts/Asset/Current Assets/", "parent_path": "/Accounts/Asset/Current Assets/",
"type": "Asset", "type": "Asset",
"opening_date": date(1970, 1, 1), "opening_date": date(1970, 1, 1),
"opening_balance": Decimal("0.00"),
}, },
{ {
"name": "Debt Accounts", "name": "Debt Accounts",
"parent_path": None, "parent_path": None,
"type": "Liability", "type": "Liability",
"opening_date": date(1970, 1, 1), "opening_date": date(1970, 1, 1),
"opening_balance": Decimal("0.00"),
}, },
{ {
"name": "Credit Card", "name": "Credit Card",
"parent_path": "/Accounts/Liability/Debt Accounts/", "parent_path": "/Accounts/Liability/Debt Accounts/",
"type": "Liability", "type": "Liability",
"opening_date": date(1970, 1, 1), "opening_date": date(1970, 1, 1),
"opening_balance": Decimal("0.00"),
}, },
] ]

View File

@@ -1,6 +1,8 @@
from sqlmodel import select from sqlmodel import select
from account.models import Account from account.models import Account
from transaction.models import Split, Transaction
class AccountResource: class AccountResource:
@classmethod @classmethod
@@ -18,6 +20,25 @@ class AccountResource:
model.parent_account = cls.get(session, model.parent_account_id) model.parent_account = cls.get(session, model.parent_account_id)
return model.parent_account return model.parent_account
@classmethod
def create_opening_transaction(cls, session, account, schema):
equity_account = cls.get_by_path(session, "/Equity/")
t = Transaction()
split_opening = Split()
split_opening.id = 0
split_opening.transaction = t
split_opening.account = account
split_opening.amount = schema.opening_balance
split_equity = Split()
split_equity.id = 1
split_equity.transaction = t
split_equity.account = equity_account
split_equity.amount = - schema.opening_balance
account.transaction_splits.append(split_opening)
@classmethod @classmethod
def schema_to_model(cls, session, schema, model=None): def schema_to_model(cls, session, schema, model=None):
try: try:
@@ -27,6 +48,7 @@ class AccountResource:
schema.path = "" schema.path = ""
schema.family = "" schema.family = ""
model = Account.model_validate(schema) model = Account.model_validate(schema)
cls.create_opening_transaction(session, model, schema)
except Exception as e: except Exception as e:
print(e) print(e)
raise raise
@@ -34,6 +56,7 @@ class AccountResource:
model.compute_family() model.compute_family()
cls.validate_parent(session, model) cls.validate_parent(session, model)
model.compute_path() model.compute_path()
return model return model
@classmethod @classmethod
@@ -68,12 +91,10 @@ class AccountResource:
@classmethod @classmethod
def create_equity_account(cls, session): def create_equity_account(cls, session):
if cls.get_by_path(session, "/Equity/") is None:
account_db = Account(name="Equity", family="Equity", type="Equity", path="/Equity/") account_db = Account(name="Equity", family="Equity", type="Equity", path="/Equity/")
session.add(account_db) session.add(account_db)
session.commit() session.commit()
session.refresh(account_db)
return account_db
@classmethod @classmethod
def select(cls): def select(cls):

View File

@@ -63,6 +63,9 @@ class CategoryWrite(BaseAccountWrite):
} }
}) })
opening_date: SkipJsonSchema[date] = Field(default=date(1970, 1, 1))
opening_balance: SkipJsonSchema[Decimal] = Field(default=0)
class CategoryCreate(CategoryWrite): class CategoryCreate(CategoryWrite):
pass pass

View File

@@ -7,8 +7,6 @@ from pydantic_core import core_schema
@dataclass @dataclass
class MonetaryAmount: class MonetaryAmount:
amount: Decimal# = Field(decimal_places=2, default=0)
@classmethod @classmethod
def __get_pydantic_core_schema__( def __get_pydantic_core_schema__(
cls, source: type[Any], handler: GetCoreSchemaHandler cls, source: type[Any], handler: GetCoreSchemaHandler
@@ -16,26 +14,24 @@ class MonetaryAmount:
assert source is MonetaryAmount assert source is MonetaryAmount
return core_schema.no_info_after_validator_function( return core_schema.no_info_after_validator_function(
cls._validate, cls._validate,
core_schema.float_schema(multiple_of=0.01), core_schema.decimal_schema(multiple_of=0.01),
serialization=core_schema.plain_serializer_function_ser_schema( serialization=core_schema.plain_serializer_function_ser_schema(
cls._serialize, cls._serialize,
info_arg=False, info_arg=False,
return_schema=core_schema.float_schema(multiple_of=0.01), return_schema=core_schema.decimal_schema(multiple_of=0.01),
), ),
) )
@staticmethod def __init__(self, *args, **kwargs):
def _validate(value: str) -> 'CompressedString': super().__init__(*args, **kwargs)
inverse_dictionary: dict[str, int] = {}
text: list[int] = []
for word in value.split(' '):
if word not in inverse_dictionary:
inverse_dictionary[word] = len(inverse_dictionary)
text.append(inverse_dictionary[word])
return MonetaryAmount(
{v: k for k, v in inverse_dictionary.items()}, text
)
@staticmethod @staticmethod
def _serialize(value: 'CompressedString') -> str: def _validate(value: Decimal) -> 'MonetaryAmount':
if value.as_tuple()[2] < -2:
raise ValueError(f'{value} has more than two decimal places.')
return value
@staticmethod
def _serialize(value: 'MonetaryAmount') -> str:
return value.amount return value.amount

View File

@@ -1,12 +1,14 @@
import main import main
from account.resource import AccountResource
from account.fixtures import inject_fixtures as account_inject_fixtures from account.fixtures import inject_fixtures as account_inject_fixtures
from db import create_db_and_tables, get_session, drop_tables from db import create_db_and_tables, get_session, drop_tables
from user import create_admin_account from user import create_admin_user
drop_tables() drop_tables()
create_db_and_tables() create_db_and_tables()
session = get_session().__next__() session = get_session().__next__()
create_admin_account(session) create_admin_user(session)
AccountResource.create_equity_account(session)
account_inject_fixtures(session) account_inject_fixtures(session)

View File

@@ -7,7 +7,7 @@ from fastapi_pagination import add_pagination
from db import create_db_and_tables from db import create_db_and_tables
from user import user_router, auth_router, create_admin_account from user import user_router, auth_router, create_admin_user
from account.account_routes import router as account_router from account.account_routes import router as account_router
from account.category_routes import router as category_router from account.category_routes import router as category_router
from payee.routes import router as payee_router from payee.routes import router as payee_router
@@ -17,7 +17,7 @@ from transaction.routes import router as transaction_router
@asynccontextmanager @asynccontextmanager
async def lifespan(app: FastAPI): async def lifespan(app: FastAPI):
create_db_and_tables() create_db_and_tables()
create_admin_account() create_admin_user()
yield yield
#do something before end #do something before end

View File

@@ -1,4 +1,5 @@
from decimal import Decimal from decimal import Decimal
from typing import Optional
from uuid import UUID, uuid4 from uuid import UUID, uuid4
from sqlmodel import Field, SQLModel, select, Relationship from sqlmodel import Field, SQLModel, select, Relationship
@@ -50,7 +51,7 @@ class Transaction(TransactionBaseId, table=True):
class SplitBase(SQLModel): class SplitBase(SQLModel):
account_id: UUID = Field(foreign_key="account.id") account_id: UUID = Field(foreign_key="account.id")
payee_id: UUID = Field(foreign_key="payee.id") payee_id: Optional[UUID] = Field(foreign_key="payee.id")
amount: Decimal = Field(decimal_places=2) amount: Decimal = Field(decimal_places=2)
class SplitBaseId(SplitBase): class SplitBaseId(SplitBase):
@@ -74,7 +75,7 @@ class SplitWrite(SplitBase):
} }
} }
}) })
payee_id: UUID = PydField(json_schema_extra={ payee_id: UUID | None = PydField(json_schema_extra={
"foreign_key": { "foreign_key": {
"reference": { "reference": {
"resource": "payees", "resource": "payees",

View File

@@ -1,4 +1,4 @@
from .manager import auth_router, reset_password_router, create_admin_account from .manager import auth_router, reset_password_router, create_admin_user
from .routes import router as user_router from .routes import router as user_router
user_router.include_router(reset_password_router) user_router.include_router(reset_password_router)

View File

@@ -43,7 +43,7 @@ reset_password_router = fastapi_users.get_reset_password_router()
auth_router = fastapi_users.get_auth_router(auth_backend) auth_router = fastapi_users.get_auth_router(auth_backend)
def create_admin_account(session=get_session().__next__()): def create_admin_user(session=get_session().__next__()):
admin_email = 'root@root.fr' admin_email = 'root@root.fr'
statement = select(User).where(User.email == admin_email).limit(1) statement = select(User).where(User.email == admin_email).limit(1)
admin_user = session.exec(statement).first() admin_user = session.exec(statement).first()