Kaynağa Gözat

Refactor cost calculation logic to utilize pre-fetched spool usage costs and remove redundant imports; update InventoryPage to adjust cost display format

Matteo Parenti 6 ay önce
ebeveyn
işleme
071c2c702a

+ 11 - 10
backend/app/api/routes/archives.py

@@ -18,6 +18,7 @@ from backend.app.core.database import get_db
 from backend.app.core.permissions import Permission
 from backend.app.core.permissions import Permission
 from backend.app.models.archive import PrintArchive
 from backend.app.models.archive import PrintArchive
 from backend.app.models.filament import Filament
 from backend.app.models.filament import Filament
+from backend.app.models.spool_usage_history import SpoolUsageHistory
 from backend.app.models.user import User
 from backend.app.models.user import User
 from backend.app.schemas.archive import ArchiveResponse, ArchiveStats, ArchiveUpdate, ReprintRequest
 from backend.app.schemas.archive import ArchiveResponse, ArchiveStats, ArchiveUpdate, ReprintRequest
 from backend.app.services.archive import ArchiveService
 from backend.app.services.archive import ArchiveService
@@ -857,9 +858,8 @@ async def rescan_archive(
         archive.designer = metadata["designer"]
         archive.designer = metadata["designer"]
 
 
     # Calculate cost: prefer spool-based cost if available, else catalog-based
     # Calculate cost: prefer spool-based cost if available, else catalog-based
-    if archive.filament_used_grams and archive.filament_type:
-        from backend.app.models.spool_usage_history import SpoolUsageHistory
 
 
+    if archive.filament_used_grams and archive.filament_type:
         usage_result = await db.execute(
         usage_result = await db.execute(
             select(func.sum(SpoolUsageHistory.cost)).where(SpoolUsageHistory.print_name == archive.print_name)
             select(func.sum(SpoolUsageHistory.cost)).where(SpoolUsageHistory.print_name == archive.print_name)
         )
         )
@@ -889,6 +889,7 @@ async def recalculate_all_costs(
     _: User | None = RequirePermissionIfAuthEnabled(Permission.ARCHIVES_UPDATE_ALL),
     _: User | None = RequirePermissionIfAuthEnabled(Permission.ARCHIVES_UPDATE_ALL),
 ):
 ):
     """Recalculate costs for all archives based on filament usage and prices."""
     """Recalculate costs for all archives based on filament usage and prices."""
+
     from backend.app.api.routes.settings import get_setting
     from backend.app.api.routes.settings import get_setting
 
 
     result = await db.execute(select(PrintArchive))
     result = await db.execute(select(PrintArchive))
@@ -902,17 +903,17 @@ async def recalculate_all_costs(
     default_cost_setting = await get_setting(db, "default_filament_cost")
     default_cost_setting = await get_setting(db, "default_filament_cost")
     default_cost_per_kg = float(default_cost_setting) if default_cost_setting else 25.0
     default_cost_per_kg = float(default_cost_setting) if default_cost_setting else 25.0
 
 
-    # Import SpoolUsageHistory for cost lookup
-    from backend.app.models.spool_usage_history import SpoolUsageHistory
+    # Pre-fetch all usage costs in one query
+    usage_costs_result = await db.execute(
+        select(SpoolUsageHistory.print_name, func.sum(SpoolUsageHistory.cost)).group_by(SpoolUsageHistory.print_name)
+    )
+    usage_costs = usage_costs_result.fetchall()
+    cost_map = {row[0]: row[1] for row in usage_costs if row[1] is not None and row[1] > 0}
 
 
     updated = 0
     updated = 0
     for archive in archives:
     for archive in archives:
-        # Prefer sum of spool_usage_history.cost for this archive's print_name
-        usage_result = await db.execute(
-            select(func.sum(SpoolUsageHistory.cost)).where(SpoolUsageHistory.print_name == archive.print_name)
-        )
-        usage_cost = usage_result.scalar()
-        if usage_cost is not None and usage_cost > 0:
+        usage_cost = cost_map.get(archive.print_name)
+        if usage_cost is not None:
             new_cost = round(usage_cost, 2)
             new_cost = round(usage_cost, 2)
         elif archive.filament_used_grams and archive.filament_type:
         elif archive.filament_used_grams and archive.filament_type:
             primary_type = archive.filament_type.split(",")[0].strip()
             primary_type = archive.filament_type.split(",")[0].strip()

+ 0 - 4
backend/tests/integration/test_cost_statistics.py

@@ -239,10 +239,6 @@ class TestSpoolCostPersistence:
         assert result["cost_per_kg"] is None
         assert result["cost_per_kg"] is None
 
 
 
 
-class TestSpoolUsageHistoryCost:
-    """Tests for cost field in SpoolUsageHistory."""
-
-
 class TestCostCalculationScenarios:
 class TestCostCalculationScenarios:
     """End-to-end tests for various cost calculation scenarios."""
     """End-to-end tests for various cost calculation scenarios."""
 
 

+ 1 - 1
frontend/src/pages/InventoryPage.tsx

@@ -257,7 +257,7 @@ const columnCells: Record<string, (ctx: CellCtx) => ReactNode> = {
   ),
   ),
   cost_per_kg: ({ spool, currencySymbol }) => (
   cost_per_kg: ({ spool, currencySymbol }) => (
     <span className="text-sm text-bambu-gray">
     <span className="text-sm text-bambu-gray">
-      {spool.cost_per_kg != null ? `${currencySymbol} ${spool.cost_per_kg.toFixed(2)}` : '-'}
+      {spool.cost_per_kg != null ? `${currencySymbol}${spool.cost_per_kg.toFixed(2)}` : '-'}
     </span>
     </span>
   ),
   ),
 };
 };