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:
@@ -463,24 +463,50 @@ function scoreMinContractDuration(need: Need, property: Property): ScoreFactor |
|
||||
}
|
||||
}
|
||||
|
||||
const FIT_OUT_UPGRADE_COST: Record<string, number> = {
|
||||
'SHELL_BASIC': 200,
|
||||
'SHELL_FULL': 350,
|
||||
'SHELL_PREMIUM': 450,
|
||||
'BASIC_FULL': 150,
|
||||
'BASIC_PREMIUM': 250,
|
||||
'FULL_PREMIUM': 100,
|
||||
}
|
||||
|
||||
function scoreFitOut(need: Need, property: Property): ScoreFactor | null {
|
||||
if (!need.requiredFitOut) return null
|
||||
const propFitOut = property.hardFacts?.fitOut
|
||||
const LABELS: Record<string, string> = { SHELL: 'Rohbau', BASIC: 'Basisausbau', FULL: 'Vollausbau', PREMIUM: 'Premiumausbau' }
|
||||
if (!propFitOut) {
|
||||
return { criterion: 'fitOut', weight: 0.04, score: 50, contribution: 2, explanation: `Ausbaustandard ${need.requiredFitOut} erforderlich — keine Daten`, estimated: true }
|
||||
}
|
||||
const reqLevel = FIT_OUT_LEVELS[need.requiredFitOut] ?? 1
|
||||
const propLevel = FIT_OUT_LEVELS[propFitOut] ?? 0
|
||||
const score = propLevel >= reqLevel ? 100 : Math.max(10, Math.round(50 - (reqLevel - propLevel) * 25))
|
||||
const LABELS: Record<string, string> = { SHELL: 'Rohbau', BASIC: 'Basisausbau', FULL: 'Vollausbau', PREMIUM: 'Premiumausbau' }
|
||||
if (propLevel >= reqLevel) {
|
||||
return {
|
||||
criterion: 'fitOut', weight: 0.04, score: 100, contribution: 4,
|
||||
explanation: `${LABELS[propFitOut] ?? propFitOut} erfüllt Anforderung (${LABELS[need.requiredFitOut] ?? need.requiredFitOut})`,
|
||||
}
|
||||
}
|
||||
// Check if combined budget (tenant + MAB) covers the upgrade gap
|
||||
const upgradeKey = `${propFitOut}_${need.requiredFitOut}`
|
||||
const upgradeCost = FIT_OUT_UPGRADE_COST[upgradeKey]
|
||||
const mab = property.hardFacts?.mieterausbaubeitragPerSqm ?? 0
|
||||
const tenantBudget = need.fitOutBudgetMaxPerSqm ?? 0
|
||||
const totalBudget = mab + tenantBudget
|
||||
if (upgradeCost && totalBudget >= upgradeCost) {
|
||||
const mabNote = mab > 0 ? ` (inkl. CHF ${mab} MAB)` : ''
|
||||
return {
|
||||
criterion: 'fitOut', weight: 0.04, score: 80, contribution: 3.2,
|
||||
explanation: `${LABELS[propFitOut]} — Ausbau auf ${LABELS[need.requiredFitOut]} mit CHF ${totalBudget}/m² Budget überbrückbar${mabNote}`,
|
||||
}
|
||||
}
|
||||
const score = Math.max(10, Math.round(50 - (reqLevel - propLevel) * 25))
|
||||
const budgetNote = tenantBudget > 0 || mab > 0
|
||||
? ` (Budget CHF ${totalBudget}/m² — Ausbau ~CHF ${upgradeCost ?? '?'}/m²)`
|
||||
: ''
|
||||
return {
|
||||
criterion: 'fitOut',
|
||||
weight: 0.04,
|
||||
score,
|
||||
contribution: score * 0.04,
|
||||
explanation: propLevel >= reqLevel
|
||||
? `Ausbaustandard ${LABELS[propFitOut] ?? propFitOut} erfüllt Anforderung ${LABELS[need.requiredFitOut] ?? need.requiredFitOut}`
|
||||
: `${LABELS[propFitOut] ?? propFitOut} — ${LABELS[need.requiredFitOut] ?? need.requiredFitOut} erforderlich`,
|
||||
criterion: 'fitOut', weight: 0.04, score, contribution: score * 0.04,
|
||||
explanation: `${LABELS[propFitOut] ?? propFitOut} — ${LABELS[need.requiredFitOut] ?? need.requiredFitOut} erforderlich${budgetNote}`,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user