From bed233521e1bb5ec25891d43f6fd8cfd57d129e2 Mon Sep 17 00:00:00 2001 From: Jens Reinemann Date: Sun, 17 May 2026 23:21:07 +0200 Subject: [PATCH] feat: Krisenvorrats-Inventar digitalisiert und Import-Skript erstellt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - inventar.md: 38 Artikel aus handschriftlicher Liste erfasst (Lebensmittel, Medikamente, Ausrüstung, Hygiene, Energie) - import-inventar.py: Python-Skript zum einmaligen PUT /api/inventory (UTF-8-safe via ensure_ascii=True, kein PowerShell-Encoding-Problem) - import-inventar.ps1: PowerShell-Variante (Umlaute via [char]-Variablen) --- import-inventar.ps1 | 203 ++++++++++++++++++++++++++++++++++++++++++++ import-inventar.py | 173 +++++++++++++++++++++++++++++++++++++ inventar.md | 158 +++++++++++++++++----------------- 3 files changed, 455 insertions(+), 79 deletions(-) create mode 100644 import-inventar.ps1 create mode 100644 import-inventar.py diff --git a/import-inventar.ps1 b/import-inventar.ps1 new file mode 100644 index 0000000..84bf00c --- /dev/null +++ b/import-inventar.ps1 @@ -0,0 +1,203 @@ +<# +.SYNOPSIS + Importiert das Bollwerk-Vorratsinventar in den Server (einmaliger PUT). + Verwendet App-Default-Kategorien (IDs 1-7) und Default-Location Keller (ID 1). +.PARAMETER BaseUrl + Server-URL. Standard: https://bollwerk.online +.PARAMETER AdminUser + Admin-Benutzername. Standard: admin +.EXAMPLE + .\import-inventar.ps1 + .\import-inventar.ps1 -BaseUrl "http://localhost:8080" +#> +param( + [string]$BaseUrl = "https://bollwerk.online", + [string]$AdminUser = "admin" +) + +$ErrorActionPreference = "Stop" + +# --------------------------------------------------------------------------- +# Umlaute via [char] - PS 5.1 liest .ps1-Dateien als ANSI/CP1252. +# UTF-8-Umlaute im Quelltext wuerden den Parser brechen (byte 0xB6 = Pilcrow). +# --------------------------------------------------------------------------- +$oe = [char]246 # o-Umlaut +$ue = [char]252 # u-Umlaut +$ae = [char]228 # a-Umlaut +$ss = [char]223 # scharfes S +$ag = [char]224 # a-grave (fuer "a 2300 kcal", "a 20 ml") + +# --------------------------------------------------------------------------- +# Passwort sicher einlesen (erscheint NICHT in der History) +# --------------------------------------------------------------------------- +$secure = Read-Host "Admin-Passwort fuer '$AdminUser' @ $BaseUrl" -AsSecureString +$AdminPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto( + [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure) +) + +# --------------------------------------------------------------------------- +# HTTP-Hilfsfunktion (identisch mit run-integration-tests.ps1 – UTF-8-safe) +# --------------------------------------------------------------------------- +function Invoke-Api { + param( + [string]$Method, + [string]$Path, + [object]$Body = $null, + [string]$Token = $null + ) + $headers = @{ "Content-Type" = "application/json" } + if ($Token) { $headers["Authorization"] = "Bearer $Token" } + $bodyJson = if ($Body) { $Body | ConvertTo-Json -Depth 10 -Compress } else { $null } + if ($bodyJson) { + $sb = [System.Text.StringBuilder]::new($bodyJson.Length * 2) + foreach ($ch in $bodyJson.ToCharArray()) { + if ([int]$ch -gt 127) { [void]$sb.Append('\u{0:x4}' -f [int]$ch) } + else { [void]$sb.Append($ch) } + } + $bodyJson = $sb.ToString() + } + $response = Invoke-RestMethod -Method $Method -Uri "$BaseUrl$Path" ` + -Body $bodyJson -Headers $headers -ErrorAction Stop + return $response +} + +# --------------------------------------------------------------------------- +# 1. Login +# --------------------------------------------------------------------------- +Write-Host "Logge ein als '$AdminUser'..." -ForegroundColor Cyan +$auth = Invoke-Api -Method POST -Path "/api/auth/login" -Body @{ + username = $AdminUser + password = $AdminPassword +} +$token = $auth.accessToken +Write-Host " Token erhalten." -ForegroundColor Green + +# --------------------------------------------------------------------------- +# 2. Vorhandenes Inventar pruefen +# --------------------------------------------------------------------------- +Write-Host "Pruefe vorhandenes Inventar..." -ForegroundColor Cyan +$existing = Invoke-Api -Method GET -Path "/api/inventory" -Token $token +if ($existing.items.Count -gt 0) { + Write-Host "" + Write-Host " WARNUNG: Das Inventar enthaelt bereits $($existing.items.Count) Artikel!" -ForegroundColor Yellow + $confirm = Read-Host " Ueberschreiben? (ja/nein)" + if ($confirm -ne "ja") { + Write-Host "Abgebrochen." -ForegroundColor Red + exit 1 + } +} + +# --------------------------------------------------------------------------- +# 3. Inventar-Daten definieren (aus inventar.md, Stand Mai 2026) +# --------------------------------------------------------------------------- +$now = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() +function NewItem($name, $catId, $qty, $unit, $expiry, $kcal, $notes) { + @{ + id = [System.Guid]::NewGuid().ToString() + name = $name + categoryId = $catId + quantity = [double]$qty + unit = $unit + unitPrice = 0.0 + kcalPerUnit = $kcal + expiryDate = $expiry + locationId = 1 + notes = if ($notes) { $notes } else { "" } + lastUpdated = $now + } +} + +# App-Default-Kategorien (IDs aus SeedDatabaseUseCase) +$categories = @( + @{ id = 1; name = "Lebensmittel" } + @{ id = 2; name = "Wasser" } + @{ id = 3; name = "Medikamente" } + @{ id = 4; name = "Ausr${ue}stung" } + @{ id = 5; name = "Hygiene" } + @{ id = 6; name = "Energie & Licht" } + @{ id = 7; name = "Dokumente" } +) + +# App-Default-Lagerort +$locations = @( + @{ id = 1; name = "Keller" } +) + +$items = @( + # -- Lebensmittel: Getreide & Huelsenfruechte (cat 1) -------------------- + NewItem "Dinkelkorn" 1 16 "Beutel" $null $null "Jahrgang 2019, vakuumiert" + NewItem "Weizen (Naturland)" 1 36 "kg" $null $null "Jahrgang 2023" + NewItem "Dinkel" 1 40 "kg" $null $null "Jahrgang 2023" + NewItem "Bio-Weizen" 1 18 "kg" $null $null "Jahrgang 2019" + NewItem "Bio-Weizen (Neu)" 1 200 "kg" $null $null "Jahrgang 05/2026, Kunststoffbehaelter, O2-Absorber, Trockenmittel, lose luftdicht" + NewItem "Gr${ue}ne Erbsen" 1 10 "kg" $null $null "Jahrgang 2019" + NewItem "Gelbe Sch${ae}lerbsen" 1 10 "kg" $null $null "Jahrgang 2019" + NewItem "Berglinsen (1 kg)" 1 1 "Pkg." "2021-12-31" $null "MHD abgelaufen" + NewItem "Berglinsen (500 g)" 1 4 "Pkg." "2023-12-31" $null "MHD abgelaufen" + NewItem "Wei${ss}e Bohnen" 1 2 "Pkg." "2022-12-31" $null "MHD abgelaufen" + NewItem "Jodsalz" 1 25 "kg" $null $null "" + + # -- Lebensmittel: Konserven (cat 1) ------------------------------------- + NewItem "Kondensmilch gezuckert (Dovgan)" 1 88 "Dosen" $null $null "MHD pruefen" + NewItem "Corned Beef (Exeter)" 1 48 "Dosen" $null $null "MHD pruefen" + NewItem "Gesch${ae}lte Tomaten" 1 5 "Dosen" $null $null "2x 2,5 kg, MHD abgelaufen" + NewItem "Tomatenmark (200 g)" 1 24 "Dosen" $null $null "MHD abgelaufen" + NewItem "Sardinen" 1 5 "Dosen" $null $null "MHD abgelaufen" + NewItem "Heringsfilet in Tomatenso${ss}e" 1 11 "Dosen" $null $null "MHD abgelaufen" + NewItem "Thunfisch gro${ss} (900 g)" 1 3 "Dosen" $null $null "MHD abgelaufen" + NewItem "Thunfisch klein" 1 9 "Dosen" $null $null "MHD abgelaufen" + NewItem "Brathering Filet" 1 2 "Dosen" $null $null "MHD abgelaufen" + NewItem "Dosenbrot" 1 6 "Dosen" $null $null "MHD abgelaufen" + + # -- Lebensmittel: Oele & Fette (cat 1) ---------------------------------- + NewItem "Oliven${oe}l (750 ml)" 1 3 "Flaschen" "2022-12-31" $null "MHD abgelaufen" + NewItem "Avocado${oe}l (250 ml)" 1 3 "Flaschen" "2026-12-31" $null "MHD 2026, pruefen" + NewItem "Sonnenblumen${oe}l (1 L)" 1 3 "Flaschen" "2021-12-31" $null "MHD abgelaufen" + NewItem "Sonnenblumen${oe}l (500 ml)" 1 1 "Flaschen" "2023-12-31" $null "MHD abgelaufen" + + # -- Lebensmittel: Getraenke (cat 1) ------------------------------------- + NewItem "Instant-Kaffee" 1 12 "Stk." "2024-12-31" $null "MHD abgelaufen" + NewItem "Trek'N'Eat Peronin" 1 8 "Stk." "2024-12-31" $null "MHD abgelaufen" + + # -- Lebensmittel: Notfallrationen (cat 1) -------------------------------- + NewItem "Conserva-Set" 1 30 "Manntage" $null 2300 "${ag} 2300 kcal/Tag, conserva.de" + NewItem "NRG-5 Katadyn (Neubestand)" 1 96 "Tagesrationen" "2036-04-30" 2300 "4x24, Produktion April 2026" + NewItem "NRG-5 Katadyn (Altbestand)" 1 19 "Tagesrationen" "2029-01-31" 2300 "Produktion 2019, 5 Rationen verbraucht" + + # -- Medikamente (cat 3) ------------------------------------------------- + NewItem "Vitamine" 3 960 "Stk." $null $null "3x240 + 2x120 Tabletten" + + # -- Ausruestung (cat 4) ------------------------------------------------- + NewItem "PMR-Funkger${ae}t (Midland G9 Pro)" 4 2 "Stk." $null $null "" + + # -- Hygiene (cat 5) ----------------------------------------------------- + NewItem "Shampoo Mini" 5 50 "Stk." $null $null "${ag} 20 ml" + NewItem "Ethanol" 5 30 "Liter" $null $null "" + NewItem "Isopropanol" 5 4 "Liter" $null $null "" + NewItem "Methanol" 5 1 "Liter" $null $null "" + NewItem "Desinfektionsmittel" 5 5 "Liter" $null $null "" + + # -- Energie & Licht (cat 6) --------------------------------------------- + NewItem "Petroleum" 6 50 "Liter" $null $null "" +) + +$inventory = @{ + version = 1 + categories = $categories + locations = $locations + items = $items + settings = @() + deletedItemIds = @() +} + +# --------------------------------------------------------------------------- +# 4. PUT /api/inventory +# --------------------------------------------------------------------------- +Write-Host "Lade $($items.Count) Artikel in $($categories.Count) Kategorien hoch..." -ForegroundColor Cyan +$result = Invoke-Api -Method PUT -Path "/api/inventory" -Body $inventory -Token $token + +Write-Host "" +Write-Host "Fertig!" -ForegroundColor Green +Write-Host " Artikel hochgeladen : $($result.items.Count)" +Write-Host " Kategorien : $($result.categories.Count)" +Write-Host " Lagerorte : $($result.locations.Count)" diff --git a/import-inventar.py b/import-inventar.py new file mode 100644 index 0000000..8219259 --- /dev/null +++ b/import-inventar.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Importiert das Bollwerk-Vorratsinventar in den Server (einmaliger PUT). +Verwendet App-Default-Kategorien (IDs 1-7) und Default-Location Keller (ID 1). + +Aufruf: + python import-inventar.py + python import-inventar.py --url http://localhost:8080 +""" +import json +import getpass +import time +import uuid +import argparse +import urllib.request +import urllib.error + +BASE_URL = "https://bollwerk.online" +ADMIN_USER = "admin" + + +def api(method, path, body=None, token=None): + url = BASE_URL + path + # ensure_ascii=True kodiert alle Umlaute als \uXXXX – kein Encoding-Problem + data = json.dumps(body, ensure_ascii=True).encode("ascii") if body else None + headers = {"Content-Type": "application/json"} + if token: + headers["Authorization"] = f"Bearer {token}" + req = urllib.request.Request(url, data=data, headers=headers, method=method) + with urllib.request.urlopen(req) as resp: + return json.loads(resp.read().decode("utf-8")) + + +def new_item(name, cat_id, qty, unit, expiry=None, kcal=None, notes=""): + now = int(time.time() * 1000) + return { + "id": str(uuid.uuid4()), + "name": name, + "categoryId": cat_id, + "quantity": float(qty), + "unit": unit, + "unitPrice": 0.0, + "kcalPerUnit": kcal, + "expiryDate": expiry, + "locationId": 1, + "notes": notes or "", + "lastUpdated": now, + } + + +# App-Default-Kategorien (IDs aus SeedDatabaseUseCase) +CATEGORIES = [ + {"id": 1, "name": "Lebensmittel"}, + {"id": 2, "name": "Wasser"}, + {"id": 3, "name": "Medikamente"}, + {"id": 4, "name": "Ausrüstung"}, + {"id": 5, "name": "Hygiene"}, + {"id": 6, "name": "Energie & Licht"}, + {"id": 7, "name": "Dokumente"}, +] + +# App-Default-Lagerort +LOCATIONS = [{"id": 1, "name": "Keller"}] + +ITEMS = [ + # --- Lebensmittel: Getreide & Hülsenfrüchte (cat 1) --------------------- + new_item("Dinkelkorn", 1, 16, "Beutel", None, None, "Jahrgang 2019, vakuumiert"), + new_item("Weizen (Naturland)", 1, 36, "kg", None, None, "Jahrgang 2023"), + new_item("Dinkel", 1, 40, "kg", None, None, "Jahrgang 2023"), + new_item("Bio-Weizen", 1, 18, "kg", None, None, "Jahrgang 2019"), + new_item("Bio-Weizen (Neu)", 1, 200, "kg", None, None, "Jahrgang 05/2026, Kunststoffbehälter, O2-Absorber, Trockenmittel, lose luftdicht"), + new_item("Grüne Erbsen", 1, 10, "kg", None, None, "Jahrgang 2019"), + new_item("Gelbe Schälerbsen", 1, 10, "kg", None, None, "Jahrgang 2019"), + new_item("Berglinsen (1 kg)", 1, 1, "Pkg.", "2021-12-31", None, "MHD abgelaufen"), + new_item("Berglinsen (500 g)", 1, 4, "Pkg.", "2023-12-31", None, "MHD abgelaufen"), + new_item("Weiße Bohnen", 1, 2, "Pkg.", "2022-12-31", None, "MHD abgelaufen"), + new_item("Jodsalz", 1, 25, "kg", None, None, ""), + + # --- Lebensmittel: Konserven (cat 1) ------------------------------------ + new_item("Kondensmilch gezuckert (Dovgan)", 1, 88, "Dosen", None, None, "MHD prüfen"), + new_item("Corned Beef (Exeter)", 1, 48, "Dosen", None, None, "MHD prüfen"), + new_item("Geschälte Tomaten", 1, 5, "Dosen", None, None, "2x 2,5 kg, MHD abgelaufen"), + new_item("Tomatenmark (200 g)", 1, 24, "Dosen", None, None, "MHD abgelaufen"), + new_item("Sardinen", 1, 5, "Dosen", None, None, "MHD abgelaufen"), + new_item("Heringsfilet in Tomatensoße", 1, 11, "Dosen", None, None, "MHD abgelaufen"), + new_item("Thunfisch groß (900 g)", 1, 3, "Dosen", None, None, "MHD abgelaufen"), + new_item("Thunfisch klein", 1, 9, "Dosen", None, None, "MHD abgelaufen"), + new_item("Brathering Filet", 1, 2, "Dosen", None, None, "MHD abgelaufen"), + new_item("Dosenbrot", 1, 6, "Dosen", None, None, "MHD abgelaufen"), + + # --- Lebensmittel: Öle & Fette (cat 1) ---------------------------------- + new_item("Olivenöl (750 ml)", 1, 3, "Flaschen", "2022-12-31", None, "MHD abgelaufen"), + new_item("Avocadoöl (250 ml)", 1, 3, "Flaschen", "2026-12-31", None, "MHD 2026, prüfen"), + new_item("Sonnenblumenöl (1 L)", 1, 3, "Flaschen", "2021-12-31", None, "MHD abgelaufen"), + new_item("Sonnenblumenöl (500 ml)", 1, 1, "Flaschen", "2023-12-31", None, "MHD abgelaufen"), + + # --- Lebensmittel: Getränke (cat 1) ------------------------------------- + new_item("Instant-Kaffee", 1, 12, "Stk.", "2024-12-31", None, "MHD abgelaufen"), + new_item("Trek'N'Eat Peronin", 1, 8, "Stk.", "2024-12-31", None, "MHD abgelaufen"), + + # --- Lebensmittel: Notfallrationen (cat 1) ------------------------------ + new_item("Conserva-Set", 1, 30, "Manntage", None, 2300, "à 2300 kcal/Tag, conserva.de"), + new_item("NRG-5 Katadyn (Neubestand)", 1, 96, "Tagesrationen", "2036-04-30", 2300, "4x24, Produktion April 2026"), + new_item("NRG-5 Katadyn (Altbestand)", 1, 19, "Tagesrationen", "2029-01-31", 2300, "Produktion 2019, 5 Rationen verbraucht"), + + # --- Medikamente (cat 3) ------------------------------------------------ + new_item("Vitamine", 3, 960, "Stk.", None, None, "3x240 + 2x120 Tabletten"), + + # --- Ausrüstung (cat 4) ------------------------------------------------- + new_item("PMR-Funkgerät (Midland G9 Pro)", 4, 2, "Stk.", None, None, ""), + + # --- Hygiene (cat 5) ---------------------------------------------------- + new_item("Shampoo Mini", 5, 50, "Stk.", None, None, "à 20 ml"), + new_item("Ethanol", 5, 30, "Liter", None, None, ""), + new_item("Isopropanol", 5, 4, "Liter", None, None, ""), + new_item("Methanol", 5, 1, "Liter", None, None, ""), + new_item("Desinfektionsmittel", 5, 5, "Liter", None, None, ""), + + # --- Energie & Licht (cat 6) -------------------------------------------- + new_item("Petroleum", 6, 50, "Liter", None, None, ""), +] + + +def main(): + global BASE_URL + parser = argparse.ArgumentParser() + parser.add_argument("--url", default=BASE_URL) + parser.add_argument("--user", default=ADMIN_USER) + args = parser.parse_args() + + BASE_URL = args.url.rstrip("/") + + password = getpass.getpass(f"Admin-Passwort für '{args.user}' @ {BASE_URL}: ") + + print("Logge ein...", end=" ", flush=True) + auth = api("POST", "/api/auth/login", {"username": args.user, "password": password}) + token = auth["accessToken"] + print("OK") + + print("Prüfe vorhandenes Inventar...", end=" ", flush=True) + existing = api("GET", "/api/inventory", token=token) + count = len(existing.get("items", [])) + print(f"{count} Artikel vorhanden.") + + if count > 0: + confirm = input(f" WARNUNG: {count} Artikel vorhanden. Überschreiben? (ja/nein): ") + if confirm.strip().lower() != "ja": + print("Abgebrochen.") + return + + inventory = { + "version": 1, + "categories": CATEGORIES, + "locations": LOCATIONS, + "items": ITEMS, + "settings": [], + "deletedItemIds": [], + } + + print(f"Lade {len(ITEMS)} Artikel hoch...", end=" ", flush=True) + result = api("PUT", "/api/inventory", inventory, token=token) + print("OK") + + print() + print("Fertig!") + print(f" Artikel : {len(result.get('items', []))}") + print(f" Kategorien : {len(result.get('categories', []))}") + print(f" Lagerorte : {len(result.get('locations', []))}") + + +if __name__ == "__main__": + main() diff --git a/inventar.md b/inventar.md index e7685f6..74ca1f8 100644 --- a/inventar.md +++ b/inventar.md @@ -1,104 +1,104 @@ # Bollwerk – Vorratsinventar > Stand: Mai 2026 -> Quelle: handschriftliche Notiz, digitalisiert und ergänzt +> Kategorien: App-Default (IDs 1–7), Lagerort: Keller (ID 1) --- -## Getreide & Hülsenfrüchte +## Kategorie 1 – Lebensmittel -| Artikel | Menge | Jahrgang / MHD | Lagerung | Notizen | -| --- | --- | --- | --- | --- | -| Dinkelkorn | ~16 Beutel | von 2019 | Vakuumbeutel | | -| Weizen (Naturland) | 36 kg | von 2023 | | | -| Dinkel | 40 kg | von 2023 | | | -| Bio-Weizen | 18 kg | von 2019 | | | -| Bio-Weizen | 200 kg | 05/2026 | Kunststoffbehälter, O₂-Absorber, Trockenmittel | lose, luftdicht | -| Grüne Erbsen | 10 kg | von 2019 | | | -| Gelbe Schälerbsen | 10 kg | von 2019 | | | -| Berglinsen (1 kg) | 1 Pkg. | MHD 2021 | | **abgelaufen** | -| Berglinsen (500 g) | 4 Pkg. | MHD 2023 | | **abgelaufen** | -| Weiße Bohnen | 2 Pkg. | MHD 2022 | | **abgelaufen** | -| Sojamehl (6-L-Behälter) | 7 | MHD 2023 | | **abgelaufen** | -| Sojamehl (1-L-Behälter) | 3 | von 2021 | | **abgelaufen** | -| Jodsalz | 25 kg | | | | +### Getreide & Hülsenfrüchte + +| Artikel | Menge | Jahrgang / MHD | Lagerung | Notizen | +| ----------------------- | ---------- | -------------- | ---------------------------------------------- | --------------- | +| Dinkelkorn | ~16 Beutel | von 2019 | Vakuumbeutel | | +| Weizen (Naturland) | 36 kg | von 2023 | | | +| Dinkel | 40 kg | von 2023 | | | +| Bio-Weizen | 18 kg | von 2019 | | | +| Bio-Weizen (Neu) | 200 kg | 05/2026 | Kunststoffbehälter, O₂-Absorber, Trockenmittel | lose, luftdicht | +| Grüne Erbsen | 10 kg | von 2019 | | | +| Gelbe Schälerbsen | 10 kg | von 2019 | | | +| Berglinsen (1 kg) | 1 Pkg. | MHD 2021 | | **abgelaufen** | +| Berglinsen (500 g) | 4 Pkg. | MHD 2023 | | **abgelaufen** | +| Weiße Bohnen | 2 Pkg. | MHD 2022 | | **abgelaufen** | +| Jodsalz | 25 kg | | | | + +### Konserven + +| Artikel | Menge | MHD | Notizen | +| ------------------------------- | ---------- | -------------- | ------- | +| Kondensmilch gezuckert (Dovgan) | 88 Dosen | MHD prüfen | | +| Corned Beef (Exeter) | 48 Dosen | MHD prüfen | | +| Geschälte Tomaten | 2 × 2,5 kg | **abgelaufen** | | +| Tomatenmark (200 g) | 24 | **abgelaufen** | | +| Sardinen | 5 | **abgelaufen** | | +| Heringsfilet in Tomatensoße | 11 | **abgelaufen** | | +| Thunfisch groß (900 g) | 3 | **abgelaufen** | | +| Thunfisch klein | 9 | **abgelaufen** | | +| Brathering Filet | 2 | **abgelaufen** | | +| Dosenbrot | 6 | **abgelaufen** | | + +### Öle & Fette + +| Artikel | Menge | MHD | Notizen | +| ----------------------- | ----- | ---- | ---------------------- | +| Olivenöl (750 ml) | 3 | 2022 | **abgelaufen** | +| Avocadoöl (250 ml) | 3 | 2026 | läuft bald ab – prüfen | +| Sonnenblumenöl (1 L) | 3 | 2021 | **abgelaufen** | +| Sonnenblumenöl (500 ml) | 1 | 2023 | **abgelaufen** | + +### Getränke + +| Artikel | Menge | MHD | Notizen | +| ------------------ | ----- | ---- | -------------- | +| Instant-Kaffee | 12 | 2024 | **abgelaufen** | +| Trek'N'Eat Peronin | 8 | 2024 | **abgelaufen** | + +### Notfallrationen + +| Artikel | Menge | Produktion | MHD | Notizen | +| -------------------------------- | --------------------------- | ---------- | ---------- | -------------------------------------- | +| Conserva-Set (à 2.300 kcal/Tag) | 30 Manntage | | | [conserva.de](https://www.conserva.de) | +| NRG-5 Katadyn (à 2.300 kcal/Tag) | 96 Tagesrationen (4 × 24) | April 2026 | April 2036 | | +| NRG-5 Katadyn (Altbestand) | 19 Tagesrationen (5 verbraucht) | 2019 | ca. 2029 | | + +> NRG-5 gesamt: 115 Tagesrationen = 264.500 kcal --- -## Konserven +## Kategorie 3 – Medikamente -| Artikel | Menge | MHD | Notizen | -| --- | --- | --- | --- | -| Kondensmilch gezuckert (Dovgan) | 88 Dosen | MHD prüfen | | -| Corned Beef (Exeter) | 48 Dosen | MHD prüfen | | -| Geschälte Tomaten | 2 × 2,5 kg | **abgelaufen** | | -| Tomatenmark (200 g) | 24 | **abgelaufen** | | -| Sardinen | 5 | **abgelaufen** | | -| Heringsfilet in Tomatensoße | 11 | **abgelaufen** | | -| Thunfisch groß (900 g) | 3 | **abgelaufen** | | -| Thunfisch klein | 9 | **abgelaufen** | | -| Brathering Filet | 2 | **abgelaufen** | | -| Dosenbrot | 6 | **abgelaufen** | | +| Artikel | Menge | MHD | Notizen | +| -------- | ---------------------------- | --- | --------------------- | +| Vitamine | 3 × 240 + 2 × 120 = 960 Stk. | | 3x240 + 2x120 Tabletten | --- -## Öle & Fette +## Kategorie 4 – Ausrüstung -| Artikel | Menge | MHD | Notizen | -| --- | --- | --- | --- | -| Olivenöl (750 ml) | 3 | 2022 | **abgelaufen** | -| Avocadoöl (250 ml) | 3 | 2026 | läuft bald ab – prüfen | -| Sonnenblumenöl (1 L) | 3 | 2021 | **abgelaufen** | -| Sonnenblumenöl (500 ml) | 1 | 2023 | **abgelaufen** | +| Artikel | Menge | Notizen | +| ------------------------------ | ----- | ------- | +| PMR-Funkgerät (Midland G9 Pro) | 2 | | --- -## Getränke & Ergänzung +## Kategorie 5 – Hygiene -| Artikel | Menge | MHD | Notizen | -| --- | --- | --- | --- | -| Instant-Kaffee | 12 | 2024 | **abgelaufen** | -| Trek'N'Eat Peronin | 8 | 2024 | **abgelaufen** | -| Vitamine | 3 × 240 + 2 × 120 = 960 Stk. | | | +| Artikel | Menge | Notizen | +| ------------------- | ---------- | ------- | +| Shampoo Mini | 50 × 20 ml | | +| Ethanol | 30 × 1 L | | +| Isopropanol | 4 × 1 L | | +| Methanol | 1 L | | +| Desinfektionsmittel | 5 × 1 L | | --- -## Notfallrationen +## Kategorie 6 – Energie & Licht -| Artikel | Menge | Produktion | MHD | Notizen | -| --- | --- | --- | --- | --- | -| Conserva-Set (à 2.300 kcal/Tag) | 30 Manntage | | | [conserva.de](https://www.conserva.de) | -| NRG-5 Katadyn (à 2.300 kcal/Tag) | 96 Tagesrationen (4 × 24) | April 2026 | April 2036 | | -| NRG-5 Katadyn (Altbestand) | 19 Tagesrationen (5 verbraucht) | 2019 | ca. 2029 | | -| **NRG-5 gesamt** | **115 Tagesrationen = 264.500 kcal** | | | | - ---- - -## Hygiene & Desinfektion - -| Artikel | Menge | Notizen | -| --- | --- | --- | -| Shampoo Mini | 50 × 20 ml | | -| Ethanol | 30 × 1 L | | -| Isopropanol | 4 × 1 L | | -| Methanol | 1 L | | -| Desinfektionsmittel | 5 × 1 L | | - ---- - -## Brennstoffe - -| Artikel | Menge | Notizen | -| --- | --- | --- | -| Petroleum | 50 L | | - ---- - -## Kommunikation & Ausrüstung - -| Artikel | Menge | Notizen | -| --- | --- | --- | -| PMR-Funkgerät (Midland G9 Pro) | 2 | | +| Artikel | Menge | Notizen | +| --------- | ----- | ------- | +| Petroleum | 50 L | | --- @@ -107,4 +107,4 @@ - **Dovgan Kondensmilch & Exeter Corned Beef**: MHD direkt auf der Dose prüfen und nachtragen. - **Avocadoöl**: MHD 2026 – zeitnah verbrauchen oder ersetzen. - Viele Fisch-/Ölkonserven sind abgelaufen – Qualität prüfen. Trockenwaren (Getreide, Hülsenfrüchte) sind in guter Lagerung oft Jahre über MHD nutzbar. -- Weitere Artikel (Medikamente, Wasser, Beleuchtung) können später über die App erfasst werden. +- Kategorien 2 (Wasser) und 7 (Dokumente) sind noch leer.