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

This commit is contained in:
Matteo Parenti
2026-02-19 18:22:07 +01:00
parent 380edfc249
commit 071c2c702a
3 changed files with 12 additions and 15 deletions
+11 -10
View File
@@ -18,6 +18,7 @@ from backend.app.core.database import get_db
from backend.app.core.permissions import Permission
from backend.app.models.archive import PrintArchive
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.schemas.archive import ArchiveResponse, ArchiveStats, ArchiveUpdate, ReprintRequest
from backend.app.services.archive import ArchiveService
@@ -857,9 +858,8 @@ async def rescan_archive(
archive.designer = metadata["designer"]
# 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(
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),
):
"""Recalculate costs for all archives based on filament usage and prices."""
from backend.app.api.routes.settings import get_setting
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_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
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)
elif archive.filament_used_grams and archive.filament_type:
primary_type = archive.filament_type.split(",")[0].strip()
@@ -239,10 +239,6 @@ class TestSpoolCostPersistence:
assert result["cost_per_kg"] is None
class TestSpoolUsageHistoryCost:
"""Tests for cost field in SpoolUsageHistory."""
class TestCostCalculationScenarios:
"""End-to-end tests for various cost calculation scenarios."""
+1 -1
View File
@@ -257,7 +257,7 @@ const columnCells: Record<string, (ctx: CellCtx) => ReactNode> = {
),
cost_per_kg: ({ spool, currencySymbol }) => (
<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>
),
};