/** * Import-Script: Excel → Supabase (Wasserzähler) * * Liest zaehlerablesen_2025.xlsx und importiert Kundendaten in die * Supabase-Tabelle `wasserzaehler`. Generiert pro Zeile einen access_token. * * Verwendung: * npx tsx scripts/import-zaehler.ts [pfad-zur-excel-datei] * * Erwartet .env.local im Projekt-Root mit: * NEXT_PUBLIC_SUPABASE_URL * SUPABASE_SERVICE_ROLE_KEY * NEXT_PUBLIC_BASE_URL (optional, default: https://gemeindeportal.vercel.app) */ import { readFileSync, existsSync } from 'fs'; import { resolve } from 'path'; import { randomUUID } from 'crypto'; import * as XLSX from 'xlsx'; import { createClient } from '@supabase/supabase-js'; // .env.local laden function loadEnv() { const envPath = resolve(__dirname, '..', '.env.local'); if (!existsSync(envPath)) { console.error('FEHLER: .env.local nicht gefunden unter:', envPath); process.exit(1); } const content = readFileSync(envPath, 'utf-8'); for (const line of content.split('\n')) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith('#')) continue; const eqIndex = trimmed.indexOf('='); if (eqIndex === -1) continue; const key = trimmed.slice(0, eqIndex).trim(); const value = trimmed.slice(eqIndex + 1).trim(); if (!process.env[key]) { process.env[key] = value; } } } loadEnv(); const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL; const SERVICE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY; const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || 'https://gemeindeportal.vercel.app'; if (!SUPABASE_URL || !SERVICE_KEY) { console.error('FEHLER: NEXT_PUBLIC_SUPABASE_URL und SUPABASE_SERVICE_ROLE_KEY müssen gesetzt sein.'); process.exit(1); } const supabase = createClient(SUPABASE_URL, SERVICE_KEY); interface ExcelRow { Kundennummer: string; Zählernummer: string; Name: string; Adresse: string; 'Letzter Stand (m³)': number; [key: string]: unknown; } async function main() { const excelPath = process.argv[2] || resolve(__dirname, '..', 'zaehlerablesen_2025.xlsx'); if (!existsSync(excelPath)) { console.error(`FEHLER: Excel-Datei nicht gefunden: ${excelPath}`); console.error('Verwendung: npx tsx scripts/import-zaehler.ts [pfad-zur-excel-datei]'); process.exit(1); } console.log(`Lese Excel-Datei: ${excelPath}\n`); const workbook = XLSX.readFile(excelPath); const sheetName = workbook.SheetNames[0]; const sheet = workbook.Sheets[sheetName]; const rows = XLSX.utils.sheet_to_json(sheet); if (rows.length === 0) { console.error('FEHLER: Keine Daten in der Excel-Datei gefunden.'); process.exit(1); } console.log(`${rows.length} Zeile(n) gefunden.\n`); const results: { kundennummer: string; name: string; token: string; url: string }[] = []; for (const row of rows) { const kundennummer = String(row.Kundennummer || '').trim(); const zaehlernummer = String(row.Zählernummer || row['Zaehlernummer'] || '').trim(); const name = String(row.Name || '').trim(); const adresse = String(row.Adresse || '').trim(); const alterStandRaw = row['Letzter Stand (m³)'] ?? row['Letzter Stand'] ?? 0; const alter_stand = typeof alterStandRaw === 'number' ? alterStandRaw : parseFloat(String(alterStandRaw)) || 0; if (!kundennummer || !zaehlernummer) { console.warn(`WARNUNG: Zeile übersprungen (fehlende Kundennummer/Zählernummer):`, row); continue; } const access_token = randomUUID(); const { error } = await supabase.from('wasserzaehler').insert({ kundennummer, zaehlernummer, haushalt_name: name, adresse, alter_stand, access_token, }); if (error) { console.error(`FEHLER bei Kundennr. ${kundennummer}:`, error.message); continue; } const url = `${BASE_URL}/wasserzaehler?token=${access_token}`; results.push({ kundennummer, name, token: access_token, url }); console.log(`OK: ${kundennummer} — ${name}`); } console.log('\n' + '='.repeat(80)); console.log('ERGEBNIS — QR-Code URLs'); console.log('='.repeat(80)); console.log('\n%-15s %-25s %-40s'.replace('%-15s', 'Kundennr.').replace('%-25s', 'Name').replace('%-40s', 'URL')); console.log('-'.repeat(80)); for (const r of results) { console.log(`${r.kundennummer.padEnd(15)} ${r.name.padEnd(25)} ${r.url}`); } console.log(`\n${results.length} von ${rows.length} Einträgen erfolgreich importiert.`); console.log(`\nAdmin-Seite mit QR-Codes: ${BASE_URL}/admin/qrcodes`); } main().catch((err) => { console.error('Unerwarteter Fehler:', err); process.exit(1); });