Folgezähler-Forms: Checkbox "Alten Zähler deaktivieren" (default an)
Beide Folgezähler-Forms (Kundenakte MeterModal + Vertragsansicht SuccessorMeterForm) bekommen eine Checkbox, die standardmäßig angehakt ist. Beim Speichern wird der Vorgänger automatisch auf isActive=false gesetzt – ein-klick-fähiger Zählerwechsel. Backend: createMeter mit successorOf und addSuccessorMeter akzeptieren deactivatePredecessor (Default true). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -539,7 +539,7 @@ export async function getCockpit(req: AuthRequest, res: Response): Promise<void>
|
|||||||
export async function addSuccessorMeter(req: AuthRequest, res: Response): Promise<void> {
|
export async function addSuccessorMeter(req: AuthRequest, res: Response): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const contractId = parseInt(req.params.id);
|
const contractId = parseInt(req.params.id);
|
||||||
const { meterId, installedAt, finalReadingPrevious } = req.body;
|
const { meterId, installedAt, finalReadingPrevious, deactivatePredecessor } = req.body;
|
||||||
|
|
||||||
const contract = await prisma.contract.findUnique({
|
const contract = await prisma.contract.findUnique({
|
||||||
where: { id: contractId },
|
where: { id: contractId },
|
||||||
@@ -648,6 +648,15 @@ export async function addSuccessorMeter(req: AuthRequest, res: Response): Promis
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Alten Zähler deaktivieren (Default), sofern der Aufrufer das nicht
|
||||||
|
// explizit auf false setzt – ein-klick-fähiger Zählerwechsel.
|
||||||
|
if (predecessorMeterId && deactivatePredecessor !== false) {
|
||||||
|
await prisma.meter.update({
|
||||||
|
where: { id: predecessorMeterId },
|
||||||
|
data: { isActive: false },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await logChange({
|
await logChange({
|
||||||
req, action: 'CREATE', resourceType: 'ContractMeter',
|
req, action: 'CREATE', resourceType: 'ContractMeter',
|
||||||
resourceId: contractMeter.id.toString(),
|
resourceId: contractMeter.id.toString(),
|
||||||
|
|||||||
@@ -488,6 +488,10 @@ export async function createMeter(
|
|||||||
predecessorMeterId: number;
|
predecessorMeterId: number;
|
||||||
installedAt?: string;
|
installedAt?: string;
|
||||||
finalReadingPrevious?: number;
|
finalReadingPrevious?: number;
|
||||||
|
// Default true im UI: alter Zähler wird nach dem Wechsel auf
|
||||||
|
// isActive=false gesetzt. Kann ausgeschaltet werden, wenn der alte
|
||||||
|
// Zähler aus irgendeinem Grund noch aktiv bleiben soll.
|
||||||
|
deactivatePredecessor?: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -622,6 +626,16 @@ export async function createMeter(
|
|||||||
data.successorOf.finalReadingPrevious,
|
data.successorOf.finalReadingPrevious,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Alten Zähler deaktivieren (Default), sofern der Aufrufer das nicht
|
||||||
|
// explizit auf false setzt. Macht den typischen Zählerwechsel-Workflow
|
||||||
|
// ein-klick-fähig.
|
||||||
|
if (data.successorOf.deactivatePredecessor !== false) {
|
||||||
|
await prisma.meter.update({
|
||||||
|
where: { id: predecessor.id },
|
||||||
|
data: { isActive: false },
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return created;
|
return created;
|
||||||
|
|||||||
@@ -361,6 +361,7 @@ function SuccessorMeterButton({
|
|||||||
const [selectedMeterId, setSelectedMeterId] = useState('');
|
const [selectedMeterId, setSelectedMeterId] = useState('');
|
||||||
const [installedAt, setInstalledAt] = useState(new Date().toISOString().split('T')[0]);
|
const [installedAt, setInstalledAt] = useState(new Date().toISOString().split('T')[0]);
|
||||||
const [finalReading, setFinalReading] = useState('');
|
const [finalReading, setFinalReading] = useState('');
|
||||||
|
const [deactivatePredecessor, setDeactivatePredecessor] = useState(true);
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const { data: metersData } = useQuery({
|
const { data: metersData } = useQuery({
|
||||||
@@ -370,13 +371,14 @@ function SuccessorMeterButton({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const addMutation = useMutation({
|
const addMutation = useMutation({
|
||||||
mutationFn: (data: { meterId: number; installedAt?: string; finalReadingPrevious?: number }) =>
|
mutationFn: (data: { meterId: number; installedAt?: string; finalReadingPrevious?: number; deactivatePredecessor?: boolean }) =>
|
||||||
contractApi.addSuccessorMeter(contractId, data),
|
contractApi.addSuccessorMeter(contractId, data),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ['contract'] });
|
queryClient.invalidateQueries({ queryKey: ['contract'] });
|
||||||
setShowForm(false);
|
setShowForm(false);
|
||||||
setSelectedMeterId('');
|
setSelectedMeterId('');
|
||||||
setFinalReading('');
|
setFinalReading('');
|
||||||
|
setDeactivatePredecessor(true);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -451,6 +453,15 @@ function SuccessorMeterButton({
|
|||||||
erfasst und fließt damit in die Verbrauchsberechnung ein.
|
erfasst und fließt damit in die Verbrauchsberechnung ein.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
<label className="flex items-center gap-2 mt-3 text-sm">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={deactivatePredecessor}
|
||||||
|
onChange={(e) => setDeactivatePredecessor(e.target.checked)}
|
||||||
|
className="rounded"
|
||||||
|
/>
|
||||||
|
Alten Zähler deaktivieren
|
||||||
|
</label>
|
||||||
<div className="flex gap-2 mt-3">
|
<div className="flex gap-2 mt-3">
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -458,6 +469,7 @@ function SuccessorMeterButton({
|
|||||||
meterId: parseInt(selectedMeterId),
|
meterId: parseInt(selectedMeterId),
|
||||||
installedAt,
|
installedAt,
|
||||||
finalReadingPrevious: finalReading ? parseFloat(finalReading) : undefined,
|
finalReadingPrevious: finalReading ? parseFloat(finalReading) : undefined,
|
||||||
|
deactivatePredecessor,
|
||||||
})}
|
})}
|
||||||
disabled={!selectedMeterId || addMutation.isPending}
|
disabled={!selectedMeterId || addMutation.isPending}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -2854,6 +2854,7 @@ function MeterModal({
|
|||||||
predecessorMeterId: '',
|
predecessorMeterId: '',
|
||||||
installedAt: today,
|
installedAt: today,
|
||||||
finalReadingPrevious: '',
|
finalReadingPrevious: '',
|
||||||
|
deactivatePredecessor: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [formData, setFormData] = useState(getInitialFormData);
|
const [formData, setFormData] = useState(getInitialFormData);
|
||||||
@@ -2919,6 +2920,7 @@ function MeterModal({
|
|||||||
finalReadingPrevious: formData.finalReadingPrevious
|
finalReadingPrevious: formData.finalReadingPrevious
|
||||||
? parseFloat(formData.finalReadingPrevious)
|
? parseFloat(formData.finalReadingPrevious)
|
||||||
: undefined,
|
: undefined,
|
||||||
|
deactivatePredecessor: formData.deactivatePredecessor,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
@@ -3037,6 +3039,15 @@ function MeterModal({
|
|||||||
onChange={(e) => setFormData({ ...formData, finalReadingPrevious: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, finalReadingPrevious: e.target.value })}
|
||||||
placeholder="Optional"
|
placeholder="Optional"
|
||||||
/>
|
/>
|
||||||
|
<label className="flex items-center gap-2 text-sm">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={formData.deactivatePredecessor}
|
||||||
|
onChange={(e) => setFormData({ ...formData, deactivatePredecessor: e.target.checked })}
|
||||||
|
className="rounded"
|
||||||
|
/>
|
||||||
|
Alten Zähler deaktivieren
|
||||||
|
</label>
|
||||||
<p className="text-xs text-blue-700">
|
<p className="text-xs text-blue-700">
|
||||||
Typ, Adresse und Tarifmodell werden vom Vorgänger übernommen. Alle Verträge,
|
Typ, Adresse und Tarifmodell werden vom Vorgänger übernommen. Alle Verträge,
|
||||||
die den Vorgänger-Zähler verwenden, werden automatisch auf diesen neuen Zähler
|
die den Vorgänger-Zähler verwenden, werden automatisch auf diesen neuen Zähler
|
||||||
|
|||||||
@@ -837,7 +837,7 @@ export const contractApi = {
|
|||||||
return res.data;
|
return res.data;
|
||||||
},
|
},
|
||||||
// Folgezähler
|
// Folgezähler
|
||||||
addSuccessorMeter: async (contractId: number, data: { meterId: number; installedAt?: string; finalReadingPrevious?: number }) => {
|
addSuccessorMeter: async (contractId: number, data: { meterId: number; installedAt?: string; finalReadingPrevious?: number; deactivatePredecessor?: boolean }) => {
|
||||||
const res = await api.post<ApiResponse<any>>(`/contracts/${contractId}/successor-meter`, data);
|
const res = await api.post<ApiResponse<any>>(`/contracts/${contractId}/successor-meter`, data);
|
||||||
return res.data;
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user