SIM-Karten: Feld "Kartennutzer" für Firmen-/Familienverträge
Bei Firmenverträgen (Vertragsinhaber = Firma, Nutzer = Mitarbeiter) und Familienverträgen (Inhaber = Eltern, Nutzer = Kind) brauchten wir ein Feld, das den tatsächlichen Nutzer der SIM-Karte erfasst. Backend: SimCard.cardUser (String?, optional), Migration 20260601100000_sim_card_user mit IF NOT EXISTS. Im Service durch Create + Update propagiert. Frontend: Input "Kartennutzer" pro SIM-Karte in ContractForm (eigene Zeile oberhalb der technischen Felder Rufnummer/SIM-Nr/ PIN/PUK). In ContractDetail wird der Nutzer als "Nutzer: <Name>" neben den Hauptkarte/Multisim-Badges angezeigt. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
-- SIM-Karte bekommt einen optionalen "Kartennutzer" – relevant bei Firmen-
|
||||
-- und Familienverträgen, wo der Vertragsinhaber (Firma/Eltern) nicht
|
||||
-- gleich dem tatsächlichen Nutzer (Mitarbeiter/Kind) ist.
|
||||
--
|
||||
-- IF NOT EXISTS macht den Re-Deploy auf Prod sicher, falls jemand schon
|
||||
-- `prisma db push` gefahren hat.
|
||||
|
||||
ALTER TABLE `SimCard`
|
||||
ADD COLUMN IF NOT EXISTS `cardUser` VARCHAR(191) NULL;
|
||||
@@ -928,6 +928,9 @@ model SimCard {
|
||||
puk String? // PUK (verschlüsselt gespeichert)
|
||||
isMultisim Boolean @default(false) // Ist dies eine Multisim-Karte?
|
||||
isMain Boolean @default(false) // Ist dies die Hauptkarte?
|
||||
// Tatsächlicher Nutzer der SIM-Karte (z.B. Mitarbeiter bei Firmenverträgen,
|
||||
// Kind bei Eltern-Vertrag) – kann vom Vertragsinhaber abweichen.
|
||||
cardUser String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
@@ -275,6 +275,7 @@ interface ContractCreateData {
|
||||
puk?: string;
|
||||
isMultisim?: boolean;
|
||||
isMain?: boolean;
|
||||
cardUser?: string;
|
||||
}[];
|
||||
};
|
||||
tvDetails?: {
|
||||
@@ -378,6 +379,7 @@ export async function createContract(data: ContractCreateData) {
|
||||
puk: sc.puk ? encrypt(sc.puk) : undefined,
|
||||
isMultisim: sc.isMultisim ?? false,
|
||||
isMain: sc.isMain ?? false,
|
||||
cardUser: sc.cardUser,
|
||||
})),
|
||||
}
|
||||
: undefined,
|
||||
@@ -614,6 +616,7 @@ export async function updateContract(
|
||||
puk: sc.puk ? encrypt(sc.puk) : (existingSc?.puk ?? undefined),
|
||||
isMultisim: sc.isMultisim ?? false,
|
||||
isMain: sc.isMain ?? false,
|
||||
cardUser: sc.cardUser,
|
||||
};
|
||||
}),
|
||||
});
|
||||
@@ -632,6 +635,7 @@ export async function updateContract(
|
||||
puk: sc.puk ? encrypt(sc.puk) : undefined,
|
||||
isMultisim: sc.isMultisim ?? false,
|
||||
isMain: sc.isMain ?? false,
|
||||
cardUser: sc.cardUser,
|
||||
})),
|
||||
}
|
||||
: undefined,
|
||||
|
||||
@@ -134,9 +134,15 @@ function SimCardDisplay({ simCard }: { simCard: SimCard }) {
|
||||
|
||||
return (
|
||||
<div className="p-3 bg-gray-50 rounded-lg border">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="flex items-center gap-2 mb-2 flex-wrap">
|
||||
{simCard.isMain && <Badge variant="success">Hauptkarte</Badge>}
|
||||
{simCard.isMultisim && <Badge variant="warning">Multisim</Badge>}
|
||||
{simCard.cardUser && (
|
||||
<span className="text-sm text-gray-700">
|
||||
<span className="text-gray-500">Nutzer:</span>{' '}
|
||||
<span className="font-medium">{simCard.cardUser}</span>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<dl className="grid grid-cols-2 md:grid-cols-4 gap-3 text-sm">
|
||||
{simCard.phoneNumber && (
|
||||
|
||||
@@ -192,6 +192,7 @@ export default function ContractForm() {
|
||||
hasExistingPuk?: boolean; // Zeigt an ob PUK bereits in DB vorhanden
|
||||
isMultisim: boolean;
|
||||
isMain: boolean;
|
||||
cardUser: string;
|
||||
}
|
||||
const [simCards, setSimCards] = useState<SimCardInput[]>([]);
|
||||
|
||||
@@ -379,6 +380,7 @@ export default function ContractForm() {
|
||||
hasExistingPuk: !!sc.puk, // true wenn verschlüsselter Wert vorhanden
|
||||
isMultisim: sc.isMultisim,
|
||||
isMain: sc.isMain,
|
||||
cardUser: sc.cardUser || '',
|
||||
})));
|
||||
} else {
|
||||
setSimCards([]);
|
||||
@@ -610,6 +612,7 @@ export default function ContractForm() {
|
||||
puk: sc.puk || undefined, // Passwort: undefined = nicht ändern
|
||||
isMultisim: sc.isMultisim,
|
||||
isMain: sc.isMain,
|
||||
cardUser: emptyToNull(sc.cardUser),
|
||||
})) : undefined,
|
||||
};
|
||||
}
|
||||
@@ -1490,6 +1493,18 @@ export default function ContractForm() {
|
||||
<Trash2 className="w-4 h-4 text-red-500" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<Input
|
||||
label="Kartennutzer"
|
||||
value={card.cardUser}
|
||||
onChange={(e) => {
|
||||
const updated = [...simCards];
|
||||
updated[index].cardUser = e.target.value;
|
||||
setSimCards(updated);
|
||||
}}
|
||||
placeholder="z.B. Mitarbeiter-/Kind-Name (optional, kann vom Vertragsinhaber abweichen)"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3">
|
||||
<Input
|
||||
label="Rufnummer"
|
||||
@@ -1578,6 +1593,7 @@ export default function ContractForm() {
|
||||
puk: '',
|
||||
isMultisim: false,
|
||||
isMain: simCards.length === 0, // Erste Karte ist Hauptkarte
|
||||
cardUser: '',
|
||||
}]);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -557,6 +557,9 @@ export interface SimCard {
|
||||
puk?: string; // verschlüsselt
|
||||
isMultisim: boolean;
|
||||
isMain: boolean;
|
||||
// Tatsächlicher Nutzer der Karte (z.B. Mitarbeiter bei Firmenvertrag,
|
||||
// Kind bei Eltern-Vertrag) – optional, kann vom Vertragsinhaber abweichen.
|
||||
cardUser?: string;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user