From 88747307e50c61cff4f0d47292005b8f8ce2f260 Mon Sep 17 00:00:00 2001 From: tonym Date: Thu, 15 Jan 2026 23:44:20 -0600 Subject: [PATCH] fix(spool): Add remaining_weight filter for low stock detection - Add remaining_weight_lt and remaining_weight_gt filter params to spool API - Filter uses computed formula: initial_weight - used_weight (falls back to filament.weight) - Update home page to use queryParams instead of Refine filters - Fixes low stock badge count on dashboard Co-Authored-By: Claude Opus 4.5 --- client/src/pages/home/index.tsx | 10 ++-------- spoolman/api/v1/spool.py | 16 ++++++++++++++++ spoolman/database/spool.py | 9 +++++++++ 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/client/src/pages/home/index.tsx b/client/src/pages/home/index.tsx index e33891b..e0d2acd 100644 --- a/client/src/pages/home/index.tsx +++ b/client/src/pages/home/index.tsx @@ -60,20 +60,14 @@ export const Home: React.FC = () => { ).length; }, [spoolsResult?.data]); - // For accurate low stock count, we need all spools + // For accurate low stock count, we need all spools with remaining_weight < threshold const { result: lowStockResult } = useList({ resource: "spool", pagination: { pageSize: 1000 }, - filters: [ - { - field: "remaining_weight", - operator: "lt", - value: LOW_STOCK_THRESHOLD, - }, - ], meta: { queryParams: { allow_archived: false, + remaining_weight_lt: LOW_STOCK_THRESHOLD, }, }, }); diff --git a/spoolman/api/v1/spool.py b/spoolman/api/v1/spool.py index aa91283..cae8bba 100644 --- a/spoolman/api/v1/spool.py +++ b/spoolman/api/v1/spool.py @@ -268,6 +268,20 @@ async def find( Optional[bool], Query(title="Needs Weighing", description="Filter by spools that need manual weigh-in."), ] = None, + remaining_weight_lt: Annotated[ + Optional[float], + Query( + title="Remaining Weight Less Than", + description="Filter by spools with remaining weight (in grams) less than this value.", + ), + ] = None, + remaining_weight_gt: Annotated[ + Optional[float], + Query( + title="Remaining Weight Greater Than", + description="Filter by spools with remaining weight (in grams) greater than this value.", + ), + ] = None, sort: Annotated[ Optional[str], Query( @@ -313,6 +327,8 @@ async def find( lot_nr=lot_nr, allow_archived=allow_archived, needs_weighing=needs_weighing, + remaining_weight_lt=remaining_weight_lt, + remaining_weight_gt=remaining_weight_gt, sort_by=sort_by, limit=limit, offset=offset, diff --git a/spoolman/database/spool.py b/spoolman/database/spool.py index 744c9d5..d975ca4 100644 --- a/spoolman/database/spool.py +++ b/spoolman/database/spool.py @@ -124,6 +124,8 @@ async def find( # noqa: C901, PLR0912 lot_nr: Optional[str] = None, allow_archived: bool = False, needs_weighing: Optional[bool] = None, + remaining_weight_lt: Optional[float] = None, + remaining_weight_gt: Optional[float] = None, sort_by: Optional[dict[str, SortOrder]] = None, limit: Optional[int] = None, offset: int = 0, @@ -170,6 +172,13 @@ async def find( # noqa: C901, PLR0912 ), ) + # Filter by remaining_weight (computed as initial_weight - used_weight, falling back to filament.weight) + remaining_weight_expr = coalesce(models.Spool.initial_weight, models.Filament.weight) - models.Spool.used_weight + if remaining_weight_lt is not None: + stmt = stmt.where(remaining_weight_expr < remaining_weight_lt) + if remaining_weight_gt is not None: + stmt = stmt.where(remaining_weight_expr > remaining_weight_gt) + total_count = None if limit is not None: