feat(order): Add finalize order functionality

This commit is contained in:
2025-12-13 11:53:41 +01:00
parent 00246819cc
commit fd544fcebc
9 changed files with 161 additions and 35 deletions

View File

@@ -41,4 +41,4 @@ async def get_user_object(request: Request, session: SessionDep) -> User:
return user
UserDep = Annotated[dict, Depends(get_user_object)]
UserDep = Annotated[User, Depends(get_user_object)]

View File

@@ -34,18 +34,18 @@ async def get_cart(request: Request, session: SessionDep, user: UserDep):
@shop_router.get("/shop/finalize_order")
async def get_cart(request: Request, session: SessionDep, user: UserDep):
async def finalize_order(request: Request, session: SessionDep, user: UserDep):
cart = user.shopping_cart
# TODO: Implement
cart.finalize_order()
cart.finalize(user.accounts[0])
return RedirectResponse(url=f"/", status_code=status.HTTP_302_FOUND)
@shop_router.get("/shop/area/{area_id}")
async def get_shop(request: Request, session: SessionDep, area_id: int):
async def get_shop_area(request: Request, session: SessionDep, area_id: int):
query = select(Area).where(Area.id == area_id)
area = session.scalars(query).one()
return templates.TemplateResponse(

View File

@@ -15,6 +15,18 @@ def create_tables():
def ensure_user(user_info: dict, session: Session) -> User:
"""
Retrieve an existing user or create a new one if it doesn't exist.
This function queries the database for a user with the given username.
If found, it returns the existing user. If not found, it creates a new user
with the provided information, adds it to the session, and returns it.
:param user_info: Dictionary containing user information with keys:
- "username" (str): The unique username to search for or create
- "display_name" (str, optional): The display name for the new user
:param session: SQLAlchemy session for database operations
:return: The existing or newly created user object
"""
statement = select(User).where(User.username == user_info["username"])
if user := session.scalars(statement).one_or_none():

View File

@@ -58,7 +58,7 @@ class User(Base):
@property
def shopping_cart(self):
for order in self.orders:
if order.account_id is None:
if order.transaction is None:
cart = order
break
else:
@@ -109,10 +109,7 @@ class Order(Base):
user_id: Mapped[int] = mapped_column(ForeignKey(TABLE_PREFIX + "user.id"))
user: Mapped[User] = relationship("User", back_populates="orders")
account_id: Mapped[int] = mapped_column(
ForeignKey(TABLE_PREFIX + "account.id"), nullable=True
)
account: Mapped[Account | None] = relationship("Account")
transaction: Mapped["Transaction | None"] = relationship("Transaction")
items: Mapped[list["OrderItem"]] = relationship(
"OrderItem", cascade="all, delete-orphan", back_populates="order"
@@ -120,12 +117,37 @@ class Order(Base):
@property
def is_in_shopping_cart(self):
return self.account is None
return self.transaction is None
@property
def total_amount(self):
return sum(item.total_amount for item in self.items)
def finalize(self, account: Account):
"""
Moves the order from the shopping cart to a given account
and adds a transaction to the account.
:param account: The account to which the order should be finalized
:raises ValueError: If the order is already finalized or empty"""
if not self.is_in_shopping_cart:
raise ValueError("Order is already finalized.")
if not self.items:
raise ValueError("Cannot finalize an empty order.")
assert account in self.user.accounts, "Account does not belong to user."
# create a transaction for the order
transaction = Transaction(
type="order",
total_amount=-self.total_amount,
order=self,
account=account,
)
session = object_session(self)
session.add(transaction)
class OrderItem(Base):
__tablename__ = TABLE_PREFIX + "order_item"
@@ -142,7 +164,7 @@ class OrderItem(Base):
TransactionTypes = typing.Literal[
"product",
"order",
"deposit",
"withdrawal",
"expense",
@@ -161,10 +183,10 @@ class Transaction(Base):
Numeric(10, 2), nullable=False
)
product_id: Mapped[int] = mapped_column(
ForeignKey(TABLE_PREFIX + "product.id"), nullable=True
order_id: Mapped[int] = mapped_column(
ForeignKey(TABLE_PREFIX + "order.id"), nullable=True
)
product: Mapped["Product"] = relationship("Product")
order: Mapped["Order"] = relationship("Order", back_populates="transaction")
account_id: Mapped[int] = mapped_column(ForeignKey(TABLE_PREFIX + "account.id"))
account: Mapped["Account"] = relationship("Account", back_populates="transactions")

View File

@@ -45,7 +45,7 @@
{% for transaction in transactions[:10] %}
<div class="list-group-item d-flex justify-content-between align-items-start py-3">
<div class="flex-grow-1">
<div class="fw-semibold">{{ transaction.product.name or transaction.type|transaction_type_de }}</div>
<div class="fw-semibold">{{ transaction.type|transaction_type_de }}</div>
<small class="text-muted">
{{ transaction.timestamp }}
</small>

View File

@@ -7,7 +7,7 @@ TRANSACTION_TYPE_DE = {
"deposit": "Einzahlung",
"withdrawal": "Auszahlung",
"expense": "Auslage",
"product": "Einkauf",
"order": "Einkauf",
}
UNITS_OF_MEASURE = {"piece": "Stück"}