441 lines
12 KiB
Markdown
441 lines
12 KiB
Markdown
# Requêtes SQL utiles - Virements Salaires
|
|
|
|
## Consultation des données
|
|
|
|
### Lister tous les virements avec détails de l'organisation
|
|
```sql
|
|
SELECT
|
|
st.id,
|
|
st.period_month,
|
|
st.period_label,
|
|
st.deadline,
|
|
st.mode,
|
|
st.total_net,
|
|
st.callsheet_url,
|
|
st.notification_sent,
|
|
st.notification_ok,
|
|
st.client_wire_received_at,
|
|
st.notes,
|
|
st.created_at,
|
|
o.name as organization_name
|
|
FROM salary_transfers st
|
|
LEFT JOIN organizations o ON o.id = st.org_id
|
|
ORDER BY st.period_month DESC, st.created_at DESC;
|
|
```
|
|
|
|
### Virements par organisation
|
|
```sql
|
|
SELECT
|
|
o.name as organization_name,
|
|
COUNT(st.id) as total_virements,
|
|
COUNT(st.callsheet_url) as virements_avec_pdf,
|
|
SUM(CASE WHEN st.callsheet_url IS NULL THEN 1 ELSE 0 END) as virements_sans_pdf,
|
|
SUM(CAST(st.total_net AS NUMERIC)) as total_montant
|
|
FROM organizations o
|
|
LEFT JOIN salary_transfers st ON st.org_id = o.id
|
|
GROUP BY o.id, o.name
|
|
ORDER BY total_virements DESC;
|
|
```
|
|
|
|
### Virements pour un mois spécifique
|
|
```sql
|
|
SELECT
|
|
st.*,
|
|
o.name as organization_name
|
|
FROM salary_transfers st
|
|
LEFT JOIN organizations o ON o.id = st.org_id
|
|
WHERE DATE_TRUNC('month', st.period_month) = '2025-01-01'::date
|
|
ORDER BY st.deadline ASC;
|
|
```
|
|
|
|
### Virements en attente de PDF
|
|
```sql
|
|
SELECT
|
|
st.id,
|
|
st.period_label,
|
|
o.name as organization_name,
|
|
st.deadline,
|
|
st.created_at
|
|
FROM salary_transfers st
|
|
LEFT JOIN organizations o ON o.id = st.org_id
|
|
WHERE st.callsheet_url IS NULL
|
|
ORDER BY st.deadline ASC;
|
|
```
|
|
|
|
### Virements avec notifications en erreur
|
|
```sql
|
|
SELECT
|
|
st.id,
|
|
st.period_label,
|
|
o.name as organization_name,
|
|
st.notification_sent,
|
|
st.notification_ok,
|
|
st.notes
|
|
FROM salary_transfers st
|
|
LEFT JOIN organizations o ON o.id = st.org_id
|
|
WHERE st.notification_sent = true
|
|
AND st.notification_ok = false
|
|
ORDER BY st.created_at DESC;
|
|
```
|
|
|
|
## Contrats associés à un virement
|
|
|
|
### Contrats pour une période donnée (logique de filtrage)
|
|
```sql
|
|
-- Récupérer tous les contrats qui seront inclus dans un PDF
|
|
-- pour une organisation et un mois donnés
|
|
SELECT
|
|
c.id,
|
|
c.employee_first_name,
|
|
c.employee_last_name,
|
|
c.employee_iban,
|
|
c.payment_date,
|
|
c.net_amount,
|
|
c.gross_amount
|
|
FROM cddu_contracts c
|
|
WHERE c.org_id = 'ORG_ID_ICI'
|
|
AND c.payment_date IS NOT NULL
|
|
AND DATE_TRUNC('month', c.payment_date) = '2025-01-01'::date
|
|
ORDER BY c.employee_last_name, c.employee_first_name;
|
|
```
|
|
|
|
### Calculer le total net pour une période
|
|
```sql
|
|
SELECT
|
|
DATE_TRUNC('month', c.payment_date) as mois,
|
|
COUNT(c.id) as nombre_contrats,
|
|
SUM(CAST(c.net_amount AS NUMERIC)) as total_net,
|
|
SUM(CAST(c.gross_amount AS NUMERIC)) as total_brut
|
|
FROM cddu_contracts c
|
|
WHERE c.org_id = 'ORG_ID_ICI'
|
|
AND c.payment_date IS NOT NULL
|
|
AND DATE_TRUNC('month', c.payment_date) = '2025-01-01'::date
|
|
GROUP BY DATE_TRUNC('month', c.payment_date);
|
|
```
|
|
|
|
### Contrats par virement (avec jointure)
|
|
```sql
|
|
SELECT
|
|
st.id as virement_id,
|
|
st.period_label,
|
|
c.id as contrat_id,
|
|
c.employee_first_name,
|
|
c.employee_last_name,
|
|
c.payment_date,
|
|
c.net_amount
|
|
FROM salary_transfers st
|
|
LEFT JOIN cddu_contracts c ON (
|
|
c.org_id = st.org_id
|
|
AND DATE_TRUNC('month', c.payment_date) = DATE_TRUNC('month', st.period_month)
|
|
AND c.payment_date IS NOT NULL
|
|
)
|
|
WHERE st.id = 'VIREMENT_ID_ICI'
|
|
ORDER BY c.employee_last_name, c.employee_first_name;
|
|
```
|
|
|
|
### Contrats sans IBAN pour une période
|
|
```sql
|
|
-- Utile pour détecter les problèmes avant génération
|
|
SELECT
|
|
c.id,
|
|
c.employee_first_name,
|
|
c.employee_last_name,
|
|
c.employee_iban,
|
|
c.payment_date,
|
|
c.net_amount
|
|
FROM cddu_contracts c
|
|
WHERE c.org_id = 'ORG_ID_ICI'
|
|
AND c.payment_date IS NOT NULL
|
|
AND DATE_TRUNC('month', c.payment_date) = '2025-01-01'::date
|
|
AND (c.employee_iban IS NULL OR c.employee_iban = '')
|
|
ORDER BY c.employee_last_name;
|
|
```
|
|
|
|
## Statistiques et rapports
|
|
|
|
### Résumé mensuel par organisation
|
|
```sql
|
|
SELECT
|
|
o.name as organisation,
|
|
TO_CHAR(st.period_month, 'YYYY-MM') as mois,
|
|
COUNT(st.id) as nb_virements,
|
|
SUM(CAST(st.total_net AS NUMERIC)) as total_montant,
|
|
COUNT(st.callsheet_url) as nb_pdf_generes,
|
|
COUNT(CASE WHEN st.notification_sent = true THEN 1 END) as nb_notifications_envoyees,
|
|
COUNT(CASE WHEN st.client_wire_received_at IS NOT NULL THEN 1 END) as nb_virements_recus
|
|
FROM organizations o
|
|
LEFT JOIN salary_transfers st ON st.org_id = o.id
|
|
WHERE st.period_month >= '2025-01-01'
|
|
GROUP BY o.id, o.name, TO_CHAR(st.period_month, 'YYYY-MM')
|
|
ORDER BY mois DESC, organisation ASC;
|
|
```
|
|
|
|
### Virements par mode de paiement
|
|
```sql
|
|
SELECT
|
|
st.mode,
|
|
COUNT(st.id) as nombre,
|
|
SUM(CAST(st.total_net AS NUMERIC)) as total_montant,
|
|
AVG(CAST(st.total_net AS NUMERIC)) as montant_moyen
|
|
FROM salary_transfers st
|
|
GROUP BY st.mode
|
|
ORDER BY nombre DESC;
|
|
```
|
|
|
|
### Délais entre création et génération PDF
|
|
```sql
|
|
SELECT
|
|
st.id,
|
|
st.period_label,
|
|
o.name as organisation,
|
|
st.created_at,
|
|
st.updated_at,
|
|
st.callsheet_url,
|
|
EXTRACT(EPOCH FROM (st.updated_at - st.created_at)) / 3600 as heures_ecart
|
|
FROM salary_transfers st
|
|
LEFT JOIN organizations o ON o.id = st.org_id
|
|
WHERE st.callsheet_url IS NOT NULL
|
|
ORDER BY heures_ecart DESC;
|
|
```
|
|
|
|
### Taux de succès des notifications
|
|
```sql
|
|
SELECT
|
|
COUNT(*) as total_virements,
|
|
COUNT(CASE WHEN notification_sent = true THEN 1 END) as notifications_envoyees,
|
|
COUNT(CASE WHEN notification_ok = true THEN 1 END) as notifications_ok,
|
|
ROUND(
|
|
100.0 * COUNT(CASE WHEN notification_ok = true THEN 1 END) /
|
|
NULLIF(COUNT(CASE WHEN notification_sent = true THEN 1 END), 0),
|
|
2
|
|
) as taux_succes_pct
|
|
FROM salary_transfers
|
|
WHERE notification_sent = true;
|
|
```
|
|
|
|
## Opérations de maintenance
|
|
|
|
### Mettre à jour le total_net d'un virement
|
|
```sql
|
|
-- Recalculer et mettre à jour le total net basé sur les contrats
|
|
UPDATE salary_transfers st
|
|
SET
|
|
total_net = (
|
|
SELECT SUM(CAST(c.net_amount AS NUMERIC))
|
|
FROM cddu_contracts c
|
|
WHERE c.org_id = st.org_id
|
|
AND c.payment_date IS NOT NULL
|
|
AND DATE_TRUNC('month', c.payment_date) = DATE_TRUNC('month', st.period_month)
|
|
),
|
|
updated_at = NOW()
|
|
WHERE st.id = 'VIREMENT_ID_ICI';
|
|
```
|
|
|
|
### Supprimer un virement (avec précaution)
|
|
```sql
|
|
-- Vérifier d'abord qu'il n'y a pas de dépendances
|
|
SELECT * FROM salary_transfers WHERE id = 'VIREMENT_ID_ICI';
|
|
|
|
-- Supprimer (ne pas oublier de supprimer le fichier S3 manuellement)
|
|
DELETE FROM salary_transfers WHERE id = 'VIREMENT_ID_ICI';
|
|
```
|
|
|
|
### Regénérer tous les PDFs manquants (via script)
|
|
```sql
|
|
-- Lister les virements sans PDF
|
|
SELECT
|
|
st.id,
|
|
st.org_id,
|
|
st.period_month,
|
|
o.name
|
|
FROM salary_transfers st
|
|
LEFT JOIN organizations o ON o.id = st.org_id
|
|
WHERE st.callsheet_url IS NULL
|
|
ORDER BY st.deadline ASC;
|
|
```
|
|
|
|
### Nettoyer les URLs S3 obsolètes
|
|
```sql
|
|
-- Lister les virements avec URLs S3 (pour vérification manuelle)
|
|
SELECT
|
|
id,
|
|
period_label,
|
|
callsheet_url,
|
|
updated_at
|
|
FROM salary_transfers
|
|
WHERE callsheet_url IS NOT NULL
|
|
ORDER BY updated_at DESC;
|
|
```
|
|
|
|
## Vérifications de cohérence
|
|
|
|
### Virements avec total_net incohérent
|
|
```sql
|
|
-- Compare le total_net stocké avec le total calculé des contrats
|
|
SELECT
|
|
st.id,
|
|
st.period_label,
|
|
st.total_net as total_enregistre,
|
|
(
|
|
SELECT SUM(CAST(c.net_amount AS NUMERIC))
|
|
FROM cddu_contracts c
|
|
WHERE c.org_id = st.org_id
|
|
AND c.payment_date IS NOT NULL
|
|
AND DATE_TRUNC('month', c.payment_date) = DATE_TRUNC('month', st.period_month)
|
|
) as total_calcule,
|
|
ABS(
|
|
CAST(st.total_net AS NUMERIC) -
|
|
COALESCE((
|
|
SELECT SUM(CAST(c.net_amount AS NUMERIC))
|
|
FROM cddu_contracts c
|
|
WHERE c.org_id = st.org_id
|
|
AND c.payment_date IS NOT NULL
|
|
AND DATE_TRUNC('month', c.payment_date) = DATE_TRUNC('month', st.period_month)
|
|
), 0)
|
|
) as difference
|
|
FROM salary_transfers st
|
|
WHERE st.total_net IS NOT NULL
|
|
HAVING ABS(
|
|
CAST(st.total_net AS NUMERIC) -
|
|
COALESCE((
|
|
SELECT SUM(CAST(c.net_amount AS NUMERIC))
|
|
FROM cddu_contracts c
|
|
WHERE c.org_id = st.org_id
|
|
AND c.payment_date IS NOT NULL
|
|
AND DATE_TRUNC('month', c.payment_date) = DATE_TRUNC('month', st.period_month)
|
|
), 0)
|
|
) > 0.01
|
|
ORDER BY difference DESC;
|
|
```
|
|
|
|
### Virements pour des organisations inexistantes
|
|
```sql
|
|
SELECT st.*
|
|
FROM salary_transfers st
|
|
LEFT JOIN organizations o ON o.id = st.org_id
|
|
WHERE o.id IS NULL;
|
|
```
|
|
|
|
### Doublons de virements (même org, même période, même mode)
|
|
```sql
|
|
SELECT
|
|
org_id,
|
|
period_month,
|
|
mode,
|
|
COUNT(*) as nombre_doublons
|
|
FROM salary_transfers
|
|
GROUP BY org_id, period_month, mode
|
|
HAVING COUNT(*) > 1
|
|
ORDER BY nombre_doublons DESC;
|
|
```
|
|
|
|
## Requêtes d'analyse
|
|
|
|
### Évolution mensuelle du nombre de virements
|
|
```sql
|
|
SELECT
|
|
TO_CHAR(period_month, 'YYYY-MM') as mois,
|
|
COUNT(*) as nombre_virements,
|
|
COUNT(CASE WHEN callsheet_url IS NOT NULL THEN 1 END) as avec_pdf,
|
|
SUM(CAST(total_net AS NUMERIC)) as total_montant
|
|
FROM salary_transfers
|
|
WHERE period_month >= '2024-01-01'
|
|
GROUP BY TO_CHAR(period_month, 'YYYY-MM')
|
|
ORDER BY mois DESC;
|
|
```
|
|
|
|
### Top 10 des organisations par volume
|
|
```sql
|
|
SELECT
|
|
o.name as organisation,
|
|
COUNT(st.id) as nb_virements,
|
|
SUM(CAST(st.total_net AS NUMERIC)) as total_montant,
|
|
AVG(CAST(st.total_net AS NUMERIC)) as montant_moyen
|
|
FROM organizations o
|
|
LEFT JOIN salary_transfers st ON st.org_id = o.id
|
|
GROUP BY o.id, o.name
|
|
ORDER BY total_montant DESC
|
|
LIMIT 10;
|
|
```
|
|
|
|
### Analyse des deadlines
|
|
```sql
|
|
SELECT
|
|
EXTRACT(DOW FROM deadline) as jour_semaine, -- 0=Dimanche, 1=Lundi, ...
|
|
COUNT(*) as nombre_virements
|
|
FROM salary_transfers
|
|
GROUP BY EXTRACT(DOW FROM deadline)
|
|
ORDER BY jour_semaine;
|
|
```
|
|
|
|
## Scripts d'export
|
|
|
|
### Export CSV des virements
|
|
```sql
|
|
COPY (
|
|
SELECT
|
|
st.id,
|
|
o.name as organisation,
|
|
st.period_label,
|
|
st.period_month,
|
|
st.deadline,
|
|
st.mode,
|
|
st.total_net,
|
|
CASE WHEN st.callsheet_url IS NOT NULL THEN 'Oui' ELSE 'Non' END as pdf_genere,
|
|
CASE WHEN st.notification_sent THEN 'Oui' ELSE 'Non' END as notification_envoyee,
|
|
CASE WHEN st.notification_ok THEN 'Oui' ELSE 'Non' END as notification_ok,
|
|
st.client_wire_received_at,
|
|
st.notes,
|
|
st.created_at
|
|
FROM salary_transfers st
|
|
LEFT JOIN organizations o ON o.id = st.org_id
|
|
WHERE st.period_month >= '2025-01-01'
|
|
ORDER BY st.period_month DESC
|
|
) TO '/tmp/virements_export.csv' WITH CSV HEADER;
|
|
```
|
|
|
|
### Export des contrats pour un virement
|
|
```sql
|
|
COPY (
|
|
SELECT
|
|
c.employee_last_name as nom,
|
|
c.employee_first_name as prenom,
|
|
c.employee_iban as iban,
|
|
c.payment_date as date_paiement,
|
|
c.net_amount as montant_net,
|
|
c.gross_amount as montant_brut
|
|
FROM cddu_contracts c
|
|
WHERE c.org_id = 'ORG_ID_ICI'
|
|
AND c.payment_date IS NOT NULL
|
|
AND DATE_TRUNC('month', c.payment_date) = '2025-01-01'::date
|
|
ORDER BY c.employee_last_name, c.employee_first_name
|
|
) TO '/tmp/contrats_export.csv' WITH CSV HEADER;
|
|
```
|
|
|
|
## Notes importantes
|
|
|
|
1. **Remplacer les placeholders** :
|
|
- `'ORG_ID_ICI'` → ID réel de l'organisation
|
|
- `'VIREMENT_ID_ICI'` → ID réel du virement
|
|
- `'2025-01-01'` → Date de période réelle
|
|
|
|
2. **Performance** :
|
|
- Pour les grandes tables, ajouter des index :
|
|
```sql
|
|
CREATE INDEX idx_salary_transfers_org_id ON salary_transfers(org_id);
|
|
CREATE INDEX idx_salary_transfers_period_month ON salary_transfers(period_month);
|
|
CREATE INDEX idx_cddu_contracts_payment_date ON cddu_contracts(payment_date);
|
|
```
|
|
|
|
3. **Types de données** :
|
|
- `total_net`, `net_amount`, `gross_amount` peuvent être `TEXT` ou `NUMERIC`
|
|
- Utiliser `CAST(...AS NUMERIC)` pour les calculs
|
|
|
|
4. **Dates** :
|
|
- `period_month` est stocké comme `DATE` (1er du mois)
|
|
- `deadline` est une `DATE` complète
|
|
- `payment_date` est une `DATE` complète
|
|
|
|
5. **RLS (Row Level Security)** :
|
|
- Ces requêtes doivent être exécutées avec un compte ayant les permissions appropriées
|
|
- En production, utiliser le Service Role Key pour les opérations sensibles
|