added extra field kwh at m3, expand cost field to 10 komma, added maloid,counter add dialog, auto set unit

This commit is contained in:
2026-02-05 20:34:45 +01:00
parent af2f444a24
commit b281801cdb
22 changed files with 329 additions and 165 deletions
+30 -19
View File
@@ -9,7 +9,6 @@ import Card from '../../components/ui/Card';
import Button from '../../components/ui/Button';
import Badge from '../../components/ui/Badge';
import Input from '../../components/ui/Input';
import Select from '../../components/ui/Select';
import Modal from '../../components/ui/Modal';
import FileUpload from '../../components/ui/FileUpload';
import { Edit, Trash2, Copy, Eye, EyeOff, ArrowLeft, ArrowRight, Download, ExternalLink, Plus, ChevronDown, ChevronUp, Gauge, CheckCircle, Circle, ClipboardList, MessageSquare } from 'lucide-react';
@@ -320,7 +319,6 @@ function MeterReadingModal({
? new Date(reading.readingDate).toISOString().split('T')[0]
: new Date().toISOString().split('T')[0],
value: reading?.value?.toString() || '',
unit: reading?.unit || defaultUnit,
notes: reading?.notes || '',
});
@@ -345,7 +343,7 @@ function MeterReadingModal({
const data = {
readingDate: new Date(formData.readingDate),
value: parseFloat(formData.value),
unit: formData.unit,
unit: defaultUnit,
notes: formData.notes || undefined,
};
if (isEditing) {
@@ -379,15 +377,12 @@ function MeterReadingModal({
required
/>
</div>
<Select
label="Einheit"
value={formData.unit}
onChange={(e) => setFormData({ ...formData, unit: e.target.value })}
options={[
{ value: 'kWh', label: 'kWh' },
{ value: 'm³', label: 'm³' },
]}
/>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Einheit</label>
<div className="h-10 flex items-center px-3 bg-gray-100 border border-gray-300 rounded-md text-gray-700">
{defaultUnit}
</div>
</div>
</div>
<Input
@@ -1931,27 +1926,43 @@ export default function ContractDetail() {
</dd>
</div>
)}
{c.energyDetails.maloId && (
<div>
<dt className="text-sm text-gray-500">MaLo-ID</dt>
<dd className="font-mono flex items-center gap-1">
{c.energyDetails.maloId}
<CopyButton value={c.energyDetails.maloId} />
</dd>
</div>
)}
{c.energyDetails.annualConsumption && (
<div>
<dt className="text-sm text-gray-500">Jahresverbrauch</dt>
<dt className="text-sm text-gray-500">
Jahresverbrauch {c.type === 'ELECTRICITY' ? '' : '(m³)'}
</dt>
<dd>
{c.energyDetails.annualConsumption.toLocaleString('de-DE')}{' '}
{c.type === 'ELECTRICITY' ? 'kWh' : 'm³'}
</dd>
</div>
)}
{c.energyDetails.basePrice && (
{c.type === 'GAS' && c.energyDetails.annualConsumptionKwh && (
<div>
<dt className="text-sm text-gray-500">Grundpreis</dt>
<dd>{c.energyDetails.basePrice.toLocaleString('de-DE')} /Monat</dd>
<dt className="text-sm text-gray-500">Jahresverbrauch (kWh)</dt>
<dd>{c.energyDetails.annualConsumptionKwh.toLocaleString('de-DE')} kWh</dd>
</div>
)}
{c.energyDetails.unitPrice && (
{c.energyDetails.basePrice != null && (
<div>
<dt className="text-sm text-gray-500">Grundpreis</dt>
<dd>{c.energyDetails.basePrice.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 10 })} /Monat</dd>
</div>
)}
{c.energyDetails.unitPrice != null && (
<div>
<dt className="text-sm text-gray-500">Arbeitspreis</dt>
<dd>
{c.energyDetails.unitPrice.toLocaleString('de-DE')} ct/
{c.type === 'ELECTRICITY' ? 'kWh' : 'm³'}
{c.energyDetails.unitPrice.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 10 })} /kWh
</dd>
</div>
)}
+18 -3
View File
@@ -228,7 +228,9 @@ export default function ContractForm() {
notes: c.notes || '',
// Energy details
meterId: c.energyDetails?.meterId?.toString() || '',
maloId: c.energyDetails?.maloId || '',
annualConsumption: c.energyDetails?.annualConsumption || '',
annualConsumptionKwh: c.energyDetails?.annualConsumptionKwh || '',
basePrice: c.energyDetails?.basePrice || '',
unitPrice: c.energyDetails?.unitPrice || '',
bonus: c.energyDetails?.bonus || '',
@@ -455,7 +457,9 @@ export default function ContractForm() {
if (['ELECTRICITY', 'GAS'].includes(data.type)) {
contractData.energyDetails = {
meterId: safeParseInt(data.meterId) ?? null,
maloId: emptyToNull(data.maloId),
annualConsumption: data.annualConsumption ? parseFloat(data.annualConsumption) : null,
annualConsumptionKwh: data.annualConsumptionKwh ? parseFloat(data.annualConsumptionKwh) : null,
basePrice: data.basePrice ? parseFloat(data.basePrice) : null,
unitPrice: data.unitPrice ? parseFloat(data.unitPrice) : null,
bonus: data.bonus ? parseFloat(data.bonus) : null,
@@ -864,16 +868,27 @@ export default function ContractForm() {
label: `${m.meterNumber}${m.location ? ` (${m.location})` : ''}`,
}))}
/>
<Input
label="MaLo-ID (Marktlokations-ID)"
{...register('maloId')}
/>
<Input
label={`Jahresverbrauch (${contractType === 'ELECTRICITY' ? 'kWh' : 'm³'})`}
type="number"
{...register('annualConsumption')}
/>
<Input label="Grundpreis (€/Monat)" type="number" step="0.01" {...register('basePrice')} />
{contractType === 'GAS' && (
<Input
label="Jahresverbrauch (kWh)"
type="number"
{...register('annualConsumptionKwh')}
/>
)}
<Input label="Grundpreis (€/Monat)" type="number" step="any" {...register('basePrice')} />
<Input
label={`Arbeitspreis (ct/${contractType === 'ELECTRICITY' ? 'kWh' : 'm³'})`}
label="Arbeitspreis (€/kWh)"
type="number"
step="0.01"
step="any"
{...register('unitPrice')}
/>
<Input label="Bonus (€)" type="number" step="0.01" {...register('bonus')} />
+17 -16
View File
@@ -1256,9 +1256,9 @@ function MetersTab({
onAdd: () => void;
onEdit: (meter: Meter) => void;
}) {
const [showReadingModal, setShowReadingModal] = useState<number | null>(null);
const [showReadingModal, setShowReadingModal] = useState<{ meterId: number; meterType: 'ELECTRICITY' | 'GAS' } | null>(null);
const [expandedMeter, setExpandedMeter] = useState<number | null>(null);
const [editingReading, setEditingReading] = useState<{ meterId: number; reading: any } | null>(null);
const [editingReading, setEditingReading] = useState<{ meterId: number; meterType: 'ELECTRICITY' | 'GAS'; reading: any } | null>(null);
const queryClient = useQueryClient();
const updateMutation = useMutation({
@@ -1333,7 +1333,7 @@ function MetersTab({
<Button
variant="ghost"
size="sm"
onClick={() => setShowReadingModal(meter.id)}
onClick={() => setShowReadingModal({ meterId: meter.id, meterType: meter.type })}
title="Zählerstand hinzufügen"
>
<Plus className="w-4 h-4" />
@@ -1430,7 +1430,7 @@ function MetersTab({
{canEdit && (
<div className="opacity-0 group-hover:opacity-100 flex gap-1">
<button
onClick={() => setEditingReading({ meterId: meter.id, reading })}
onClick={() => setEditingReading({ meterId: meter.id, meterType: meter.type, reading })}
className="text-gray-400 hover:text-blue-600"
title="Bearbeiten"
>
@@ -1467,7 +1467,8 @@ function MetersTab({
<MeterReadingModal
isOpen={true}
onClose={() => setShowReadingModal(null)}
meterId={showReadingModal}
meterId={showReadingModal.meterId}
meterType={showReadingModal.meterType}
customerId={customerId}
/>
)}
@@ -1477,6 +1478,7 @@ function MetersTab({
isOpen={true}
onClose={() => setEditingReading(null)}
meterId={editingReading.meterId}
meterType={editingReading.meterType}
customerId={customerId}
reading={editingReading.reading}
/>
@@ -2601,24 +2603,26 @@ function MeterReadingModal({
isOpen,
onClose,
meterId,
meterType,
customerId,
reading,
}: {
isOpen: boolean;
onClose: () => void;
meterId: number;
meterType: 'ELECTRICITY' | 'GAS';
customerId: number;
reading?: { id: number; readingDate: string; value: number; unit: string; notes?: string } | null;
}) {
const queryClient = useQueryClient();
const isEditing = !!reading;
const defaultUnit = meterType === 'ELECTRICITY' ? 'kWh' : 'm³';
const getInitialFormData = () => ({
readingDate: reading?.readingDate
? new Date(reading.readingDate).toISOString().split('T')[0]
: new Date().toISOString().split('T')[0],
value: reading?.value?.toString() || '',
unit: reading?.unit || 'kWh',
notes: reading?.notes || '',
});
@@ -2645,7 +2649,7 @@ function MeterReadingModal({
const data = {
readingDate: new Date(formData.readingDate),
value: parseFloat(formData.value),
unit: formData.unit,
unit: defaultUnit,
notes: formData.notes || undefined,
};
if (isEditing) {
@@ -2684,15 +2688,12 @@ function MeterReadingModal({
required
/>
</div>
<Select
label="Einheit"
value={formData.unit}
onChange={(e) => setFormData({ ...formData, unit: e.target.value })}
options={[
{ value: 'kWh', label: 'kWh' },
{ value: 'm³', label: 'm³' },
]}
/>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Einheit</label>
<div className="h-10 flex items-center px-3 bg-gray-100 border border-gray-300 rounded-md text-gray-700">
{defaultUnit}
</div>
</div>
</div>
<Input
+5 -3
View File
@@ -336,9 +336,11 @@ export interface EnergyContractDetails {
contractId: number;
meterId?: number;
meter?: Meter;
annualConsumption?: number;
basePrice?: number;
unitPrice?: number;
maloId?: string; // Marktlokations-ID
annualConsumption?: number; // kWh für Strom, m³ für Gas
annualConsumptionKwh?: number; // kWh für Gas (zusätzlich zu m³)
basePrice?: number; // €/Monat
unitPrice?: number; // €/kWh (Arbeitspreis)
bonus?: number;
previousProviderName?: string;
previousCustomerNumber?: string;