import type { AssetType, ResultType, AvailabilityStatus, AvailabilityType, FreshnessStatus, RiskLevel, SourceType, DataQualityLevel, } from './enums' import { AvailabilityStatus as AS } from './enums' import type { PropertyUnit } from './unit' import type { Lease } from './lease' // Re-export unit types so all existing imports from 'property' continue to work export type { PropertyUnit, UnitBundle, UnitNeedMatch } from './unit' // ── Location / Address ──────────────────────────────────────────────────────── export interface Location { city: string district?: string canton?: string region?: string country: string coordinates?: { lat: number; lng: number } } export interface Address { street: string houseNumber: string postalCode: string city: string country: string } // ── Source Metadata ─────────────────────────────────────────────────────────── export interface SourceMeta { sourceType: SourceType | string sourceLabel?: string sourceUrl?: string sourceUpdatedAt?: string externalId?: string } // ── Data Quality ────────────────────────────────────────────────────────────── export interface DataQuality { score: number qualityLevel?: DataQualityLevel missingCriticalFields: string[] missingOptionalFields: string[] lastVerifiedAt?: string freshness: FreshnessStatus warnings: string[] } // ── Hard Facts ──────────────────────────────────────────────────────────────── export interface PropertyHardFacts { floor?: number fitOut?: 'SHELL' | 'BASIC' | 'FULL' | 'PREMIUM' mieterausbaubeitragPerSqm?: number /** true = Vermieter übernimmt den Ausbau & preist ihn in die Miete ein (kein Aufschlag, kein MAB). */ fitOutByLandlord?: boolean parking?: number publicTransportScore?: number usageType?: string ceilingHeightM?: number loadingDocksCount?: number powerSupplyKva?: number hasServerRoom?: boolean isBarrierFree?: boolean hasAirConditioning?: boolean hasStorefront?: boolean } // ── Soft Factors ────────────────────────────────────────────────────────────── export interface SoftFactors { prestigeScore?: number visibilityScore?: number footfallScore?: number commuterAccessScore?: number talentAccessScore?: number esgScore?: number flexibilityScore?: number expansionPotentialScore?: number taxEnvironmentScore?: number // Legacy aliases kept for mock-data backward compatibility prestige?: number accessibility?: number talentAccess?: number esgRating?: string passerbyFrequency?: 'LOW' | 'MEDIUM' | 'HIGH' | 'VERY_HIGH' parkingSpots?: number publicTransportMinutes?: number infrastructureNotes?: string } // ── Property ────────────────────────────────────────────────────────────────── export interface Property { id: string organizationId?: string title: string assetType: AssetType resultType: ResultType location: Location address: Address areaSqm: number areaSqmMin?: number areaSqmMax?: number rentPricePerSqm: number rentChfSqmYear?: number totalRentMonthly?: number ancillaryCosts?: number availabilityDate: string availabilityStatus: AvailabilityStatus availabilityType?: AvailabilityType sourceType: string sourceLabel?: string sourceUrl?: string sourceUpdatedAt?: string sourceMeta?: SourceMeta confidenceScore: number dataQuality: DataQuality softFactors?: SoftFactors hardFacts?: PropertyHardFacts // Legacy fields — kept for backward compat floorLevel?: number expansionPotentialSqm?: number contractDurationMonths?: number riskLevel?: RiskLevel description?: string images?: string[] floorPlanUrl?: string /** @deprecated Use unit.leases[].contractDocumentUrl instead */ leaseContractUrl?: string /** @deprecated Use unit.leases[].contractDocumentName instead */ leaseContractName?: string propertyNumber?: string units?: PropertyUnit[] mapImageUrl?: string /** @deprecated Use unit.leases instead */ leaseTerm?: string /** @deprecated Use unit.leases[].startDate instead */ leaseStartDate?: string /** @deprecated Use unit.leases[].endDate instead */ leaseEndDate?: string /** @deprecated Use unit.leases[].breakoutOption instead */ breakoutOption?: boolean /** @deprecated Use unit.leases[].breakoutOptionDate instead */ breakoutOptionDate?: string /** @deprecated Use unit.leases[].tenant.companyName instead */ currentTenant?: string importedFrom?: string importedAt?: string lastUpdatedAt?: string schattenmarktRelease?: { enabled: boolean; leadTimeMonths?: number; anonymous?: boolean } status?: 'ACTIVE' | 'INACTIVE' | 'DRAFT' | 'ARCHIVED' lastReviewedAt?: string createdAt: string updatedAt: string } export type CreatePropertyInput = Omit export type UpdatePropertyInput = Partial /** * Returns the rentable units of a property. * For properties without explicit units, synthesises one unit from property-level data * so that all matching logic can operate uniformly at unit level. * Property-level lease fields are synthesised into a proper Lease object for backward compat. */ export function getEffectiveUnits(p: Property): PropertyUnit[] { if (p.units && p.units.length > 0) return p.units const syntheticLease: Lease | undefined = p.currentTenant ? { id: `${p.id}-lease`, unitId: p.id, tenant: { id: `${p.id}-tenant`, companyName: p.currentTenant }, rentPerSqm: p.rentPricePerSqm, startDate: p.leaseStartDate ?? '', endDate: p.leaseEndDate ?? '', contractDurationYears: p.contractDurationMonths ? Math.round(p.contractDurationMonths / 12) : undefined, breakoutOption: p.breakoutOption, breakoutOptionDate: p.breakoutOptionDate, status: p.leaseEndDate && new Date(p.leaseEndDate) > new Date() ? 'ACTIVE' : 'EXPIRED', sourceSystem: p.importedFrom ?? 'MANUAL', contractDocumentUrl: p.leaseContractUrl, contractDocumentName: p.leaseContractName, } : undefined return [{ id: p.id, propertyId: p.id, floorLevel: p.hardFacts?.floor ?? p.floorLevel ?? 0, unitLabel: undefined, areaSqm: p.areaSqm, available: p.availabilityStatus === AS.AVAILABLE_NOW || p.availabilityStatus === AS.AVAILABLE_SOON, rentPricePerSqm: p.rentPricePerSqm, leases: syntheticLease ? [syntheticLease] : [], schattenmarktRelease: p.schattenmarktRelease?.enabled ? { enabled: true, availableFrom: p.leaseEndDate } : undefined, }] }