12 KiB
12 KiB
Requêtes SQL utiles - Virements Salaires
Consultation des données
Lister tous les virements avec détails de l'organisation
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
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
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
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
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)
-- 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
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)
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
-- 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
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
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
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
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
-- 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)
-- 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)
-- 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
-- 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
-- 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
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)
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
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
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
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
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
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
-
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
-
Performance :
- Pour les grandes tables, ajouter des index :
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);
- Pour les grandes tables, ajouter des index :
-
Types de données :
total_net,net_amount,gross_amountpeuvent êtreTEXTouNUMERIC- Utiliser
CAST(...AS NUMERIC)pour les calculs
-
Dates :
period_monthest stocké commeDATE(1er du mois)deadlineest uneDATEcomplètepayment_dateest uneDATEcomplète
-
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