# Loxya | Documentation

# Annexes

<a href="/wiki/appendices#appendices-dependencies" id="appendices-dependencies" class="wiki__page__link">
    <i class="fas fa-link"></i>
</a>

## Dépendances et compatibilité des versions

### Version de PHP et MySQL

Voici un tableau qui récapitule, pour chaque version du logiciel, les différentes **versions de PHP et MySQL (ou MariaDB)** qui sont officiellement supportées et testées.

Toute utilisation d'une **version inférieure** de PHP ne **fonctionnera pas**, c'est certain.  
Les versions de PHP **supérieures** à celles indiquées fonctionnent _sûrement_ sans problème _la plupart du temps_, mais n'ont pas encore été testées et ne sont donc **pas supportées** officiellement.

| Version de Loxya | PHP (versions testées) | MySQL | MariaDB |
| --- | --- | --- | --- |
| **0.16.x** _(nov. 2021)_ | 7.3 | >= 5.7.9 | >= 10.2.7 |
| **0.17.x** _(janv. 2022)_ | 7.3 | >= 5.7.9 | >= 10.2.7 |
| **0.18.x** _(mars 2022)_ | 7.4 | >= 5.7.9 | >= 10.2.7 |
| **0.19.x** _(juil. 2022)_ | 7.4 | >= 5.7.9 | >= 10.2.7 |
| **0.20.x** _(mars 2023)_ | 8.0 ou 8.1 | >= 5.7.9 | >= 10.2.7 |
| **0.21.x** _(mai 2023)_ | 8.0 ou 8.1 | >= 5.7.9 | >= 10.2.7 |
| **0.22.x** _(août 2023)_ | 8.0 ou 8.1 | >= 5.7.9 | >= 10.2.7 |
| **0.23.x** _(nov. 2023)_ | 8.0 ou 8.1 | >= 5.7.9 | >= 10.2.7 |
| **0.24.x** _(mai 2024)_ | 8.1, 8.2 ou 8.3 | >= 5.7.9 | >= 10.2.7 |
| **1.0.x** _(nov. 2024)_ | 8.1, 8.2 ou 8.3 | >= 5.7.9 | >= 10.2.7 |
| **1.1.x** _(avril. 2025)_ | 8.1, 8.2 ou 8.3 | >= 5.7.9 | >= 10.2.7 |
| **1.2.x** _(sept. 2025)_ | 8.1, 8.2 ou 8.3 | >= 5.7.9 | >= 10.2.7 |
| **1.3.x** _(mai 2026)_ | 8.1, 8.2 ou 8.3 | >= 5.7.9 | >= 10.2.7 |

**Notes**&nbsp;:

- Le numéro de la version du logiciel installé se trouve dans le fichier `src/VERSION`.
- L'information de la (les) version(s) de PHP officiellement supportée(s) pour la version installée est affichée dans le fichier `composer.json`, dans `require`→`php-64bits`.

### Modules PHP requis

Les **extensions PHP** (modules) requis par le logiciel sont les suivants&nbsp;:

`bcmath`, `curl`, `dom`, `fileinfo`, `gettext`, `iconv`, `intl`, `json`, `mbstring`, `pcre`, `PDO`, `pdo_mysql`, `openssl`, et `xml`.

### Weasyprint

La présence du **logiciel <a href="https://weasyprint.org" target="_blank" rel="nofollow noreferrer">Weasyprint</a>** est requise depuis la **version 1.3** de Loxya. Il est utilisé pour la **génération des documents PDF** du logiciel (fiches de sortie, devis, factures, export de la liste du matériel ou des rapports d'inventaire).

La **version minimale** requise est `Weasyprint 66.0`.

Vous trouverez plus d'informations sur ce point dans la documentation de l'installation de Loxya [à l'étape _"configurer le serveur"_](/wiki/install#install-web-server).

<a href="/wiki/appendices#appendices-logo-pdf" id="appendices-logo-pdf" class="wiki__page__link">
    <i class="fas fa-link"></i>
</a>

## Ajouter un logo en en-tête des PDF

Pour ajouter le logo de votre structure en en-tête des fiches de sortie, des devis et des factures, il faut&nbsp;:

1. **Ajouter le fichier** du logo dans le dossier `src/public/img/`. Ce fichier doit être une **image** du type **JPEG** ou **PNG**. Dans l'idéal, cette image ne devrait avoir une résolution d'au moins 400x250 pixels (notez aussi le rapport d'affichage, qui devrait rester plus large que haut). Évitez tout de même les images trop volumineuses, qui pourraient ralentir la génération des documents PDF.
2. Assurez-vous que le fichier est bien **accessible en lecture** par l'utilisateur système du serveur web (par ex. `www-data`).
3. Ensuite, dans le [fichier de configuration](#appendices-settings), insérer la clé `organization.logo` avec comme valeur le **nom de ce fichier**.  
Pour exemple, voici un extrait du fichier de configuration modifié pour ajouter un logo `logo.jpg`&nbsp;:

```
    },
    "organization": {
        "logo": "logo.jpg",
        (...)
```


<a href="/wiki/appendices#appendices-emails" id="appendices-emails" class="wiki__page__link">
    <i class="fas fa-link"></i>
</a>

## Paramétrer l'envoi d'e-mails

Dans le fichier `/src/App/Config/settings.json`, vous pouvez ajouter une section qui permettra au logiciel d’envoyer des e-mails, grâce à la **fonction mail** du serveur, ou alors via un **serveur SMTP**, ou encore en utilisant l’**API de <a href="https://www.mailjet.com/fr/" target="_blank">Mailjet <i class="fas fa-external-link-alt"></i></a>** (service d’envoi d’e-mails). Cette section se présente ainsi&nbsp;:

```json
    "email": {
        "from": {
            "email": "notifications@example.com",
            "name": "Notifications Exemple"
        },
        "driver": "mail",
        "smtp": {
            "host": "your.smtp.com",
            "port": 465,
            "username": "notifications@example.com",
            "password": "mot-de-passe-smtp",
            "security": "ssl"
        },
        "mailjet": {
            "apiKey": "Clé d’API de Mailjet",
            "apiSecretKey": "Clé secrète pour l’API de Mailjet"
        }
    },
```

Il faut bien sûr ne pas oublier d’utiliser la bonne valeur de `driver` ("mail", "smtp" ou "mailjet") selon le service choisi et renseigné.

<i class="fas fa-info-circle"></i> Retrouvez le détail de chaque paramètre du fichier de configuration dans [cette section](/wiki/appendices#appendices-settings).

Pour **tester ces paramètres** en envoyant un **e-mail de test**, vous pouvez utiliser la commande suivante dans un terminal, en vous plaçant dans le dossier racine du projet&nbsp;:

```sh
./bin/console test:email votre-adresse@exemple.com
```

Vous devriez ensuite recevoir un e-mail à l'adresse donnée dont le sujet est _«&nbsp;Test de la configuration Loxya&nbsp;»_, et le corps du message _«&nbsp;L'envoi des e-mails depuis Loxya fonctionne correctement&nbsp;!&nbsp;»_. Si ce n'est pas le cas, alors le message d'erreur affiché dans votre terminal vous donnera plus d'informations sur l'origine du problème.

<a href="/wiki/appendices#appendices-notifications" id="appendices-notifications" class="wiki__page__link">
    <i class="fas fa-link"></i>
</a>

## Configurer les notifications <small>(<i class="fas fa-crown"></i> Premium)</small>

Une fois que l’envoi des e-mails est configuré (voir point précédent), vous pouvez activer et paramétrer les notifications elles-mêmes. Pour cela, il faut utiliser la section suivante dans le fichier `/src/App/Config/settings.json`&nbsp;:

```json
    "notifications": {
        "overdue": {
            "enabled": true,
            "days": [1, 15, 22, 27, 30]
        },
        "preparers": {
            "enabled": true,
            "daysBefore": 1
        }
    },
```

Vous pouvez activer les notifications suivantes en mettant à `true` les paramètres `enabled` de&nbsp;:

- `overdue`&nbsp;: Envoi d’une **notification au bénéficiaire** d’un événement ou d’une réservation, tant qu’il n’a pas retourné l’intégralité du matériel.
- `preparers`&nbsp;: Envoi d’une **notification au préparateur de commande** assigné à un événement, quelques jours avant le début de cet événement.

Bien entendu, pour que l’envoi des notifications fonctionne, il faut paramétrer une **tâche CRON** (voir ci-dessous).

<i class="fas fa-info-circle"></i> Retrouvez le détail de chaque paramètre du fichier de configuration dans [cette section](/wiki/appendices#appendices-settings).


<a href="/wiki/appendices#appendices-environment-variables" id="appendices-environment-variables" class="wiki__page__link">
    <i class="fas fa-link"></i>
</a>

## Variables d'environnement serveur

La présence d'un fichier nommée `.env` à la racine du projet permettra à Loxya de configurer certains aspect de son propre fonctionnement.

Voici un exemple de fichier `env`, avec des commentaires expliquant les possibilités&nbsp;:

```
APP_ENV=production

# - Permet de voir le contenu des documents exportés (pdf, csv, etc.)
#   dans le navigateur à la place de les télécharger sous forme de fichier.
DEBUG_EXPORT=false

# - Permet de définir la timezone de l'application.
TIMEZONE=Europe/Paris
```

- `APP_ENV` permet de définir le type d'installation de Loxya. Dans la plupart des cas, il faut laisser la valeur "production" à moins de réellement savoir ce que vous faites.
- `DEBUG_EXPORT` est un booléen qui permet d'empêcher la création des fichiers PDF, lors de la génération des documents par Loxya (fiches de sortie, devis, factures...), mais plutôt d'avoir un rendu "brut" en HTML du document, à ouvrir dans un nouvel onglet. Cela peut être utile lors de la [modification des modèles de documents](#appendices-bill-template), pour gagner du temps.
- `TIMEZONE` permet de forcer le fuseau horaire de votre instance de Loxya sur un fuseau précis, quand le serveur lui-même n'est pas configuré sur le même fuseau horaire que vos utilisateurs.

<a href="/wiki/appendices#appendices-cron" id="appendices-cron" class="wiki__page__link">
    <i class="fas fa-link"></i>
</a>

## Tâches CRON

Certaines tâches doivent être **exécutées régulièrement** par le logiciel, comme par exemple l’envoi de notifications par e-mail, ou le "nettoyage" de données obsolètes.

Dans le fichier `src/App/Config/CRON.json` se trouve la liste des tâches à créer, avec&nbsp;:

- `time`&nbsp;: La **récurrence** à utiliser. Par exemple, "daily" signifie une fois par jour (à vous de choisir l’heure précise).
- `command`&nbsp;: La **commande** à exécuter, grâce à `bin/console` (en spécifiant le chemin complet). Par exemple&nbsp;:  
`/var/www/loxya/bin/console notifications:reminders`.


<a href="/wiki/appendices#appendices-barcode-scanner" id="appendices-barcode-scanner" class="wiki__page__link">
    <i class="fas fa-link"></i>
</a>

## Choisir et paramétrer un scanner de code-barres <small>(<i class="fas fa-crown"></i> Premium)</small>

Les codes-barres peuvent être&nbsp;:

- soit **générés par Loxya** (en mode "auto", voir la configuration), et il vous suffit de les télécharger pour les imprimer.
- soit **fournis par l'utilisateur** (en mode "manuel", voir la configuration).

Plusieurs formats de codes-barres sont pris en charge. Codes-barres à deux dimensions&nbsp;:

- format **QR Code**
- format **PDF417**

Et des codes-barres à 1 dimension&nbsp;:

- format **Code 39**
- format **Code 128**

> <i class="fas fa-info-circle"></i> Attention  
> En **mode "manuel"**, ainsi que pour les **codes-barres à 1 dimension**, le scanner de codes-barres devra impérativement pouvoir **ajouter automatiquement un préfixe et un suffixe** au code-barre scanné. Assurez-vous donc de choisir un modèle qui permet cela.

> - Le **préfixe** à ajouter est composé des caractères suivants&nbsp;: `^#[`
> - Le **suffixe** à ajouter est composé des caractères suivants&nbsp;: `]#$`

> Généralement, il suffit de scanner les caractères ASCII correspondants dans le manuel utilisateur de la scanette pour les activer.

Dans un éditeur de texte, en scannant un code-barre, vous devriez voir apparaître une chaîne de caractères ressemblant à ceci&nbsp;: `^#[C0D384R]#$`

Si c'est le cas, alors votre douchette est correctement paramétrée 👍. Sinon, il faut revoir sa configuration, notamment la **langue** qui correspond à la disposition de votre clavier.


<a href="/wiki/appendices#appendices-sso" id="appendices-sso" class="wiki__page__link">
    <i class="fas fa-link"></i>
</a>

## Authentification unique (SSO) via CAS ou SAML2 <small>(<i class="fas fa-crown"></i> Premium)</small>

Vous avez la possibilité d’utiliser un système d’authentification unique via un serveur CAS, et / ou SAML2.

Pour cela, vous devez bien sûr **autoriser** l'instance du logiciel à **accéder à votre serveur CAS ou SAML2**, et le **paramétrer correctement**. Ensuite, vous pouvez activer la fonctionnalité en renseignant la section suivante du fichier `/src/App/Config/settings.json`&nbsp;:

```json
    "auth": {
        "cookie": "auth",
        "CAS": {
            "enabled": true,
            "host": "your.cas.server",
            "uri": "/cas",
            "port": 8080,
            "cert": false,
            "attributes": {
                "pseudo": "givenName",
                "email": ["mail", "email"],
                "firstName": "firstName",
                "lastName": "lastName",
                "group": "group"
            },
            "groupsMapping": {
                "MonGroupeBeneficiaireCAS": "visitor"
            },
            "beneficiaryGroups": ["MonGroupeBeneficiaireCAS"],
            "defaultGroup": null,
            "nonCASUserLinking": true
        },
        "SAML2": {
            "enabled": true,
            "signRequests": false,
            "signingCert": {
                "key": "La clé du certificat de votre instance du logiciel",
                "cert": "Le certificat de votre instance du logiciel, encodé en base64"
            },
            "idp": {
                "entityId": "URL de l'Identity Provider",
                "singleSignOnServiceUrl": "URL du service de login",
                "singleLogoutServiceUrl": "URL du service de logout",
                "signingCert": "Le certificat de votre Identity Provider"
            },
            "attributes": {
                "pseudo": "givenName",
                "email": ["mail", "email"],
                "firstName": "first_name",
                "lastName": "last_name",
                "group": "group"
            },
            "beneficiaryGroups": ["MonGroupeBeneficiaireSAML2"],
            "groupsMapping": {
                "Administrateur": "admin",
                "MonGroupeBeneficiaireSAML2": "visitor"
            },
            "defaultGroup": null,
            "nonSAMLUserLinking": true,
            "disableUnsolicitedRequests": false,
            "proxyVars": false
        }
    },
```

<i class="fas fa-info-circle"></i> Retrouvez le détail de chaque paramètre du fichier de configuration dans [cette section](/wiki/appendices#appendices-settings).

<a href="/wiki/appendices#appendices-sso-cas" id="appendices-sso-cas" class="wiki__page__link">
    <i class="fas fa-link"></i>
</a>

Pour **activer** l'une ou l'autre (ou les deux) méthodes d'authentification, il faut utiliser le paramètre **`enabled`** des clés `auth.cas` et/ou `auth.saml2`, en lui mettant la valeur `true`.

### CAS

Voici le détail de chaque paramètre qu'il vous faudra renseigner pour faire fonctionner la SSO avec CAS Dans Loxya.

**`host`**

Il s'agit du **nom d'hôte de votre serveur CAS**. Par exemple `cas.votre-domaine.com`

**`port`**

Le **numéro de port** à utiliser pour la connexion au serveur CAS. Par défaut, `8443`.

**`uri`**

L'URI à laquelle le serveur CAS **répond**. Par exemple, `/cas`.

**`cert`**

Le **certificat** du serveur CAS. Cette option accepte plusieurs valeurs&nbsp;:

- `false`: Désactive la verification SSL du serveur CAS.
- Le **chemin vers le fichier** de certificat au format PEM.

#### Mapping des attributs

L'option `attributes` permet de **mettre en correspondance** les données attendues pour **créer un utilisateur** dans Loxya, avec les **attributs** qui sont renvoyés dans la **réponse du fournisseur d'identité** (IdP).

La valeur de ce paramètre attend un tableau associatif (objet JSON) qui contient les clés suivantes&nbsp;:

- `pseudo`&nbsp;: l'attribut à utiliser pour créer le pseudo de l'utilisateur. Par exemple, `givenName`. **Attention**, cette valeur doit être unique pour chacun de vos utilisateurs.
- `email`&nbsp;: l'attribut à utiliser pour créer l'adresse e-mail de l'utilisateur.
- `group`&nbsp;: l'attribut à utiliser pour créer le rôle de l'utilisateur. Par exemple, `group` ou `role`.
- `firstName`&nbsp;: l'attribut à utiliser pour créer le prénom de l'utilisateur. Par exemple, `firstName`.
- `lastName`&nbsp;: l'attribut à utiliser pour créer le nom de famille de l'utilisateur. Par exemple, `lastName`.

Si vous spécifiez un **tableau** en valeur de l'une de ces clés, la valeur de la clé sera cherché dans tous les attributs correspondants. Dès qu'un de ces attributs existe, c'est cette valeur qui sera utilisée.  
Par exemple, si vous définissez&nbsp;: `"email": ["mail", "e-mail"]`, l'attribut `mail` sera recherché pour l'adresse e-mail de l'utilisateur. Si cet attribut n'existe pas ou qu'il est vide, alors c'est l'attribut `e-mail` sera recherché. Si aucun attribut n'est trouvé, alors une valeur de remplacement par défaut sera utilisée quand c'est possible (pour l'email ce sera un email fictif / interne à l'application par exemple).

#### Groupe(s) liés aux bénéficiaires

Le paramètre `beneficiaryGroups` permet de définir un (ou plusieurs) groupes CAS qui, si présents dans l'attribut de groupe retourné par le fournisseur d'identité (voir l'attribut "group" ci-dessus), feront de l'utilisateur un bénéficiaire pouvant réserver via la réservation publique (si cette fonctionnalité est activée et disponible).

Si la valeur de cette option est `null` (c'est le cas par défaut), la fonctionnalité sera désactivée (= pas de création de profils bénéficiaire lors de la connexion CAS). Il est à noter que ceci ne remplace pas la récupération du groupe dont il est fait mention ci-dessous. Cette option permet seulement d'indiquer que l'utilisateur est *aussi* un bénéficiaire.

Vous avez également la possibilité d'utiliser un caractère «&nbsp;wildcard&nbsp;» (joker) pour ce paramètre, c'est à dire le caractère `*`. Ainsi, tout utilisateur qui se connecte pour la première fois via CAS aura un compte bénéficiaire associé, peu importe son groupe CAS. Cela est aussi utile si votre serveur CAS ne remonte pas les groupes des utilisateurs.

À vous ensuite de choisir dans quel groupe vous voulez le mettre du côté de Loxya via l'option `groupsMapping` et/ou `defaultGroup`.  

Exemples de valeur&nbsp;:

- `"MonGroupeBénéficiaireCAS"`
- `["MonGroupeBénéficiaireCAS1", "MonGroupeBénéficiaireCAS2"]`
- `*`

#### Mapping des groupes / rôles

Le paramètre `groupsMapping` permet de mettre en correspondance un groupe Loxya avec un groupe provenant de l'attribut de groupe retourné par le fournisseur d'identité (voir l'attribut "group" ci-dessus).

Les rôles Loxya disponibles sont les suivants&nbsp;:

- `administration`&nbsp;: Administration,
- `management`&nbsp;: Gestion,
- `readonly-planning-general`&nbsp;: Consultation du planning général,
- `readonly-planning-self`&nbsp;: Consultation de son propre planning,
- `external`&nbsp;: Externe (accès aux demandes de réservation uniquement).

Exemple de valeur&nbsp;: `["MonGroupeBénéficiaireCAS" => "external"]`.

Le paramètre `defaultGroup` permet de **définir un rôle** à l'utilisateur même si **aucune correspondance** n'a été trouvée avec l'attribut fourni via le `groupsMapping` ci-dessus.

- Si la valeur de cette option est non `null`, l'utilisateur sera automatiquement assigné à ce groupe ci.
- Si la valeur est `null`, l'utilisateur ne sera pas autorisé à se connecter.

#### Rattachement des utilisateurs existants

En activant le paramètre `nonCASUserLinking` (en le mettant sur `true`), vous activez le rattachement des utilisateurs CAS avec les utilisateurs existants.

Si cette option est _activée_, un utilisateur CAS qui se connecte avec une adresse e-mail déjà connue par Loxya pour un utilisateur non lié à SAML sera **automatiquement rattaché** à cet utilisateur, et disposera de ses autorisations.

Si cette option est _désactivée_, les utilisateurs CAS ayant déjà un compte non CAS dans l'application seront **rejetés**.

<a href="/wiki/appendices#appendices-sso-saml2" id="appendices-sso-saml2" class="wiki__page__link">
    <i class="fas fa-link"></i>
</a>

### SAML 2.0

Voici le détail de chaque paramètre qu'il vous faudra renseigner pour faire fonctionner la SSO avec SAML 2.0 dans Loxya.

**`signRequests`**

Il s'agit d'une option qui permet d'activer ou non la **signature** des **requêtes envoyées au fournisseur d'identité** (IdP). Le certificat utilisé pour signez ces requêtes est un **certificat x509** fourni dans le paramètre `signingCert` (voir ci-dessous).

Par défaut, cette option est désactivée (= `false`). Vous pouvez l'activer (en la mettant à `true`) si votre IdP exige des requêtes sécurisées.

**`signingCert`**

Le paramètre `auth.SAML2.signinCert` et ses clés `key` et `cert` servent à renseigner le **certificat** qui sera utilisé pour **signer les requêtes** (et les réponses) **au fournisseur d'identité**. Ce certificat doit-être fourni, que la signature des requêtes (c'est à dire le paramètre `signRequests` ci-dessus) soit **activée ou non**.

Vous pouvez générer ces deux valeurs via la commande suivante (attention à bien remplacer ce qui se trouve entre crochets par le nom de domaine de votre instance de Loxya)&nbsp;:

```bash
openssl req \
    -x509 -nodes -days 3652 -new -newkey rsa:3072 \
    -subj '/CN=[Hôte de l'instance de Loxya]/C=FR' \
    -keyout signingCert.key \
    -out signingCert.crt
```

La commande ci-dessus générera deux fichiers à l'endroit ou elle est exécutée&nbsp;:

- Un fichier `signingCert.key` dont le contenu doit être mis en valeur de `signingCert.key`.  
Pour cela, récupérez son contenu via&nbsp;: `awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' ./signingCert.key | pbcopy`  
Ceci devrait produire une chaîne commençant par `-----BEGIN PRIVATE KEY-----\n`.
- Un fichier `signingCert.crt` dont le contenu doit être mis en valeur de `signingCert.cert`.  
Pour cela, récupérez son contenu via&nbsp;: `awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' ./signingCert.crt | pbcopy`
Ceci devrait produire une chaîne commençant par `-----BEGIN CERTIFICATE-----\n`.

NOTE&nbsp;: Les fichiers générés ci-dessus ne seront pas utilisés par l'application directement donc vous pouvez en disposer comme vous voulez.

#### Le fournisseur d'identité (IdP)

Les 4 paramètres se trouvant dans `auth.SAML2.idp` servent à définir les URLs et le certificat que Loxya devra utiliser pour effectuer l'authentification des utilisateurs via SAML 2.0 auprès de votre IdP ("Identity Provider").

**`entityId`**

Il s'agit de l'URI de votre fournisseur d'identité. Généralement, cette URI ressemble à `https://[idp-url]/metadata`.

**`singleSignOnServiceUrl`**

Il s'agit de l'URL d'accès à la connexion ("Single Sign On", ou "SSO") du fournisseur d'identité. Généralement, elle ressemble à `https://[idp-url]/sso`.

**`singleLogoutServiceUrl`**

Il s'agit de l'URL d'accès à la déconnexion ("Single Logout Service", ou "SLS") du fournisseur d'identité. Généralement, elle ressemble à `https://[idp-url]/sls`.

**`signinCert`**

Le paramètre `auth.SAML2.idp.signinCert` renseigne le certificat public du fournisseur d'identité (IdP). Il attend une chaîne de caractère, que vous pouvez récupérer à partir du fichier de certificat public de l'IdP (au format PEM), en exécutant la commande suivante&nbsp;:

```bash
awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' "[le fichier du certificat].crt" | pbcopy
```

Ceci devrait produire une chaîne commençant par `-----BEGIN CERTIFICATE-----\n`. C'est cette chaîne qu'il faut renseigner dans le paramètre `auth.SAML2.idp.signinCert`.

#### Mapping des attributs

L'option `attributes` permet de **mettre en correspondance** les données attendues pour **créer un utilisateur** dans Loxya, avec les **attributs** qui sont renvoyés dans la **réponse du fournisseur d'identité** (IdP).

La valeur de ce paramètre attend un tableau associatif (objet JSON) qui contient les clés suivantes&nbsp;:

- `pseudo`&nbsp;: l'attribut à utiliser pour créer le pseudo de l'utilisateur. Par exemple, `givenName`.
- `email`&nbsp;: l'attribut à utiliser pour créer l'adresse e-mail de l'utilisateur.
- `group`&nbsp;: l'attribut à utiliser pour créer le rôle de l'utilisateur. Par exemple, `group` ou `role`.
- `firstName`&nbsp;: l'attribut à utiliser pour créer le prénom de l'utilisateur. Par exemple, `firstName`.
- `lastName`&nbsp;: l'attribut à utiliser pour créer le nom de famille de l'utilisateur. Par exemple, `lastName`.

Si vous spécifiez un **tableau** en valeur de l'une de ces clés, la valeur de la clé sera cherché dans tous les attributs correspondants. Dès qu'un de ces attributs existe, c'est cette valeur qui sera utilisée.  
Par exemple, si vous définissez&nbsp;: `"email": ["mail", "e-mail"]`, l'attribut `mail` sera recherché pour l'adresse e-mail de l'utilisateur. Si cet attribut n'existe pas ou qu'il est vide, alors c'est l'attribut `e-mail` sera recherché. Si aucun attribut n'est trouvé, alors une valeur de remplacement par défaut sera utilisée quand c'est possible (pour l'email ce sera un email fictif / interne à l'application par exemple).

#### Groupe(s) liés aux bénéficiaires

Le paramètre `beneficiaryGroups` permet de définir un (ou plusieurs) groupes SAML 2.0 qui, si présents dans l'attribut de groupe retourné par le fournisseur d'identité (voir l'attribut "group" ci-dessus), feront de l'utilisateur un bénéficiaire pouvant réserver via la réservation publique (si cette fonctionnalité est activée et disponible).

Si la valeur de cette option est `null` (c'est le cas par défaut), la fonctionnalité sera désactivée (= pas de création de profils bénéficiaire lors de la connexion SAML 2.0). Il est à noter que ceci ne remplace pas la récupération du groupe dont il est fait mention ci-dessous. Cette option permet seulement d'indiquer que l'utilisateur est *aussi* un bénéficiaire.

Vous avez également la possibilité d'utiliser un caractère «&nbsp;wildcard&nbsp;» (joker) pour ce paramètre, c'est à dire le caractère `*`. Ainsi, tout utilisateur qui se connecte pour la première fois via SAML 2.0 aura un compte bénéficiaire associé, peu importe son groupe. Cela est aussi utile si votre IdP ne remonte pas correctement les groupes des utilisateurs.

À vous ensuite de choisir dans quel groupe vous voulez le mettre du côté de Loxya via l'option `groupsMapping` et/ou `defaultGroup`.  
Exemples de valeur&nbsp;:

- `"MonGroupeBénéficiaireSAML"`
- `["MonGroupeBénéficiaireSAML1", "MonGroupeBénéficiaireSAML2"]`
- `*`

#### Mapping des groupes / rôles

Le paramètre `groupsMapping` permet de mettre en correspondance un groupe Loxya avec un groupe provenant de l'attribut de groupe retourné par le fournisseur d'identité (voir l'attribut "group" ci-dessus).

Les rôles Loxya disponibles sont les suivants&nbsp;:

- `administration`&nbsp;: Administration,
- `management`&nbsp;: Gestion,
- `readonly-planning-general`&nbsp;: Consultation du planning général,
- `readonly-planning-self`&nbsp;: Consultation de son propre planning,
- `external`&nbsp;: Externe (accès aux demandes de réservation uniquement).

Exemple de valeur&nbsp;: `["MonGroupeBénéficiaireSAML" => "external"]`.

Le paramètre `defaultGroup` permet de **définir un rôle** à l'utilisateur même si **aucune correspondance** n'a été trouvée avec l'attribut fourni via le `groupsMapping` ci-dessus.

- Si la valeur de cette option est non `null`, l'utilisateur sera automatiquement assigné à ce groupe ci.
- Si la valeur est `null`, l'utilisateur ne sera pas autorisé à se connecter.

#### Rattachement des utilisateurs existants

En activant le paramètre `nonSAMLUserLinking` (en le mettant sur `true`), vous activez le rattachement des utilisateurs SAML 2.0 avec les utilisateurs existants.

Si cette option est _activée_, un utilisateur SAML 2.0 qui se connecte avec une adresse e-mail déjà connue par Loxya pour un utilisateur non lié à SAML sera **automatiquement rattaché** à cet utilisateur, et disposera de ses autorisations.

Si cette option est _désactivée_, les utilisateurs SAML ayant déjà un compte non SAML dans l'application seront **rejetés**.

#### Permettre à l'IdP de connecter / déconnecter une session Loxya

Le paramètre `disableUnsolicitedRequests` permet de désactiver la prise en charge des requêtes de connexion / déconnexion qui proviennent du fournisseur d'identité sans que l'utilisateur soit passé par Loxya pour provoquer ces actions.

Par défaut, cette option est sur `false`, c'est à dire que l'IdP peut déconnecter un utilisateur qui serait en train d'utiliser Loxya.

#### Définir un Proxy

Le paramètre `proxyVars` permet d'utiliser les headers `X-Forwarded-*` / `HTTP_X_FORWARDED_PROTO` pour déterminer le port / domaine / protocole.

Ceci est utile notamment si l'application tourne derriere un load balancer qui met fin au protocole SSL.

<a href="/wiki/appendices#appendices-coef" id="appendices-coef" class="wiki__page__link">
    <i class="fas fa-link"></i>
</a>

## Coefficient de tarif dégressif

> **Obsolète**&nbsp;: Depuis la version `1.0`, plusieurs coefficients de tarif dégressif sont paramétrables, directement dans [les paramètres du logiciel](/wiki/usage#usage-settings-billing-degressive-rates).

<a href="/wiki/appendices#appendices-settings" id="appendices-settings" class="wiki__page__link">
    <i class="fas fa-link"></i>
</a>

## Le fichier de configuration en détail

Ce fichier est créé automatiquement par [l'assistant d'installation](/wiki/wizard), mais en cas de modification il est bon de savoir à quoi servent ses composantes.

> <i class="fas fa-exclamation-triangle"></i> **Important**  
> Veillez bien à ne pas casser la structure du fichier de configuration, car cela pourrait rendre le logiciel complètement inutilisable.

<a href="/wiki/appendices#appendices-settings-example" id="appendices-settings-example" class="wiki__page__link">
    <i class="fas fa-link"></i>
</a>

### Exemple (format JSON)

Voici un exemple de contenu de ce fichier de configuration principal, expliqué ligne par ligne dans un tableau, en dessous&nbsp;: 

```json
{
    "baseUrl": "https://example-loxya.net",
    "mainCountry": "FR",
    "defaultLang": "fr",
    "currency": "EUR",
    "JWTSecret": "super_long_secret_string_to_never_commit",
    "healthcheck": true,
    "enableCORS": false,
    "sessionExpireHours": 12,
    "maxItemsPerPage": 100,
    "maxFetchPeriod": 90,
    "maxConcurrentFetches": 2,
    "returnPolicy": "auto",
    "billingMode": "partial",
    "features": {
        "technicians": true,
        "ata-carnet": false
    },
    "db": {
        "host": "localhost",
        "port": 3306,
        "database": "loxya",
        "username": "myuser",
        "password": "mypassword",
        "prefix": ""
    },
    "organization": {
        "name": "Votre société",
        "logo": "votre_logo.jpg",
        "registrationId": "12345678900015",
        "legalType": "FR.SARL",
        "shareCapital": "45000",
        "activityCode": "90.01Z",
        "tradeRegistryCity": "Paris",
        "isVatExempted": false,
        "vatExemptionCode": null,
        "vatExemptionReason": null,
        "vatNumber": "FR11233445566",
        "isVatDueOnInvoice": false,
        "street": [
            "32, rue du matos",
            "Bat. gauche"
        ],
        "postalCode": "75000",
        "administrativeArea": null,
        "locality": "Paris",
        "country": "FR",
        "phone": "+33123456789",
        "email": "contact@your-company.com",
        "website": "https://your-company.com"
    },
    "estimates": {
        "validityDays": 15
    },
    "invoices": {
        "routingIdentifier": "0225:123456789",
        "paymentTermDays": 30,
        "paymentMethods": {
            "cheque": true,
            "transfer": {
                "holder": "Votre société",
                "iban": "FR76 1234 5678 9000 1234 5678 987",
                "bic": "SBMAAA20"
            },
            "cash": false,
            "card": false
        },
        "mentions": {
            "seller-identity": null,
            "trade-register": null,
            "vat-due-on-invoice": null,
            "no-early-payment-discount": null,
            "late-payment-penalty": null,
            "late-payment-flat-fee": null
        }
    },
    "measurementUnits": {
        "materials": {
            "weight": "kg"
        }
    },
    "proxy": {
        "enabled": false,
        "host": "proxy.loxya.test",
        "port": 3128
    },
    "colorSwatches": [
        "#f5006b",
        "#06b5a9",
        "#f8ad14",
        "#9fde3f",
        "#ff6e21"
    ],
    "logger": {
        "timezone": "Europe/Paris",
        "level": 200,
        "max_files": 5
    },
    "maxFileUploadSize": 26214400,
    "authorizedFileTypes": ["application/pdf", "application/zip", "text/csv", "..."],
    "authorizedImageTypes": ["image/jpeg", "image/png", "image/webp", "..."],
    "email": {
        "from": {
            "email": "noreply@example.com",
            "name": "Notification Loxya"
        },
        "driver": "mail",
        "smtp": {
            "host": "localhost",
            "port": 1025,
            "username": null,
            "password": null,
            "security": ""
        },
        "mailjet": {
            "apiKey": "mailjet-api-key",
            "apiSecretKey": "mailjet-api-secret"
        }
    },

    //////////////////////////////////////////////////
    // La suite est spécifique à la variante Premium :
    //////////////////////////////////////////////////

    "enableApiDocs": true,
    "barcodes": {
        "mode": "auto",
        "format": "qr-code"
    },
    "handScanner": {
        "scanTimeout": 100,
        "inputLayout": "azerty"
    },
    "exports": {
        "materials": {
            "advanced": true
        }
    },
    "auth": {
        "cookie": "auth",
        "inactivityWatcher": {
            "enabled": false,
            "threshold": 3,
        },
        "CAS": {
            "enabled": false,
            "host": "cas.loxya.dev",
            "port": 8080,
            "uri": "/cas",
            "cert": false,
            "attributes": {
                "pseudo": "givenName",
                "email": ["mail", "email"],
                "firstName": "first_name",
                "lastName": "last_name",
                "group": "group"
            },
            "beneficiaryGroups": "*",
            "groupsMapping": {
                "Administrateur": "administration",
                "MonGroupeBeneficiaireCAS": "external"
            },
            "defaultGroup": "external",
            "nonCASUserLinking": true
        },
        "SAML2": {
            "enabled": false,
            "signRequests": false,
            "signingCert": {
                "key": null,
                "cert": null
            },
            "idp": {
                "entityId": null,
                "singleSignOnServiceUrl": null,
                "singleLogoutServiceUrl": null,
                "signingCert": null
            },
            "attributes": {
                "pseudo": "givenName",
                "email": ["mail", "email"],
                "firstName": "first_name",
                "lastName": "last_name",
                "group": "group"
            },
            "beneficiaryGroups": "*",
            "groupsMapping": {
                "Administrateur": "administration",
                "MonGroupeBeneficiaireCAS": "external"
            },
            "defaultGroup": "external",
            "nonSAMLUserLinking": true,
            "disableUnsolicitedRequests": false,
            "proxyVars": false
        }
    },
    "signup": {
        "restrictedEmailDomains": ["my-company.com", "other.org"],
        "expireAfter": 120,
        "requireAddress": "auto",
        "allowCompanies": true,
        "maxRegistrationBeforeCooldown": 2
    },
    "legalDocuments": {
        "privacyPolicy": null,
        "termsOfUse": null,
    },
    "notifications": {
        "overdue": {
            "enabled": true,
            "days": [1, 15, 22, 27, 30],
            "events": {
                "enabled": true
            },
            "reservations": {
                "enabled": true
            }
        },
        "preparers": {
            "enabled": true,
            "daysBefore": 1
        }
    }
}
```

<a href="/wiki/appendices#appendices-settings-table" id="appendices-settings-table" class="wiki__page__link">
    <i class="fas fa-link"></i>
</a>

### Détails du fichier de configuration

| Clé | Description | Type de valeur | Valeur par défaut |
| --- | --- | --- | --- |
| baseUrl | Adresse web (URL) de l'application | string | _[URL actuel]_ |
| mainCountry | Le code ISO du pays principal où est utilisé le logiciel | string | `"FR"` |
| defaultLang | Langue de l'interface par défaut (en étant non connecté) | "fr" ou "en" | `"fr"` |
| currency | Le code ISO de la devise à utiliser pour les prix et les montants | string | `"EUR"` |
| JWTSecret | Passphrase utilisée pour la création du JWT (Json Web Token) | string | _[Valeur aléatoire]_ |
| healthcheck | Détermine si la route `/healthcheck` est activée ou non.<br />Cette route permet de savoir si l'API est accessible. Elle retourne la date de dernière modification. | boolean | `false` |
| enableCORS | Activer le _"Cross Origin Resource Sharing"_ | boolean | `false` |
| sessionExpireHours | Nombre d'heures avant expiration d'une session utilisateur.<br />**Attention**, ne **pas** utiliser la valeur `0`, car cela risque de faire planter le login&nbsp;! | integer | `12` |
| maxItemsPerPage | Nombre d'enregistrements retournés par pages pour la pagination | integer | `100` |
| maxFetchPeriod | Période maximale (en jours) pendant laquelle les données peuvent être récupérées | integer | `90` |
| maxConcurrentFetches | Nombre de requêtes simultanées maximum pour la récupération du matériel manquant | integer | `2` |
| returnPolicy | Politique de retour des événements et réservations | "auto" ou "manual" | `"auto"` |
| billingMode | Le mode d'utilisation de la facturation. Trois valeurs possibles&nbsp;:<br />- `all`&nbsp;: facturation de tous les événements,<br />- `partial`&nbsp;: facturation des événements facturables uniquement,<br />- `none`&nbsp;: pas de facturation du tout (mode prêt) | string | `"partial"` |
| colorSwatches | Liste des couleurs personnalisées, à utiliser dans le color-picker de de l'étape 1 de l'édition des événements.<br />Exemple&nbsp;: `["#f5006b", "#06b5a9", "#365576"]`. | array (ou&nbsp;null) | `null` |
| **features** | Activation / désactivation de certaines fonctionnalités. | **object** |  |
| →&nbsp;features.technicians | Activation ou non de la fonctionnalité de gestion des techniciens. | boolean | `true` |
| maxFileUploadSize | Taille maximum des fichiers pouvant être envoyés | integer | `26 214 400` (25Mo) |
| authorizedFileTypes | Liste des types de fichiers pouvant être envoyés | array | _une liste prédéfinie_ |
| authorizedImageTypes | Liste des types de fichiers images pouvant être envoyés | array | _une liste prédéfinie_ |
| **db** | Informations de connection à la base de données MySQL | **object** |  |
| →&nbsp;db.host | Le nom du serveur hôte à utiliser pour la connexion MySQL | string | `"localhost"` |
| →&nbsp;db.port | Le numéro de port du serveur hôte pour la connexion MySQL | string | `3306` |
| →&nbsp;db.username | Le nom de l'utilisateur MySQL à utiliser pour la connexion à la base de données | string | `"root"` |
| →&nbsp;db.password | Le mot de passe de l'utilisateur MySQL | string | `""` |
| →&nbsp;db.database | Le nom de la base de données | string | `"loxya"` |
| →&nbsp;db.prefix | Un préfixe éventuel à ajouter devant chaque nom de table de la base de données | string | `""` |
| **organization** | Informations de l'organisation pour la création des documents (devis, factures...) | **object** |  |
| →&nbsp;organization.name | Raison sociale (nom) de l'organisation | string | `""` |
| →&nbsp;organization.logo | Nom du fichier (image JPG ou PNG) du logo l'organisation, à placer dans le dossier `src/public/img/` | string (ou&nbsp;null) | `null` |
| →&nbsp;organization.registrationId | Numéro d'identification de l'organisation (par ex. le n° SIRET ou SIREN en France) | string | `null` |
| →&nbsp;organization.legalType | Type de structure légale de l'organisation (par ex. "FR.SARL" ou "FR.SAS" en France) | string | `null` |
| →&nbsp;organization.shareCapital | Montant du capital social si pertinent pour l'organisation (société) | string | `null` |
| →&nbsp;organization.activityCode | Le code de l'activité principale de l'organisation (société) (par ex. le code APE en France) | string | `null` |
| →&nbsp;organization.tradeRegistryCity | La ville où l'organisation est immatriculée (par ex. le R.C.S. en France) | string | `null` |
| →&nbsp;organization.isVatExempted | Est-ce que l'organisation est exemptée de T.V.A. ? (par ex. en France, les micro-entreprises) | boolean | `false` |
| →&nbsp;organization.vatExemptionCode | Si l'organisation est exemptée de T.V.A., le code d'exemption | string | `null` |
| →&nbsp;organization.vatExemptionReason | Si l'organisation est exemptée de T.V.A., le code de la raison de l'exemption | string | `null` |
| →&nbsp;organization.vatNumber | Numéro de TVA intracommunautaire de l'organisation (si assujettie) | string | `""` |
| →&nbsp;organization.isVatDueOnInvoice | Est-ce que l'organisation est assujettie à la T.V.A. sur les débits plutôt qu'à l'encaissement ? | boolean | `false` |
| →&nbsp;organization.street | Adresse postale (rue et numéro) de l'organisation. Possibilité de passer un tableau avec deux lignes pour ajouter un complément d'adresse. | string ou array | `[]` |
| →&nbsp;organization.postalCode | Code postal de l'organisation | string | `null` |
| →&nbsp;organization.administrativeArea | Subdivision administrative de l'organisation | string | `null` |
| →&nbsp;organization.locality | Localité (ville ou village) de l'organisation | string | `null` |
| →&nbsp;organization.country | Code ISO du pays de l'organisation | string | `"FR"` |
| →&nbsp;organization.phone | Numéro de téléphone de l'organisation | string | `null` |
| →&nbsp;organization.email | Adresse email de contact de l'organisation | string | `null` |
| **estimates** | Paramètres des devis | **object** |  |
| →&nbsp;estimates.validityDays | Durée (en jours) par défaut de la validité des devis | number | `15` |
| **invoices** | Paramètres de la facturation | **object** |  |
| →&nbsp;invoices.routingIdentifier | Adresse électronique de l'organisation pour l'envoi des e-factures. Utilisé notamment pour recevoir les statuts de traitement des e-factures (dans le cadre du e-invoicing). | string | `null` |
| →&nbsp;invoices.paymentTermDays | Délai de paiement par défaut (en jours) des factures. `0` signifie "à réception". | number | `15` |
| →&nbsp;**invoices.paymentMethods** | Moyens de paiements pris en charge | **object** |  |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;invoices.paymentMethods.cash | Prendre en charge les paiements en espèces. | boolean | `false` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;invoices.paymentMethods.card | Prendre en charge les paiements en carte bancaire. | boolean | `false` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;invoices.paymentMethods.cheque | Prendre en charge les paiements par chèque. | boolean | `false` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;invoices.paymentMethods.**transfert** | Prendre en charge les paiements par virement bancaire SEPA. | **object** | `null` |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;invoices.paymentMethods.transfert.holder | Le nom du titulaire du compte bancaire. | string | `null` |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;invoices.paymentMethods.transfert.iban | L'IBAN du compte bancaire. | string | `null` |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;invoices.paymentMethods.transfert.bic | Le code BIC/SWIFT du compte bancaire. | string | `null` |
| →&nbsp;**invoices.mentions** | Textes personnalisés pour les mentions légales des devis et factures. Permet d'écraser les mentions par défaut, et d'ajouter celles qui ne sont pas affichées par défaut. | **object** |  |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;invoices.mentions.seller-identity | Identité complète du vendeur. | string | [pré-rempli] |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;invoices.mentions.trade-register | Mention d'immatriculation au registre du commerce. | string | [pré-rempli] |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;invoices.mentions.vat-due-on-invoice | Mention d'option pour la "TVA sur les débits". | string | [pré-rempli] |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;invoices.mentions.no-early-payment-discount | Absence d'escompte pour paiement anticipé. | string | `null` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;invoices.mentions.late-payment-penalty | Pénalité exigible en cas de retard de paiement. | string | [pré-rempli] |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;invoices.mentions.late-payment-flat-fee | Indemnité forfaitaire pour frais de recouvrement en cas de retard de paiement. | string | [pré-rempli] |
| **measurementUnits** | Unités de mesure à utiliser | **object** |  |
| →&nbsp;measurementUnits.**materials** | Unités de mesure à utiliser pour le matériel | **object** |  |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;measurementUnits.materials.weight | Unité de mesure du poids du matériel | string | `"kg"` |
| **proxy** | Utilisation d'un proxy&nbsp;: un objet contenant les données du proxy | **object** |  |
| →&nbsp;proxy.enabled | Activation du proxy ou non | boolean | `false` |
| →&nbsp;proxy.host | Nom d'hôte du proxy | string | `""` |
| →&nbsp;proxy.port | Numéro du port du proxy | number | |
| **logger** | Paramétrage du logging (fichiers de logs du système, voir dans `/src/var/logs`) | **object** |  |
| →&nbsp;logger.level | Le niveau de log à utiliser (voir [cette documentation](https://github.com/Seldaek/monolog/blob/main/doc/01-usage.md#log-levels).) | number | `250` |
| →&nbsp;logger.max_files | Le nombre maximum de fichiers de log à conserver | number | `10` |
| **email** | Paramétrage de l'envoi des e-mails | **object** |  |
| →&nbsp;email.**from** | Nom et adresse e-mail de l'expéditeur.<br />Peut aussi être une simple chaîne de caractère (devant être une adresse e-mail valide). | **object** (ou&nbsp;string) | `""` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;email.from.email | Adresse e-mail de l'expéditeur | string |  |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;email.from.name | Nom de l'expéditeur | string |  |
| →&nbsp;email.**driver** | Type de système à utiliser pour l'envoi des e-mails. Peut être soit `mail`, soit `smtp`, soit `mailjet` (voir ci-dessous) | string | `"mail"` |
| →&nbsp;email.**smtp** | Pour une utilisation du système d'envoi `smtp` (voir ci-dessus) | **object** |  |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;email.smtp.host | Nom d'hôte du serveur SMTP à utiliser | string | `"localhost"` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;email.smtp.port | Numéro du port SMTP à utiliser | number | `1025` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;email.smtp.username | Nom d'utilisateur pour le compte SMTP à utiliser | string (ou&nbsp;null) | `null` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;email.smtp.password | Mot de passe pour le compte SMTP | string (ou&nbsp;null) | `null` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;email.smtp.security | Type de cryptage à utiliser pour SMTP. Peut être une chaîne vide, ou `tls`, ou `ssl` | string | `""` |
| →&nbsp;email.**mailjet** | Pour une utilisation de l'API de <a href="https://www.mailjet.com/fr/" target="_blank" noreferrer nofollow>Mailjet <i class="fas fa-external-link-alt"></i></a> pour envoyer les e-mails | **object** |  |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;email.mailjet.apiKey | Votre clé d'API fournie par Mailjet | string (ou&nbsp;null) | `null` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;email.mailjet.apiSecretKey | Votre clé secrète fournie par Mailjet | string (ou&nbsp;null) | `null` |

Les éléments suivants sont **spécifiques à la variante <i class="fas fa-crown"></i> Premium**&nbsp;:

| Clé | Description | Type de valeur | Valeur par défaut |
| --- | --- | --- | --- |
| enableApiDocs | Activation de la documentation de l'API | boolean | `true` |
| **barcodes** | Paramétrage de la génération des codes-barres | **object** |  |
| →&nbsp;barcodes.mode | Mode de génération des codes-barres. Si `auto`, l'application génère elle-même les codes-barres depuis un identifiant interne. Si `manual`, l'application attend que le contenu de chaque code-barres soit passé. | "auto" ou "manual" | `"auto"` |
| →&nbsp;barcodes.format | Type de codes-barres à prendre en charge (`qr-code`, `pdf417`, `code39` ou `code128`). | string | `"qr-code"` |
| **handScanner** | Paramétrage du scanner de code-barres | **object** |  |
| →&nbsp;handScanner.scanTimeout | Le temps (en millisecondes) permettant de détecter un scan de code-barres. | number | `100` |
| →&nbsp;handScanner.inputLayout | Le type de clavier à émuler (doit être similaire à celui utilisé par les utilisateurs) .| string | `"qwerty"` |
| **exports** | Configuration du comportement des fonctionnalités d'export de données. | **object** |  |
| →&nbsp;exports.**materials** | Configuration du comportement des fonctionnalités d'export de données relatives au matériel. | **object** |  |
| →&nbsp;&nbsp;&nbsp;&nbsp;exports.materials.advanced | Inclusion ou non des données des codes-barres du matériel et des unités de matériel. | boolean | `true` |
| **auth** | Paramétrage de l'authentification | **object** |  |
| →&nbsp;auth.**cookie** | Nom du cookie à utiliser pour le stockage de session. | string | `"auth"` |
| →&nbsp;auth.**inactivityWatcher** | Déconnexion automatique de l'utilisateur externe. | **object** |  |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.inactivityWatcher.enabled | Pour activer la déconnexion automatique. | boolean | `false` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.inactivityWatcher.threshold | Délai avant que l'utilisateur ne soit considéré comme inactif (en minutes). | number | 3 |
| →&nbsp;auth.**CAS** | Paramétrage de l'authentification CAS. | **object** |  |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.CAS.enabled | Pour activer l'authentification unifiée (SSO) via un serveur CAS. | boolean | `false` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.CAS.host | Nom complet du serveur CAS hôte. | string | `"cas.loxya.test"` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.CAS.port | Numéro du port pour accéder au serveur CAS. | number | `8443` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.CAS.uri | Chemin vers l'authentification sur le serveur CAS. | string | `"/cas"` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.CAS.cert | Pour activer ou non la récupération du certificat SSL. | boolean | `false` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.CAS.**attributes** | Paramétrage fin de la correspondance des attributs CAS pour la création des utilisateurs dans Loxya. Peuvent être soit une chaîne de caractère, soit un tableau. Si c'est un tableau, le premier attribut trouvé avec une valeur non nulle sera utilisé. | **object** |  |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.CAS.attributes.pseudo | Les noms des attributs CAS à utiliser pour construire le pseudo de l'utilisateur. | string (ou&nbsp;array) | `"givenName"` |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.CAS.attributes.email | Les noms des attributs CAS à utiliser pour construire l'adresse email de l'utilisateur. | string (ou&nbsp;array) | `["mail", "email"]` |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.CAS.attributes.group | Les noms des attributs CAS à utiliser pour choisir le groupe de l'utilisateur. | string (ou&nbsp;array) | `"group"` |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.CAS.attributes.firstName | Les noms des attributs CAS à utiliser pour construire le prénom de l'utilisateur. | string (ou&nbsp;array) | `"first_name"` |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.CAS.attributes.lastName | Les noms des attributs CAS à utiliser pour construire le nom de famille de l'utilisateur. | string (ou&nbsp;array) | `"last_name"` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.CAS.beneficiaryGroups | Un (ou plusieurs) groupe(s) CAS dont la présence permettra de créer un _bénéficiaire_ en même temps que l'utilisateur. | string (ou&nbsp;array) (ou&nbsp;null) | `null` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.CAS.groupsMapping | Un tableau de correspondance entre les groupes CAS et les groupes des utilisateurs de Loxya.<br />Par exemple&nbsp;: `{ "Administrateur": "admin", "Etudiant": "external" }` | object | `{}` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.CAS.defaultGroup | Le groupe utilisateur de Loxya à utiliser si _aucun groupe CAS_ n'a été trouvé dans `groupsMapping` (voir ci-dessus).<br />Si `null`, l'utilisateur ne sera pas autorisé à se connecter. | string (ou&nbsp;null) | `null` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.CAS.nonCASUserLinking | Quand un utilisateur se connecte via CAS pour la première fois alors qu'un utilisateur existe déjà avec la même adresse e-mail, détermine si le système doit le lier à son compte CAS ou non. | boolean | `true` |
| →&nbsp;auth.**SAML2** | Paramétrage de l'authentification SAML 2.0. | **object** |  |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.enabled | Pour activer l'authentification unifiée (SSO) en utilisant SAML 2.0. | boolean | `false` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.signRequests | Détermine si les requêtes envoyées au fournisseur d'identité (IDP) doivent être signées ou non. | boolean | `false` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.**signingCert** | Le certificat qui sera utilisé pour signer les requêtes et les réponses au fournisseur d'identité (IDP).<br />Obligatoire, même si la signature des requête est désactivée. | **object** |  |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.signingCert.key | Le contenu d'un fichier "signinCert.key" valide (généré via `openssl`). | string | `null` |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.signingCert.cert | Le contenu d'un fichier "signinCert.crt" valide (généré via `openssl`). | string | `null` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.**idp** | Configuration du fournisseur d'identité (IdP). | **object** |  |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.idp.entityId | Identifiant (URI) du fournisseur d'identité. | string | `null` |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.idp.singleSignOnServiceUrl | L'URL de l'endpoint SSO (= Connexion) du fournisseur d'identité. | string | `null` |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.idp.singleLogoutServiceUrl | L'URL de l'endpoint SLS (= Déconnexion) du fournisseur d'identité. | string | `null` |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.idp.signingCert | Le contenu du certificat public du fournisseur d'identité. | string | `null` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.**attributes** | Paramétrage fin de la correspondance des attributs SAML2 pour la création des utilisateurs dans Loxya. Peuvent être soit une chaîne de caractère, soit un tableau. Si c'est un tableau, le premier attribut trouvé avec une valeur non nulle sera utilisé. | **object** |  |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.attributes.pseudo | Les noms des attributs SAML2 à utiliser pour construire le pseudo de l'utilisateur. | string (ou&nbsp;array) | `"givenName"` |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.attributes.email | Les noms des attributs SAML2 à utiliser pour construire l'adresse email de l'utilisateur. | string (ou&nbsp;array) | `["mail", "email"]` |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.attributes.group | Les noms des attributs SAML2 à utiliser pour choisir le groupe de l'utilisateur. | string (ou&nbsp;array) | `"group"` |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.attributes.firstName | Les noms des attributs SAML2 à utiliser pour construire le prénom de l'utilisateur. | string (ou&nbsp;array) | `"first_name"` |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.attributes.lastName | Les noms des attributs SAML2 à utiliser pour construire le nom de famille de l'utilisateur. | string (ou&nbsp;array) | `"last_name"` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.beneficiaryGroups | Un (ou plusieurs) groupe(s) SAML2 dont la présence permettra de créer un _bénéficiaire_ en même temps que l'utilisateur. | string (ou&nbsp;array) (ou&nbsp;null) | `null` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.groupsMapping | Un tableau de correspondance entre les groupes SAML2 et les groupes des utilisateurs de Loxya.<br />Par exemple&nbsp;: `{ "Administrateur": "admin", "Etudiant": "external" }` | object | `{}` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.defaultGroup | Le groupe utilisateur de Loxya à utiliser si aucun groupe SAML2 n'a été trouvé dans `groupsMapping` (voir ci-dessus).<br />Si `null`, l'utilisateur ne sera pas autorisé à se connecter. | string (ou&nbsp;null) | `null` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.nonSAMLUserLinking | Quand un utilisateur se connecte via SAML2 pour la première fois alors qu'un utilisateur existe déjà avec la même adresse e-mail, détermine si le système doit le lier à son compte SAML2 ou non. | boolean | `true` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.disableUnsolicitedRequests | Détermine si Loxya doit prendre en charge les requêtes de connexion / déconnexion provenant du fournisseur d'identité sans que l'utilisateur soit passé par Loxya. | boolean | `false` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;auth.SAML2.proxyVars | Permet d'utiliser les headers `X-Forwarded-*` / `HTTP_X_FORWARDED_PROTO` pour déterminer le port / domaine / protocole. Ceci est utile notamment si l'application tourne derriere un load balancer qui met fin au protocole SSL. | boolean | `false` |
| **signup** | Paramétrage de l'inscription publique | **object** |  |
| →&nbsp;auth.restrictedEmailDomains | Permet de restreindre le(s) domaine(s) des adresses e-mails acceptées pour la création de compte | array | `null` |
| →&nbsp;auth.expireAfter | Délai au delà duquel les comptes en cours de création, non confirmés, expirent (en minutes) | integer | `120` |
| →&nbsp;auth.requireAddress | L'adresse postale est-elle requise lors de l'inscription&nbsp;? Si "auto", l'adresse sera requise si la facturation est activée | string (ou&nbsp;boolean) | `"auto"` |
| →&nbsp;auth.allowCompanies | L'inscription est-elle ouverte aux entreprises ? | boolean | `true` |
| →&nbsp;auth.maxRegistrationBeforeCooldown | Le nombre de création de comptes "temporaires" pour une même IP avant qu'une temporisation ne soit mise en place | integer | `2` |
| **legalDocuments** | Liens vers les documents légaux qui seront notamment utilisés pour rediriger les utilisateurs lorsqu'ils demandent à els consulter lors de leur inscription | **object** |  |
| →&nbsp;logger.privacyPolicy | Lien vers la politique de confidentialité | string (ou null) | `null` |
| →&nbsp;logger.termsOfUse | Lien vers les conditions générales d'utilisation | string (ou null) | `null` |
| **notifications** | Paramétrage des notifications | **object** |  |
| →&nbsp;notifications.**overdue** | Notifications en cas de non retour du matériel à temps par les bénéficiaires | **object** |  |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;notifications.overdue.enabled | Activation de ce type de notification | boolean | `false` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;notifications.overdue.days | Liste du nombre de jours après lesquels il faut envoyer une notification tant que le matériel n'est pas restitué. | array | `[1,15,22,27,30]` |
| →&nbsp;notifications.**preparers** | Notifications des préparateurs de commande avant un événement | **object** |  |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;notifications.preparers.enabled | Activation de ce type de notification | boolean | `false` |
| &nbsp;&nbsp;&nbsp;&nbsp;→&nbsp;notifications.preparers.daysBefore | Nombre de jours avant la date de l'événement pour envoyer la notification. | number | `1` |

<a href="/wiki/appendices#appendices-bill-template" id="appendices-bill-template" class="wiki__page__link">
    <i class="fas fa-link"></i>
</a>

## Personnaliser les devis et factures

> <i class="fas fa-exclamation-triangle color-error"></i> **Attention**  
> En France, la **modification** de la structure des données des factures est **fortement déconseillée**, à moins de savoir parfaitement ce que vous faites, car avec la facturation électronique et notamment le format de fichier **"Factur-X"**, la norme impose une **correspondance stricte** entre la **partie visuelle du PDF** et le **contenu de la partie XML** du fichier. Cela est d'autant plus vrai que Loxya utilise le mode _EXTENDED-CTC-FR_ de la norme _EN 16931_.

Il est possible de personnaliser la présentation des devis et factures, en modifiant les fichiers suivants&nbsp;:

- `src/views/pdf/estimate/index.twig` pour les devis,
- `src/views/pdf/invoice/index.twig` pour les factures.

Il s'agit d'un fichier écrit **en HTML**, il faut donc maîtriser ce langage. Le système de template utilisé est <a href="https://twig.symfony.com/doc/3.x/" target="_blank" rel="nofollow noreferrer">Twig 3 <i class="fas fa-external-link-alt"></i></a>. Le système fourni des *variables* permettant d'afficher les diverses données de la facture. Pour utiliser une variable, il suffit d'écrire son nom entre double-accolades, comme ceci&nbsp;: `{{ variable }}`, ou `{{ variable.name }}`.

Pour modifier le style des éléments qui composent ces documents, vous pouvez modifier les fichiers **SCSS** suivants&nbsp;:

- `src/views/pdf/estimate/index.scss` pour les devis,
- `src/views/pdf/invoice/index.scss` pour les factures.

Voici la liste des variables disponibles (vous pouvez vous inspirer de ce qui est contenu dans les fichiers originaux pour plus de détails)&nbsp;:

| Variable | Description | Type |
| --- | --- | --- |
| **`locale`** | **Langue utilisée pour les traductions** (`fr`, ou `en`). | `string` |
| **`number`** | **Numéro de la facture ou du devis**. | `string` |
| **`isDraft`** | **Est-ce un brouillon&nbsp;?** | `boolean` |
| **`isCreditNote`** | **Est-ce un avoir&nbsp;?** | `boolean` |
| **`isPrepayment`** | **Est-ce une facture d'acompte&nbsp;?** | `boolean` |
| **`date`** | **Date du devis ou de la facture**. | `DateTime*` |
| **`dueDate`** | **Date d'expiration du devis ou d'échéance de la facture** | `DateTime*` |
| **`dueDelay`** | **Délai en jour avant expiration du devis ou échéance de la facture** | `number` |
| <br />**`seller`** | <br />**Informations du vendeur** (votre organisation). | <br />**`object`** |
| `seller.name` | Nom (raison sociale) du vendeur. | `string` |
| `seller.logo` | Logo à afficher en en-tête de la facture. | `string` |
| `seller.identifier` | Le numéro d'identification du vendeur (SIRET/SIREN en France). | `string` |
| `seller.address` | Adresse complète du vendeur (utiliser la méthode `.format()` pour l'affichage). | `object` |
| `seller.country` | Pays du vendeur (un objet contenant notamment le code et le nom du pays). | `object` |
| `seller.phone` | Numéro de téléphone du vendeur. | `string` |
| `seller.email` | Adresse e-mail de contact du vendeur. | `string` |
| `seller.vatNumber` | Le numéro de T.V.A. du vendeur (si applicable). | `string` |
| `seller.activityCode` | Le code de l'activité du vendeur (APE en France). | `string` |
| `seller.shareCapital` | Le montant du capital social du vendeur (si applicable). | `string` |
| `seller.tradeRegistryCity` | La ville d'enregistrement du vendeur (registre du commerce en France) (si applicable). | `string` |
| <br />**`buyer`** | <br />**Information de l'acheteur** (bénéficiaire) | <br />**`object`** |
| `buyer.name` | Le nom complet de l'acheteur (raison sociale si c'est une société). | `string` |
| `buyer.isCompany` | Est ce que l'acheteur est une société&nbsp;? | `boolean` |
| `buyer.identifier` | Le numéro d'identification de l'acheteur (SIRET/SIREN en France), si c'est une société. | `string` |
| `buyer.vatNumber` | Le numéro de T.V.A. de l'acheteur, si c'est une société. | `string` |
| `buyer.isSameVatArea` | Et-ce que l'acheteur (si c'est une société) est dans la même zone légale de T.V.A. que le vendeur&nbsp;? | `boolean` |
| `buyer.address` | Adresse complète de l'acheteur (utiliser la méthode `.format()` pour l'affichage). | `object` |
| `buyer.country` | Pays de l'acheteur (un objet contenant notamment le code et le nom du pays). | `object` |
| `buyer.contactName` | Le nom complet de la personne contact associée à l'acheteur (si c'est une société). | `string` |
| `buyer.serviceCode` | Le code service de l'acheteur (si c'est une institution par exemple). | `string` |
| `buyer.reference` | La référence de l'acheteur. | `string` |
| **`currency`** | **Devise utilisée dans le document** (code ISO, par ex. `EUR`). | `string` |
| **`orderNumber`** | **Numéro du bon de commande** | `string` |
| **`parentEstimate`** | **Le devis auquel est rattaché la facture**<br />(un objet avec les données du devis, notamment `number` et `date`) | `object` |
| **`parentInvoice`** | **La facture à laquelle est rattaché l'avoir**<br />(un objet avec les données de la facture, notamment `number` et `date`) | `object` |
| <br />**`booking`** | <br />**L'événement ou la réservation lié au devis ou à la facture** (si pertinent) | <br />`object` |
| `booking.type` | Le type de booking (événement ou réservation) | `string` |
| `booking.title` | Le titre de l'événement (si c'en est un) | `string` |
| `booking.reference` | La référence de la réservation (si c'en est une) | `string` |
| `booking.period` | La période (de facturation) de l'événement ou réservation | `Period***` |
| <br />**`materials`** | <br />**Détail du matériel, trié par sous-catégories** (voir ci-dessous). | <br />`object` |
| **`materials[category]`** | La liste du matériel de la catégorie (le nom de la catégorie est dans la clé). | `array` |
| `materials[category][x][reference]` | Référence du matériel. | `string` |
| `materials[category][x][name]` | Nom du matériel. | `string` |
| `materials[category][x][description]` | Description du matériel. | `string` |
| `materials[category][x][quantity]` | Quantité du matériel. | `integer` |
| `materials[category][x][unit_price_period]` | Tarif de location (unité) pour la période. | `float**` |
| `materials[category][x][unit_replacement_price]` | Prix de remplacement du matériel (unité). | `float**` |
| `materials[category][x][tax]` | La taxe associée au matériel (voir la macro `lineTax` dans le fichier original). | `object` |
| `materials[category][x][has_discount]` | Est-ce que la ligne de matériel a une remise&nbsp;? | `boolean` |
| `materials[category][x][discount_rate]` | Le taux de remise du matériel. | `float**` |
| `materials[category][x][total_without_taxes]` | Montant total hors taxe pour la ligne de matériel. | `float**` |
| <br />**`extras`** | <br />**Lignes additionnelles**. | <br />`array` |
| `extras[x].description` | Description de la ligne additionnelle. | `string` |
| `extras[x].quantity` | Quantité de la ligne additionnelle. | `integer` |
| `extras[x].unit_price` | Prix unitaire de la ligne additionnelle. | `float**` |
| `extras[x].total_without_taxes` | Sous-total hors-taxes de la ligne additionnelle. | `float**` |
| `extras[x].tax` | La taxe associée au matériel (voir la macro `lineTax` dans le fichier original). | `object` |
| `extras[x].has_discount` | Est-ce que la ligne additionnelle a une remise&nbsp;? | `number` |
| `extras[x].discount_rate` | Le taux de remise de la ligne additionnelle. | `number` |
| <br />**`prepayments`** | <br />**Les acomptes existants** (liés au même devis) | <br />`array` |
| `prepayments[x].index` | Le numéro d'apparition dans la liste des acomptes. | `integer` |
| `prepayments[x].date` | La date de l'acompte. | `DateTime*` |
| `prepayments[x].number` | Le numéro de facture de l'acompte. | `string` |
| `prepayments[x].tax` | La taxe associée à l'acompte (voir la macro `lineTax` dans le fichier original). | `object` |
| `prepayments[x].amount` | Le montant de l'acompte. | `string` |
| **`totalPrepayments`** | **Montant total des acomptes existants** | `float**` |
| **`hasGlobalDiscount`** | **Y a-t-il une remise globale&nbsp;?** | `boolean` |
| **`globalDiscountRate`** | **Taux de la remise globale** (en %). | `float**` |
| **`totalGlobalDiscount`** | **Montant total de la remise**. | `float**` |
| **`totalWithoutGlobalDiscount`** | **Montant total hors remise globale**. | `float**` |
| **`totalWithoutTaxes`** | **Montant total hors taxes**. | `float**` |
| **`totalTaxes`** | **Montant total des taxes**. | `float**` |
| **`totalWithTaxes`** | **Montant total toutes taxes comprises**. | `float**` |
| **`globalTaxRegime`** | **Le régime de taxe à utiliser** (voir `Loxya\Support\Invoicing\TaxRegime`) | `TaxRegime` |
| **`isVatExempted`** | **Est-ce que le devis ou la facture est exemptée de T.V.A.&nbsp;?** | `boolean` |
| **`isVatOutOfScope`** | **Est-ce que le devis ou la facture est hors contexte de la facturation électronique&nbsp;?** | `boolean` |
| **`isSimpleVatSystem`** | **Est-ce que le système de taxe s'appelle "T.V.A." ou non&nbsp;?** | `boolean` |
| <br />**`taxFootnotes`** | <br />**Liste de notes à propos des taxes** | <br />`array` |
| `taxFootnotes[x].index` | Le numéro d'apparition dans la liste des notes de T.V.A. | `integer` |
| `taxFootnotes[x].reason` | La note de T.V.A. elle-même. | `string` |
| **`globalTaxExemptionReason`** | **Mention expliquant la raison d'exemption de T.V.A.** | `string` |
| **`legalMentions`** | **Liste des mentions légales** (tableau de `string`) | `array` |
| **`specialMentions`** | **Mention spéciale** | `string` |
| **`paymentMethods`** | **Méthodes de paiement acceptées** (tableau d'objets contenant la clé `value`). | `array` |
| <br />**`bankTransferData`** | <br />**Le RIB du vendeur (compte bancaire à créditer)** | <br />`object` |
| `bankTransferData.iban` | Le code IBAN à afficher. | `string` |
| `bankTransferData.bic` | Le code BIC/SWIFT à afficher. | `string` |
| `bankTransferData.holder` | Le nom complet du détenteur du compte. | `string` |
| **`showTotalReplacementPrice`** | **Faut-il afficher le total des valeurs de remplacement du matériel&nbsp;?** | `boolean` |
| **`totalReplacement`** | **Montant total des valeurs de remplacement du matériel** | `float**` |
| **`showTotalisableProperties`** | **Faut-il afficher les totaux des caractéristiques spéciales&nbsp;?** | `boolean` |
| <br />**`totalisableProperties`** | <br />**Liste des caractéristiques spéciales à totaliser** | <br />`array` |
| `totalisableProperties[x].name` | Le nom de la caractéristique spéciale. | `string` |
| `totalisableProperties[x].value` | La valeur du total correspondant à la caractéristique spéciale. | `float**` |
| `totalisableProperties[x].unit` | L'unité de mesure éventuelle de la caractéristique spéciale. | `string` |


<a href="/wiki/appendices#appendices-summaries-template" id="appendices-summaries-template" class="wiki__page__link">
    <i class="fas fa-link"></i>
</a>

## Personnaliser les fiches de sortie

De même que pour les devis et factures, il est possible de modifier les fichiers suivants pour personnaliser les fiches de sortie&nbsp;:

- `src/views/pdf/event-summary.twig` pour les événements,
- `src/views/pdf/reservation-summary.twig` pour les réservations (<i class="fas fa-crown"></i> Premium).

Il s'agit également d'un fichier écrit **en HTML**, avec le système de template <a href="https://twig.symfony.com/doc/3.x/" target="_blank" rel="nofollow noreferrer">Twig 3 <i class="fas fa-external-link-alt"></i></a>. Des *variables* permettent d'utiliser les données de l'événement ou la réservation, qu'il suffit d'écrire entre double-accolades, comme ceci&nbsp;: `{{ variable }}`, ou `{{ variable.name }}`.

Voici la liste des variables disponibles&nbsp;:

| Variable | Description | Type |
| --- | --- | --- |
| **`now`** | **Date du jour** | DateTime* |
| **`currency`** | **Devise de la facture** (code ISO, par ex. `EUR`). | `string` |
| **`locale`** | **Langue utilisée pour les traductions** (`fr`, ou `en`). | `string` |
| **`currencyName`** | **Nom de la devise de la facture** (p. ex. `Euro`). | `string` |
| **`sortedBy`** | **Le classement général du matériel** (soit `lists`, soit `parks`) | `string` |
| **`materialDisplayMode`** | **Le classement du matériel dans les listes** (voir les [paramètres](/wiki/usage#usage-settings-release-sheets)) | `string` |
| **`customText`** | **Texte personnalisé en bas de page** (voir les [paramètres](/wiki/usage#usage-settings-release-sheets)).<br />Contient les clés&nbsp;: `title` (le titre) et `content` (le texte). | `Array` |
| **`showLegalNumbers`** | **Les numéros légaux doivent-ils apparaître&nbsp;?** (voir les [paramètres](/wiki/usage#usage-settings-release-sheets)) | `boolean` |
| **`showReplacementPrices`** | **Les valeurs de remplacement doivent-elles apparaître dans la liste du matériel&nbsp;?** (voir les [paramètres](/wiki/usage#usage-settings-release-sheets)) | `boolean` |
| **`showDescriptions`** | **Les descriptions du matériel doivent-elles apparaître dans la liste&nbsp;?** (voir les [paramètres](/wiki/usage#usage-settings-release-sheets)) | `boolean` |
| **`showTags`** | **Les tags associés au matériel doivent-ils apparaître dans la liste&nbsp;?** (voir les [paramètres](/wiki/usage#usage-settings-release-sheets)) | `boolean` |
| **`showUnitsSerialNumbers`** | **Les numéros de série des unités du matériel doivent-ils apparaître dans la liste&nbsp;?** (voir les [paramètres](/wiki/usage#usage-settings-release-sheets)) | `boolean` |
| **`showPictures`** | **Les photos du matériel doivent-elles apparaître dans la liste&nbsp;?** (voir les [paramètres](/wiki/usage#usage-settings-release-sheets)) | `boolean` |
| <br />**`company`** | <br />**Informations de votre société**. | <br />`Array` |
| `company.name` | Nom de votre société. | `string` |
| `company.street` | Adresse de votre société (rue et n°). | `string` |
| `company.zipCode` | Code postal de votre société. | `string` |
| `company.locality` | Ville de votre société. | `string` |
| `company.country` | Pays de votre société. | `string` |
| `company.phone` | Numéro de téléphone de votre société. | `string` |
| `company.email` | Adresse e-mail de contact de votre société. | `string` |
| `company.vatNumber` | Le numéro de T.V.A. de votre société. | `string` |
| `company.legalNumbers` | Numéros spéciaux de votre société.<br />(Par exemple, n° SIRET ou code APE, voir [fichier de config](/wiki/appendices#appendices-settings)).<br />Pour chaque numéro, les clés `name` et `value` sont disponibles. | `Array` |
| <br />**`event`** | <br />**Informations de l'événement à facturer**. | <br />`Array` |
| `event.title` | Titre (nom) de l'événement. | `string` |
| `event.description` | Description de l'événement. | `string` |
| `event.operation_period` | Période d'exploitation de l'événement. | `Period***` |
| `event.mobilization_period` | Période de mobilisation du matériel de l'événement. | `Period***` |
| `event.location` | Localisation (lieu) de l'événement. | `string` |
| `event.preparer` | Le préparateur de commande de l'événement (avec `full_name`, `email`, `phone`...) | `Array` |
| `event.is_confirmed` | L'événement est-il confirmé&nbsp;? | `boolean` |
| `event.total_replacement` | Le montant total des valeurs de remplacement du matériel de l'événement. | `float**` |
| <br />**`beneficiaries`** | La liste des bénéficiaires de l'événement. | `Array` |
| `beneficiaries[x][full_name]` | Nom complet du bénéficiaire. | `string` |
| `beneficiaries[x][street]` | Adresse (rue et numéro) du bénéficiaire. | `string` |
| `beneficiaries[x][postal_code]` | Code postal du bénéficiaire. | `string` |
| `beneficiaries[x][locality]` | Ville du bénéficiaire. | `string` |
| <br />**`technicians`** | La liste des assignations de techniciens de l'événement. | `Array` |
| `technicians[x][name]` | Nom complet du technicien. | `string` |
| `technicians[x][phone]` | Numéro de téléphone du technicien. | `string` |
| `technicians[x][assignments]` | Liste des assignations du technicien.<br .>Chaque élément contient&nbsp;: `period` (dates de l'assignation) et `position` (poste du technicien). | `Array` |
| <br />**`materialsSorted`** | <br />**Liste du matériel** (le tri dépend de `materialDisplayMode` et de `sortedBy`). | <br />`Array` |
| `materialsSorted[x][name]` | Nom de la sous-catégorie, ou du parc. | `string` |
| `materialsSorted[x][materials]` | Liste du matériel de la sous-catégorie ou du parc. | `Array` |
| `materialsSorted[x][materials][x][reference]` | Référence du matériel. | `string` |
| `materialsSorted[x][materials][x][name]` | Nom du matériel. | `string` |
| `materialsSorted[x][materials][x][quantity]` | Quantité du matériel alloué. | `integer` |
| `materialsSorted[x][materials][x][rentalPrice]` | Tarif de location (unité). | `float**` |
| `materialsSorted[x][materials][x][replacementPrice]` | Prix de remplacement total du matériel. | `float**` |
| `materialsSorted[x][materials][x][total]` | Montant total pour le matériel. | `float**` |
| <br />**`totalisableProperties`** | <br />**Liste des caractéristiques spéciales à totaliser** | <br />`array` |
| `totalisableProperties[x].name` | Le nom de la caractéristique spéciale. | `string` |
| `totalisableProperties[x].value` | La valeur du total correspondant à la caractéristique spéciale. | `float**` |
| `totalisableProperties[x].unit` | L'unité de mesure éventuelle de la caractéristique spéciale. | `string` |

`*` Les variables du type `DateTime` peuvent être formatées avec <a href="https://twig.symfony.com/doc/3.x/filters/format_date.html" target="_blank" rel="nofollow noreferrer">le filtre `format_date()` de Twig <i class="fas fa-external-link-alt"></i></a>.  
`**` Les variables du type `float` peuvent être formatées avec <a href="https://twig.symfony.com/doc/3.x/filters/format_currency.html" target="_blank" rel="nofollow noreferrer">le filtre `format_currency()` de Twig <i class="fas fa-external-link-alt"></i></a> si on veut un montant en devise, ou bien le filtre `format_percent_number()` si on veut un nombre en %.
`***` Les variables du type `Period` peuvent être formatées avec le filtre `format_period(type, locale)`.


