diff --git a/app/routers/collections.py b/app/routers/collections.py index 3d65820..666aef3 100644 --- a/app/routers/collections.py +++ b/app/routers/collections.py @@ -97,9 +97,13 @@ async def list_collections( session: AsyncSession = Depends(get_async_session), ): """List all collections for the current user.""" - # Get collections with item counts + # Get collections with item counts and total value stmt = ( - select(Collection, func.count(Item.id).label("item_count")) + select( + Collection, + func.count(Item.id).label("item_count"), + func.sum(Item.estimated_value).label("total_value"), + ) .outerjoin(Item, Collection.id == Item.collection_id) .where(Collection.user_id == str(user.id)) .group_by(Collection.id) @@ -121,6 +125,7 @@ async def list_collections( created_at=row.Collection.created_at, updated_at=row.Collection.updated_at, item_count=row.item_count, + total_value=row.total_value or None, preview_images=previews.get(row.Collection.id, []), ) for row in rows @@ -135,7 +140,11 @@ async def get_collection( ): """Get a single collection by ID.""" stmt = ( - select(Collection, func.count(Item.id).label("item_count")) + select( + Collection, + func.count(Item.id).label("item_count"), + func.sum(Item.estimated_value).label("total_value"), + ) .outerjoin(Item, Collection.id == Item.collection_id) .where(Collection.id == str(collection_id), Collection.user_id == str(user.id)) .group_by(Collection.id) @@ -160,6 +169,7 @@ async def get_collection( created_at=row.Collection.created_at, updated_at=row.Collection.updated_at, item_count=row.item_count, + total_value=row.total_value or None, preview_images=previews.get(row.Collection.id, []), ) diff --git a/app/schemas/collection.py b/app/schemas/collection.py index 1e3155f..97fccb1 100644 --- a/app/schemas/collection.py +++ b/app/schemas/collection.py @@ -1,4 +1,5 @@ from datetime import datetime +from decimal import Decimal from uuid import UUID from pydantic import BaseModel, ConfigDict, Field, field_validator @@ -64,4 +65,5 @@ class CollectionReadWithCount(CollectionRead): """Schema for reading a Collection with item count.""" item_count: int = 0 + total_value: Decimal | None = None preview_images: list[ImagePreview] = [] diff --git a/tests/test_collections.py b/tests/test_collections.py index ee539ab..6a5f55a 100644 --- a/tests/test_collections.py +++ b/tests/test_collections.py @@ -427,3 +427,105 @@ async def test_collection_no_images_empty_preview( assert response.status_code == 200 data = response.json() assert data["preview_images"] == [] + + +@pytest.mark.asyncio +async def test_list_collections_total_value( + client: AsyncClient, session: AsyncSession, test_user: User, auth_client +): + """Test that listing collections includes total_value summing estimated_value.""" + collection = Collection(user_id=str(test_user.id), name="Valued Collection") + session.add(collection) + await session.commit() + await session.refresh(collection) + + item1 = Item( + user_id=str(test_user.id), + collection_id=collection.id, + name="Item 1", + estimated_value=100.50, + ) + item2 = Item( + user_id=str(test_user.id), + collection_id=collection.id, + name="Item 2", + estimated_value=250.00, + ) + item3 = Item( + user_id=str(test_user.id), + collection_id=collection.id, + name="Item 3 (no value)", + ) + session.add_all([item1, item2, item3]) + await session.commit() + + response = await client.get("/collections") + assert response.status_code == 200 + data = response.json() + assert len(data) == 1 + assert data[0]["total_value"] == "350.50" + + +@pytest.mark.asyncio +async def test_get_collection_total_value( + client: AsyncClient, session: AsyncSession, test_user: User, auth_client +): + """Test that getting a single collection includes total_value.""" + collection = Collection(user_id=str(test_user.id), name="My Collection") + session.add(collection) + await session.commit() + await session.refresh(collection) + + item = Item( + user_id=str(test_user.id), + collection_id=collection.id, + name="Valuable Item", + estimated_value=999.99, + ) + session.add(item) + await session.commit() + + response = await client.get(f"/collections/{collection.id}") + assert response.status_code == 200 + data = response.json() + assert data["total_value"] == "999.99" + + +@pytest.mark.asyncio +async def test_collection_total_value_null_when_no_values( + client: AsyncClient, session: AsyncSession, test_user: User, auth_client +): + """Test that total_value is null when no items have estimated_value.""" + collection = Collection(user_id=str(test_user.id), name="No Values") + session.add(collection) + await session.commit() + await session.refresh(collection) + + item = Item( + user_id=str(test_user.id), + collection_id=collection.id, + name="Item without value", + ) + session.add(item) + await session.commit() + + response = await client.get(f"/collections/{collection.id}") + assert response.status_code == 200 + data = response.json() + assert data["total_value"] is None + + +@pytest.mark.asyncio +async def test_empty_collection_total_value_null( + client: AsyncClient, session: AsyncSession, test_user: User, auth_client +): + """Test that total_value is null for empty collections.""" + collection = Collection(user_id=str(test_user.id), name="Empty") + session.add(collection) + await session.commit() + await session.refresh(collection) + + response = await client.get(f"/collections/{collection.id}") + assert response.status_code == 200 + data = response.json() + assert data["total_value"] is None