From 2ee06630b93c1f1ed2b0f785b52ac7ed18db5dbc Mon Sep 17 00:00:00 2001
From: duffyduck
Date: Sat, 30 May 2026 14:39:05 +0200
Subject: [PATCH] =?UTF-8?q?Folgez=C3=A4hler-Forms:=20Checkbox=20"Alten=20Z?=
=?UTF-8?q?=C3=A4hler=20deaktivieren"=20(default=20an)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
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)
---
backend/src/controllers/contract.controller.ts | 11 ++++++++++-
backend/src/services/customer.service.ts | 14 ++++++++++++++
frontend/src/pages/contracts/ContractDetail.tsx | 14 +++++++++++++-
frontend/src/pages/customers/CustomerDetail.tsx | 11 +++++++++++
frontend/src/services/api.ts | 2 +-
5 files changed, 49 insertions(+), 3 deletions(-)
diff --git a/backend/src/controllers/contract.controller.ts b/backend/src/controllers/contract.controller.ts
index 17fa3247..730b58c5 100644
--- a/backend/src/controllers/contract.controller.ts
+++ b/backend/src/controllers/contract.controller.ts
@@ -539,7 +539,7 @@ export async function getCockpit(req: AuthRequest, res: Response): Promise
export async function addSuccessorMeter(req: AuthRequest, res: Response): Promise {
try {
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({
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({
req, action: 'CREATE', resourceType: 'ContractMeter',
resourceId: contractMeter.id.toString(),
diff --git a/backend/src/services/customer.service.ts b/backend/src/services/customer.service.ts
index 5868e7f4..918f02d5 100644
--- a/backend/src/services/customer.service.ts
+++ b/backend/src/services/customer.service.ts
@@ -488,6 +488,10 @@ export async function createMeter(
predecessorMeterId: number;
installedAt?: string;
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,
);
}
+
+ // 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;
diff --git a/frontend/src/pages/contracts/ContractDetail.tsx b/frontend/src/pages/contracts/ContractDetail.tsx
index 10fa9995..ed90726f 100644
--- a/frontend/src/pages/contracts/ContractDetail.tsx
+++ b/frontend/src/pages/contracts/ContractDetail.tsx
@@ -361,6 +361,7 @@ function SuccessorMeterButton({
const [selectedMeterId, setSelectedMeterId] = useState('');
const [installedAt, setInstalledAt] = useState(new Date().toISOString().split('T')[0]);
const [finalReading, setFinalReading] = useState('');
+ const [deactivatePredecessor, setDeactivatePredecessor] = useState(true);
const queryClient = useQueryClient();
const { data: metersData } = useQuery({
@@ -370,13 +371,14 @@ function SuccessorMeterButton({
});
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),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['contract'] });
setShowForm(false);
setSelectedMeterId('');
setFinalReading('');
+ setDeactivatePredecessor(true);
},
});
@@ -451,6 +453,15 @@ function SuccessorMeterButton({
erfasst und fließt damit in die Verbrauchsberechnung ein.
)}
+