# Guide développeur UpImo

Ce document sert de carte d'entrée dans le projet. Il décrit l'architecture, les principaux flux métier, les commandes utiles et les points de vigilance constatés dans l'état actuel du dépôt.

## Vue d'ensemble

UpImo est une application Laravel de gestion d'actifs immobiliers. Elle permet de gérer des tiers, des immeubles, des baux, des indices de révision, des données annuelles d'immeuble et des factures calculées à partir des règles métier de location.

Le projet est organisé autour de deux couches:

- Une application Laravel principale dans `app/`, `routes/`, `resources/`, `database/`.
- Un module partagé `lncshared/`, déclaré comme sous-module Git, qui fournit les modèles dynamiques, contrôleurs CRUD génériques, composants Vue communs, helpers, règles de validation et commandes Artisan.

La logique métier importante est côté API. L'interface web actuelle sert surtout de point d'entrée SAML, de shell Blade/Vue et de support à des composants partagés.

## Stack technique

- Backend: PHP 8.0, Laravel 8, Laravel Passport, Laravel UI.
- Frontend: Vue 2, Laravel Mix 2, Webpack, Bootstrap 4, Element UI, VeeValidate, Axios.
- Base de données: PostgreSQL recommandé, car les migrations utilisent `jsonb`.
- Authentification: SAML2 pour l'accès web, Passport/OAuth pour les API et le protocole Excel.
- Stockage: filesystem local par défaut, avec support S3 via Flysystem.
- Tests: PHPUnit 9, structure de tests Laravel standard dans `tests/`.

Fichiers de référence:

- `composer.json`: dépendances PHP et autoload.
- `package.json`: scripts et dépendances front.
- `webpack.mix.js`: compilation de `resources/assets/js/app.js` et `resources/assets/sass/app.scss`.
- `.env.example`: configuration locale type.
- `routes/web.php`: routes web, login, SAML, routes historiques.
- `routes/api.php`: API métier protégée par `auth:api`.

## Installation locale

Pré-requis:

- PHP 8.0 compatible avec les extensions Laravel/PostgreSQL.
- Composer.
- Node.js compatible avec l'ancienne chaîne Laravel Mix 2 / Webpack.
- PostgreSQL.

Installation type:

```bash
composer install
npm install
cp .env.example .env
php artisan key:generate
php artisan migrate
npm run dev
php artisan serve
```

Le fichier `.env.example` contient des valeurs de développement historiques. Pour une installation saine, adapter au minimum:

- `APP_URL`
- `APP_ENV`
- `APP_DEBUG`
- `APP_KEY`
- `DB_HOST`
- `DB_DATABASE`
- `DB_USERNAME`
- `DB_PASSWORD`
- les paramètres SAML
- les paramètres Passport si l'API OAuth est utilisée

Attention: le dépôt contient aussi un fichier `.env` local. Ne jamais utiliser ce fichier tel quel en production et éviter de propager des secrets réels dans Git.

## Commandes utiles

Backend:

```bash
php artisan route:list
php artisan migrate
php artisan tinker
vendor/bin/phpunit
```

Frontend:

```bash
npm run dev
npm run watch
npm run prod
```

Commandes fournies par `lncshared`:

- `CreateUser`: création d'utilisateur.
- `Empower`: passage d'un utilisateur en admin.
- `JsonImport`: import JSON par chunks via endpoint.

Elles sont enregistrées dans `app/Console/Kernel.php`.

## Architecture backend

### Routes web

Les routes web servent principalement à:

- afficher la page d'accueil/login SAML via `/`;
- rediriger vers SAML via `/login`;
- gérer `/logout`;
- fournir un login local de développement via `/admin-login`;
- exposer l'admin Laravel sous `/admin`;
- exposer les routes `lncshared` comme `media/img/{filename}`.

La page d'accueil actuelle est `resources/views/welcome.blade.php`. Elle affiche le login SAML et initialise une app Vue globale via `resources/views/layouts/app.blade.php`.

### Routes API

Les routes API sont regroupées dans `routes/api.php` et protégées par `auth:api`.

Endpoints CRUD principaux:

- `api/parties`: tiers, propriétaires, clients.
- `api/properties`: actifs immobiliers.
- `api/leases`: baux.
- `api/indexes`: indices de révision.
- `api/property_yearlys`: données annuelles par actif.
- `api/app_settings`: paramètres applicatifs.
- `api/dynamic_fields`: définition des champs dynamiques.
- `api/invoices`: factures.

Endpoints métier notables:

- `api/fill_invoice`: prépare/calcul une facture sans forcément la stocker.
- `api/get_invoice_num`: génère ou propose un numéro de facture.
- `api/find_invoice_num`: recherche des factures.
- `api/invoice_credit_note`: crée un avoir à partir d'une facture.
- `api/export_invoices`: export CSV des factures.
- `api/yearly_invoices`: liste annuelle des factures.
- `api/yearly_charges`: synthèse des provisions de charges.
- `api/etat_locatif`: état locatif à une date.
- `api/calc_yearly_summary`: synthèse annuelle par compte/client.
- `api/simul_rent`: données de simulation de loyer.
- `api/taxe_bureaux_tss`: données taxes bureaux/TSS.
- `api/recalc_percentages`: recalcul des tantièmes.
- `api/xl_init`: données d'initialisation pour Excel.

`php artisan route:list` fonctionne dans l'état actuel et montre aussi les routes Passport, SAML.

## Modèle métier

### `Party`

Fichier: `app/Models/Party.php`

Un `Party` représente un tiers: propriétaire, client/locataire ou autre acteur. Le modèle utilise:

- UUID comme clé primaire.
- `name` court, `full_name` long.
- alias via le trait `NameFullName`.
- champs dynamiques stockés dans `data`.

Relations:

- un tiers peut avoir des baux comme client;
- un tiers peut posséder des propriétés via `owner_id`.

### `Property`

Fichier: `app/Models/Property.php`

Une `Property` représente un actif immobilier. Elle appartient à un propriétaire (`Party`) et possède des baux.

Responsabilités importantes:

- calculer les baux actifs à une date;
- recalculer les tantièmes de charges par année;
- porter des paramètres dynamiques d'immeuble comme surfaces, TVA, taxes, indicateurs de refacturation.

### `Lease`

Fichier: `app/Models/Lease.php`

Un `Lease` représente un bail. Sa clé primaire est composite:

```text
property_id + client_id + start_date
```

Responsabilités importantes:

- lier un actif immobilier à un locataire;
- stocker les paramètres de loyer, d'indexation et de refacturation;
- calculer le loyer annuel courant via `calc_yearly_rent`;
- gérer les avenants (`is_addendum`, `add_parent_lease_date`, `add_start_index_date`);
- stocker les tantièmes de charges par année dans `chargesPercentage`.

La révision de loyer s'appuie sur les indices (`Index`) et les périodes trimestrielles (`Period`).

### `Index`

Fichier: `app/Models/Index.php`

Un `Index` stocke une valeur d'indice pour une période. Sa clé primaire est composite:

```text
index_code + period_code
```

Le format de période attendu est `yyyyQn`, par exemple `2024Q1`.

## Indices: synchronisation INSEE (ICC / ILAT / IRL)

Cette fonctionnalité permet de récupérer automatiquement les **10 derniers points** des indices de référence INSEE et de les stocker dans la table `indexes` (modèle `App\Models\Index`).

### Mapping des séries (idbank INSEE)

Le mapping entre `index_code` (UpImo) et le code série INSEE (idbank) est stocké dans:

- `resources/assets/js/data/insee_index_series.json`

Exemple (actuel):

- `ICC` → `000008630` (page insee.fr: [ICC](https://www.insee.fr/fr/statistiques/serie/000008630))
- `ILAT` → `001617112` (page insee.fr: [ILAT](https://www.insee.fr/fr/statistiques/serie/001617112))
- `IRL` → `001515333` (API fournie en exemple: `https://api.insee.fr/series/BDM/V1/data/SERIES_BDM/001515333?lastNObservations=5`)

### Endpoint API

Route:

- `GET /api/indexes/insee/sync`

Paramètres query:

- `lastN` (défaut: 10): nombre de points les plus récents à récupérer par série.
- `force` (défaut: 0): si `1`, **écrase** les valeurs existantes en base lorsqu'elles diffèrent.

Comportement:

- Sans `force`, si une ligne `indexes(index_code, period_code)` existe déjà et que `index_value_n` est différent, la mise à jour est **ignorée** et l'écart est retourné dans `discrepancies`.
- Avec `force=1`, les valeurs existantes sont **mises à jour**.

Stockage en base (table `indexes`):

- `index_code`: `ICC`, `ILAT`, `IRL`
- `period_code`: converti depuis INSEE `YYYY-Qn` vers `YYYYQn`
- `index_value_n`: valeur de l'indice
- `index_date`: fin de trimestre (calculée via `App\Services\Period`)
- `publication_date`: date JO INSEE (`DATE_JO`) quand disponible
- `data`: trace JSON de la source (`source=insee`, `idbank`, `title_fr`, `last_update`, observation brute)

Contrôleur:

- `app/Http/Controllers/API/InseeIndexSyncController.php`

### Accès INSEE (sans token par défaut)

Le client tente en priorité un endpoint **public** (sans OAuth) :

- `https://bdm.insee.fr/series/sdmx/data/SERIES_BDM/{idbank}?lastNObservations=...`

Si cet appel échoue et que les identifiants OAuth INSEE sont configurés, il tente ensuite l'endpoint `api.insee.fr` avec token.

Configuration:

- `config/insee.php`
- variables optionnelles `.env`:
  - `INSEE_PUBLIC_BASE_URL` (défaut: `https://bdm.insee.fr/series/sdmx`)
  - `INSEE_BASE_URL` (défaut: `https://api.insee.fr`)
  - `INSEE_TOKEN_URL` (défaut: `https://api.insee.fr/token`)
  - `INSEE_CLIENT_ID`
  - `INSEE_CLIENT_SECRET`

### UI: bouton de mise à jour + récapitulatif

Sur la page web **Indices** (liste), un bouton permet de lancer la synchro INSEE:

- libellé: “Mise à jour avec les derniers indices”
- option: switch “Forcer”

Après l'exécution, l'écran affiche un récapitulatif (insérés / mis à jour / ignorés, séries traitées, écarts), et la liste `smart-table` se **rafraîchit automatiquement**.

Implémentation front:

- `resources/assets/js/components/models/indexes/table.vue`

### `PropertyYearly`

Fichier: `app/Models/PropertyYearly.php`

Une ligne annuelle attachée à un actif. Elle porte les données variables par année:

- surfaces annuelles;
- provisions de charges;
- taxes foncières;
- taxe bureaux;
- TSS;
- autres paramètres annuels utilisés dans les calculs.

Clé primaire composite:

```text
property_id + year
```

### `Invoice`

Fichier: `app/Models/Invoice.php`

Une facture est identifiée par:

```text
owner_id + invoice_num
```

Elle stocke:

- propriétaire;
- client;
- date de facture;
- numéro;
- éventuel numéro d'avoir;
- données dynamiques;
- corps de facture dans `body`.

Le corps `body` contient les lignes calculées, les montants, les périodes, les libellés, la TVA, les comptes comptables et les données liées au bail/propriété/client.

## Services métier

### `InvoiceService`

Fichier: `app/Services/InvoiceService.php`

C'est le service central de génération de facture. Il:

- résout le client;
- trouve le bail applicable;
- calcule les périodes de facturation;
- calcule l'échéance;
- génère le numéro de facture via `InvoiceNumService`;
- ajoute les lignes de loyer, charges et taxes;
- ajoute les données liées: bail, propriété, propriétaire, client;
- calcule le total TTC.

Options de calcul importantes:

- `invoice_calc_rent`
- `invoice_calc_charges`
- `invoice_calc_taxes_TF`
- `invoice_calc_taxes_office`
- `invoice_calc_taxes_TSS`
- `settings_tax_optional`
- `settings_disable_period_check`
- `settings_transparency_level`
- `grace_period`

### `RentService`

Fichier: `app/Services/RentService.php`

Calcule une ligne de loyer pour une période donnée. Il utilise le loyer annuel révisé du bail, applique un ratio de période et enrichit les libellés de transparence.

### `InvoiceTaxService`

Fichier: `app/Services/InvoiceTaxService.php`

Ajoute les lignes de taxes refacturables:

- taxe foncière;
- ordures ménagères / DOM;
- taxe bureaux;
- TSS.

Les calculs s'appuient sur les données annuelles `PropertyYearly`, les surfaces du bail et les tantièmes du locataire.

La période d'application est l'année civile, découpée sur toute la chaîne d'occupation (bail d'origine + avenants) : une ligne par segment, avec le tantième et les surfaces de ce bail.

### `InvoiceNumService`

Fichier: `app/Services/InvoiceNumService.php`

Génère un numéro de facture par propriétaire. Le format peut venir du champ JSON `legal` du propriétaire:

- `invoice_num_pattern`
- `invoice_start_num_n`

Le pattern supporte des fragments de date et un compteur `n`, par exemple `FACT{n10}`.

### `Period`

Fichier: `app/Services/Period.php`

Manipule les périodes trimestrielles au format `yyyyQn`. Ce service sert notamment à ajuster les périodes d'indexation en fonction des dates anniversaires de bail.

## Champs dynamiques

Le projet repose fortement sur `lncshared` et son modèle `DynamicModel`.

Le principe:

- les colonnes stables restent dans les tables principales;
- les champs métier évolutifs sont décrits dans la table `dynamic_fields`;
- leurs valeurs sont stockées dans des colonnes JSONB, généralement `data`;
- côté PHP, ces champs se lisent et s'écrivent comme des attributs normaux du modèle.

Fichiers clés:

- `lncshared/app/Models/DynamicModel.php`
- `lncshared/app/Models/DynamicField.php`
- `lncshared/app/Http/Controllers/API/CrudController.php`
- `lncshared/resources/assets/js/components/forms/vue-form.vue`
- `lncshared/resources/assets/js/components/tables/smart-table.vue`

Ce système permet d'ajouter des champs sans migration pour chaque nouvelle donnée métier. En contrepartie, il faut bien garder la table `dynamic_fields` cohérente, car elle pilote la validation, les formulaires et certains comportements API.

## Frontend

Le frontend est une app Vue 2 montée dans `resources/views/layouts/app.blade.php`.

Point d'entrée:

- `resources/assets/js/bootstrap.js`: Axios, CSRF, Vue, Element UI, VeeValidate, plugins.
- `resources/assets/js/app.js`: enregistrement des composants applicatifs.
- `lncshared/resources/assets/js/main.js`: composants partagés.
- `resources/assets/sass/app.scss`: styles principaux.

Composants actuellement présents dans l'application:

- dashboard: `search.vue`, `excel-login.vue`;
- charts: `pie-chart.vue`, `doughnut-chart.vue`;
- dynamic fields: `form.vue`, `table.vue`;
- timelines: `timeline.vue`, `news.vue`.

Composants partagés `lncshared`:

- formulaires génériques;
- inputs typés;
- listes de champs;
- tableaux intelligents;
- modal;
- loader;
- composants charts;
- profil/alias génériques.

## Authentification et accès

### SAML

Le login web passe par SAML:

- routes `saml2/login`, `saml2/acs`, `saml2/logout`, `saml2/metadata`;
- listener `app/Listeners/SamlLoginListener.php`;
- configuration `config/saml2_settings.php`.

Le listener attend des attributs SAML email, prénom, nom et groupe. Les groupes reconnus sont:

- `Tikeasy-RO`
- `Tikeasy-Users`
- `Tikeasy-Master`

Un utilisateur peut être créé automatiquement à la première connexion. Le groupe master donne le statut admin.

### Passport / OAuth

Les API sont protégées par `auth:api`, donc par Laravel Passport.

`lncshared` ajoute aussi un grant type OAuth personnalisé `protocol`, utilisé par l'intégration Excel:

- endpoint: `api/protocol-token/create`;
- composant front: `resources/assets/js/components/dashboard/excel-login.vue`;
- protocole client: `lncom://token=...&url=...`.

## Base de données

Tables principales:

- `users`
- `parties`
- `properties`
- `leases`
- `indexes`
- `invoices`
- `property_yearlys`
- `app_settings`
- `dynamic_fields`
- `protocol_tokens`
- tables Passport

Beaucoup de tables métier possèdent une colonne `data` en JSONB. Les champs dynamiques y sont stockés, puis exposés comme des attributs Laravel.

Plusieurs modèles utilisent des clés composées:

- `Lease`: `property_id`, `client_id`, `start_date`.
- `Invoice`: `owner_id`, `invoice_num`.
- `Index`: `index_code`, `period_code`.
- `PropertyYearly`: `property_id`, `year`.

Le support Eloquent des clés composées vient du trait `LncShared\App\Models\Traits\HasCompositePrimaryKey`.

## Exports et intégrations

### Excel

L'intégration Excel actuelle repose sur:

- `api/xl_init`: renvoie les parties et propriétés;
- `api/protocol-token/create`: crée un token court;
- `excel-login.vue`: ouvre un lien `lncom://`.

### QuickBooks

Attention: l'intégration QuickBooks est annoncée comme supprimée côté intention projet, mais des reliquats sont encore présents dans l'état local scanné:

- dépendances dans `composer.json`;
- configuration `config/quickbooks.php`;
- service `app/Services/QuickBook.php`;
- route `/test` dans `routes/web.php`;
- routes QuickBooks visibles dans `php artisan route:list`;
- références `quickbooks` dans certains exports de facture.

Pour éviter la confusion, considérer QuickBooks comme une dette technique à nettoyer ou à confirmer, pas comme une fonctionnalité active à développer.

## Points de vigilance constatés

### Frontend Vue possiblement incohérent après le pull

`resources/assets/js/app.js` référence encore plusieurs composants qui ne sont pas présents dans `resources/assets/js/components`, notamment autour de:

- companies;
- portfolios;
- deals;
- news form/inputs.

Si `npm run dev` échoue, commencer par ce fichier. Il faudra soit restaurer les composants de la nouvelle interface, soit retirer les enregistrements obsolètes.

### Routes et vues legacy

Certaines vues Blade référencent encore des routes `companies`, `portfolios`, `deals` ou `ajax.namesearch`. Dans le `route:list` actuel, ces routes ne sont pas toutes présentes côté Laravel actif, même si un fichier généré `resources/assets/js/routes.js` contient encore d'anciennes routes.

Avant de remettre le dashboard historique en service, vérifier:

- les routes web réellement déclarées;
- les contrôleurs/modèles correspondants;
- les composants Vue nécessaires;
- le fichier Ziggy généré si utilisé.

### QuickBooks non totalement retiré

Même si la suppression QuickBooks est l'objectif, le code local contient encore des références actives. Une suppression complète devrait vérifier:

- `composer.json` et `composer.lock`;
- `config/quickbooks.php`;
- `config/services.php`;
- `routes/web.php`;
- `database/migrations/2018_03_11_141103_create_quick_books_tokens_table.php`;
- `resources/views/vendor/quickbooks`;
- `app/Services/QuickBook.php`;
- les champs JSON `quickbooks` lus dans les exports.

### Données sensibles

Le fichier `.env` existe dans le dépôt local et `.env.example` contient des identifiants de développement. Avant toute mise en production ou partage externe:

- vérifier que `.env` n'est pas suivi par Git;
- remplacer les secrets;
- désactiver `APP_DEBUG`;
- éviter `APP_ENV=debug`;
- vérifier les clés SAML, Passport et base de données.

### Encodage

Plusieurs messages métier français apparaissent avec des caractères mal encodés dans le code. Cela ne bloque pas forcément l'exécution, mais peut dégrader les messages utilisateur et exports.

## Workflow conseillé pour un changement

1. Identifier si le changement concerne un champ stable ou un champ dynamique.
2. Pour un champ stable, ajouter migration, modèle, règles, ressource/API et front.
3. Pour un champ dynamique, préférer la table `dynamic_fields` si la donnée est purement métier/configurable.
4. Vérifier les règles dans le modèle concerné.
5. Tester l'endpoint API avec un token Passport.
6. Si le front est touché, lancer `npm run dev` et tester la page dans un navigateur.
7. Pour les calculs de facture, tester au minimum les cas loyer, charges, taxes et avoir.

## Skills Codex installés pour ce projet

Les skills installés pour faciliter la suite du développement sont:

- `playwright`: tests navigateur et validation de l'interface Laravel/Vue.
- `security-best-practices`: revue des risques classiques autour des secrets, auth, API et dépendances.
- `security-threat-model`: modélisation des menaces pour SAML, OAuth, données client/propriétaire et exports.

Après installation de nouveaux skills Codex, redémarrer Codex pour les prendre en compte.
