812 lines
21 KiB
Markdown
812 lines
21 KiB
Markdown
# Modélisation d'un système bancaire
|
||
|
||
Une banque locale souhaite informatiser la gestion de ses titulaires et de leurs comptes.
|
||
|
||
Vous êtes chargé(e) de concevoir et d’implémenter le schéma relationnel de base permettant de gérer :
|
||
|
||
1. Les clients de la banque,
|
||
2. Les comptes bancaires,
|
||
3. Le lien entre les clients et les comptes.
|
||
4. Les dépots et les retraits d'argent.
|
||
5. Les virements entre compte.
|
||
6. Les devises et les taux de change.
|
||
|
||
L'objectif de ces travaux pratiques est de :
|
||
|
||
- Concevoir un **modèle relationnel** à partir d’un scénario réaliste.
|
||
- Utiliser les **contraintes d’intégrité** pour garantir la cohérence des données.
|
||
- Manipuler des **jointures** et des **relations n–n**.
|
||
- Comprendre la notion d’**héritage logique** en base de données.
|
||
|
||
La diffusion de cette application est internationale, vous vous efforcerez d'utiliser des termes anglais pour nommer les entités et les propriétés. Réferrez pour cela vous au [glossaire](banque.glossaire.md)
|
||
|
||
Pour les entités vous utiliserez le singuler et écrirez le tout en minuscule.
|
||
|
||
## Séance 1 : Le Diagramme Entités Relations (ERD)
|
||
|
||
### 1. Les titulaires
|
||
|
||
Un client de la banque est appelé un titulaire. Il peut être une **personne physique** ou une **entreprise**.
|
||
|
||
- Quelles informations faut-il conserver pour tous les titulaires ?
|
||
- Quelles informations sont spécifiques à chaque type de titulaire ?
|
||
- Comment représenter cette distinction en base relationnelle ?
|
||
|
||
> [!TIP]
|
||
> Indice : on peut utiliser une table abstraite `holder`, puis des tables `person` et `company` qui héritent logiquement de celle-ci.
|
||
|
||
#### Pourquoi séparer `person` et `company` ?
|
||
|
||
Parce que leurs attributs diffèrent (nom/prénom vs raison sociale).
|
||
Cela évite les **colonnes inutiles** et permet des **contraintes spécifiques** à chaque type.
|
||
|
||
#### Quelle contrainte empêche d’insérer une person sans holder ?
|
||
|
||
La clé étrangère `references holder(id)` dans `person`.
|
||
|
||
```mermaid
|
||
erDiagram
|
||
person {
|
||
bigint id PK
|
||
text firstname
|
||
text lastname
|
||
date birthdate
|
||
}
|
||
|
||
company {
|
||
bigint id PK
|
||
text name
|
||
text registration_number
|
||
date creation_date
|
||
}
|
||
|
||
holder {
|
||
bigint id PK
|
||
timestamp creation_date
|
||
text type
|
||
}
|
||
|
||
%% Relations
|
||
|
||
person |o--|| holder : is
|
||
company |o--|| holder : is
|
||
```
|
||
|
||
### 2. Les comptes
|
||
|
||
- Chaque titulaire peut détenir **un** ou **plusieurs comptes**.
|
||
- Un compte bancaire doit pouvoir appartenir à **un** ou **plusieurs titulaires** (compte individuel / compte joint).
|
||
- Chaque compte dispose d’un numéro de compte unique, d’un solde et d'une date d'ouverture.
|
||
- Dans le cas d'un compte joint, les parts de propriété d'un compte doivent pouvoir être précisées.
|
||
|
||
```mermaid
|
||
erDiagram
|
||
person {
|
||
bigint id PK
|
||
text firstname
|
||
text lastname
|
||
date birthdate
|
||
}
|
||
|
||
company {
|
||
bigint id PK
|
||
text name
|
||
text registration_number
|
||
date creation_date
|
||
}
|
||
|
||
holder {
|
||
bigint id PK
|
||
timestamp creation_date
|
||
text type
|
||
}
|
||
|
||
account {
|
||
bigint id PK
|
||
timestamp creation_date
|
||
decimal balance
|
||
}
|
||
|
||
account_holder {
|
||
bigint account_id FK
|
||
bigint holder_id FK
|
||
decimal share
|
||
}
|
||
|
||
%% Relations
|
||
|
||
person |o--|| holder : is
|
||
company |o--|| holder : is
|
||
holder }|--|{ account_holder : a
|
||
account_holder ||--|{ account : hold
|
||
```
|
||
|
||
### 3. Les opérations
|
||
|
||
```mermaid
|
||
erDiagram
|
||
person {
|
||
bigint id PK
|
||
text firstname
|
||
text lastname
|
||
date birthdate
|
||
}
|
||
|
||
company {
|
||
bigint id PK
|
||
text name
|
||
text registration_number
|
||
date creation_date
|
||
}
|
||
|
||
bank {
|
||
bigint id PK
|
||
text name
|
||
}
|
||
|
||
holder {
|
||
bigint id PK
|
||
timestamp creation_date
|
||
text type
|
||
}
|
||
|
||
account {
|
||
bigint id PK
|
||
timestamp creation_date
|
||
decimal balance
|
||
text currency_code FK
|
||
}
|
||
|
||
account_holder {
|
||
bigint account_id FK
|
||
bigint holder_id FK
|
||
decimal share
|
||
}
|
||
|
||
operation {
|
||
bigint id PK
|
||
bigint transaction_id FK
|
||
bigint account_id FK
|
||
decimal amount
|
||
text direction
|
||
}
|
||
|
||
%% Relations
|
||
|
||
person |o--|| holder : is
|
||
company |o--|| holder : is
|
||
bank |o--|| holder : is
|
||
holder }|--|{ account_holder : a
|
||
account_holder ||--|{ account : hold
|
||
operation }o--|| account : concerne
|
||
```
|
||
|
||
### 4. Les transactions
|
||
|
||
#### La double écriture comptable
|
||
|
||
Jusqu’à présent, les dépôts et retraits modifiaient directement le solde d’un compte.
|
||
Mais dans un vrai système bancaire ou comptable, chaque opération financière doit être enregistrée en double :
|
||
|
||
- Un débit sur un compte (celui qui reçoit)
|
||
- Un crédit sur un autre (celui qui cède)
|
||
|
||
La somme des débits doit toujours être égale à la somme des crédits.
|
||
|
||
Chaque opération comporte au moins :
|
||
|
||
* un **compte concerné** ;
|
||
* une **date** ;
|
||
* un **montant** ;
|
||
* un **sens** (débit ou crédit).
|
||
|
||
|
||
#### Méthode 1 : montant relatif (positif/négatif)
|
||
|
||
On stocke un seul champ `montant`.
|
||
|
||
* **Crédit** → montant positif
|
||
* **Débit** → montant négatif
|
||
|
||
##### Exemple
|
||
|
||
| id | compte | date | montant |
|
||
| -- | ------ | ---------- | ------- |
|
||
| 1 | 123 | 2025-11-06 | +200.00 |
|
||
| 2 | 123 | 2025-11-07 | -50.00 |
|
||
|
||
|
||
##### Avantages
|
||
|
||
* **Simple à manipuler** pour les calculs (sommes, soldes, agrégations).
|
||
* Pas besoin de colonne supplémentaire pour le sens.
|
||
* Représente naturellement le comportement du solde d’un compte.
|
||
|
||
##### Inconvénients
|
||
|
||
* Le **signe a une signification métier** implicite → risque d’erreur d’interprétation.
|
||
* **Moins lisible** pour les utilisateurs finaux ou pour des exports comptables.
|
||
* Pas toujours compatible avec les règles de la **comptabilité en partie double** (où débit et crédit doivent être visibles séparément).
|
||
|
||
#### Méthode 2 : deux colonnes (débit / crédit)
|
||
|
||
Deux colonnes numériques, l’une pour le débit, l’autre pour le crédit.
|
||
Une seule des deux contient une valeur non nulle.
|
||
|
||
##### Exemple
|
||
|
||
| id | compte | date | debit | credit |
|
||
| -- | ------ | ---------- | ----- | ------ |
|
||
| 1 | 123 | 2025-11-06 | 0.00 | 200.00 |
|
||
| 2 | 123 | 2025-11-07 | 50.00 | 0.00 |
|
||
|
||
##### Avantages
|
||
|
||
* Très **clair visuellement** et **conforme aux usages comptables**.
|
||
* Facilite les **exports vers des logiciels comptables**.
|
||
* On peut facilement filtrer les débits et crédits séparément.
|
||
|
||
##### Inconvénients
|
||
|
||
* **Redondance potentielle** (une des deux colonnes sera toujours à zéro).
|
||
* Les calculs de soldes nécessitent des **expressions plus complexes** :
|
||
`SUM(credit) - SUM(debit)`
|
||
* Risque d’incohérence si les deux colonnes contiennent des valeurs remplies par erreur.
|
||
|
||
#### Méthode 3 : montant absolu + colonne sens ('D' / 'C')
|
||
|
||
Le montant est toujours positif.
|
||
Le sens est indiqué par une lettre (`D` ou `C`).
|
||
|
||
##### Exemple
|
||
|
||
| id | compte | date | montant | sens |
|
||
| -- | ------ | ---------- | ------- | ---- |
|
||
| 1 | 123 | 2025-11-06 | 200.00 | C |
|
||
| 2 | 123 | 2025-11-07 | 50.00 | D |
|
||
|
||
|
||
##### Avantages
|
||
|
||
* **Lisible** et **intuitif** : correspond au vocabulaire métier.
|
||
* Facilite la lecture humaine et les exports comptables.
|
||
* Pas d’ambiguïté sur le signe numérique.
|
||
|
||
##### Inconvénients
|
||
|
||
* Nécessite une **jointure logique** du sens pour les calculs :
|
||
`SUM(CASE WHEN sens='C' THEN montant ELSE -montant END)`
|
||
* Moins direct pour les traitements purement mathématiques.
|
||
* L’usage de lettres rend le **stockage un peu moins compact** (mais négligeable en pratique).
|
||
|
||
|
||
#### Méthode 4 : montant absolu + colonne sens numérique (1 / -1)
|
||
|
||
Le montant est toujours positif, et une colonne numérique `sens` vaut `1` (crédit) ou `-1` (débit).
|
||
|
||
##### Exemple
|
||
|
||
| id | compte | date | montant | sens |
|
||
| -- | ------ | ---------- | ------- | ---- |
|
||
| 1 | 123 | 2025-11-06 | 200.00 | 1 |
|
||
| 2 | 123 | 2025-11-07 | 50.00 | -1 |
|
||
|
||
##### Avantages
|
||
|
||
* **Compact et performant** pour les calculs :
|
||
`SUM(montant * sens)` donne directement le solde.
|
||
* Moins d’ambiguïté qu’un signe caché dans le montant.
|
||
* Combine la **rigueur du modèle mathématique** avec la **clarté du stockage absolu**.
|
||
|
||
##### Inconvénients
|
||
|
||
* **Moins lisible** pour un utilisateur non technique.
|
||
* Moins standard pour la comptabilité classique (on préfère `D` / `C`).
|
||
* Nécessite une convention claire sur la signification de `1` et `-1`.
|
||
|
||
#### Synthèse comparative
|
||
|
||
| Méthode | Structure | Lisibilité | Facilité calculs | Conformité comptable | Risque d'erreur |
|
||
| ------- | ---------------------- | ------------| ---------------- | -------------------- | --------------- |
|
||
| 1 | montant relatif (+/-) | ★☆☆ | ★★★ | ★☆☆ | Moyen |
|
||
| 2 | débit / crédit séparés | ★★★ | ★☆☆ | ★★★ | Faible |
|
||
| 3 | valeur + sens 'D'/'C' | ★★★ | ★★☆ | ★★★ | Faible |
|
||
| 4 | valeur + sens 1/-1 | ★★☆ | ★★★ | ★★☆ | Moyen |
|
||
|
||
|
||
### 5. Les devises
|
||
|
||
```mermaid
|
||
erDiagram
|
||
person {
|
||
bigint id PK
|
||
text firstname
|
||
text lastname
|
||
date birthdate
|
||
}
|
||
|
||
company {
|
||
bigint id PK
|
||
text name
|
||
text registration_number
|
||
date creation_date
|
||
}
|
||
|
||
bank {
|
||
bigint id PK
|
||
text name
|
||
}
|
||
|
||
holder {
|
||
bigint id PK
|
||
timestamp creation_date
|
||
text type
|
||
}
|
||
|
||
account {
|
||
bigint id PK
|
||
timestamp creation_date
|
||
decimal balance
|
||
text currency_code FK
|
||
}
|
||
|
||
account_holder {
|
||
bigint account_id PK,FK
|
||
bigint holder_id PK,FK
|
||
decimal share
|
||
}
|
||
|
||
currency {
|
||
text code PK
|
||
}
|
||
|
||
exchange_rate {
|
||
date date PK
|
||
text currency_code PK,FK
|
||
decimal rate
|
||
}
|
||
|
||
transaction {
|
||
bigint id PK
|
||
timestamp transaction_date
|
||
decimal amount
|
||
}
|
||
|
||
operation {
|
||
bigint id PK
|
||
bigint transaction_id FK
|
||
bigint account_id FK
|
||
decimal amount
|
||
text direction
|
||
}
|
||
|
||
%% Relations
|
||
|
||
person |o--|| holder : is
|
||
company |o--|| holder : is
|
||
bank |o--|| holder : is
|
||
holder }|--|{ account_holder : a
|
||
account_holder ||--|{ account : hold
|
||
currency ||--|{ account : tenu
|
||
exchange_rate }o--|| currency : a
|
||
transaction ||--|{ operation : a
|
||
operation }o--|| account : a
|
||
```
|
||
|
||
---
|
||
|
||
# Séance 2 : Implémentation du modèle
|
||
|
||
Voir les adresses des serveurs [postgreSQL](https://sources.neotech.fr/Universite/tp/src/branch/main/geii3_2025.md)
|
||
|
||
Voir la syntaxe de [postgreSQL](syntaxe.md)
|
||
|
||
### 1. Titulaires
|
||
|
||
Création de la table `holder`.
|
||
|
||
```sql
|
||
create table holder (
|
||
"id" bigint primary key generated always as identity,
|
||
"type" text,
|
||
"created_at" timestamp
|
||
);
|
||
```
|
||
|
||
- entier sur 64 bits : `bigint`
|
||
- clé primaire : `primary key`
|
||
- incrément automatique : `generated always as identity`
|
||
|
||
Création de la table `person`.
|
||
|
||
```sql
|
||
create table person (
|
||
"id" bigint primary key references holder(id),
|
||
"firstname" text,
|
||
"lastname" text,
|
||
"birthdate" date
|
||
);
|
||
```
|
||
|
||
- Créer un compte individuel pour _Françoise Zanetti_, née le 12 avril 1995.
|
||
- Créer une entreprise nommée _Boulangerie de Valorgue_, créée le 19 août 2014, numéro d’immatriculation FR19803269968.
|
||
- Ajouter un nouveau titulaire : _Justin Hébrard_ né le 11/03/1993.
|
||
|
||
```sql
|
||
create table bank (
|
||
"id" bigint primary key references holder(id),
|
||
"name" text
|
||
);
|
||
```
|
||
|
||
```sql
|
||
insert into holder (type) values ('BANK') returning holder;
|
||
```
|
||
|
||
```sql
|
||
insert into bank (id, name) values (10, 'Banque de l''Est');
|
||
```
|
||
|
||
#### 1.1 Contraintes à respecter
|
||
|
||
- Chaque `person` ou `company` doit correspondre à exactement un seul `holder`.
|
||
- La suppression d’un `holder` doit supprimer automatiquement la ligne correspondante dans `person` ou `company`.
|
||
- La banque souhaite désormais que toute personne titulaire d’un compte ait au moins 15 ans à la date de création de sa fiche. Il n'y a pas de restriction sur l'âge de la société.
|
||
- Le type doit être contraint à `'PERSON'` ou `'COMPANY'`.
|
||
|
||
Il existe deux méthodes pour gérer le type.
|
||
|
||
1. vérifier par la commande `CHECK` la validité de la valeur.
|
||
|
||
2. utiliser une énumération `enum`.
|
||
|
||
```sql
|
||
create type holder_type as enum ('BANK', 'PERSON', 'COMPANY');
|
||
```
|
||
|
||
#### 1.2 Vérifications
|
||
|
||
- Lister tous les titulaires. Pour réutiliser rapidement la requête enregistrer la dans une vue.
|
||
- Supprimer un titulaire, vérifier que cela supprime l'individu ou la société correspondante.
|
||
|
||
#### 1.4 L'intégrité des données
|
||
|
||
Lorsque l’on tente d'insèrer une nouvelle personne qui n'a pas l'âge requis. La ligne dans `holder` est d'abord créée, puis l'insertion dans `person` échoue à cause de la vérification d'âge. Mais la ligne du titulaire est toujours présente **sans être rattachée** à une personne. On parle alors d'enregistrement **orphelin**.
|
||
|
||
Chaque commande SQL est exécutée indépendamment. Si la deuxième commande échoue, la première n’est pas annulée automatiquement.
|
||
|
||
Réalisez l’insertion d’un titulaire complet (dans holder et person) à l’aide d’une **transaction**.
|
||
Testez le cas où la contrainte d’âge échoue et vérifiez que rien n’est inséré dans holder.
|
||
|
||
```sql
|
||
begin;
|
||
...
|
||
commit;
|
||
```
|
||
|
||
#### 1.5 Procédure stockée
|
||
|
||
Pour fiabiliser le process et être sûr que l'execution s'effectue toujours dans une transaction,
|
||
nous allons encapsuler la création d’un titulaire dans une **procédure stockée**.
|
||
|
||
Créez une **procédure stockée** appelée `create_person_holder`.
|
||
|
||
Cette procédure prend en paramètre :
|
||
|
||
* le prénom (`p_firstname text`)
|
||
* le nom (`p_lastname text`)
|
||
* la date de naissance (`p_birthdate date`)
|
||
|
||
Cette procédure doit :
|
||
|
||
* Vérifier que la personne a **au moins 15 ans** ;
|
||
* Créer automatiquement un enregistrement dans `holder` de type `'PERSON'`;
|
||
* Récupérer l’identifiant généré et créer la fiche dans `person` ;
|
||
* Afficher un message de confirmation.
|
||
|
||
3. Si la personne a moins de 15 ans :
|
||
|
||
* La procédure doit **refuser la création** et afficher une erreur claire.
|
||
|
||
|
||
>[!NOTE]
|
||
> La procédure garantit l’**atomicité** : soit tout est créé, soit rien.
|
||
|
||
### 2. Les comptes
|
||
|
||
```sql
|
||
create table account (
|
||
"id" bigint primary key generated always as identity,
|
||
"creation_date" date default current_date,
|
||
"balance" decimal check (balance >= 0),
|
||
"currency_code" text references currency (code)
|
||
);
|
||
```
|
||
|
||
- Le solde des comptes ne peuvent être négatifs.
|
||
- Créez un compte joint à 50/50 pour Françoise et Justin.
|
||
|
||
Écrire une requête pour vérifier la somme des parts
|
||
|
||
Jusqu’à présent, nous savons créer des titulaires (`holder`) de façon sûre.
|
||
Il est temps de leur ouvrir des **comptes bancaires**.
|
||
|
||
Un compte doit toujours :
|
||
|
||
* être associé à **au moins un titulaire**,
|
||
* et la somme des parts (`share`) des titulaires doit être exactement **égale à 1** (c’est-à-dire 100 % du compte).
|
||
|
||
Nous allons donc écrire une **procédure stockée** `create_account` qui vérifie ces règles avant d’enregistrer les données.
|
||
|
||
1. Créez une **procédure stockée** `create_account` qui :
|
||
|
||
* prend en paramètre :
|
||
|
||
* `p_holders int[]` (tableau des identifiants de titulaires),
|
||
* `p_shares numeric[]` (tableau des parts correspondantes).
|
||
|
||
* vérifie que :
|
||
|
||
* le nombre d’éléments dans `p_holders` et `p_shares` est identique ;
|
||
* la somme des parts est **exactement égale à 1** ;
|
||
* chaque `holder_id` existe dans la table `holder`.
|
||
|
||
2. Si tout est correct :
|
||
|
||
* crée un nouveau compte dans `account` ;
|
||
* insère les lignes correspondantes dans `account_holder`.
|
||
|
||
3. Si une condition échoue :
|
||
|
||
* la procédure doit lever une **erreur explicite** (`RAISE EXCEPTION`) ;
|
||
* aucune insertion ne doit être faite (transaction annulée).
|
||
|
||
* Les tableaux peuvent être parcourus avec une boucle :
|
||
|
||
```sql
|
||
for i in 1..array_length(p_holders, 1) loop
|
||
...
|
||
end loop;
|
||
```
|
||
|
||
* Pour vérifier la somme :
|
||
|
||
```sql
|
||
select sum(unnest(p_shares));
|
||
```
|
||
|
||
(ou additionner dans la boucle)
|
||
|
||
* Vous pouvez lever une erreur personnalisée :
|
||
|
||
```sql
|
||
raise exception 'La somme des parts doit être égale à 1 (%.4f)', somme;
|
||
```
|
||
|
||
#### Exemples d’appel
|
||
|
||
```sql
|
||
call create_account(
|
||
'FR761234567890',
|
||
'Compte commun',
|
||
array[1, 5],
|
||
array[0.5, 0.5]
|
||
);
|
||
```
|
||
|
||
Crée un compte partagé 50/50 entre les titulaires 1 et 2.
|
||
|
||
|
||
```sql
|
||
call create_account(
|
||
'FR009999999999',
|
||
'Compte déséquilibré',
|
||
array[1, 5],
|
||
array[0.7, 0.4]
|
||
);
|
||
```
|
||
|
||
Doit refuser la création avec une erreur claire :
|
||
|
||
```
|
||
ERROR: La somme des parts (1.1000) doit être égale à 1.0000
|
||
```
|
||
### 3. Monnaies et taux de change
|
||
|
||
```sql
|
||
create table currency (
|
||
"code" text primary key
|
||
);
|
||
```
|
||
|
||
```sql
|
||
insert into currency values ('EUR');
|
||
insert into currency values ('YEN');
|
||
```
|
||
|
||
|
||
```sql
|
||
create table exchange_rate (
|
||
"currency_code" text references currency(code) on delete cascade,
|
||
"date" date ,
|
||
"rate" decimal not null,
|
||
primary key (currency_code, date)
|
||
);
|
||
```
|
||
|
||
```sql
|
||
insert into exchange_rate values
|
||
('YEN', '2025-11-03', 177.57),
|
||
('YEN', '2025-11-04', 176.39),
|
||
('YEN', '2025-11-05', 176.67),
|
||
('YEN', '2025-11-06', 177.15),
|
||
('YEN', '2025-11-07', 176.99);
|
||
```
|
||
|
||
### 4. Les opérations et transactions
|
||
|
||
Implémenter les transactions et opérations.
|
||
|
||
```sql
|
||
create table transaction (
|
||
"id" bigint primary key generated always as identity,
|
||
"transaction_date" timestamp default current_timestamp,
|
||
"amount" decimal check (amount > 0)
|
||
);
|
||
```
|
||
|
||
```sql
|
||
create table operation (
|
||
"id" bigint primary key generated always as identity,
|
||
"transaction_id" bigint references transaction(id),
|
||
"account_id" bigint references account(id),
|
||
"amount" decimal check (amount > 0),
|
||
"direction" text check (direction in ('DEBIT', 'CREDIT'))
|
||
);
|
||
```
|
||
|
||
# Séance 3 : Exploitation des données
|
||
|
||
Créer des vues pour
|
||
|
||
## **1. Vue : liste des titulaires détaillés**
|
||
|
||
Créer une vue *holder_details* permettant d’afficher **tous les titulaires** (banque, personne ou entreprise) sous une forme unifiée.
|
||
|
||
La vue doit contenir :
|
||
|
||
* l’identifiant du titulaire
|
||
* son type
|
||
* un champ `display_name` calculé ainsi :
|
||
|
||
* pour un titulaire de type *PERSON* : *firstname lastname*
|
||
* pour un titulaire de type *COMPANY* : *name*
|
||
* pour un titulaire de type *BANK* : *name*
|
||
|
||
## **2. Vue : liste des comptes avec devise et solde**
|
||
|
||
Créer une vue *account_summary* affichant :
|
||
|
||
* l’identifiant du compte
|
||
* la date d’ouverture
|
||
* la devise
|
||
* le solde
|
||
* le nombre de titulaires du compte
|
||
|
||
## **3. Vue : comptes par titulaire**
|
||
|
||
Créer une vue *holder_accounts* permettant de lister les comptes détenus par chaque titulaire, avec :
|
||
|
||
* le titulaire (id et type)
|
||
* le nom du titulaire
|
||
* l’identifiant du compte
|
||
* la part détenue (`share`)
|
||
* le solde total du compte
|
||
|
||
La vue doit fusionner les informations venant de **holder, person et company**.
|
||
|
||
## **4. Vue : opérations enrichies**
|
||
|
||
Créer une vue *operation_details* affichant les opérations avec :
|
||
|
||
* la date de l’opération
|
||
* le compte impacté
|
||
* la direction (DEBIT ou CREDIT)
|
||
* le montant de l’opération
|
||
* le montant signé (crédit positif, débit négatif)
|
||
* le solde du compte **après l’opération** (bonus : fenêtre analytique)
|
||
|
||
|
||
## **5. Vue : taux de change du jour**
|
||
|
||
Créer une vue *today_exchange_rates* qui affiche pour **chaque devise** son taux de change du jour présent dans `exchange_rate`.
|
||
|
||
Si aucun taux du jour n’existe, la ligne ne doit pas apparaître.
|
||
|
||
## **6. Vue : solde converti en EUR**
|
||
|
||
Créer une vue *account_balance_eur* pour afficher :
|
||
|
||
* compte
|
||
* devise d’origine
|
||
* solde original
|
||
* taux de change correspondant à la date du jour
|
||
* solde converti en EUR (solde × taux)
|
||
|
||
## **7. Vue : transactions complètes**
|
||
|
||
Créer une vue *transaction_summary* affichant un regroupement par transaction :
|
||
|
||
* id de la transaction
|
||
* date
|
||
* montant total de la transaction (somme des opérations)
|
||
* liste des comptes concernés (optionnel : concaténation)
|
||
|
||
## **8. Vue : comptes en découvert imminent**
|
||
|
||
Créer une vue *accounts_at_risk* listant les comptes dont le solde est inférieur à 50 (dans leur devise), ou qui auraient un solde négatif s'ils effectuaient un débit supplémentaire de 20.
|
||
|
||
> Vérification simple : `balance - 20 < 0 OR balance < 50`.
|
||
|
||
## **9. Vue : âge des personnes**
|
||
|
||
Créer une vue *person_age* indiquant :
|
||
|
||
* id
|
||
* nom complet
|
||
* date de naissance
|
||
* âge en années (utiliser `age()`)
|
||
|
||
## **10. Vue : répartition des parts d’un compte**
|
||
|
||
Créer une vue *account_shares* affichant :
|
||
|
||
* id du compte
|
||
* nombre de titulaires
|
||
* somme des parts
|
||
* une colonne booléenne `is_valid` vérifiant si la somme = 1
|
||
|
||
> Objectif : vérifier que les parts des comptes joints sont bien réparties.
|
||
|
||
## **11. Vue : solde par titulaire**
|
||
|
||
Créer une vue *holder_total_balance* indiquant pour chaque titulaire :
|
||
|
||
* id
|
||
* type
|
||
* nom
|
||
* somme des soldes de tous ses comptes (pondérée par `share`), calcul :
|
||
`total = SUM(share × balance)`
|
||
|
||
## **12. Vue : opérations d’un compte en sens unique**
|
||
|
||
Créer une vue *account_debits* listant uniquement les opérations de type DEBIT, avec :
|
||
|
||
* date
|
||
* compte
|
||
* montant négatif
|
||
|
||
Créer une seconde vue *account_credits* (montants positifs).
|
||
|
||
## **13. Vue : dernier taux connu pour chaque devise**
|
||
|
||
Créer une vue *latest_exchange_rate* qui donne, pour chaque devise, son taux de change le plus récent :
|
||
|
||
## **14. Vue : liste des entreprises avec ancienneté**
|
||
|
||
Créer une vue *company_age* indiquant :
|
||
|
||
* id
|
||
* name
|
||
* registration_number
|
||
* age de l’entreprise en années (`age(current_date, created_at)`)
|
||
|
||
## **15. Vue : recapitulatif bancaire complet**
|
||
|
||
Créer une vue *bank_overview* qui croise :
|
||
|
||
* les titulaires
|
||
* leurs comptes
|
||
* leurs transactions
|
||
* leurs opérations
|