feat(fahrzeug): Funkkenner-Feld ergänzt (BOS-Digitalfunk-Kennung)
Nutzer-Fund: funkkenner (z.B. OPTA-Nummer) fehlte in fahrzeugdetails komplett - eigenes Feld, nicht dasselbe wie funkrufname (gesprochener Rufname vs. formale Funk-Identifikationsnummer im Digitalfunknetz). Migration 0018, Model/Schema/Frontend-Formular ergänzt. Auch im Kachel- Backlog (arbeitskacheln/05_fleet.md, FLEET-002) nachgezogen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
This commit is contained in:
@@ -4221,3 +4221,140 @@ Keine Commits in dieser Session.
|
|||||||
- frontend/src/styles/global.css | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
- frontend/src/styles/global.css | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
---
|
---
|
||||||
|
## 2026-09-05 15:07 – 15:11 (3m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** asb-material
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- 95e04b5 style: Sidebar-Layout statt Topbar (Vorbild OpenFleet-Dashboard-Screenshot)
|
||||||
|
- 024576c style: schlicheres Design (flach statt Schatten, kleinerer Radius)
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- frontend/src/styles/global.css | 55 +++++++++++++++++++++++++++++++++++++++++--------------
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-09-05 15:13 – 15:16 (3m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** asb-material
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- 5524ef9 docs: Arbeitskacheln-Backlog als Dateien angelegt (arbeitskacheln/)
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- arbeitskacheln/00_index.md | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
- arbeitskacheln/01_foundation.md | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
- arbeitskacheln/02_identity.md | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
- arbeitskacheln/03_digital_file.md | 123 +++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-09-05 15:16 – 15:18 (1m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** asb-material
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- 862236f docs: nächste 10 Arbeitskacheln (FILE-006/007, ASSET-001..008)
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- arbeitskacheln/00_index.md | 6 ++-
|
||||||
|
- arbeitskacheln/03_digital_file.md | 46 +++++++++++++++++++++-
|
||||||
|
- arbeitskacheln/04_assets.md | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-09-05 15:19 – 15:20 (1m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** asb-material
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- c7784f0 docs: nächste 10 Arbeitskacheln (FLEET-001..006, INV-001..004)
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- arbeitskacheln/00_index.md | 4 ++-
|
||||||
|
- arbeitskacheln/05_fleet.md | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
- arbeitskacheln/06_inventory.md | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-09-05 15:22 – 15:23 (1m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** asb-material
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- b7c7276 docs: Fehler korrigiert - Betriebsstunden fälschlich fahrzeug-only geplant
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- arbeitskacheln/00_index.md | 7 ++++---
|
||||||
|
- arbeitskacheln/04_assets.md | 39 ++++++++++++++++++++++++++++++++++++++-
|
||||||
|
- arbeitskacheln/05_fleet.md | 54 ++++++++++++++++++++++++++++++------------------------
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-09-05 15:23 – 15:25 (1m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** asb-material
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- 8fc5453 docs: Inventory-Epic komplettiert (INV-005..007)
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- arbeitskacheln/00_index.md | 6 +++---
|
||||||
|
- arbeitskacheln/06_inventory.md | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-09-05 15:25 – 15:26 (1m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** asb-material
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- 6aa45a3 docs: Warehouse-Epic komplettiert (WH-001..007)
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- arbeitskacheln/00_index.md | 3 +-
|
||||||
|
- arbeitskacheln/07_warehouse.md | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-09-05 15:28 – 15:29 (1m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** asb-material
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- f7070c5 docs: Loadout-Epic komplettiert (LOAD-001..006)
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- arbeitskacheln/00_index.md | 3 +-
|
||||||
|
- arbeitskacheln/08_loadout.md | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-09-05 15:30 – 15:31 (1m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** asb-material
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- 81a7271 docs: Inspections-Epic komplettiert (INSP-001..006)
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- arbeitskacheln/00_index.md | 3 +-
|
||||||
|
- arbeitskacheln/09_inspections.md | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-09-05 15:31 – 15:32 (1m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** asb-material
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- 71dc679 docs: Maintenance-Epic komplettiert (MAINT-001..006)
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- arbeitskacheln/00_index.md | 5 ++--
|
||||||
|
- arbeitskacheln/10_maintenance.md | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-09-05 15:32 – 15:33 (1m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** asb-material
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- 11a62db docs: Defects-Epic komplettiert (DEFECT-001..005)
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- arbeitskacheln/00_index.md | 3 ++-
|
||||||
|
- arbeitskacheln/11_defects.md | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
---
|
||||||
|
|||||||
@@ -23,16 +23,19 @@ Details zu FLEET-001 … FLEET-006 (vollständig).
|
|||||||
- **Tests:** CRUD-Test.
|
- **Tests:** CRUD-Test.
|
||||||
- **DoD:** mind. ein Fahrzeugtyp angelegt.
|
- **DoD:** mind. ein Fahrzeugtyp angelegt.
|
||||||
|
|
||||||
## FLEET-002 — Fahrzeuge (Kennzeichen/Funkrufname)
|
## FLEET-002 — Fahrzeuge (Kennzeichen/Funkrufname/Funkkenner)
|
||||||
|
|
||||||
|
- **Nachtrag (Nutzer-Fund):** Funkkenner (BOS-Digitalfunk-Kennung, z.B. OPTA-Nummer) fehlte
|
||||||
|
— ist ein eigenes Feld, nicht dasselbe wie Funkrufname (gesprochener Rufname wie
|
||||||
|
„Florian München 1/44" vs. formale Funk-Identifikationsnummer im Digitalfunknetz).
|
||||||
- **Ziel:** Fahrzeug-Spezialfelder am Asset.
|
- **Ziel:** Fahrzeug-Spezialfelder am Asset.
|
||||||
- **Beschreibung:** Kennzeichen, Fahrgestellnummer, Funkrufname als 1:1-Erweiterungstabelle
|
- **Beschreibung:** Kennzeichen, Fahrgestellnummer, Funkrufname, Funkkenner als
|
||||||
zu Asset.
|
1:1-Erweiterungstabelle zu Asset.
|
||||||
- **Benutzerwert:** Fahrzeuge sind die wichtigste Ressourcenklasse im Rettungsdienst — eigene
|
- **Benutzerwert:** Fahrzeuge sind die wichtigste Ressourcenklasse im Rettungsdienst — eigene
|
||||||
Kennfelder sind Pflicht.
|
Kennfelder sind Pflicht.
|
||||||
- **Abhängigkeiten:** ASSET-003, FLEET-001
|
- **Abhängigkeiten:** ASSET-003, FLEET-001
|
||||||
- **Datenmodell:** `fahrzeugdetails` (asset_id, kennzeichen, fahrgestellnummer,
|
- **Datenmodell:** `fahrzeugdetails` (asset_id, kennzeichen, fahrgestellnummer,
|
||||||
funkrufname)
|
funkrufname, funkkenner)
|
||||||
- **Backend:** CRUD/Upsert
|
- **Backend:** CRUD/Upsert
|
||||||
- **Frontend:** Formular
|
- **Frontend:** Formular
|
||||||
- **Mobile:** Anzeige
|
- **Mobile:** Anzeige
|
||||||
@@ -139,4 +142,6 @@ Betriebsstunden-Feld) — und selbst der Fahrzeug-Kilometerstand hat **keine
|
|||||||
Rückwärtslauf-Validierung** (reiner Zahlen-Input). Beides zusammen: ASSET-009 wäre in MABEA
|
Rückwärtslauf-Validierung** (reiner Zahlen-Input). Beides zusammen: ASSET-009 wäre in MABEA
|
||||||
kein reiner Nachzug, sondern verlangt (a) ein neues generisches Zählerfeld an Objekt/
|
kein reiner Nachzug, sondern verlangt (a) ein neues generisches Zählerfeld an Objekt/
|
||||||
GeraetInstanz und (b) eine bisher fehlende Validierungsregel. FLEET-006 ist in MABEA (noch)
|
GeraetInstanz und (b) eine bisher fehlende Validierungsregel. FLEET-006 ist in MABEA (noch)
|
||||||
nicht relevant, da kein Operations-Epic existiert.
|
nicht relevant, da kein Operations-Epic existiert. **FLEET-002-Nachtrag:** `funkkenner`
|
||||||
|
fehlte in MABEAs `fahrzeugdetails` komplett (nur `funkrufname` vorhanden) — echte, kleine
|
||||||
|
Feld-Lücke, wird direkt nachgezogen (siehe Commit).
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
"""fahrzeugdetails.funkkenner (Nutzer-Fund): BOS-Digitalfunk-Kennung (z.B.
|
||||||
|
OPTA-Nummer) ist ein eigenes Feld, nicht dasselbe wie funkrufname (gesprochener
|
||||||
|
Rufname wie "Florian München 1/44" vs. formale Funk-Identifikationsnummer im
|
||||||
|
Digitalfunknetz). Additiv, nullable.
|
||||||
|
|
||||||
|
Revision ID: 0018_funkkenner
|
||||||
|
Revises: 0017_granulare_rollen
|
||||||
|
Create Date: 2026-09-05
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
revision: str = "0018_funkkenner"
|
||||||
|
down_revision: Union[str, None] = "0017_granulare_rollen"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
op.execute("ALTER TABLE fahrzeugdetails ADD COLUMN funkkenner TEXT")
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
op.execute("ALTER TABLE fahrzeugdetails DROP COLUMN funkkenner")
|
||||||
@@ -17,6 +17,9 @@ class Fahrzeugdetails(Base):
|
|||||||
objekt_id: Mapped[int] = mapped_column(ForeignKey("objekt.id"), primary_key=True)
|
objekt_id: Mapped[int] = mapped_column(ForeignKey("objekt.id"), primary_key=True)
|
||||||
kennzeichen: Mapped[str | None] = mapped_column(String)
|
kennzeichen: Mapped[str | None] = mapped_column(String)
|
||||||
funkrufname: Mapped[str | None] = mapped_column(String)
|
funkrufname: Mapped[str | None] = mapped_column(String)
|
||||||
|
# BOS-Digitalfunk-Kennung (z.B. OPTA-Nummer) - eigenes Feld, nicht dasselbe wie
|
||||||
|
# funkrufname (gesprochener Rufname vs. formale Funk-Identifikationsnummer).
|
||||||
|
funkkenner: Mapped[str | None] = mapped_column(String)
|
||||||
baujahr: Mapped[int | None] = mapped_column(Integer)
|
baujahr: Mapped[int | None] = mapped_column(Integer)
|
||||||
kilometerstand: Mapped[int | None] = mapped_column(Integer)
|
kilometerstand: Mapped[int | None] = mapped_column(Integer)
|
||||||
betriebsstunden: Mapped[Decimal | None] = mapped_column(Numeric)
|
betriebsstunden: Mapped[Decimal | None] = mapped_column(Numeric)
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class ObjektUpdate(BaseModel):
|
|||||||
class FahrzeugdetailsUpsert(BaseModel):
|
class FahrzeugdetailsUpsert(BaseModel):
|
||||||
kennzeichen: str | None = None
|
kennzeichen: str | None = None
|
||||||
funkrufname: str | None = None
|
funkrufname: str | None = None
|
||||||
|
funkkenner: str | None = None
|
||||||
baujahr: int | None = None
|
baujahr: int | None = None
|
||||||
kilometerstand: int | None = None
|
kilometerstand: int | None = None
|
||||||
betriebsstunden: Decimal | None = None
|
betriebsstunden: Decimal | None = None
|
||||||
@@ -50,6 +51,7 @@ class FahrzeugdetailsRead(BaseModel):
|
|||||||
objekt_id: int
|
objekt_id: int
|
||||||
kennzeichen: str | None
|
kennzeichen: str | None
|
||||||
funkrufname: str | None
|
funkrufname: str | None
|
||||||
|
funkkenner: str | None
|
||||||
baujahr: int | None
|
baujahr: int | None
|
||||||
kilometerstand: int | None
|
kilometerstand: int | None
|
||||||
betriebsstunden: Decimal | None
|
betriebsstunden: Decimal | None
|
||||||
|
|||||||
@@ -15,12 +15,18 @@ async def test_fahrzeugdetails_upsert(client, objekt_mit_position, admin_user):
|
|||||||
|
|
||||||
gesetzt = await client.put(
|
gesetzt = await client.put(
|
||||||
f"/api/v1/objekte/{objekt.id}/fahrzeugdetails",
|
f"/api/v1/objekte/{objekt.id}/fahrzeugdetails",
|
||||||
json={"kennzeichen": "XX-YY 123", "kilometerstand": 42315, "naechste_hu": "2027-05-01"},
|
json={
|
||||||
|
"kennzeichen": "XX-YY 123",
|
||||||
|
"kilometerstand": 42315,
|
||||||
|
"naechste_hu": "2027-05-01",
|
||||||
|
"funkkenner": "OPTA-12345",
|
||||||
|
},
|
||||||
headers=auth_header(token),
|
headers=auth_header(token),
|
||||||
)
|
)
|
||||||
assert gesetzt.status_code == 200
|
assert gesetzt.status_code == 200
|
||||||
assert gesetzt.json()["kennzeichen"] == "XX-YY 123"
|
assert gesetzt.json()["kennzeichen"] == "XX-YY 123"
|
||||||
assert gesetzt.json()["kilometerstand"] == 42315
|
assert gesetzt.json()["kilometerstand"] == 42315
|
||||||
|
assert gesetzt.json()["funkkenner"] == "OPTA-12345"
|
||||||
|
|
||||||
aktualisiert = await client.put(
|
aktualisiert = await client.put(
|
||||||
f"/api/v1/objekte/{objekt.id}/fahrzeugdetails",
|
f"/api/v1/objekte/{objekt.id}/fahrzeugdetails",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ export interface Fahrzeugdetails {
|
|||||||
objekt_id: number;
|
objekt_id: number;
|
||||||
kennzeichen: string | null;
|
kennzeichen: string | null;
|
||||||
funkrufname: string | null;
|
funkrufname: string | null;
|
||||||
|
funkkenner: string | null;
|
||||||
baujahr: number | null;
|
baujahr: number | null;
|
||||||
kilometerstand: number | null;
|
kilometerstand: number | null;
|
||||||
betriebsstunden: string | null;
|
betriebsstunden: string | null;
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ export function ObjektSection({
|
|||||||
body: {
|
body: {
|
||||||
kennzeichen: details.kennzeichen || null,
|
kennzeichen: details.kennzeichen || null,
|
||||||
funkrufname: details.funkrufname || null,
|
funkrufname: details.funkrufname || null,
|
||||||
|
funkkenner: details.funkkenner || null,
|
||||||
baujahr: details.baujahr || null,
|
baujahr: details.baujahr || null,
|
||||||
kilometerstand: details.kilometerstand || null,
|
kilometerstand: details.kilometerstand || null,
|
||||||
betriebsstunden: details.betriebsstunden || null,
|
betriebsstunden: details.betriebsstunden || null,
|
||||||
@@ -431,6 +432,12 @@ export function ObjektSection({
|
|||||||
value={details.funkrufname ?? ""}
|
value={details.funkrufname ?? ""}
|
||||||
onChange={(e) => setDetails((d) => ({ ...d, funkrufname: e.target.value }))}
|
onChange={(e) => setDetails((d) => ({ ...d, funkrufname: e.target.value }))}
|
||||||
/>
|
/>
|
||||||
|
<input
|
||||||
|
className="input"
|
||||||
|
placeholder="Funkkenner (BOS-Digitalfunk)"
|
||||||
|
value={details.funkkenner ?? ""}
|
||||||
|
onChange={(e) => setDetails((d) => ({ ...d, funkkenner: e.target.value }))}
|
||||||
|
/>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<input
|
<input
|
||||||
className="input"
|
className="input"
|
||||||
|
|||||||
Reference in New Issue
Block a user