feat(supply): per-unit fit-out/cost-bearer/MAB/parking with object fallback (resolveUnitFacts)
- unit: add fitOutByLandlord, mieterausbaubeitragPerSqm, parkingSpots (parking = allocation from object pool) - lib/unitFacts.resolveUnitFacts: single resolver (unit value ?? object value) used by scorer + card adapter - matchSyncService: pre-market unit scored via resolved facts (fit-out, MAB, cost-bearer, parking allocation) - matchCardAdapter: fit-out label/viability/investment via resolveUnitFacts - UnitStructurePanel: consolidate ALL per-unit overrides into the one expandable row editor (price, availability, Ausbaustandard, Wer-baut-aus, MAB, parking allocation); show parking on row - PreMarketUnitGrid/Panel: drop the duplicate per-unit fit-out select (now in the unit editor) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
647493bf3c
commit
fd6ed105bb
7 files changed
+144
-71
No files matched your search
@@ -13,6 +13,7 @@ import type {
|
||||
import { getCityIntelligence } from '../../lib/locationIntelligence'
|
||||
import { formatUnitTitle, formatMultiUnitFloors, getUnitAvailability } from '../../domain/unit'
|
||||
import { calcFitOutInvestment } from '../../lib/fitOutUtils'
|
||||
import { resolveUnitFacts } from '../../lib/unitFacts'
|
||||
import { AvailabilityStatus } from '../../domain/enums'
|
||||
|
||||
const HARD_CRITERIA = new Set(['area', 'location', 'budget', 'timing'])
|
||||
@@ -152,30 +153,30 @@ export function buildMatchCardViewModel(
|
||||
preMarketUnit: unit,
|
||||
preMarketAllUnits: property?.units,
|
||||
fitOutLabel: (() => {
|
||||
// Einheit-Ausbau hat Vorrang vor dem Objekt-Ausbau (Pre-Market)
|
||||
const fitOut = unit?.fitOut ?? property?.hardFacts?.fitOut
|
||||
// Einheit-Werte haben Vorrang vor Objekt-Werten (Pre-Market / Mehr-Einheiten)
|
||||
if (!property) return undefined
|
||||
const { fitOut, mabPerSqm } = resolveUnitFacts(property, unit)
|
||||
if (!fitOut) return undefined
|
||||
const LABELS: Record<string, string> = { SHELL: 'Rohbau', BASIC: 'Basisausbau', FULL: 'Vollausbau', PREMIUM: 'Premiumausbau' }
|
||||
const mab = property?.hardFacts?.mieterausbaubeitragPerSqm
|
||||
if (fitOut === 'FULL' || fitOut === 'PREMIUM') return `${LABELS[fitOut]} — bezugsfertig`
|
||||
if (mab) return `${LABELS[fitOut]} + CHF ${mab} MAB`
|
||||
if (mabPerSqm) return `${LABELS[fitOut]} + CHF ${mabPerSqm} MAB`
|
||||
return LABELS[fitOut] ?? fitOut
|
||||
})(),
|
||||
fitOutViable: (() => {
|
||||
const fitOut = unit?.fitOut ?? property?.hardFacts?.fitOut
|
||||
if (!property) return undefined
|
||||
const { fitOut, mabPerSqm } = resolveUnitFacts(property, unit)
|
||||
if (!fitOut) return undefined
|
||||
if (fitOut === 'FULL' || fitOut === 'PREMIUM') return true
|
||||
const mab = property?.hardFacts?.mieterausbaubeitragPerSqm ?? 0
|
||||
if (fitOut === 'BASIC' && mab >= 150) return true
|
||||
if (fitOut === 'SHELL' && mab >= 350) return true
|
||||
if (fitOut === 'BASIC' && mabPerSqm >= 150) return true
|
||||
if (fitOut === 'SHELL' && mabPerSqm >= 350) return true
|
||||
return undefined
|
||||
})(),
|
||||
fitOutInvestment: (() => {
|
||||
const fitOut = unit?.fitOut ?? property?.hardFacts?.fitOut
|
||||
if (!property) return undefined
|
||||
const { fitOut, mabPerSqm } = resolveUnitFacts(property, unit)
|
||||
if (!fitOut || fitOut === 'FULL' || fitOut === 'PREMIUM') return undefined
|
||||
const mab = property?.hardFacts?.mieterausbaubeitragPerSqm ?? 0
|
||||
const area = unit?.areaSqm ?? property?.areaSqm ?? 0
|
||||
return calcFitOutInvestment(fitOut, area, mab, 0) ?? undefined
|
||||
const area = unit?.areaSqm ?? property.areaSqm ?? 0
|
||||
return calcFitOutInvestment(fitOut, area, mabPerSqm, 0) ?? undefined
|
||||
})(),
|
||||
isDivisible: property?.units ? property.units.length > 1 : false,
|
||||
minDivisibleUnitSqm: (() => {
|
||||
|
||||
Reference in new issue
Block a user