- {/* Empty Spool Weight — hidden in Spoolman mode (managed per filament type in Spoolman) */}
+ {/* Empty Spool Weight. Shown in Spoolman mode too: Spoolman keeps a
+ per-spool spool_weight that overrides the filament type's, and it is
+ the tare every weigh-in subtracts (#2908). */}
- {spoolmanMode ? (
-
{t('inventory.spoolWeightManagedBySpoolman')}
- ) : (
-
updateField('core_weight', weight)}
- catalogId={formData.core_weight_catalog_id}
- onCatalogIdChange={(id) => updateField('core_weight_catalog_id', id)}
- />
- )}
+ updateField('core_weight', weight)}
+ catalogId={formData.core_weight_catalog_id}
+ onCatalogIdChange={(id) => updateField('core_weight_catalog_id', id)}
+ />
{/* Current Weight (remaining filament) */}
diff --git a/frontend/src/components/spool-form/types.ts b/frontend/src/components/spool-form/types.ts
index 0455da63c..d30388a14 100644
--- a/frontend/src/components/spool-form/types.ts
+++ b/frontend/src/components/spool-form/types.ts
@@ -212,9 +212,6 @@ export interface AdditionalSectionProps extends SectionProps {
globalLowStockThreshold: number;
availableLocations?: { id: number; name: string }[];
onCreateLocation?: (name: string) => Promise<{ id: number; name: string } | null>;
- // When true the empty-spool weight is managed by Spoolman on the filament
- // object, so SpoolWeightPicker is hidden and an info notice is shown instead.
- spoolmanMode?: boolean;
}
// PA Profile section props
diff --git a/frontend/src/i18n/locales/de.ts b/frontend/src/i18n/locales/de.ts
index 58318e219..68f6fd348 100644
--- a/frontend/src/i18n/locales/de.ts
+++ b/frontend/src/i18n/locales/de.ts
@@ -5027,7 +5027,6 @@ export default {
spoolmanFilamentUnlinked: 'Verknüpfung mit Filamentkatalog aufgehoben',
noSpoolmanFilaments: 'Keine Filamente im Spoolman-Katalog gefunden',
spoolmanFilamentColorSwatch: 'Filamentfarbe',
- spoolWeightManagedBySpoolman: 'Das Leerspulengewicht wird pro Filamenttyp in Spoolman verwaltet',
spoolmanCatalogLoadFailed: 'Spoolman-Filamentkatalog konnte nicht geladen werden',
},
diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts
index a4b781fca..17e066df4 100644
--- a/frontend/src/i18n/locales/en.ts
+++ b/frontend/src/i18n/locales/en.ts
@@ -5072,7 +5072,6 @@ export default {
spoolmanFilamentUnlinked: 'Filament catalog link cleared',
noSpoolmanFilaments: 'No filaments found in Spoolman catalog',
spoolmanFilamentColorSwatch: 'Filament color',
- spoolWeightManagedBySpoolman: 'Empty spool weight is managed per filament type in Spoolman',
spoolmanCatalogLoadFailed: 'Failed to load Spoolman filament catalog',
},
diff --git a/frontend/src/i18n/locales/es.ts b/frontend/src/i18n/locales/es.ts
index b6a34be64..3a262eba4 100644
--- a/frontend/src/i18n/locales/es.ts
+++ b/frontend/src/i18n/locales/es.ts
@@ -5034,7 +5034,6 @@ export default {
spoolmanFilamentUnlinked: 'Vínculo del catálogo de filamentos borrado',
noSpoolmanFilaments: 'No se encontraron filamentos en el catálogo de Spoolman',
spoolmanFilamentColorSwatch: 'Color del filamento',
- spoolWeightManagedBySpoolman: 'El peso de la bobina vacía se gestiona por tipo de filamento en Spoolman',
spoolmanCatalogLoadFailed: 'Error al cargar el catálogo de filamentos de Spoolman',
},
diff --git a/frontend/src/i18n/locales/fr.ts b/frontend/src/i18n/locales/fr.ts
index bd0104008..369a81c9b 100644
--- a/frontend/src/i18n/locales/fr.ts
+++ b/frontend/src/i18n/locales/fr.ts
@@ -5016,7 +5016,6 @@ export default {
spoolmanFilamentUnlinked: 'Lien avec le catalogue de filaments supprimé',
noSpoolmanFilaments: 'Aucun filament trouvé dans le catalogue Spoolman',
spoolmanFilamentColorSwatch: 'Couleur du filament',
- spoolWeightManagedBySpoolman: 'Le poids de la bobine vide est géré par type de filament dans Spoolman',
spoolmanCatalogLoadFailed: 'Impossible de charger le catalogue Spoolman',
},
diff --git a/frontend/src/i18n/locales/it.ts b/frontend/src/i18n/locales/it.ts
index 3cc6e0363..050fdefc3 100644
--- a/frontend/src/i18n/locales/it.ts
+++ b/frontend/src/i18n/locales/it.ts
@@ -5015,7 +5015,6 @@ export default {
spoolmanFilamentUnlinked: 'Collegamento al catalogo filamenti rimosso',
noSpoolmanFilaments: 'Nessun filamento trovato nel catalogo Spoolman',
spoolmanFilamentColorSwatch: 'Colore del filamento',
- spoolWeightManagedBySpoolman: 'Il peso della bobina vuota è gestito per tipo di filamento in Spoolman',
spoolmanCatalogLoadFailed: 'Impossibile caricare il catalogo Spoolman',
},
diff --git a/frontend/src/i18n/locales/ja.ts b/frontend/src/i18n/locales/ja.ts
index fd4edc72a..8da8d71ab 100644
--- a/frontend/src/i18n/locales/ja.ts
+++ b/frontend/src/i18n/locales/ja.ts
@@ -5027,7 +5027,6 @@ export default {
spoolmanFilamentUnlinked: 'フィラメントカタログのリンクを解除しました',
noSpoolmanFilaments: 'Spoolmanカタログにフィラメントが見つかりません',
spoolmanFilamentColorSwatch: 'フィラメントの色',
- spoolWeightManagedBySpoolman: '空スプールの重量はSpoolmanでフィラメントタイプごとに管理されています',
spoolmanCatalogLoadFailed: 'Spoolmanのフィラメントカタログを読み込めませんでした',
},
diff --git a/frontend/src/i18n/locales/ko.ts b/frontend/src/i18n/locales/ko.ts
index eacc0fce4..95bc872ac 100644
--- a/frontend/src/i18n/locales/ko.ts
+++ b/frontend/src/i18n/locales/ko.ts
@@ -4792,7 +4792,6 @@ export default {
spoolmanFilamentUnlinked: '필라멘트 카탈로그 연결 해제됨',
noSpoolmanFilaments: 'Spoolman 카탈로그에서 필라멘트를 찾을 수 없음',
spoolmanFilamentColorSwatch: '필라멘트 색상',
- spoolWeightManagedBySpoolman: '빈 스풀 무게는 Spoolman에서 필라멘트 유형별로 관리됩니다',
spoolmanCatalogLoadFailed: 'Spoolman 필라멘트 카탈로그 불러오기 실패',
subtitle: '스풀 관리',
storageLocationNone: '위치 미설정',
diff --git a/frontend/src/i18n/locales/nl.ts b/frontend/src/i18n/locales/nl.ts
index 0b6d78722..ac156b2ab 100644
--- a/frontend/src/i18n/locales/nl.ts
+++ b/frontend/src/i18n/locales/nl.ts
@@ -5072,7 +5072,6 @@ export default {
spoolmanFilamentUnlinked: 'Koppeling met filamentcatalogus gewist',
noSpoolmanFilaments: 'Geen filamenten gevonden in Spoolman-catalogus',
spoolmanFilamentColorSwatch: 'Filamentkleur',
- spoolWeightManagedBySpoolman: 'Leeg spoelgewicht wordt per filamenttype beheerd in Spoolman',
spoolmanCatalogLoadFailed: 'Spoolman-filamentcatalogus laden mislukt',
},
diff --git a/frontend/src/i18n/locales/pt-BR.ts b/frontend/src/i18n/locales/pt-BR.ts
index d6a6cb8ab..8ffb3c445 100644
--- a/frontend/src/i18n/locales/pt-BR.ts
+++ b/frontend/src/i18n/locales/pt-BR.ts
@@ -5015,7 +5015,6 @@ export default {
spoolmanFilamentUnlinked: 'Vínculo com o catálogo de filamentos removido',
noSpoolmanFilaments: 'Nenhum filamento encontrado no catálogo Spoolman',
spoolmanFilamentColorSwatch: 'Cor do filamento',
- spoolWeightManagedBySpoolman: 'O peso do carretel vazio é gerenciado por tipo de filamento no Spoolman',
spoolmanCatalogLoadFailed: 'Falha ao carregar catálogo de filamentos do Spoolman',
},
diff --git a/frontend/src/i18n/locales/ru.ts b/frontend/src/i18n/locales/ru.ts
index 3703d70a3..1017c2dda 100644
--- a/frontend/src/i18n/locales/ru.ts
+++ b/frontend/src/i18n/locales/ru.ts
@@ -4792,7 +4792,6 @@ export default {
spoolmanFilamentUnlinked: "Связь с каталогом филаментов удалена",
noSpoolmanFilaments: "В каталоге Spoolman филаменты не найдены",
spoolmanFilamentColorSwatch: "Цвет филамента",
- spoolWeightManagedBySpoolman: "Масса пустой катушки задаётся в Spoolman для каждого типа филамента",
spoolmanCatalogLoadFailed: "Не удалось загрузить каталог филаментов Spoolman",
},
timelapse: {
diff --git a/frontend/src/i18n/locales/sv.ts b/frontend/src/i18n/locales/sv.ts
index 242e979cf..19acfda2a 100644
--- a/frontend/src/i18n/locales/sv.ts
+++ b/frontend/src/i18n/locales/sv.ts
@@ -5072,7 +5072,6 @@ errors: {
spoolmanFilamentUnlinked: 'Filamentkataloglänk rensad',
noSpoolmanFilaments: 'Inga filament hittade i Spoolman-katalog',
spoolmanFilamentColorSwatch: 'Filamentfärg',
- spoolWeightManagedBySpoolman: 'Tom rullvikt hanteras per filamenttyp i Spoolman',
spoolmanCatalogLoadFailed: 'Kunde inte ladda Spoolman filamentkatalog',
},
diff --git a/frontend/src/i18n/locales/tr.ts b/frontend/src/i18n/locales/tr.ts
index be696ac83..d458b4da9 100644
--- a/frontend/src/i18n/locales/tr.ts
+++ b/frontend/src/i18n/locales/tr.ts
@@ -5004,7 +5004,6 @@ export default {
spoolmanFilamentUnlinked: 'Filament katalog bağlantısı temizlendi',
noSpoolmanFilaments: 'Spoolman kataloğunda filament bulunamadı',
spoolmanFilamentColorSwatch: 'Filament rengi',
- spoolWeightManagedBySpoolman: 'Boş makara ağırlığı Spoolman\'de filament türü başına yönetilir',
spoolmanCatalogLoadFailed: 'Spoolman filament kataloğu yüklenemedi',
},
diff --git a/frontend/src/i18n/locales/uk.ts b/frontend/src/i18n/locales/uk.ts
index 027383105..bba9b8423 100644
--- a/frontend/src/i18n/locales/uk.ts
+++ b/frontend/src/i18n/locales/uk.ts
@@ -5069,7 +5069,6 @@ export default {
spoolmanFilamentUnlinked: "Посилання на каталог філаментів видалено",
noSpoolmanFilaments: "У каталозі Spoolman не знайдено філаментів",
spoolmanFilamentColorSwatch: "Колір філаменту",
- spoolWeightManagedBySpoolman: "Вага порожньої котушки керується залежно від типу філаменту в Spoolman",
spoolmanCatalogLoadFailed: "Не вдалося завантажити каталог філаментів Spoolman",
},
diff --git a/frontend/src/i18n/locales/zh-CN.ts b/frontend/src/i18n/locales/zh-CN.ts
index 0459e99bb..0587dce8c 100644
--- a/frontend/src/i18n/locales/zh-CN.ts
+++ b/frontend/src/i18n/locales/zh-CN.ts
@@ -5015,7 +5015,6 @@ export default {
spoolmanFilamentUnlinked: '已解除耗材目录关联',
noSpoolmanFilaments: 'Spoolman 目录中未找到耗材',
spoolmanFilamentColorSwatch: '耗材颜色',
- spoolWeightManagedBySpoolman: '空线轴重量在 Spoolman 中按耗材类型管理',
spoolmanCatalogLoadFailed: '无法加载 Spoolman 耗材目录',
},
diff --git a/frontend/src/i18n/locales/zh-TW.ts b/frontend/src/i18n/locales/zh-TW.ts
index 1837075fc..67f3e9b53 100644
--- a/frontend/src/i18n/locales/zh-TW.ts
+++ b/frontend/src/i18n/locales/zh-TW.ts
@@ -5015,7 +5015,6 @@ export default {
spoolmanFilamentUnlinked: '已解除耗材目錄關聯',
noSpoolmanFilaments: 'Spoolman 目錄中未找到耗材',
spoolmanFilamentColorSwatch: '耗材顏色',
- spoolWeightManagedBySpoolman: '空線軸重量在 Spoolman 中按耗材類型管理',
spoolmanCatalogLoadFailed: '無法載入 Spoolman 耗材目錄',
},
diff --git a/frontend/src/pages/spoolbuddy/SpoolBuddyDashboard.tsx b/frontend/src/pages/spoolbuddy/SpoolBuddyDashboard.tsx
index 469b3749f..1f9bf53e5 100644
--- a/frontend/src/pages/spoolbuddy/SpoolBuddyDashboard.tsx
+++ b/frontend/src/pages/spoolbuddy/SpoolBuddyDashboard.tsx
@@ -413,6 +413,11 @@ export function SpoolBuddyDashboard() {
try {
const weight = liveWeight ?? displayedWeight;
if (spoolmanMode) {
+ // No tare is sent. The quick-create has no input for one, so the 250
+ // that used to go here was a placeholder, not a user's choice -- and
+ // since #2908 a sent value is written to the spool's own spool_weight,
+ // which would stamp every spool the kiosk creates and stop it
+ // inheriting the filament type's.
const created = await api.createSpoolmanInventorySpool({
material: 'PLA',
subtype: null,
@@ -422,8 +427,6 @@ export function SpoolBuddyDashboard() {
effect_type: null,
brand: null,
label_weight: 1000,
- core_weight: 250,
- core_weight_catalog_id: null,
weight_used: 0,
slicer_filament: null,
slicer_filament_name: null,
@@ -442,7 +445,7 @@ export function SpoolBuddyDashboard() {
last_weighed_at: weight !== null ? new Date().toISOString() : null,
category: null,
low_stock_threshold_pct: null,
- });
+ } as Parameters
[0]);
await api.linkTagToSpoolmanSpool(created.id, {
tag_uid: sbState.unknownTagUid || undefined,
tray_uuid: (!sbState.unknownTagUid && sbState.unknownTrayUuid) ? sbState.unknownTrayUuid : undefined,
diff --git a/frontend/src/pages/spoolbuddy/SpoolBuddyWriteTagPage.tsx b/frontend/src/pages/spoolbuddy/SpoolBuddyWriteTagPage.tsx
index 975f77f7b..296490622 100644
--- a/frontend/src/pages/spoolbuddy/SpoolBuddyWriteTagPage.tsx
+++ b/frontend/src/pages/spoolbuddy/SpoolBuddyWriteTagPage.tsx
@@ -470,6 +470,10 @@ function NewSpoolTouchForm({ currencySymbol, onCreated, selectedSpool, spoolmanM
const [viewMode, setViewMode] = useState('simple');
const [activeSubTab, setActiveSubTab] = useState('filament');
const [formData, setFormData] = useState(defaultFormData);
+ // The empty spool weight picker is on screen in Spoolman mode too. Track
+ // whether the user reached for it, so an untouched form does not send its
+ // default (issue #2908).
+ const [coreWeightTouched, setCoreWeightTouched] = useState(false);
const [errors, setErrors] = useState>>({});
const [quickAdd, setQuickAdd] = useState(false);
const [quantity, setQuantity] = useState(1);
@@ -671,6 +675,9 @@ function NewSpoolTouchForm({ currencySymbol, onCreated, selectedSpool, spoolmanM
const updateField = (key: K, value: SpoolFormData[K]) => {
setFormData(prev => ({ ...prev, [key]: value }));
+ if (key === 'core_weight') {
+ setCoreWeightTouched(true);
+ }
if (errors[key]) {
setErrors(prev => ({ ...prev, [key]: undefined }));
}
@@ -736,7 +743,7 @@ function NewSpoolTouchForm({ currencySymbol, onCreated, selectedSpool, spoolmanM
}
const presetName = selectedPresetOption?.displayName || presetInputValue || null;
- const payload = {
+ const payload: Record = {
material: formData.material,
subtype: formData.subtype || null,
brand: formData.brand || null,
@@ -745,8 +752,15 @@ function NewSpoolTouchForm({ currencySymbol, onCreated, selectedSpool, spoolmanM
extra_colors: formData.extra_colors || null,
effect_type: formData.effect_type || null,
label_weight: formData.label_weight,
- core_weight: formData.core_weight,
- core_weight_catalog_id: formData.core_weight_catalog_id,
+ // Only send a per-spool tare in Spoolman mode when the user actually set
+ // one here; otherwise let it keep inheriting from the filament type.
+ // The catalogue id has no field on the Spoolman side, so a catalogue
+ // selection does not round-trip there; only the weight does.
+ ...(spoolmanMode
+ ? coreWeightTouched
+ ? { core_weight: formData.core_weight }
+ : {}
+ : { core_weight: formData.core_weight, core_weight_catalog_id: formData.core_weight_catalog_id }),
weight_used: formData.weight_used,
slicer_filament: formData.slicer_filament || null,
slicer_filament_name: presetName,
@@ -774,8 +788,8 @@ function NewSpoolTouchForm({ currencySymbol, onCreated, selectedSpool, spoolmanM
// internal bulk returns InventorySpool[]. Mirrors SpoolFormModal's
// duck-typed handling so partial failures surface as a warning toast.
const raw = spoolmanMode
- ? await api.bulkCreateSpoolmanInventorySpools(payload, quantity)
- : await api.bulkCreateSpools(payload, quantity);
+ ? await api.bulkCreateSpoolmanInventorySpools(payload as Parameters[0], quantity)
+ : await api.bulkCreateSpools(payload as Parameters[0], quantity);
const created: InventorySpool[] =
spoolmanMode && raw && typeof raw === 'object' && 'created' in raw
? (raw as { created: InventorySpool[] }).created
@@ -786,8 +800,8 @@ function NewSpoolTouchForm({ currencySymbol, onCreated, selectedSpool, spoolmanM
if (created.length > 0) onCreated(created[0]);
} else {
const created = spoolmanMode
- ? await api.createSpoolmanInventorySpool(payload)
- : await api.createSpool(payload);
+ ? await api.createSpoolmanInventorySpool(payload as Parameters[0])
+ : await api.createSpool(payload as Parameters[0]);
await saveKProfiles(created.id);
onCreated(created);
}