Parcourir la source

refactor(inventory): rename /reset-usage to /reset-consumed-counter to match what it actually does (issue #1644)

  The old endpoint name implied that calling it would drop weight_used to
  0. In practice it only stamps weight_used_baseline = weight_used so the
  Inventory page's "Total Consumed" widget (weight_used - baseline) reads
  0 going forward, while remaining (label_weight - weight_used) is
  preserved. Calling the endpoint via curl and seeing weight_used
  unchanged in the JSON response is confusing.

  New paths:
  - internal: /api/v1/inventory/spools/{id}/reset-consumed-counter
             /api/v1/inventory/spools/reset-consumed-counter-bulk
  - spoolman: /api/v1/spoolman/inventory/spools/{id}/reset-consumed-counter
             /api/v1/spoolman/inventory/spools/reset-consumed-counter-bulk

  Behaviour is unchanged in both modes; internal stamps the baseline
  directly, Spoolman-mode PATCHes upstream used_weight=0 and the
  _map_spoolman_spool read mapping reconstructs the same "displayed
  consumed = 0, remaining unchanged" Bambuddy-visible shape. Parity
  between modes was already in place and is preserved.

  The Spoolman-client method reset_spool_usage keeps its name because it
  describes what is sent upstream to Spoolman, not what Bambuddy's
  endpoint promises to callers.

  Frontend:
  - api.resetSpoolUsage / bulkResetSpoolUsage (and Spoolman variants)
    renamed to resetSpoolConsumedCounter / bulkResetSpoolConsumedCounter.
  - Button labels: "Reset usage to 0" -> "Reset counter" / "Reset all
    counters" (short, unambiguous); tooltips and confirm-modal bodies
    still spell out the full semantics.
maziggy il y a 3 mois
Parent
commit
9554ebd05d

Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
CHANGELOG.md


+ 10 - 4
backend/app/api/routes/inventory.py

@@ -1071,8 +1071,8 @@ async def restore_spool(
     return result.scalar_one()
 
 
-@router.post("/spools/{spool_id}/reset-usage", response_model=SpoolResponse)
-async def reset_spool_usage(
+@router.post("/spools/{spool_id}/reset-consumed-counter", response_model=SpoolResponse)
+async def reset_spool_consumed_counter(
     spool_id: int,
     db: AsyncSession = Depends(get_db),
     _: User | None = RequirePermissionIfAuthEnabled(Permission.INVENTORY_UPDATE),
@@ -1084,6 +1084,12 @@ async def reset_spool_usage(
     weight_used` (remaining) is unchanged. weight_locked is also left
     alone — the spool keeps receiving AMS auto-sync updates. Matches
     Spoolman's split between used_weight and remaining_weight (#1390).
+
+    The earlier name `/reset-usage` was misleading: callers reasonably
+    expected `weight_used` itself to drop to 0 and were surprised when
+    the response showed it unchanged. The current name describes what
+    the endpoint actually does — reset the "Total Consumed" counter
+    widget, not the lifetime weight_used field.
     """
     result = await db.execute(select(Spool).where(Spool.id == spool_id))
     spool = result.scalar_one_or_none()
@@ -1097,8 +1103,8 @@ async def reset_spool_usage(
     return result.scalar_one()
 
 
-@router.post("/spools/reset-usage-bulk")
-async def bulk_reset_spool_usage(
+@router.post("/spools/reset-consumed-counter-bulk")
+async def bulk_reset_spool_consumed_counter(
     payload: dict,
     db: AsyncSession = Depends(get_db),
     _: User | None = RequirePermissionIfAuthEnabled(Permission.INVENTORY_UPDATE),

+ 17 - 7
backend/app/api/routes/spoolman_inventory.py

@@ -865,13 +865,23 @@ async def restore_spool(
         raise HTTPException(status_code=502, detail="Spoolman returned malformed spool data") from exc
 
 
-@router.post("/spools/{spool_id}/reset-usage")
-async def reset_spool_usage(
+@router.post("/spools/{spool_id}/reset-consumed-counter")
+async def reset_spool_consumed_counter(
     spool_id: int = Path(..., gt=0),
     db: AsyncSession = Depends(get_db),
     _: User | None = RequirePermissionIfAuthEnabled(Permission.INVENTORY_UPDATE),
 ) -> dict:
-    """Zero the spool's used_weight in Spoolman without touching anything else."""
+    """Zero the displayed "Total Consumed" counter for a Spoolman spool.
+
+    Spoolman doesn't have a native "baseline" field, so the implementation
+    reaches for the closest equivalent: PATCH `used_weight=0` upstream.
+    The read mapping in ``_map_spoolman_spool`` then derives Bambuddy's
+    `weight_used = label - remaining_weight` and `baseline = weight_used -
+    real_used_weight`, so the Inventory page's `weight_used - baseline`
+    display lands at 0 while remaining (= label - weight_used) is preserved
+    — parity with the internal-mode endpoint (#1390, see also
+    ``backend/app/api/routes/inventory.py::reset_spool_consumed_counter``).
+    """
     client = await _get_client(db)
     async with _translate_spoolman_errors():
         spool = await client.reset_spool_usage(spool_id)
@@ -882,13 +892,13 @@ async def reset_spool_usage(
         raise HTTPException(status_code=502, detail="Spoolman returned malformed spool data") from exc
 
 
-@router.post("/spools/reset-usage-bulk")
-async def bulk_reset_spool_usage(
+@router.post("/spools/reset-consumed-counter-bulk")
+async def bulk_reset_spool_consumed_counter(
     payload: dict = Body(...),
     db: AsyncSession = Depends(get_db),
     _: User | None = RequirePermissionIfAuthEnabled(Permission.INVENTORY_UPDATE),
 ) -> dict:
-    """Bulk-reset used_weight to 0 across the given Spoolman spool IDs.
+    """Bulk reset the "Total Consumed" counter across the given Spoolman spool IDs.
 
     Caller passes an explicit list of IDs — no "reset all" shortcut, since
     a typo on a wildcard would wipe the entire inventory's tracking.
@@ -909,7 +919,7 @@ async def bulk_reset_spool_usage(
                 await client.reset_spool_usage(spool_id)
             reset_count += 1
         except HTTPException as exc:
-            logger.warning("Spoolman reset-usage failed for spool %s: %s", spool_id, exc.detail)
+            logger.warning("Spoolman reset-consumed-counter failed for spool %s: %s", spool_id, exc.detail)
     return {"reset": reset_count}
 
 

+ 15 - 10
backend/tests/integration/test_spool_reset_usage.py

@@ -1,4 +1,9 @@
-"""Reset-usage endpoint regressions (#1390 follow-up).
+"""Reset-consumed-counter endpoint regressions (#1390 follow-up).
+
+Endpoint paths renamed from ``/reset-usage`` to ``/reset-consumed-counter``
+to match what the endpoint actually does (the previous name implied
+``weight_used`` itself would drop to 0, which surprised callers reading
+the JSON response — see the discussion that drove this rename).
 
 The per-spool and bulk reset endpoints stamp `weight_used_baseline =
 weight_used` instead of zeroing `weight_used` directly. This decouples
@@ -60,7 +65,7 @@ class TestResetSpoolUsage:
         """
         spool = await spool_factory(label_weight=1000, weight_used=456.0)
 
-        response = await async_client.post(f"/api/v1/inventory/spools/{spool.id}/reset-usage")
+        response = await async_client.post(f"/api/v1/inventory/spools/{spool.id}/reset-consumed-counter")
 
         assert response.status_code == 200
         body = response.json()
@@ -86,7 +91,7 @@ class TestResetSpoolUsage:
         """
         spool = await spool_factory(weight_used=100.0, weight_locked=False)
 
-        response = await async_client.post(f"/api/v1/inventory/spools/{spool.id}/reset-usage")
+        response = await async_client.post(f"/api/v1/inventory/spools/{spool.id}/reset-consumed-counter")
 
         assert response.status_code == 200
         await db_session.refresh(spool)
@@ -100,7 +105,7 @@ class TestResetSpoolUsage:
         """If the user previously locked the spool, the lock is preserved."""
         spool = await spool_factory(weight_used=500.0, weight_locked=True)
 
-        response = await async_client.post(f"/api/v1/inventory/spools/{spool.id}/reset-usage")
+        response = await async_client.post(f"/api/v1/inventory/spools/{spool.id}/reset-consumed-counter")
 
         assert response.status_code == 200
         await db_session.refresh(spool)
@@ -117,7 +122,7 @@ class TestResetSpoolUsage:
         counter while remaining keeps decrementing normally.
         """
         spool = await spool_factory(label_weight=1000, weight_used=456.0)
-        await async_client.post(f"/api/v1/inventory/spools/{spool.id}/reset-usage")
+        await async_client.post(f"/api/v1/inventory/spools/{spool.id}/reset-consumed-counter")
 
         # Simulate a 50g print (usage_tracker increments weight_used).
         await db_session.refresh(spool)
@@ -133,7 +138,7 @@ class TestResetSpoolUsage:
     @pytest.mark.asyncio
     @pytest.mark.integration
     async def test_reset_404_for_missing_spool(self, async_client: AsyncClient):
-        response = await async_client.post("/api/v1/inventory/spools/99999/reset-usage")
+        response = await async_client.post("/api/v1/inventory/spools/99999/reset-consumed-counter")
         assert response.status_code == 404
 
 
@@ -149,7 +154,7 @@ class TestBulkResetSpoolUsage:
         untouched = await spool_factory(weight_used=300.0)
 
         response = await async_client.post(
-            "/api/v1/inventory/spools/reset-usage-bulk",
+            "/api/v1/inventory/spools/reset-consumed-counter-bulk",
             json={"spool_ids": [target1.id, target2.id]},
         )
 
@@ -173,7 +178,7 @@ class TestBulkResetSpoolUsage:
     async def test_bulk_reset_rejects_empty_list(self, async_client: AsyncClient):
         """Empty list must be rejected — guards against accidental wildcard wipes."""
         response = await async_client.post(
-            "/api/v1/inventory/spools/reset-usage-bulk",
+            "/api/v1/inventory/spools/reset-consumed-counter-bulk",
             json={"spool_ids": []},
         )
         assert response.status_code == 400
@@ -183,7 +188,7 @@ class TestBulkResetSpoolUsage:
     async def test_bulk_reset_rejects_missing_field(self, async_client: AsyncClient):
         """Missing spool_ids field must be rejected."""
         response = await async_client.post(
-            "/api/v1/inventory/spools/reset-usage-bulk",
+            "/api/v1/inventory/spools/reset-consumed-counter-bulk",
             json={},
         )
         assert response.status_code == 400
@@ -196,7 +201,7 @@ class TestBulkResetSpoolUsage:
         locked = await spool_factory(weight_used=200.0, weight_locked=True)
 
         response = await async_client.post(
-            "/api/v1/inventory/spools/reset-usage-bulk",
+            "/api/v1/inventory/spools/reset-consumed-counter-bulk",
             json={"spool_ids": [unlocked.id, locked.id]},
         )
 

+ 7 - 7
backend/tests/integration/test_spoolman_inventory_api.py

@@ -560,13 +560,13 @@ class TestSpoolmanInventoryCRUD:
 
     @pytest.mark.asyncio
     @pytest.mark.integration
-    async def test_reset_spool_usage(
+    async def test_reset_spool_consumed_counter(
         self,
         async_client: AsyncClient,
         spoolman_settings,
         mock_spoolman_client,
     ):
-        """POST /spoolman/inventory/spools/{id}/reset-usage zeroes used_weight in Spoolman.
+        """POST /spoolman/inventory/spools/{id}/reset-consumed-counter zeroes the displayed counter.
 
         Parity with internal mode (#1390): the InventorySpool response
         carries `weight_used = label - remaining` and
@@ -575,7 +575,7 @@ class TestSpoolmanInventoryCRUD:
         while remaining (= label - weight_used) preserves Spoolman's
         independent remaining_weight field.
         """
-        response = await async_client.post("/api/v1/spoolman/inventory/spools/42/reset-usage")
+        response = await async_client.post("/api/v1/spoolman/inventory/spools/42/reset-consumed-counter")
 
         assert response.status_code == 200
         body = response.json()
@@ -588,15 +588,15 @@ class TestSpoolmanInventoryCRUD:
 
     @pytest.mark.asyncio
     @pytest.mark.integration
-    async def test_bulk_reset_spool_usage(
+    async def test_bulk_reset_spool_consumed_counter(
         self,
         async_client: AsyncClient,
         spoolman_settings,
         mock_spoolman_client,
     ):
-        """Bulk endpoint resets each listed spool and returns the count."""
+        """Bulk endpoint resets each listed spool's counter and returns the count."""
         response = await async_client.post(
-            "/api/v1/spoolman/inventory/spools/reset-usage-bulk",
+            "/api/v1/spoolman/inventory/spools/reset-consumed-counter-bulk",
             json={"spool_ids": [1, 2, 3]},
         )
 
@@ -614,7 +614,7 @@ class TestSpoolmanInventoryCRUD:
     ):
         """Empty list must be rejected — guards against accidental wildcard wipes."""
         response = await async_client.post(
-            "/api/v1/spoolman/inventory/spools/reset-usage-bulk",
+            "/api/v1/spoolman/inventory/spools/reset-consumed-counter-bulk",
             json={"spool_ids": []},
         )
 

+ 4 - 4
frontend/src/__tests__/pages/InventoryPageArchivedConsumed.test.tsx

@@ -179,7 +179,7 @@ describe('InventoryPage — Total Consumed includes archived (#1390 follow-up)',
     });
   });
 
-  it('Reset-usage eraser is rendered for archived spools too', async () => {
+  it('Reset-consumed-counter eraser is rendered for archived spools too', async () => {
     // The per-card eraser is gated on weight_used > 0, NOT on archived_at,
     // so the archived spool above (weight_used=500) must render an eraser
     // button matching the localized tooltip. Multiple erasers exist on the
@@ -191,19 +191,19 @@ describe('InventoryPage — Total Consumed includes archived (#1390 follow-up)',
     // surface archived spools first; the easiest assertion that doesn't
     // depend on chip clicks is via the bulk-reset wiring: when archived
     // are included in the resetable set, the total is non-zero — i.e.
-    // the "Reset all usage" button stays visible. The CHANGELOG entry
+    // the "Reset all counters" button stays visible. The CHANGELOG entry
     // walks through the per-card flow.
     render(<InventoryPageRouter />);
 
     await waitFor(() => {
-      // Reset-all-usage button is gated on `totalConsumed > 0 &&
+      // Reset-all-consumed-counters button is gated on `totalConsumed > 0 &&
       // resetableSpoolIds.length > 0`. resetableSpoolIds now includes
       // archived spools — so even if every active spool had its baseline
       // == weight_used (consumed = 0), the button must remain visible
       // as long as ANY spool (archived included) still has unreset usage.
       // The 800g assertion already proves totalConsumed > 0; here we
       // just check the bulk-reset button rendered.
-      expect(screen.getByRole('button', { name: /reset all spool usage/i })).toBeInTheDocument();
+      expect(screen.getByRole('button', { name: /reset all counters/i })).toBeInTheDocument();
     });
   });
 });

+ 8 - 8
frontend/src/api/client.ts

@@ -4883,10 +4883,10 @@ export const api = {
     request<InventorySpool>(`/inventory/spools/${id}/archive`, { method: 'POST' }),
   restoreSpool: (id: number) =>
     request<InventorySpool>(`/inventory/spools/${id}/restore`, { method: 'POST' }),
-  resetSpoolUsage: (id: number) =>
-    request<InventorySpool>(`/inventory/spools/${id}/reset-usage`, { method: 'POST' }),
-  bulkResetSpoolUsage: (spoolIds: number[]) =>
-    request<{ reset: number }>(`/inventory/spools/reset-usage-bulk`, {
+  resetSpoolConsumedCounter: (id: number) =>
+    request<InventorySpool>(`/inventory/spools/${id}/reset-consumed-counter`, { method: 'POST' }),
+  bulkResetSpoolConsumedCounter: (spoolIds: number[]) =>
+    request<{ reset: number }>(`/inventory/spools/reset-consumed-counter-bulk`, {
       method: 'POST',
       body: JSON.stringify({ spool_ids: spoolIds }),
     }),
@@ -5054,10 +5054,10 @@ export const api = {
     request<InventorySpool>(`/spoolman/inventory/spools/${id}/archive`, { method: 'POST' }),
   restoreSpoolmanInventorySpool: (id: number) =>
     request<InventorySpool>(`/spoolman/inventory/spools/${id}/restore`, { method: 'POST' }),
-  resetSpoolmanInventorySpoolUsage: (id: number) =>
-    request<InventorySpool>(`/spoolman/inventory/spools/${id}/reset-usage`, { method: 'POST' }),
-  bulkResetSpoolmanInventorySpoolUsage: (spoolIds: number[]) =>
-    request<{ reset: number }>(`/spoolman/inventory/spools/reset-usage-bulk`, {
+  resetSpoolmanInventorySpoolConsumedCounter: (id: number) =>
+    request<InventorySpool>(`/spoolman/inventory/spools/${id}/reset-consumed-counter`, { method: 'POST' }),
+  bulkResetSpoolmanInventorySpoolConsumedCounter: (spoolIds: number[]) =>
+    request<{ reset: number }>(`/spoolman/inventory/spools/reset-consumed-counter-bulk`, {
       method: 'POST',
       body: JSON.stringify({ spool_ids: spoolIds }),
     }),

+ 9 - 9
frontend/src/i18n/locales/de.ts

@@ -3829,15 +3829,15 @@ export default {
     inPrinter: 'Im Drucker',
     lowStock: 'Niedriger Bestand',
     sinceTracking: 'Seit Beginn der Erfassung',
-    resetUsage: 'Verbrauch auf 0 zurücksetzen',
-    resetUsageTooltip: 'Den verbrauchten Gramm-Zähler dieser Spule auf null setzen',
-    resetUsageConfirm: 'Verbrauchten Gramm-Zähler dieser Spule auf 0 zurücksetzen? Künftige Drucke zählen wieder ab null. Die Spule selbst, ihre Restgewichtsberechnung und Ihre Einstellungen bleiben unverändert.',
-    resetAllUsage: 'Verbrauch aller Spulen zurücksetzen',
-    resetAllUsageTooltip: 'Den verbrauchten Gramm-Zähler auf jeder Spule auf null setzen',
-    resetAllUsageConfirm: 'Verbrauchten Gramm-Zähler auf allen {{count}} Spulen (archivierte eingeschlossen) auf 0 zurücksetzen? Das löscht den Wert „Insgesamt verbraucht“, sodass künftige Drucke wieder ab null gezählt werden. Spulen und Restgewichte bleiben unverändert.',
-    usageReset: 'Spulenverbrauch auf 0 zurückgesetzt',
-    allUsageReset: '{{count}} Spule(n) zurückgesetzt',
-    resetUsageFailed: 'Zurücksetzen des Spulenverbrauchs fehlgeschlagen',
+    resetConsumedCounter: 'Zähler zurücksetzen',
+    resetConsumedCounterTooltip: 'Den verbrauchten Gramm-Zähler dieser Spule auf null setzen. Das Restgewicht bleibt unverändert.',
+    resetConsumedCounterConfirm: 'Verbrauchten Gramm-Zähler dieser Spule auf 0 zurücksetzen? Künftige Drucke zählen wieder ab null. Die Spule selbst, ihre Restgewichtsberechnung und Ihre Einstellungen bleiben unverändert.',
+    resetAllConsumedCounters: 'Alle Zähler zurücksetzen',
+    resetAllConsumedCountersTooltip: 'Den verbrauchten Gramm-Zähler auf jeder Spule auf null setzen. Die Restgewichte bleiben unverändert.',
+    resetAllConsumedCountersConfirm: 'Verbrauchten Gramm-Zähler auf allen {{count}} Spulen (archivierte eingeschlossen) auf 0 zurücksetzen? Das löscht den Wert „Insgesamt verbraucht“, sodass künftige Drucke wieder ab null gezählt werden. Spulen und Restgewichte bleiben unverändert.',
+    consumedCounterReset: 'Zähler zurückgesetzt',
+    allConsumedCountersReset: 'Zähler für {{count}} Spule(n) zurückgesetzt',
+    resetConsumedCounterFailed: 'Zähler konnte nicht zurückgesetzt werden',
     loadedInAms: 'Im AMS/Ext geladen',
     remaining: 'Verbleibend',
     weightCheck: 'Gewichtskontrolle',

+ 9 - 9
frontend/src/i18n/locales/en.ts

@@ -3841,15 +3841,15 @@ export default {
     inPrinter: 'In Printer',
     lowStock: 'Low Stock',
     sinceTracking: 'Since tracking started',
-    resetUsage: 'Reset usage to 0',
-    resetUsageTooltip: 'Zero the consumed-grams counter for this spool',
-    resetUsageConfirm: 'Reset this spool\'s consumed-grams counter to 0? Future prints will track from zero again. The spool itself, its remaining weight calculation, and your settings are not changed.',
-    resetAllUsage: 'Reset all spool usage',
-    resetAllUsageTooltip: 'Zero the consumed-grams counter on every spool',
-    resetAllUsageConfirm: 'Reset the consumed-grams counter to 0 on all {{count}} spools (archived ones included)? This clears the "Total Consumed" stat so future prints track from zero. Spools and remaining weights are not changed.',
-    usageReset: 'Spool usage reset to 0',
-    allUsageReset: 'Reset {{count}} spool(s)',
-    resetUsageFailed: 'Failed to reset spool usage',
+    resetConsumedCounter: 'Reset counter',
+    resetConsumedCounterTooltip: 'Zero the consumed-grams counter for this spool. Remaining weight is not changed.',
+    resetConsumedCounterConfirm: 'Reset this spool\'s consumed-grams counter to 0? Future prints will track from zero again. The spool itself, its remaining weight calculation, and your settings are not changed.',
+    resetAllConsumedCounters: 'Reset all counters',
+    resetAllConsumedCountersTooltip: 'Zero the consumed-grams counter on every spool. Remaining weights are not changed.',
+    resetAllConsumedCountersConfirm: 'Reset the consumed-grams counter to 0 on all {{count}} spools (archived ones included)? This clears the "Total Consumed" stat so future prints track from zero. Spools and remaining weights are not changed.',
+    consumedCounterReset: 'Counter reset',
+    allConsumedCountersReset: 'Counter reset for {{count}} spool(s)',
+    resetConsumedCounterFailed: 'Failed to reset counter',
     loadedInAms: 'Loaded in AMS/Ext',
     remaining: 'Remaining',
     weightCheck: 'Weight Check',

+ 9 - 9
frontend/src/i18n/locales/es.ts

@@ -3837,15 +3837,15 @@ export default {
     inPrinter: 'En la impresora',
     lowStock: 'Existencias bajas',
     sinceTracking: 'Desde que comenzó el seguimiento',
-    resetUsage: 'Restablecer el uso a 0',
-    resetUsageTooltip: 'Poner a cero el contador de gramos consumidos de esta bobina',
-    resetUsageConfirm: '¿Restablecer a 0 el contador de gramos consumidos de esta bobina? Las impresiones futuras volverán a contar desde cero. La bobina en sí, su cálculo de peso restante y sus ajustes no cambian.',
-    resetAllUsage: 'Restablecer el uso de todas las bobinas',
-    resetAllUsageTooltip: 'Poner a cero el contador de gramos consumidos de todas las bobinas',
-    resetAllUsageConfirm: '¿Restablecer a 0 el contador de gramos consumidos de las {{count}} bobinas (incluidas las archivadas)? Esto borra la estadística «Total consumido» para que las impresiones futuras cuenten desde cero. Las bobinas y los pesos restantes no cambian.',
-    usageReset: 'Uso de la bobina restablecido a 0',
-    allUsageReset: 'Se restablecieron {{count}} bobina(s)',
-    resetUsageFailed: 'Error al restablecer el uso de la bobina',
+    resetConsumedCounter: 'Restablecer contador',
+    resetConsumedCounterTooltip: 'Poner a cero el contador de gramos consumidos de esta bobina. El peso restante no cambia.',
+    resetConsumedCounterConfirm: '¿Restablecer a 0 el contador de gramos consumidos de esta bobina? Las impresiones futuras volverán a contar desde cero. La bobina en sí, su cálculo de peso restante y sus ajustes no cambian.',
+    resetAllConsumedCounters: 'Restablecer todos los contadores',
+    resetAllConsumedCountersTooltip: 'Poner a cero el contador de gramos consumidos de todas las bobinas. Los pesos restantes no cambian.',
+    resetAllConsumedCountersConfirm: '¿Restablecer a 0 el contador de gramos consumidos de las {{count}} bobinas (incluidas las archivadas)? Esto borra la estadística «Total consumido» para que las impresiones futuras cuenten desde cero. Las bobinas y los pesos restantes no cambian.',
+    consumedCounterReset: 'Contador restablecido',
+    allConsumedCountersReset: 'Contador restablecido para {{count}} bobina(s)',
+    resetConsumedCounterFailed: 'Error al restablecer el contador',
     loadedInAms: 'Cargada en AMS/ext.',
     remaining: 'Restante',
     weightCheck: 'Comprobación de peso',

+ 9 - 9
frontend/src/i18n/locales/fr.ts

@@ -3818,15 +3818,15 @@ export default {
     inPrinter: 'Dans Imprimante',
     lowStock: 'Stock Bas',
     sinceTracking: 'Depuis le début du suivi',
-    resetUsage: 'Réinitialiser l\'usage à 0',
-    resetUsageTooltip: 'Remettre à zéro le compteur de grammes consommés pour cette bobine',
-    resetUsageConfirm: 'Remettre à 0 le compteur de grammes consommés de cette bobine ? Les futures impressions repartiront de zéro. La bobine, son calcul de poids restant et vos paramètres ne sont pas modifiés.',
-    resetAllUsage: 'Réinitialiser l\'usage de toutes les bobines',
-    resetAllUsageTooltip: 'Remettre à zéro le compteur de grammes consommés sur chaque bobine',
-    resetAllUsageConfirm: 'Remettre à 0 le compteur de grammes consommés sur les {{count}} bobines (archivées incluses) ? Cela efface la valeur « Total Consommé » pour que les futures impressions repartent de zéro. Les bobines et les poids restants ne sont pas modifiés.',
-    usageReset: 'Usage de la bobine remis à 0',
-    allUsageReset: '{{count}} bobine(s) réinitialisée(s)',
-    resetUsageFailed: 'Échec de la réinitialisation de l\'usage',
+    resetConsumedCounter: 'Réinitialiser le compteur',
+    resetConsumedCounterTooltip: 'Remettre à zéro le compteur de grammes consommés pour cette bobine. Le poids restant n\'est pas modifié.',
+    resetConsumedCounterConfirm: 'Remettre à 0 le compteur de grammes consommés de cette bobine ? Les futures impressions repartiront de zéro. La bobine, son calcul de poids restant et vos paramètres ne sont pas modifiés.',
+    resetAllConsumedCounters: 'Réinitialiser tous les compteurs',
+    resetAllConsumedCountersTooltip: 'Remettre à zéro le compteur de grammes consommés sur chaque bobine. Les poids restants ne sont pas modifiés.',
+    resetAllConsumedCountersConfirm: 'Remettre à 0 le compteur de grammes consommés sur les {{count}} bobines (archivées incluses) ? Cela efface la valeur « Total Consommé » pour que les futures impressions repartent de zéro. Les bobines et les poids restants ne sont pas modifiés.',
+    consumedCounterReset: 'Compteur réinitialisé',
+    allConsumedCountersReset: 'Compteur réinitialisé pour {{count}} bobine(s)',
+    resetConsumedCounterFailed: 'Échec de la réinitialisation du compteur',
     loadedInAms: 'Chargé dans AMS/Ext',
     remaining: 'Restant',
     weightCheck: 'Vérification poids',

+ 9 - 9
frontend/src/i18n/locales/it.ts

@@ -3817,15 +3817,15 @@ export default {
     inPrinter: 'In stampante',
     lowStock: 'Scorta bassa',
     sinceTracking: 'Dall\'inizio del tracciamento',
-    resetUsage: 'Azzera utilizzo',
-    resetUsageTooltip: 'Azzera il contatore di grammi consumati per questa bobina',
-    resetUsageConfirm: 'Azzerare il contatore di grammi consumati di questa bobina? Le stampe future ripartiranno da zero. La bobina, il calcolo del peso rimanente e le impostazioni non vengono modificati.',
-    resetAllUsage: 'Azzera utilizzo di tutte le bobine',
-    resetAllUsageTooltip: 'Azzera il contatore di grammi consumati su ogni bobina',
-    resetAllUsageConfirm: 'Azzerare il contatore di grammi consumati su tutte le {{count}} bobine (incluse quelle archiviate)? La statistica "Totale Consumato" verrà azzerata e le stampe future ripartiranno da zero. Bobine e pesi rimanenti restano invariati.',
-    usageReset: 'Utilizzo della bobina azzerato',
-    allUsageReset: '{{count}} bobina/e azzerata/e',
-    resetUsageFailed: 'Impossibile azzerare l\'utilizzo della bobina',
+    resetConsumedCounter: 'Azzera contatore',
+    resetConsumedCounterTooltip: 'Azzera il contatore di grammi consumati per questa bobina. Il peso rimanente non viene modificato.',
+    resetConsumedCounterConfirm: 'Azzerare il contatore di grammi consumati di questa bobina? Le stampe future ripartiranno da zero. La bobina, il calcolo del peso rimanente e le impostazioni non vengono modificati.',
+    resetAllConsumedCounters: 'Azzera tutti i contatori',
+    resetAllConsumedCountersTooltip: 'Azzera il contatore di grammi consumati su ogni bobina. I pesi rimanenti non vengono modificati.',
+    resetAllConsumedCountersConfirm: 'Azzerare il contatore di grammi consumati su tutte le {{count}} bobine (incluse quelle archiviate)? La statistica "Totale Consumato" verrà azzerata e le stampe future ripartiranno da zero. Bobine e pesi rimanenti restano invariati.',
+    consumedCounterReset: 'Contatore azzerato',
+    allConsumedCountersReset: 'Contatore azzerato per {{count}} bobina/e',
+    resetConsumedCounterFailed: 'Impossibile azzerare il contatore',
     loadedInAms: 'Caricato in AMS/Est',
     remaining: 'Rimanente',
     weightCheck: 'Controllo Peso',

+ 9 - 9
frontend/src/i18n/locales/ja.ts

@@ -3829,15 +3829,15 @@ export default {
     inPrinter: 'プリンター内',
     lowStock: '残量少',
     sinceTracking: '追跡開始以降',
-    resetUsage: '使用量を0にリセット',
-    resetUsageTooltip: 'このスプールの消費量カウンタを0にする',
-    resetUsageConfirm: 'このスプールの消費量カウンタを0にリセットしますか?以降の印刷は再びゼロからカウントされます。スプール自体、残量計算、設定は変更されません。',
-    resetAllUsage: '全スプールの使用量をリセット',
-    resetAllUsageTooltip: 'すべてのスプールの消費量カウンタを0にする',
-    resetAllUsageConfirm: '{{count}}件すべてのスプール(アーカイブ済みを含む)の消費量カウンタを0にリセットしますか?「累計消費量」の値がクリアされ、以降の印刷はゼロからカウントされます。スプール自体と残量は変更されません。',
-    usageReset: 'スプールの使用量を0にリセットしました',
-    allUsageReset: '{{count}}件のスプールをリセットしました',
-    resetUsageFailed: 'スプールの使用量リセットに失敗しました',
+    resetConsumedCounter: 'カウンタをリセット',
+    resetConsumedCounterTooltip: 'このスプールの消費量カウンタを0にする。残量は変更されません。',
+    resetConsumedCounterConfirm: 'このスプールの消費量カウンタを0にリセットしますか?以降の印刷は再びゼロからカウントされます。スプール自体、残量計算、設定は変更されません。',
+    resetAllConsumedCounters: 'すべてのカウンタをリセット',
+    resetAllConsumedCountersTooltip: 'すべてのスプールの消費量カウンタを0にする。残量は変更されません。',
+    resetAllConsumedCountersConfirm: '{{count}}件すべてのスプール(アーカイブ済みを含む)の消費量カウンタを0にリセットしますか?「累計消費量」の値がクリアされ、以降の印刷はゼロからカウントされます。スプール自体と残量は変更されません。',
+    consumedCounterReset: 'カウンタをリセットしました',
+    allConsumedCountersReset: '{{count}}件のスプールのカウンタをリセットしました',
+    resetConsumedCounterFailed: 'カウンタのリセットに失敗しました',
     loadedInAms: 'AMS/Extに装填中',
     remaining: '残り',
     weightCheck: '重量チェック',

+ 9 - 9
frontend/src/i18n/locales/ko.ts

@@ -3679,15 +3679,15 @@ export default {
     spoolmanCatalogLoadFailed: 'Spoolman 필라멘트 카탈로그 불러오기 실패',
     subtitle: '스풀 관리',
     storageLocationNone: '위치 미설정',
-    resetUsage: '사용량을 0으로 초기화',
-    resetUsageTooltip: '이 스풀의 소비된 그램 카운터를 0으로 초기화합니다',
-    resetUsageConfirm: '이 스풀의 소비된 그램 카운터를 0으로 초기화하시겠습니까? 이후 인쇄는 처음부터 추적됩니다. 스풀 자체, 잔여 무게 계산 및 설정은 변경되지 않습니다.',
-    resetAllUsage: '모든 스풀 사용량 초기화',
-    resetAllUsageTooltip: '모든 스풀의 소비된 그램 카운터를 0으로 초기화합니다',
-    resetAllUsageConfirm: '{{count}}개 스풀(아카이브 포함)의 소비된 그램 카운터를 모두 0으로 초기화하시겠습니까? 이는 "총 소비량" 통계를 지워 이후 인쇄가 처음부터 추적되도록 합니다. 스풀과 잔여 무게는 변경되지 않습니다.',
-    usageReset: '스풀 사용량이 0으로 초기화되었습니다',
-    allUsageReset: '{{count}}개 스풀 초기화됨',
-    resetUsageFailed: '스풀 사용량 초기화 실패'
+    resetConsumedCounter: '카운터 초기화',
+    resetConsumedCounterTooltip: '이 스풀의 소비된 그램 카운터를 0으로 초기화합니다. 잔여 무게는 변경되지 않습니다.',
+    resetConsumedCounterConfirm: '이 스풀의 소비된 그램 카운터를 0으로 초기화하시겠습니까? 이후 인쇄는 처음부터 추적됩니다. 스풀 자체, 잔여 무게 계산 및 설정은 변경되지 않습니다.',
+    resetAllConsumedCounters: '모든 카운터 초기화',
+    resetAllConsumedCountersTooltip: '모든 스풀의 소비된 그램 카운터를 0으로 초기화합니다. 잔여 무게는 변경되지 않습니다.',
+    resetAllConsumedCountersConfirm: '{{count}}개 스풀(아카이브 포함)의 소비된 그램 카운터를 모두 0으로 초기화하시겠습니까? 이는 "총 소비량" 통계를 지워 이후 인쇄가 처음부터 추적되도록 합니다. 스풀과 잔여 무게는 변경되지 않습니다.',
+    consumedCounterReset: '카운터가 초기화되었습니다',
+    allConsumedCountersReset: '{{count}}개 스풀의 카운터가 초기화되었습니다',
+    resetConsumedCounterFailed: '카운터 초기화 실패'
   },
   timelapse: {
     title: '타임랩스',

+ 9 - 9
frontend/src/i18n/locales/pt-BR.ts

@@ -3817,15 +3817,15 @@ export default {
     inPrinter: 'Na Impressora',
     lowStock: 'Estoque Baixo',
     sinceTracking: 'Desde o início do rastreamento',
-    resetUsage: 'Zerar uso',
-    resetUsageTooltip: 'Zerar o contador de gramas consumidas desta bobina',
-    resetUsageConfirm: 'Zerar o contador de gramas consumidas desta bobina? As impressões futuras voltarão a contar do zero. A bobina em si, o cálculo do peso restante e as configurações não são alterados.',
-    resetAllUsage: 'Zerar uso de todas as bobinas',
-    resetAllUsageTooltip: 'Zerar o contador de gramas consumidas em todas as bobinas',
-    resetAllUsageConfirm: 'Zerar o contador de gramas consumidas nas {{count}} bobinas (incluindo as arquivadas)? Isso limpa o "Total Consumido" para que as impressões futuras contem do zero. Bobinas e pesos restantes não são alterados.',
-    usageReset: 'Uso da bobina zerado',
-    allUsageReset: '{{count}} bobina(s) zerada(s)',
-    resetUsageFailed: 'Falha ao zerar o uso da bobina',
+    resetConsumedCounter: 'Zerar contador',
+    resetConsumedCounterTooltip: 'Zerar o contador de gramas consumidas desta bobina. O peso restante não é alterado.',
+    resetConsumedCounterConfirm: 'Zerar o contador de gramas consumidas desta bobina? As impressões futuras voltarão a contar do zero. A bobina em si, o cálculo do peso restante e as configurações não são alterados.',
+    resetAllConsumedCounters: 'Zerar todos os contadores',
+    resetAllConsumedCountersTooltip: 'Zerar o contador de gramas consumidas em todas as bobinas. Os pesos restantes não são alterados.',
+    resetAllConsumedCountersConfirm: 'Zerar o contador de gramas consumidas nas {{count}} bobinas (incluindo as arquivadas)? Isso limpa o "Total Consumido" para que as impressões futuras contem do zero. Bobinas e pesos restantes não são alterados.',
+    consumedCounterReset: 'Contador zerado',
+    allConsumedCountersReset: 'Contador zerado em {{count}} bobina(s)',
+    resetConsumedCounterFailed: 'Falha ao zerar o contador',
     loadedInAms: 'Carregado no AMS/Ext',
     remaining: 'Restante',
     weightCheck: 'Verificação de Peso',

+ 9 - 9
frontend/src/i18n/locales/tr.ts

@@ -3811,15 +3811,15 @@ export default {
     inPrinter: 'Yazıcıda',
     lowStock: 'Düşük Stok',
     sinceTracking: 'Takip başladığından beri',
-    resetUsage: 'Kullanımı 0\'a sıfırla',
-    resetUsageTooltip: 'Bu makara için tüketilen gram sayacını sıfırla',
-    resetUsageConfirm: 'Bu makaranın tüketilen gram sayacı 0\'a sıfırlansın mı? Gelecekteki baskılar sıfırdan tekrar takip edilecek. Makaranın kendisi, kalan ağırlık hesaplaması ve ayarlarınız değiştirilmez.',
-    resetAllUsage: 'Tüm makara kullanımını sıfırla',
-    resetAllUsageTooltip: 'Her makarada tüketilen gram sayacını sıfırla',
-    resetAllUsageConfirm: 'Tüm {{count}} makaradaki (arşivlenmişler dahil) tüketilen gram sayacı 0\'a sıfırlansın mı? Bu, "Toplam Tüketim" istatistiğini temizler, böylece gelecekteki baskılar sıfırdan takip edilir. Makaralar ve kalan ağırlıklar değiştirilmez.',
-    usageReset: 'Makara kullanımı 0\'a sıfırlandı',
-    allUsageReset: '{{count}} makara sıfırlandı',
-    resetUsageFailed: 'Makara kullanımı sıfırlanamadı',
+    resetConsumedCounter: 'Sayacı sıfırla',
+    resetConsumedCounterTooltip: 'Bu makara için tüketilen gram sayacını sıfırla. Kalan ağırlık değişmez.',
+    resetConsumedCounterConfirm: 'Bu makaranın tüketilen gram sayacı 0\'a sıfırlansın mı? Gelecekteki baskılar sıfırdan tekrar takip edilecek. Makaranın kendisi, kalan ağırlık hesaplaması ve ayarlarınız değiştirilmez.',
+    resetAllConsumedCounters: 'Tüm sayaçları sıfırla',
+    resetAllConsumedCountersTooltip: 'Her makarada tüketilen gram sayacını sıfırla. Kalan ağırlıklar değişmez.',
+    resetAllConsumedCountersConfirm: 'Tüm {{count}} makaradaki (arşivlenmişler dahil) tüketilen gram sayacı 0\'a sıfırlansın mı? Bu, "Toplam Tüketim" istatistiğini temizler, böylece gelecekteki baskılar sıfırdan takip edilir. Makaralar ve kalan ağırlıklar değiştirilmez.',
+    consumedCounterReset: 'Sayaç sıfırlandı',
+    allConsumedCountersReset: '{{count}} makaranın sayacı sıfırlandı',
+    resetConsumedCounterFailed: 'Sayaç sıfırlanamadı',
     loadedInAms: 'AMS/Ext\'te Yüklü',
     remaining: 'Kalan',
     weightCheck: 'Ağırlık Kontrolü',

+ 9 - 9
frontend/src/i18n/locales/zh-CN.ts

@@ -3823,15 +3823,15 @@ export default {
     inPrinter: '在打印机中',
     lowStock: '库存不足',
     sinceTracking: '自开始追踪',
-    resetUsage: '将用量重置为 0',
-    resetUsageTooltip: '将此料盘的已消耗克数计数器清零',
-    resetUsageConfirm: '将此料盘的已消耗克数计数器重置为 0?后续打印将从零开始计数。料盘本身、剩余重量计算和您的设置不会改变。',
-    resetAllUsage: '重置所有料盘的用量',
-    resetAllUsageTooltip: '将每个料盘的已消耗克数计数器清零',
-    resetAllUsageConfirm: '将全部 {{count}} 个料盘(含已归档)的已消耗克数计数器重置为 0?这将清空"累计消耗"统计值,后续打印从零开始计数。料盘和剩余重量不会改变。',
-    usageReset: '料盘用量已重置为 0',
-    allUsageReset: '已重置 {{count}} 个料盘',
-    resetUsageFailed: '重置料盘用量失败',
+    resetConsumedCounter: '重置计数器',
+    resetConsumedCounterTooltip: '将此料盘的已消耗克数计数器清零。剩余重量保持不变。',
+    resetConsumedCounterConfirm: '将此料盘的已消耗克数计数器重置为 0?后续打印将从零开始计数。料盘本身、剩余重量计算和您的设置不会改变。',
+    resetAllConsumedCounters: '重置所有计数器',
+    resetAllConsumedCountersTooltip: '将每个料盘的已消耗克数计数器清零。剩余重量保持不变。',
+    resetAllConsumedCountersConfirm: '将全部 {{count}} 个料盘(含已归档)的已消耗克数计数器重置为 0?这将清空"累计消耗"统计值,后续打印从零开始计数。料盘和剩余重量不会改变。',
+    consumedCounterReset: '计数器已重置',
+    allConsumedCountersReset: '已重置 {{count}} 个料盘的计数器',
+    resetConsumedCounterFailed: '重置计数器失败',
     loadedInAms: '已装载到 AMS/外置',
     remaining: '剩余',
     weightCheck: '重量检查',

+ 9 - 9
frontend/src/i18n/locales/zh-TW.ts

@@ -3823,15 +3823,15 @@ export default {
     inPrinter: '在印表機中',
     lowStock: '庫存不足',
     sinceTracking: '自開始追蹤',
-    resetUsage: '將用量重置為 0',
-    resetUsageTooltip: '將此料盤的已消耗克數計數器歸零',
-    resetUsageConfirm: '將此料盤的已消耗克數計數器重置為 0?後續列印將從零開始計算。料盤本身、剩餘重量計算與您的設定均不會變更。',
-    resetAllUsage: '重置所有料盤的用量',
-    resetAllUsageTooltip: '將每個料盤的已消耗克數計數器歸零',
-    resetAllUsageConfirm: '將全部 {{count}} 個料盤(含已封存)的已消耗克數計數器重置為 0?這將清空「累計消耗」統計值,後續列印從零開始計算。料盤與剩餘重量不會變更。',
-    usageReset: '料盤用量已重置為 0',
-    allUsageReset: '已重置 {{count}} 個料盤',
-    resetUsageFailed: '重置料盤用量失敗',
+    resetConsumedCounter: '重置計數器',
+    resetConsumedCounterTooltip: '將此料盤的已消耗克數計數器歸零。剩餘重量不會變更。',
+    resetConsumedCounterConfirm: '將此料盤的已消耗克數計數器重置為 0?後續列印將從零開始計算。料盤本身、剩餘重量計算與您的設定均不會變更。',
+    resetAllConsumedCounters: '重置所有計數器',
+    resetAllConsumedCountersTooltip: '將每個料盤的已消耗克數計數器歸零。剩餘重量不會變更。',
+    resetAllConsumedCountersConfirm: '將全部 {{count}} 個料盤(含已封存)的已消耗克數計數器重置為 0?這將清空「累計消耗」統計值,後續列印從零開始計算。料盤與剩餘重量不會變更。',
+    consumedCounterReset: '計數器已重置',
+    allConsumedCountersReset: '已重置 {{count}} 個料盤的計數器',
+    resetConsumedCounterFailed: '重置計數器失敗',
     loadedInAms: '已裝載到 AMS/外接',
     remaining: '剩餘',
     weightCheck: '重量檢查',

+ 35 - 31
frontend/src/pages/InventoryPage.tsx

@@ -467,8 +467,8 @@ function InventoryPage({ spoolmanMode = false, spoolmanModeReady = true }: { spo
   const [formModal, setFormModal] = useState<{ spool?: InventorySpool | null; mode: SpoolFormMode } | null>(null);
   const deepLinkHandled = useRef(false);
   const [confirmAction, setConfirmAction] = useState<
-    | { type: 'delete' | 'archive' | 'reset-usage'; spoolId: number }
-    | { type: 'reset-all-usage' }
+    | { type: 'delete' | 'archive' | 'reset-consumed-counter'; spoolId: number }
+    | { type: 'reset-all-consumed-counters' }
     | null
   >(null);
   // Label printing (#809). null = closed; otherwise the IDs to print labels for.
@@ -687,27 +687,31 @@ function InventoryPage({ spoolmanMode = false, spoolmanModeReady = true }: { spo
     },
   });
 
-  const resetUsageMutation = useMutation({
+  const resetConsumedCounterMutation = useMutation({
     mutationFn: (id: number) =>
-      spoolmanMode ? api.resetSpoolmanInventorySpoolUsage(id) : api.resetSpoolUsage(id),
+      spoolmanMode
+        ? api.resetSpoolmanInventorySpoolConsumedCounter(id)
+        : api.resetSpoolConsumedCounter(id),
     onSuccess: () => {
       queryClient.invalidateQueries({ queryKey: spoolsQueryKey });
-      showToast(t('inventory.usageReset'), 'success');
+      showToast(t('inventory.consumedCounterReset'), 'success');
     },
     onError: () => {
-      showToast(t('inventory.resetUsageFailed'), 'error');
+      showToast(t('inventory.resetConsumedCounterFailed'), 'error');
     },
   });
 
-  const bulkResetUsageMutation = useMutation({
+  const bulkResetConsumedCounterMutation = useMutation({
     mutationFn: (ids: number[]) =>
-      spoolmanMode ? api.bulkResetSpoolmanInventorySpoolUsage(ids) : api.bulkResetSpoolUsage(ids),
+      spoolmanMode
+        ? api.bulkResetSpoolmanInventorySpoolConsumedCounter(ids)
+        : api.bulkResetSpoolConsumedCounter(ids),
     onSuccess: (data) => {
       queryClient.invalidateQueries({ queryKey: spoolsQueryKey });
-      showToast(t('inventory.allUsageReset', { count: data.reset }), 'success');
+      showToast(t('inventory.allConsumedCountersReset', { count: data.reset }), 'success');
     },
     onError: () => {
-      showToast(t('inventory.resetUsageFailed'), 'error');
+      showToast(t('inventory.resetConsumedCounterFailed'), 'error');
     },
   });
 
@@ -1145,10 +1149,10 @@ function InventoryPage({ spoolmanMode = false, spoolmanModeReady = true }: { spo
               </div>
               {stats.totalConsumed > 0 && resetableSpoolIds.length > 0 && (
                 <button
-                  onClick={() => setConfirmAction({ type: 'reset-all-usage' })}
+                  onClick={() => setConfirmAction({ type: 'reset-all-consumed-counters' })}
                   className="p-1 text-bambu-gray hover:text-red-400 rounded transition-colors"
-                  title={t('inventory.resetAllUsageTooltip')}
-                  aria-label={t('inventory.resetAllUsage')}
+                  title={t('inventory.resetAllConsumedCountersTooltip')}
+                  aria-label={t('inventory.resetAllConsumedCounters')}
                 >
                   <Eraser className="w-3.5 h-3.5" />
                 </button>
@@ -1747,7 +1751,7 @@ function InventoryPage({ spoolmanMode = false, spoolmanModeReady = true }: { spo
                           onArchive={(id) => setConfirmAction({ type: 'archive', spoolId: id })}
                           onDelete={(id) => setConfirmAction({ type: 'delete', spoolId: id })}
                           onPrintLabel={(id) => setLabelPickerSpoolIds([id])}
-                          onResetUsage={(id) => setConfirmAction({ type: 'reset-usage', spoolId: id })}
+                          onResetConsumedCounter={(id) => setConfirmAction({ type: 'reset-consumed-counter', spoolId: id })}
                           visibleColumns={visibleColumns}
                           assignmentMap={assignmentMap}
                           catalogMap={catalogMap}
@@ -1773,7 +1777,7 @@ function InventoryPage({ spoolmanMode = false, spoolmanModeReady = true }: { spo
                         onArchive={() => setConfirmAction({ type: 'archive', spoolId: spool.id })}
                         onDelete={() => setConfirmAction({ type: 'delete', spoolId: spool.id })}
                         onPrintLabel={() => setLabelPickerSpoolIds([spool.id])}
-                        onResetUsage={() => setConfirmAction({ type: 'reset-usage', spoolId: spool.id })}
+                        onResetConsumedCounter={() => setConfirmAction({ type: 'reset-consumed-counter', spoolId: spool.id })}
                         visibleColumns={visibleColumns}
                         assignmentMap={assignmentMap}
                         catalogMap={catalogMap}
@@ -1874,25 +1878,25 @@ function InventoryPage({ spoolmanMode = false, spoolmanModeReady = true }: { spo
         />
       )}
 
-      {/* Confirm Modal (delete / archive / reset-usage / reset-all-usage) */}
+      {/* Confirm Modal (delete / archive / reset-consumed-counter / reset-all-consumed-counters) */}
       {confirmAction && (
         <ConfirmModal
           title={
             confirmAction.type === 'delete' ? t('common.delete') :
             confirmAction.type === 'archive' ? t('inventory.archive') :
-            confirmAction.type === 'reset-usage' ? t('inventory.resetUsage') :
-            t('inventory.resetAllUsage')
+            confirmAction.type === 'reset-consumed-counter' ? t('inventory.resetConsumedCounter') :
+            t('inventory.resetAllConsumedCounters')
           }
           message={
             confirmAction.type === 'delete' ? t('inventory.deleteConfirm') :
             confirmAction.type === 'archive' ? t('inventory.archiveConfirm') :
-            confirmAction.type === 'reset-usage' ? t('inventory.resetUsageConfirm') :
-            t('inventory.resetAllUsageConfirm', { count: resetableSpoolIds.length })
+            confirmAction.type === 'reset-consumed-counter' ? t('inventory.resetConsumedCounterConfirm') :
+            t('inventory.resetAllConsumedCountersConfirm', { count: resetableSpoolIds.length })
           }
           confirmText={
             confirmAction.type === 'delete' ? t('common.delete') :
             confirmAction.type === 'archive' ? t('inventory.archive') :
-            t('inventory.resetUsage')
+            t('inventory.resetConsumedCounter')
           }
           variant={confirmAction.type === 'archive' ? 'warning' : 'danger'}
           onConfirm={() => {
@@ -1900,10 +1904,10 @@ function InventoryPage({ spoolmanMode = false, spoolmanModeReady = true }: { spo
               deleteMutation.mutate(confirmAction.spoolId);
             } else if (confirmAction.type === 'archive') {
               archiveMutation.mutate(confirmAction.spoolId);
-            } else if (confirmAction.type === 'reset-usage') {
-              resetUsageMutation.mutate(confirmAction.spoolId);
+            } else if (confirmAction.type === 'reset-consumed-counter') {
+              resetConsumedCounterMutation.mutate(confirmAction.spoolId);
             } else {
-              bulkResetUsageMutation.mutate(resetableSpoolIds);
+              bulkResetConsumedCounterMutation.mutate(resetableSpoolIds);
             }
             setConfirmAction(null);
           }}
@@ -2115,7 +2119,7 @@ function SpoolCard({
 
 /* Single spool row for table view */
 function SpoolTableRow({
-  spool, remaining, pct, onEdit, onCopy, onRestore, onArchive, onDelete, onPrintLabel, onResetUsage,
+  spool, remaining, pct, onEdit, onCopy, onRestore, onArchive, onDelete, onPrintLabel, onResetConsumedCounter,
   visibleColumns, assignmentMap, catalogMap, currencySymbol, dateFormat, t, onSyncWeight,
 }: {
   spool: InventorySpool;
@@ -2127,7 +2131,7 @@ function SpoolTableRow({
   onArchive: () => void;
   onDelete: () => void;
   onPrintLabel?: () => void;
-  onResetUsage?: () => void;
+  onResetConsumedCounter?: () => void;
   visibleColumns: string[];
   assignmentMap: Record<number, LocationDisplay>;
   catalogMap: Record<number, SpoolCatalogEntry>;
@@ -2163,12 +2167,12 @@ function SpoolTableRow({
               <Printer className="w-4 h-4" />
             </button>
           )}
-          {onResetUsage && spool.weight_used > 0 && (
+          {onResetConsumedCounter && spool.weight_used > 0 && (
             // Eraser also shows on archived spools (#1390 follow-up):
             // archived consumed weight now counts in "Total Consumed", so
             // the user needs a way to zero an archived spool's tracking
             // counter individually without having to un-archive it first.
-            <button onClick={onResetUsage} className="p-1.5 text-bambu-gray hover:text-orange-400 rounded transition-colors" title={t('inventory.resetUsageTooltip')}>
+            <button onClick={onResetConsumedCounter} className="p-1.5 text-bambu-gray hover:text-orange-400 rounded transition-colors" title={t('inventory.resetConsumedCounterTooltip')}>
               <Eraser className="w-4 h-4" />
             </button>
           )}
@@ -2193,7 +2197,7 @@ function SpoolTableRow({
 /* Grouped spool rows for table view */
 function SpoolTableGroup({
   spools, headerSpool, remaining, pct, isExpanded, onToggle,
-  onEdit, onCopy, onArchive, onDelete, onPrintLabel, onResetUsage,
+  onEdit, onCopy, onArchive, onDelete, onPrintLabel, onResetConsumedCounter,
   visibleColumns, assignmentMap, catalogMap, currencySymbol, dateFormat, t, onSyncWeight,
 }: {
   spools: InventorySpool[];
@@ -2209,7 +2213,7 @@ function SpoolTableGroup({
   onArchive: (id: number) => void;
   onDelete: (id: number) => void;
   onPrintLabel?: (spoolId: number) => void;
-  onResetUsage?: (id: number) => void;
+  onResetConsumedCounter?: (id: number) => void;
   visibleColumns: string[];
   assignmentMap: Record<number, LocationDisplay>;
   catalogMap: Record<number, SpoolCatalogEntry>;
@@ -2263,7 +2267,7 @@ function SpoolTableGroup({
             onArchive={() => onArchive(spool.id)}
             onDelete={() => onDelete(spool.id)}
             onPrintLabel={onPrintLabel ? () => onPrintLabel(spool.id) : undefined}
-            onResetUsage={onResetUsage ? () => onResetUsage(spool.id) : undefined}
+            onResetConsumedCounter={onResetConsumedCounter ? () => onResetConsumedCounter(spool.id) : undefined}
             visibleColumns={visibleColumns}
             assignmentMap={assignmentMap}
             catalogMap={catalogMap}

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
static/assets/index-C7bMJs5u.js


+ 1 - 1
static/index.html

@@ -26,7 +26,7 @@
 
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-6Wj7SYfZ.js"></script>
+    <script type="module" crossorigin src="/assets/index-C7bMJs5u.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-Df3XYvpK.css">
   </head>
   <body>

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff