Ceci est une ancienne révision du document !
Table des matières
Sprint 6 — Multi-Propriétés & Gestion du Parc
Objectif
Faire évoluer la plateforme d'un modèle mono-propriété vers une plateforme Enterprise multi-sites.
Cette couche permet de gérer :
Portefeuilles Groupes de propriétés Propriétaires Exploitants Actifs Performance globale
sur plusieurs centaines ou milliers de propriétés.
À l'issue du Sprint 6 :
✓ Portfolio Management ✓ Property Groups ✓ Owners ✓ Operators ✓ Asset Management ✓ Portfolio Analytics ✓ Enterprise Multi-Property Platform
Architecture cible
Tenant ↓ Portfolio ├── Property Groups ├── Properties ├── Owners ├── Operators ├── Assets └── Portfolio Analytics ↓ Reservations ↓ Finance ↓ Operations
Domaines métier
Le Sprint 6 introduit :
Portfolio PropertyGroup Owner Operator Asset AssetAssignment PortfolioAnalytics PortfolioKPI
Sous-sprints
Sprint 6-A
Fondation Multi-Propriétés
Portfolio Property Groups Owners Operators
Sprint 6-B
Asset Management
Assets Lifecycle Assignments Maintenance Integration
Sprint 6-C
Portfolio Analytics
Portfolio KPIs Revenue Occupancy Benchmarking Forecasting
Sprint 6-D
Enterprise Governance
Multi-Site RBAC Approvals Compliance Delegation
Livrables fin Sprint 6
Portfolio Engine Property Group Management Owner Management Operator Management Asset Platform Portfolio Analytics Enterprise Multi-Site Platform
Sprint 6-A.1 — Implémentation Prisma Portfolio Core
Objectif
Créer la fondation Multi-Propriétés Enterprise.
Cette couche permet de regrouper plusieurs propriétés dans des structures hiérarchiques :
Tenant ↓ Portfolio ↓ Property Group ↓ Properties
avec gestion des :
Propriétaires Exploitants Groupes Portefeuilles
À l'issue de cette étape :
✓ Portfolio ✓ PropertyGroup ✓ Owner ✓ Operator ✓ Multi-Property Ready ✓ Finance Ready ✓ Reservation Ready ✓ Analytics Ready
Architecture cible
Tenant ↓ Portfolio ├── Property Groups │ ├── Owners │ ├── Operators │ └── Properties ↓ Reservations ↓ Finance ↓ Analytics
Sprint 6-A.1-A
Extension Prisma
Étape 1 — Portfolio
Ajouter dans schema.prisma
model Portfolio {
id String
@id
@default(uuid())
tenantId String
name String
code String
@unique
description String?
active Boolean
@default(true)
createdAt DateTime
@default(now())
updatedAt DateTime
@updatedAt
tenant Tenant
@relation(
fields:[tenantId],
references:[id]
)
propertyGroups PropertyGroup[]
properties Property[]
@@index([tenantId])
@@index([active])
@@index([code])
}
Étape 2 — PropertyGroup
Ajouter
model PropertyGroup {
id String
@id
@default(uuid())
tenantId String
portfolioId String
name String
code String
description String?
active Boolean
@default(true)
createdAt DateTime
@default(now())
updatedAt DateTime
@updatedAt
portfolio Portfolio
@relation(
fields:[portfolioId],
references:[id],
onDelete:Cascade
)
properties Property[]
@@unique([
portfolioId,
code
])
@@index([tenantId])
@@index([portfolioId])
@@index([active])
}
Étape 3 — Owner
Ajouter
model Owner {
id String
@id
@default(uuid())
tenantId String
ownerCode String
@unique
type OwnerType
companyName String?
firstName String?
lastName String?
email String?
phone String?
taxIdentifier String?
active Boolean
@default(true)
createdAt DateTime
@default(now())
updatedAt DateTime
@updatedAt
properties Property[]
@@index([tenantId])
@@index([ownerCode])
@@index([active])
}
Étape 4 — Operator
Ajouter
model Operator {
id String
@id
@default(uuid())
tenantId String
operatorCode String
@unique
companyName String
email String?
phone String?
active Boolean
@default(true)
createdAt DateTime
@default(now())
updatedAt DateTime
@updatedAt
properties Property[]
@@index([tenantId])
@@index([operatorCode])
@@index([active])
}
Étape 5 — OwnerType
Ajouter
enum OwnerType {
INDIVIDUAL
COMPANY
INVESTMENT_FUND
REIT
GOVERNMENT
}
Sprint 6-A.1-B
Extension du modèle Property
Étape 6 — Ajouter Portfolio
Dans Property
portfolioId String?
portfolio Portfolio?
@relation(
fields:[portfolioId],
references:[id]
)
Étape 7 — Ajouter Property Group
Dans Property
propertyGroupId String?
propertyGroup PropertyGroup?
@relation(
fields:[propertyGroupId],
references:[id]
)
Étape 8 — Ajouter Owner
Dans Property
ownerId String?
owner Owner?
@relation(
fields:[ownerId],
references:[id]
)
Étape 9 — Ajouter Operator
Dans Property
operatorId String?
operator Operator?
@relation(
fields:[operatorId],
references:[id]
)
Étape 10 — Ajouter Index
@@index([portfolioId]) @@index([propertyGroupId]) @@index([ownerId]) @@index([operatorId])
Sprint 6-A.1-C
Relations Tenant
Étape 11 — Ajouter
Dans Tenant
portfolios Portfolio[] owners Owner[] operators Operator[]
Étape 12 — Multi-Tenant
Garantir
que :
Portfolio PropertyGroup Owner Operator
appartiennent toujours au même :
tenantId
Sprint 6-A.1-D
Contraintes Métier
Étape 13 — Portfolio
Supporter
1 Tenant ↓ N Portfolios
Étape 14 — Property Group
Supporter
1 Portfolio ↓ N Property Groups
Étape 15 — Owner
Supporter
1 Owner ↓ N Properties
Étape 16 — Operator
Supporter
1 Operator ↓ N Properties
Sprint 6-A.1-E
Finance Ready
Étape 17 — Préparer
Compatible avec :
Revenue Sharing Owner Statements Commission Engine Asset Accounting
Étape 18 — Préparer
Compatible avec :
Multi-Owner Properties Operator Contracts Portfolio Finance
Sprint 6-A.1-F
Reservation Ready
Étape 19 — Préparer
Compatible avec :
Portfolio Occupancy Group Reservations Multi-Site Reporting
Étape 20 — Préparer
Compatible avec :
Cross Property Search Portfolio Analytics Enterprise Reporting
Sprint 6-A.1-G
Analytics Ready
Étape 21 — Préparer
Compatible avec :
Portfolio Revenue Portfolio Occupancy Benchmarking Forecasting Asset Performance
Étape 22 — Préparer
Compatible avec :
Executive Dashboard Owner Dashboard Operator Dashboard
Sprint 6-A.1-H
Migration
Étape 23 — Générer
npx prisma migrate dev \
--name portfolio_core
Étape 24 — Générer
npx prisma generate
Étape 25 — Vérifier
npx prisma studio
Présence de :
Portfolio PropertyGroup Owner Operator
Définition de terminé
Le Sprint 6-A.1 est terminé lorsque :
✓ Portfolio créé ✓ PropertyGroup créé ✓ Owner créé ✓ Operator créé ✓ Relations Property créées ✓ Relations Tenant créées ✓ Multi-Tenant Ready ✓ Finance Ready ✓ Analytics Ready ✓ Migration exécutée
Livrables
Portfolio PropertyGroup Owner Operator OwnerType Portfolio Core Schema
Sprint 6-A.2 — Gestion Multi-Propriétés Enterprise
Objectif
Construire la couche métier Multi-Propriétés Enterprise.
Cette couche permet de gérer :
Portefeuilles Groupes de propriétés Propriétaires Exploitants Hiérarchie multi-sites Sécurité multi-niveaux
à partir d'une interface unique.
À l'issue de cette étape :
✓ PortfolioModule ✓ PropertyGroupModule ✓ OwnerModule ✓ OperatorModule ✓ Portfolio Hierarchy ✓ Portfolio Security ✓ Portfolio Dashboard ✓ Enterprise Multi-Site Management
Architecture cible
Portfolio ├── Property Groups │ ├── Properties │ ├── Owners │ ├── Operators │ └── Portfolio Dashboard ↓ Reservations ↓ Finance ↓ Analytics
Sprint 6-A.2-A
Modules Métier
Étape 1 — Création
src/modules/portfolio ├── portfolio │ ├── property-groups │ ├── owners │ ├── operators │ ├── dashboard │ └── portfolio.module.ts
Étape 2 — Services
Créer
PortfolioService PropertyGroupService OwnerService OperatorService PortfolioHierarchyService PortfolioDashboardService
Étape 3 — Controllers
Créer
PortfolioController PropertyGroupController OwnerController OperatorController PortfolioDashboardController
Sprint 6-A.2-B
Gestion des Portefeuilles
Étape 4 — PortfolioService
Créer
portfolio.service.ts
Méthodes
createPortfolio() updatePortfolio() archivePortfolio() getPortfolio() getPortfolioSummary()
Étape 5 — Endpoints
Ajouter
GET /portfolios
GET /portfolios/{id}
POST /portfolios
PUT /portfolios/{id}
DELETE /portfolios/{id}
Étape 6 — Validation
Vérifier
tenantId unicité code ownership
sur chaque opération.
Sprint 6-A.2-C
Gestion des Groupes
Étape 7 — PropertyGroupService
Créer
property-group.service.ts
Méthodes
createGroup() assignProperty() removeProperty() moveProperty()
Étape 8 — Structure
Supporter
Portfolio ↓ Region ↓ City ↓ Properties
Étape 9 — Endpoints
Ajouter
GET /property-groups
POST /property-groups
PUT /property-groups/{id}
DELETE /property-groups/{id}
POST /property-groups/{id}/properties
Sprint 6-A.2-D
Gestion des Propriétaires
Étape 10 — OwnerService
Créer
owner.service.ts
Méthodes
createOwner() updateOwner() assignProperty() getOwnerPortfolio()
Étape 11 — Informations
Gérer
Personne physique Société Coordonnées Fiscalité Contrats
Étape 12 — Endpoints
Ajouter
GET /owners
GET /owners/{id}
POST /owners
PUT /owners/{id}
DELETE /owners/{id}
Sprint 6-A.2-E
Gestion des Exploitants
Étape 13 — OperatorService
Créer
operator.service.ts
Méthodes
createOperator() updateOperator() assignProperty() getOperatorPerformance()
Étape 14 — Endpoints
Ajouter
GET /operators
GET /operators/{id}
POST /operators
PUT /operators/{id}
DELETE /operators/{id}
Sprint 6-A.2-F
Hiérarchie Enterprise
Étape 15 — PortfolioHierarchyService
Créer
portfolio-hierarchy.service.ts
Construire
Portfolio ↓ PropertyGroup ↓ Property
Étape 16 — Fonctions
Supporter
getTree() getChildren() getAncestors() calculateAggregates()
Étape 17 — Agrégations
Calculer
Properties Count Revenue Occupancy Reservations Reviews
par niveau.
Sprint 6-A.2-G
Portfolio Security
Étape 18 — Portfolio Scope
Ajouter
portfolioId propertyGroupId propertyId
comme scopes de sécurité.
Étape 19 — RBAC
Supporter
Portfolio Admin Portfolio Manager Group Manager Property Manager Owner Viewer
Étape 20 — Filtrage
Imposer
sur toutes les requêtes :
tenantId portfolioId
afin d'éviter toute fuite de données.
Étape 21 — Decorators
Créer
@PortfolioScope() @PropertyGroupScope()
pour les futurs modules.
Sprint 6-A.2-H
Dashboard Portfolio
Étape 22 — PortfolioDashboardService
Créer
portfolio-dashboard.service.ts
Agréger
Properties Reservations Revenue Occupancy Reviews Incidents
Étape 23 — Endpoint
Ajouter
GET /portfolios/{id}/dashboard
Étape 24 — Exemple
Réponse
{
"properties":124,
"occupancy":81.4,
"revenue":2450000,
"reservations":8421,
"rating":4.7
}
Étape 25 — KPIs
Mesurer
Revenue Occupancy ADR RevPAR Reservations Guest Satisfaction
Sprint 6-A.2-I
API Enterprise
Étape 26 — Recherche
Ajouter
GET /portfolios/search GET /owners/search GET /operators/search
Étape 27 — Pagination
Supporter
Page Limit Sort Filters
sur tous les endpoints.
Étape 28 — Export
Ajouter
GET /portfolios/{id}/export
Formats :
CSV Excel PDF
Sprint 6-A.2-J
Gouvernance
Étape 29 — Permissions
Ajouter
portfolio.read portfolio.create portfolio.update portfolio.delete portfolio.dashboard owner.manage operator.manage
Étape 30 — Audit
Journaliser
PORTFOLIO_CREATED PORTFOLIO_UPDATED PROPERTY_ASSIGNED OWNER_ASSIGNED OPERATOR_ASSIGNED PORTFOLIO_DASHBOARD_VIEWED
Étape 31 — Préparation Sprint 6-B
Compatible avec :
Assets Equipment Lifecycle Depreciation Capital Expenditure
Définition de terminé
Le Sprint 6-A.2 est terminé lorsque :
✓ PortfolioModule créé ✓ PropertyGroupModule créé ✓ OwnerModule créé ✓ OperatorModule créé ✓ Portfolio Hierarchy créée ✓ Portfolio Security créée ✓ Portfolio Dashboard créé ✓ Audit intégré
Livrables
PortfolioModule PropertyGroupModule OwnerModule OperatorModule PortfolioHierarchyService PortfolioDashboardService Portfolio Security Layer Enterprise Multi-Property Platform
Sprint 6-B.1 — Asset Management Core
Objectif
Mettre en place le moteur de gestion des actifs physiques du parc immobilier.
Cette couche permet de gérer :
Mobilier Équipements Électroménager Matériel technique Inventaire Cycle de vie
sur l'ensemble du portefeuille immobilier.
À l'issue de cette étape :
✓ Asset ✓ AssetCategory ✓ AssetAssignment ✓ AssetLifecycle ✓ AssetInventory ✓ AssetTracking ✓ Enterprise Asset Management
Architecture cible
Portfolio ↓ Properties ↓ Asset Management ├── Asset Catalog ├── Asset Assignment ├── Lifecycle ├── Inventory ├── Tracking └── Maintenance Integration ↓ Operations ↓ Finance
Sprint 6-B.1-A
Extension Prisma
Étape 1 — AssetCategory
Ajouter dans schema.prisma
model AssetCategory {
id String
@id
@default(uuid())
tenantId String
name String
code String
description String?
depreciationYears Int?
createdAt DateTime
@default(now())
updatedAt DateTime
@updatedAt
assets Asset[]
@@unique([
tenantId,
code
])
@@index([tenantId])
}
Étape 2 — Asset
Ajouter
model Asset {
id String
@id
@default(uuid())
tenantId String
assetCode String
@unique
categoryId String
propertyId String?
name String
description String?
serialNumber String?
manufacturer String?
model String?
purchaseDate DateTime?
purchasePrice Decimal?
@db.Decimal(14,2)
currentValue Decimal?
@db.Decimal(14,2)
status AssetStatus
condition AssetCondition
warrantyUntil DateTime?
qrCode String?
barcode String?
createdAt DateTime
@default(now())
updatedAt DateTime
@updatedAt
category AssetCategory
@relation(
fields:[categoryId],
references:[id]
)
assignments AssetAssignment[]
lifecycleEvents AssetLifecycle[]
@@index([tenantId])
@@index([categoryId])
@@index([propertyId])
@@index([status])
}
Étape 3 — AssetAssignment
Ajouter
model AssetAssignment {
id String
@id
@default(uuid())
assetId String
propertyId String
assignedAt DateTime
@default(now())
unassignedAt DateTime?
assignedBy String?
notes String?
asset Asset
@relation(
fields:[assetId],
references:[id],
onDelete:Cascade
)
@@index([assetId])
@@index([propertyId])
}
Étape 4 — AssetLifecycle
Ajouter
model AssetLifecycle {
id String
@id
@default(uuid())
assetId String
eventType AssetLifecycleEvent
eventDate DateTime
@default(now())
description String?
cost Decimal?
@db.Decimal(14,2)
metadata Json?
asset Asset
@relation(
fields:[assetId],
references:[id],
onDelete:Cascade
)
@@index([assetId])
@@index([eventType])
@@index([eventDate])
}
Étape 5 — AssetInventory
Ajouter
model AssetInventory {
id String
@id
@default(uuid())
tenantId String
propertyId String?
inventoryDate DateTime
totalAssets Int
verifiedAssets Int
missingAssets Int
notes String?
createdAt DateTime
@default(now())
@@index([tenantId])
@@index([propertyId])
@@index([inventoryDate])
}
Étape 6 — AssetTracking
Ajouter
model AssetTracking {
id String
@id
@default(uuid())
assetId String
propertyId String?
locationDescription String?
trackedAt DateTime
@default(now())
source TrackingSource
metadata Json?
@@index([assetId])
@@index([trackedAt])
}
Étape 7 — Enums
Ajouter
enum AssetStatus {
ACTIVE
IN_STOCK
IN_MAINTENANCE
RETIRED
LOST
DISPOSED
}
enum AssetCondition {
EXCELLENT
GOOD
FAIR
POOR
DAMAGED
}
enum AssetLifecycleEvent {
PURCHASED
ASSIGNED
MOVED
INSPECTED
MAINTAINED
REPAIRED
RETIRED
DISPOSED
}
enum TrackingSource {
MANUAL
QR_CODE
BARCODE
RFID
NFC
IOT
}
Étape 8 — Migration
Générer
npx prisma migrate dev \
--name asset_management_core
Sprint 6-B.1-B
Asset Management Module
Étape 9 — Création
src/modules/assets ├── assets │ ├── categories │ ├── assignments │ ├── lifecycle │ ├── inventory │ ├── tracking │ └── assets.module.ts
Étape 10 — Services
Créer
AssetService AssetCategoryService AssetAssignmentService AssetLifecycleService AssetInventoryService AssetTrackingService
Sprint 6-B.1-C
Gestion des Actifs
Étape 11 — AssetService
Créer
asset.service.ts
Méthodes
createAsset() updateAsset() retireAsset() searchAssets() calculateCurrentValue()
Étape 12 — Types d'actifs
Supporter
Mobilier TV Climatisation Électroménager Literie Sécurité Équipements extérieurs
Étape 13 — Codes
Générer
AST-2027-000001
pour chaque actif.
Sprint 6-B.1-D
Affectation
Étape 14 — AssetAssignmentService
Créer
asset-assignment.service.ts
Méthodes
assignAsset() moveAsset() unassignAsset() getAssetHistory()
Étape 15 — Historiser
Conserver
Date Property Utilisateur Action
pour chaque mouvement.
Sprint 6-B.1-E
Cycle de Vie
Étape 16 — AssetLifecycleService
Créer
asset-lifecycle.service.ts
Journaliser
Achat Affectation Déplacement Réparation Retrait
Étape 17 — Méthodes
recordEvent() getTimeline() calculateLifecycleCost()
Étape 18 — Finance
Préparer
Amortissement CAPEX OPEX
pour Sprint 6-B.2.
Sprint 6-B.1-F
Inventaire
Étape 19 — AssetInventoryService
Créer
asset-inventory.service.ts
Méthodes
createInventory() verifyAsset() closeInventory() generateVarianceReport()
Étape 20 — Contrôles
Identifier
Actifs présents Actifs manquants Actifs déplacés
Sprint 6-B.1-G
Tracking
Étape 21 — AssetTrackingService
Créer
asset-tracking.service.ts
Méthodes
trackAsset() scanQrCode() scanBarcode() getLastLocation()
Étape 22 — Technologies
Préparer
QR Code Barcode RFID NFC IoT
Étape 23 — Temps réel
Supporter
Position Historique Mouvements
des actifs.
Sprint 6-B.1-H
API Asset Management
Étape 24 — Endpoints
Ajouter
GET /assets
GET /assets/{id}
POST /assets
PUT /assets/{id}
DELETE /assets/{id}
POST /assets/{id}/assign
POST /assets/{id}/move
GET /assets/{id}/timeline
GET /assets/inventory
Étape 25 — Recherche
Supporter
Catégorie Statut Property Condition Valeur
Étape 26 — Export
Ajouter
GET /assets/export
Formats :
CSV Excel PDF
Sprint 6-B.1-I
Sécurité
Étape 27 — Permissions
Ajouter
asset.read asset.create asset.update asset.assign asset.inventory asset.dispose asset.admin
Étape 28 — Multi-Propriété
Filtrer
par :
Portfolio PropertyGroup Property
sur toutes les requêtes.
Étape 29 — Audit
Journaliser
ASSET_CREATED ASSET_ASSIGNED ASSET_MOVED ASSET_RETIRED INVENTORY_COMPLETED TRACKING_SCAN
Sprint 6-B.1-J
Préparation Sprint 6-B.2
Étape 30 — Compatible
Préparer :
Depreciation Engine Asset Valuation Capital Planning Lifecycle Forecasting
Définition de terminé
Le Sprint 6-B.1 est terminé lorsque :
✓ Asset créé ✓ AssetCategory créé ✓ AssetAssignment créé ✓ AssetLifecycle créé ✓ AssetInventory créé ✓ AssetTracking créé ✓ Audit intégré ✓ Multi-Property Ready
Livrables
Asset AssetCategory AssetAssignment AssetLifecycle AssetInventory AssetTracking AssetService AssetAssignmentService AssetLifecycleService AssetInventoryService AssetTrackingService Enterprise Asset Management Platform