Outils pour utilisateurs

Outils du site


ujusum:3-codage:2-sprints:6-sprint-6

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

ujusum/3-codage/2-sprints/6-sprint-6.1781108824.txt.gz · Dernière modification : 2026/06/10 18:27 de 83.202.252.200

DokuWiki Appliance - Powered by TurnKey Linux