feat: Reale Jahresbelastung, must-have fixes, fitOut data coverage

Reale Jahresbelastung (Change 6):
- FitOutCostPanel zeigt Jahresmiete + amortisierte Ausbaukosten für alle fitOut-Werte
- FULL/PREMIUM: Ausbau CHF 0 (bezugsfertig), SHELL/BASIC: CRB/BKP-Richtwerte amortisiert über 5 J.
- Match-Card-Chip zeigt geschätzte Investition (orange wenn >100k)
- FitOutInvestment-Typ + calcFitOutInvestment() in fitOutUtils.ts
- BackendAIService + MockAIService mit generateFitOutAdvice (IAIService-Interface)

Must-have Kriterien (Nicht prüfbar Fix):
- ÖV-Anbindung: Minutengrenze aus Freitext extrahiert, gegen publicTransportMinutes geprüft
- Mindestfläche: m²-Wert aus Freitext extrahiert, gegen areaSqm geprüft
- Ausbaugrad: neues Keyword-Rule für FULL/PREMIUM

fitOut-Datenpflege:
- fitOut-Werte zu 32 fehlenden Properties ergänzt (Logistik=SHELL, Standard=BASIC, Modern=FULL)
- MAB-Werte (200/150/250 CHF/m²) zu 3 BASIC-Objekten hinzugefügt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-06-06 20:09:55 +02:00
parent a825672197
commit b2530f9e20
40 changed files with 1755 additions and 733 deletions
+31
View File
@@ -145,3 +145,34 @@ export function getMarketRent(city: string, assetType: string): number | null {
if (assetType === 'RETAIL') return intel.medianRentRetail
return intel.medianRentOffice
}
// ── City coordinates (WGS84) ─────────────────────────────────────────────────
const CITY_COORDS: Record<string, { lat: number; lng: number }> = {
'Zürich': { lat: 47.3769, lng: 8.5417 },
'Zürich-West': { lat: 47.3862, lng: 8.5195 },
'Zürich-Nord': { lat: 47.4108, lng: 8.5450 },
'Basel': { lat: 47.5596, lng: 7.5886 },
'Bern': { lat: 46.9480, lng: 7.4474 },
'Luzern': { lat: 47.0502, lng: 8.3093 },
'Genf': { lat: 46.2044, lng: 6.1432 },
'Lausanne': { lat: 46.5197, lng: 6.6323 },
'Zug': { lat: 47.1661, lng: 8.5158 },
'Winterthur': { lat: 47.5004, lng: 8.7256 },
'St. Gallen': { lat: 47.4245, lng: 9.3767 },
'Lugano': { lat: 46.0037, lng: 8.9511 },
'Baar': { lat: 47.1965, lng: 8.5286 },
'Muttenz': { lat: 47.5218, lng: 7.6468 },
'Pratteln': { lat: 47.5163, lng: 7.6926 },
'Reinach BL': { lat: 47.4948, lng: 7.5946 },
'Allschwil': { lat: 47.5529, lng: 7.5368 },
'Binningen': { lat: 47.5363, lng: 7.5687 },
}
export function lookupCityCoords(location: string): { lat: number; lng: number } | null {
if (CITY_COORDS[location]) return CITY_COORDS[location]
// Prefix fallback: "Zürich Kreis 5" → "Zürich"
const base = location.split(' ')[0]
const key = Object.keys(CITY_COORDS).find(k => k.startsWith(base) || base === k)
return key ? CITY_COORDS[key] : null
}