From 47b7535bbcf2514bd059f28c2cd9f5803890fa4f Mon Sep 17 00:00:00 2001 From: odentas Date: Tue, 14 Oct 2025 17:33:43 +0200 Subject: [PATCH] Modification virements salaires staff --- VIREMENTS_SALAIRES_LOGIQUE_CLARIFIEE.md | 179 +++++++ .../staff/salary-transfers/search/route.ts | 7 +- .../staff/virements-salaires/[id]/route.ts | 6 + .../staff/virements-salaires/create/route.ts | 3 + components/staff/SalaryTransfersGrid.tsx | 487 +++++++++++++++--- .../migrations/add_salaires_payes_column.sql | 76 +++ .../transpose_callsheet_to_num_appel.sql | 23 + 7 files changed, 716 insertions(+), 65 deletions(-) create mode 100644 VIREMENTS_SALAIRES_LOGIQUE_CLARIFIEE.md create mode 100644 supabase/migrations/add_salaires_payes_column.sql create mode 100644 supabase/migrations/transpose_callsheet_to_num_appel.sql diff --git a/VIREMENTS_SALAIRES_LOGIQUE_CLARIFIEE.md b/VIREMENTS_SALAIRES_LOGIQUE_CLARIFIEE.md new file mode 100644 index 0000000..5d66f4d --- /dev/null +++ b/VIREMENTS_SALAIRES_LOGIQUE_CLARIFIEE.md @@ -0,0 +1,179 @@ +# Clarification de la logique des virements salaires + +## Date : 14 octobre 2025 + +## Contexte + +Les champs de la table `salary_transfers` ont été clarifiés pour mieux refléter le cycle de vie d'un virement : + +## Nouvelle logique des champs + +### 1. **notification_sent** (boolean) +- **Signification** : La notification a été envoyée au client +- **Valeur par défaut** : `false` +- **Quand passe à true** : Lors de l'appel à l'API `/api/staff/virements-salaires/[id]/notify-client` +- **Affichage** : Badge "✓ Envoyée" / "Non envoyée" + +### 2. **notification_ok** (boolean) - NOUVELLE SIGNIFICATION +- **Ancienne signification** : Statut de la notification (OK / Erreur) +- **Nouvelle signification** : **Paiement client reçu** (Oui / Non) +- **Valeur par défaut** : `false` +- **Quand passe à true** : + - Manuellement par le staff via le modal d'édition + - Automatiquement quand on coche "Paiement client reçu" + - Via le bouton "Marquer comme reçu" +- **Affichage** : Badge "✓ Reçu" / "Non reçu" + +### 3. **client_wire_received_at** (timestamp, optionnel) +- **Signification** : Date de réception du virement client +- **Valeur par défaut** : `null` +- **Quand renseigné** : + - Automatiquement à la date du jour lors de la cochage de "Paiement client reçu" + - Manuellement modifiable dans le formulaire d'édition + - Peut être effacé +- **Affichage** : Date formatée ou "—" si non renseignée + +### 4. **salaires_payes** (boolean) - NOUVEAU CHAMP +- **Signification** : Les salaires ont été payés aux salariés +- **Valeur par défaut** : `false` +- **Quand passe à true** : + - Manuellement par le staff via le modal d'édition + - Via le bouton "Marquer comme payés" +- **Affichage** : Badge "✓ Payés" / "Non payés" + +### 5. **num_appel** (text) +- **Signification** : Numéro d'appel de fonds (ex: "00001") +- **Correction** : Le champ est maintenant pris en compte lors de la **création** d'un virement +- **Affichage** : Police monospace dans le tableau et le modal + +## Cycle de vie d'un virement + +``` +1. CRÉATION + └─> num_appel renseigné + └─> notification_sent = false + └─> notification_ok = false + └─> salaires_payes = false + +2. GÉNÉRATION PDF + └─> callsheet_url renseigné + +3. NOTIFICATION CLIENT + └─> notification_sent = true + └─> Email envoyé au client + +4. RÉCEPTION PAIEMENT CLIENT + └─> notification_ok = true + └─> client_wire_received_at = date (optionnel) + +5. PAIEMENT DES SALAIRES + └─> salaires_payes = true +``` + +## Interface utilisateur + +### Affichage en lecture (Modal) + +Le modal affiche maintenant 3 sections distinctes : + +1. **Notification client** + - Statut : Envoyée / Non envoyée + +2. **Paiement client reçu** + - Statut : Reçu / Non reçu + - Date : (si renseignée) + - Bouton rapide : "Marquer comme reçu" + +3. **Salaires payés** + - Statut : Payés / Non payés + - Bouton rapide : "Marquer comme payés" + +### Formulaire d'édition + +Le formulaire permet maintenant de modifier : + +1. **Paiement client** + - ☑ Checkbox "Paiement client reçu" + - 📅 Date de réception (optionnelle) + - ❌ Bouton pour effacer la date + +2. **Salaires** + - ☑ Checkbox "Salaires payés aux salariés" + +### Tableau principal + +Les colonnes ont été réorganisées : + +| Colonne | Contenu | +|---------|---------| +| Notification | Badge "✓ Envoyée" / "Non envoyée" | +| Paiement client | Badge "✓ Reçu" / "Non reçu" + date | +| Salaires payés | Badge "✓ Payés" / "Non payés" | + +### Filtres + +Nouveaux filtres disponibles : + +- **Notification envoyée** : Oui / Non +- **Paiement client reçu** : Oui / Non +- **Date virement client** : Avec date / Sans date +- **Salaires payés** : Oui / Non (NOUVEAU) + +## API + +### Création - POST /api/staff/virements-salaires/create + +**Champs pris en compte :** +- `num_appel` ✅ (corrigé) +- `salaires_payes` ✅ (nouveau, défaut: false) + +### Mise à jour - PATCH /api/staff/virements-salaires/[id] + +**Nouveaux champs acceptés :** +- `notification_ok` : boolean +- `salaires_payes` : boolean + +## Migration base de données + +Exécuter le fichier : +```bash +supabase/migrations/add_salaires_payes_column.sql +``` + +Ce script : +- ✅ Ajoute la colonne `salaires_payes` +- ✅ Vérifie l'existence de `num_appel` +- ✅ Ajoute des commentaires pour clarifier la logique +- ✅ Crée des index pour améliorer les performances +- ✅ Met à jour les données existantes (notification_ok pour les virements avec date) + +## Corrections apportées + +### ✅ 1. Notification liée à notification_sent +Avant : Confusion entre "notification envoyée" et "notification OK" +Après : `notification_sent` = notification envoyée (clair) + +### ✅ 2. Virement reçu lié à notification_ok + client_wire_received_at +Avant : `notification_ok` = statut erreur/OK de la notification +Après : `notification_ok` = paiement client reçu (oui/non), `client_wire_received_at` = date (optionnelle) + +### ✅ 3. Paiement modifiable avec ou sans date +Avant : Obligation de mettre une date pour marquer comme reçu +Après : Checkbox séparée + date optionnelle + +### ✅ 4. Salaires payés lié à salaires_payes +Avant : Pas de champ dédié +Après : Nouveau champ `salaires_payes` (boolean) + +### ✅ 5. Numéro d'appel pris en compte à la création +Avant : `num_appel` uniquement en modification +Après : `num_appel` pris en compte dès la création + +## Tests recommandés + +1. ✅ Créer un nouveau virement avec `num_appel` +2. ✅ Marquer le paiement client comme reçu (avec et sans date) +3. ✅ Marquer les salaires comme payés +4. ✅ Vérifier les filtres fonctionnent correctement +5. ✅ Tester les boutons rapides "Marquer comme reçu" et "Marquer comme payés" +6. ✅ Vérifier l'affichage dans le tableau principal diff --git a/app/api/staff/salary-transfers/search/route.ts b/app/api/staff/salary-transfers/search/route.ts index 2306ba8..080e104 100644 --- a/app/api/staff/salary-transfers/search/route.ts +++ b/app/api/staff/salary-transfers/search/route.ts @@ -25,8 +25,11 @@ export async function GET(req: Request) { const limit = Math.min(500, parseInt(url.searchParams.get("limit") || "100", 10)); const offset = Math.max(0, parseInt(url.searchParams.get("offset") || "0", 10)); - // Build base query - let query = sb.from("salary_transfers").select("*", { count: "exact" }); + // Build base query with organization name + let query = sb.from("salary_transfers").select(` + *, + organizations!org_id(name) + `, { count: "exact" }); if (q) { // simple ilike search on period_label, callsheet_url, notes diff --git a/app/api/staff/virements-salaires/[id]/route.ts b/app/api/staff/virements-salaires/[id]/route.ts index d1e4a18..f48fff1 100644 --- a/app/api/staff/virements-salaires/[id]/route.ts +++ b/app/api/staff/virements-salaires/[id]/route.ts @@ -50,6 +50,9 @@ export async function PATCH( num_appel, total_net, notes, + client_wire_received_at, + notification_ok, + salaires_payes, } = body; // 4) Build update object (only include provided fields) @@ -64,6 +67,9 @@ export async function PATCH( if (num_appel !== undefined) updateData.num_appel = num_appel; if (total_net !== undefined) updateData.total_net = total_net; if (notes !== undefined) updateData.notes = notes; + if (client_wire_received_at !== undefined) updateData.client_wire_received_at = client_wire_received_at; + if (notification_ok !== undefined) updateData.notification_ok = notification_ok; + if (salaires_payes !== undefined) updateData.salaires_payes = salaires_payes; console.log("[update salary transfer] Update data:", updateData); diff --git a/app/api/staff/virements-salaires/create/route.ts b/app/api/staff/virements-salaires/create/route.ts index 96eac27..9eb4305 100644 --- a/app/api/staff/virements-salaires/create/route.ts +++ b/app/api/staff/virements-salaires/create/route.ts @@ -45,6 +45,7 @@ export async function POST(req: NextRequest) { period_label, deadline, mode, + num_appel, total_net, notes, } = body; @@ -79,10 +80,12 @@ export async function POST(req: NextRequest) { period_label: period_label || null, deadline, mode, + num_appel: num_appel || null, total_net: total_net || null, notes: notes || null, notification_sent: false, notification_ok: false, + salaires_payes: false, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; diff --git a/components/staff/SalaryTransfersGrid.tsx b/components/staff/SalaryTransfersGrid.tsx index c15b7ea..5377314 100644 --- a/components/staff/SalaryTransfersGrid.tsx +++ b/components/staff/SalaryTransfersGrid.tsx @@ -39,6 +39,7 @@ function formatAmount(amount: string | number | null | undefined): string { type SalaryTransfer = { id: string; org_id?: string | null; + organizations?: { name: string } | null; period_month?: string | null; period_label?: string | null; mode?: string | null; @@ -49,6 +50,7 @@ type SalaryTransfer = { notification_sent?: boolean | null; notification_ok?: boolean | null; client_wire_received_at?: string | null; + salaires_payes?: boolean | null; notes?: string | null; created_at?: string | null; updated_at?: string | null; @@ -84,6 +86,7 @@ export default function SalaryTransfersGrid({ const [notificationSentFilter, setNotificationSentFilter] = useState(null); const [notificationOkFilter, setNotificationOkFilter] = useState(null); const [hasClientWireFilter, setHasClientWireFilter] = useState(null); + const [salariesPayesFilter, setSalariesPayesFilter] = useState(null); const [deadlineFrom, setDeadlineFrom] = useState(null); const [deadlineTo, setDeadlineTo] = useState(null); const [sortField, setSortField] = useState("period_month"); @@ -128,6 +131,10 @@ export default function SalaryTransfersGrid({ email_notifs_cc?: string | null; } | null>(null); + // Selection state + const [selectedTransferIds, setSelectedTransferIds] = useState>(new Set()); + const [showBulkActionsMenu, setShowBulkActionsMenu] = useState(false); + // Confirmation modals const [showGeneratePdfConfirm, setShowGeneratePdfConfirm] = useState(false); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); @@ -216,6 +223,7 @@ export default function SalaryTransfersGrid({ if (notificationSentFilter) params.set('notification_sent', notificationSentFilter); if (notificationOkFilter) params.set('notification_ok', notificationOkFilter); if (hasClientWireFilter) params.set('has_client_wire', hasClientWireFilter); + if (salariesPayesFilter) params.set('salaires_payes', salariesPayesFilter); if (deadlineFrom) params.set('deadline_from', deadlineFrom); if (deadlineTo) params.set('deadline_to', deadlineTo); params.set('sort', sortField); @@ -239,7 +247,7 @@ export default function SalaryTransfersGrid({ // Debounce searches when filters change useEffect(() => { // if no filters applied, prefer initial data - const noFilters = !q && !orgFilter && !modeFilter && !notificationSentFilter && !notificationOkFilter && !hasClientWireFilter && !deadlineFrom && !deadlineTo && sortField === 'period_month' && sortOrder === 'desc'; + const noFilters = !q && !orgFilter && !modeFilter && !notificationSentFilter && !notificationOkFilter && !hasClientWireFilter && !salariesPayesFilter && !deadlineFrom && !deadlineTo && sortField === 'period_month' && sortOrder === 'desc'; if (noFilters) { setRows(initialData || []); return; @@ -248,7 +256,7 @@ export default function SalaryTransfersGrid({ const t = setTimeout(() => fetchServer(0), 300); return () => clearTimeout(t); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [q, orgFilter, modeFilter, notificationSentFilter, notificationOkFilter, hasClientWireFilter, deadlineFrom, deadlineTo, sortField, sortOrder, limit]); + }, [q, orgFilter, modeFilter, notificationSentFilter, notificationOkFilter, hasClientWireFilter, salariesPayesFilter, deadlineFrom, deadlineTo, sortField, sortOrder, limit]); // derive options from initialData for simple selects const modes = useMemo(() => Array.from(new Set((initialData || []).map((r) => r.mode).filter(Boolean) as string[])).slice(0,50), [initialData]); @@ -423,6 +431,9 @@ export default function SalaryTransfersGrid({ num_appel: editForm.num_appel, total_net: editForm.total_net, notes: editForm.notes, + client_wire_received_at: editForm.client_wire_received_at, + notification_ok: editForm.notification_ok, + salaires_payes: editForm.salaires_payes, }; console.log("[handleUpdateTransfer] Payload:", payload); @@ -569,6 +580,131 @@ export default function SalaryTransfersGrid({ setSendingNotification(false); } } + + // Selection functions + const isAllSelected = selectedTransferIds.size > 0 && selectedTransferIds.size === rows.length; + const isSomeSelected = selectedTransferIds.size > 0 && selectedTransferIds.size < rows.length; + + const toggleSelectAll = () => { + if (isAllSelected) { + setSelectedTransferIds(new Set()); + } else { + setSelectedTransferIds(new Set(rows.map(r => r.id))); + } + }; + + const toggleSelectTransfer = (transferId: string) => { + setSelectedTransferIds(prev => { + const newSet = new Set(prev); + if (newSet.has(transferId)) { + newSet.delete(transferId); + } else { + newSet.add(transferId); + } + return newSet; + }); + }; + + // Bulk actions + async function handleBulkUpdateNotificationSent(value: boolean) { + if (selectedTransferIds.size === 0) return; + + try { + const updates = Array.from(selectedTransferIds).map(id => ({ + id, + notification_sent: value + })); + + // Update via API + for (const update of updates) { + await fetch(`/api/staff/virements-salaires/${update.id}`, { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + credentials: "include", + body: JSON.stringify({ notification_sent: value }) + }); + } + + // Update local state + setRows(prev => prev.map(r => + selectedTransferIds.has(r.id) ? { ...r, notification_sent: value } : r + )); + + toast.success(`${selectedTransferIds.size} virement(s) mis à jour`); + setSelectedTransferIds(new Set()); + setShowBulkActionsMenu(false); + } catch (err: any) { + console.error("Bulk update error:", err); + toast.error("Erreur lors de la mise à jour groupée"); + } + } + + async function handleBulkUpdatePaymentReceived(value: boolean) { + if (selectedTransferIds.size === 0) return; + + try { + const updates = Array.from(selectedTransferIds).map(id => ({ + id, + notification_ok: value, + client_wire_received_at: value ? new Date().toISOString() : null + })); + + // Update via API + for (const update of updates) { + await fetch(`/api/staff/virements-salaires/${update.id}`, { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + credentials: "include", + body: JSON.stringify({ + notification_ok: value, + client_wire_received_at: value ? new Date().toISOString() : null + }) + }); + } + + // Update local state + setRows(prev => prev.map(r => + selectedTransferIds.has(r.id) + ? { ...r, notification_ok: value, client_wire_received_at: value ? new Date().toISOString() : r.client_wire_received_at } + : r + )); + + toast.success(`${selectedTransferIds.size} virement(s) mis à jour`); + setSelectedTransferIds(new Set()); + setShowBulkActionsMenu(false); + } catch (err: any) { + console.error("Bulk update error:", err); + toast.error("Erreur lors de la mise à jour groupée"); + } + } + + async function handleBulkUpdateSalariesPaid(value: boolean) { + if (selectedTransferIds.size === 0) return; + + try { + // Update via API + for (const id of Array.from(selectedTransferIds)) { + await fetch(`/api/staff/virements-salaires/${id}`, { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + credentials: "include", + body: JSON.stringify({ salaires_payes: value }) + }); + } + + // Update local state + setRows(prev => prev.map(r => + selectedTransferIds.has(r.id) ? { ...r, salaires_payes: value } : r + )); + + toast.success(`${selectedTransferIds.size} virement(s) mis à jour`); + setSelectedTransferIds(new Set()); + setShowBulkActionsMenu(false); + } catch (err: any) { + console.error("Bulk update error:", err); + toast.error("Erreur lors de la mise à jour groupée"); + } + } return (
@@ -584,6 +720,93 @@ export default function SalaryTransfersGrid({
+ {/* Barre d'actions groupées */} + {selectedTransferIds.size > 0 && ( +
+
+ + {selectedTransferIds.size} virement(s) sélectionné(s) + + +
+ +
+ + + {showBulkActionsMenu && ( +
+
+
+ Notification +
+ + + +
+ +
+ Paiement client +
+ + + +
+ +
+ Salaires +
+ + +
+
+ )} +
+
+ )} + {/* Filters */}
{/* Ligne du haut: recherche + bouton filtres */} @@ -641,6 +864,7 @@ export default function SalaryTransfersGrid({ setNotificationSentFilter(null); setNotificationOkFilter(null); setHasClientWireFilter(null); + setSalariesPayesFilter(null); setDeadlineFrom(null); setDeadlineTo(null); setSortField('period_month'); @@ -676,7 +900,7 @@ export default function SalaryTransfersGrid({ {/* Filtre Notification OK */}
setHasClientWireFilter(e.target.value || null)} className="w-full px-3 py-2 border rounded-lg bg-white text-sm" + > + + + + +
+ + {/* Filtre Salaires payés */} +
+ + { + if (input) input.indeterminate = isSomeSelected; + }} + onChange={toggleSelectAll} + className="w-4 h-4 text-blue-600 rounded focus:ring-blue-500" + /> + + Organisation { setSortField('period_month'); setSortOrder((o) => o === 'asc' ? 'desc' : 'asc'); }}> Période {sortField === 'period_month' ? (sortOrder === 'asc' ? '▲' : '▼') : ''} N° Appel - Mode { setSortField('deadline'); setSortOrder((o) => o === 'asc' ? 'desc' : 'asc'); }}> Échéance {sortField === 'deadline' ? (sortOrder === 'asc' ? '▲' : '▼') : ''} @@ -752,12 +1003,11 @@ export default function SalaryTransfersGrid({ Statut PDF Notification - { setSortField('client_wire_received_at'); setSortOrder((o) => o === 'asc' ? 'desc' : 'asc'); }}> - Virement reçu {sortField === 'client_wire_received_at' ? (sortOrder === 'asc' ? '▲' : '▼') : ''} + { setSortField('notification_ok'); setSortOrder((o) => o === 'asc' ? 'desc' : 'asc'); }}> + Paiement client {sortField === 'notification_ok' ? (sortOrder === 'asc' ? '▲' : '▼') : ''} - Notes - { setSortField('created_at'); setSortOrder((o) => o === 'asc' ? 'desc' : 'asc'); }}> - Créé le {sortField === 'created_at' ? (sortOrder === 'asc' ? '▲' : '▼') : ''} + { setSortField('salaires_payes'); setSortOrder((o) => o === 'asc' ? 'desc' : 'asc'); }}> + Salaires payés {sortField === 'salaires_payes' ? (sortOrder === 'asc' ? '▲' : '▼') : ''} Actions @@ -769,6 +1019,21 @@ export default function SalaryTransfersGrid({ className="border-t hover:bg-slate-50 cursor-pointer transition-colors" onClick={() => handleOpenDetails(r)} > + {/* Checkbox */} + e.stopPropagation()}> + toggleSelectTransfer(r.id)} + className="w-4 h-4 text-blue-600 rounded focus:ring-blue-500" + /> + + + {/* Organisation */} + +
{r.organizations?.name || "—"}
+ + {/* Période */}
{r.period_label || "—"}
@@ -780,15 +1045,6 @@ export default function SalaryTransfersGrid({
{r.num_appel || "—"}
- {/* Mode */} - - - {r.mode || "—"} - - - {/* Échéance */} {formatDate(r.deadline)} @@ -811,47 +1067,38 @@ export default function SalaryTransfersGrid({ {/* Notification */} + + + {r.notification_sent ? '✓ Envoyée' : 'Non envoyée'} + + + + {/* Paiement client reçu */}
- {r.notification_sent ? 'Envoyée' : 'Non envoyée'} + {r.notification_ok ? '✓ Reçu' : 'Non reçu'} - {r.notification_sent && ( - - {r.notification_ok ? 'OK' : 'Erreur'} + {r.client_wire_received_at && ( + + {formatDate(r.client_wire_received_at)} )}
- - {/* Virement client reçu */} + + {/* Salaires payés */} - {r.client_wire_received_at ? ( - - {formatDate(r.client_wire_received_at)} - - ) : ( - - Non reçu - - )} + + {r.salaires_payes ? '✓ Payés' : 'Non payés'} + - - {/* Notes */} - - {r.notes ? ( -
- {r.notes} -
- ) : "—"} - - - {/* Créé le */} - {formatDate(r.created_at)} {/* Actions */} e.stopPropagation()}> @@ -1250,6 +1497,82 @@ export default function SalaryTransfersGrid({ />
+ {/* Date de réception du virement client */} +
+ + + {/* Checkbox pour marquer comme reçu/non reçu */} +
+ +
+ + {/* Date de réception (optionnelle) */} +
+ +
+ setEditForm({ + ...editForm, + client_wire_received_at: e.target.value ? `${e.target.value}T00:00:00` : null + })} + className="flex-1 px-3 py-2 border rounded-lg text-sm" + /> + {editForm.client_wire_received_at && ( + + )} +
+
+
+ + {/* Salaires payés */} +
+ + +

+ Cochez cette case lorsque vous avez effectué les virements aux salariés +

+
+ {/* Boutons d'action */}
+ )} +
+
+
+ {selectedTransfer.notification_ok ? '✓ Reçu' : '✗ Non reçu'} +
+ {selectedTransfer.client_wire_received_at && ( +
+ Le {formatDate(selectedTransfer.client_wire_received_at)} +
)}
-
Virement client reçu
-
- {selectedTransfer.client_wire_received_at - ? formatDate(selectedTransfer.client_wire_received_at) - : "Non reçu"} +
+
Salaires payés
+ {!selectedTransfer.salaires_payes && !isEditing && ( + + )} +
+
+ {selectedTransfer.salaires_payes ? '✓ Payés' : '✗ Non payés'}
diff --git a/supabase/migrations/add_salaires_payes_column.sql b/supabase/migrations/add_salaires_payes_column.sql new file mode 100644 index 0000000..aa2e90d --- /dev/null +++ b/supabase/migrations/add_salaires_payes_column.sql @@ -0,0 +1,76 @@ +-- Migration pour ajouter les colonnes manquantes à salary_transfers +-- Date: 2025-01-14 + +-- 1. Ajouter la colonne salaires_payes si elle n'existe pas +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'salary_transfers' + AND column_name = 'salaires_payes' + ) THEN + ALTER TABLE salary_transfers + ADD COLUMN salaires_payes BOOLEAN DEFAULT false; + + COMMENT ON COLUMN salary_transfers.salaires_payes IS 'Indique si les salaires ont été payés aux salariés'; + END IF; +END $$; + +-- 2. Vérifier que la colonne num_appel existe +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'salary_transfers' + AND column_name = 'num_appel' + ) THEN + ALTER TABLE salary_transfers + ADD COLUMN num_appel TEXT; + + COMMENT ON COLUMN salary_transfers.num_appel IS 'Numéro d''appel de fonds (ex: 00001)'; + END IF; +END $$; + +-- 3. Vérifier que la colonne callsheet_date existe (pour la génération PDF) +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'salary_transfers' + AND column_name = 'callsheet_date' + ) THEN + ALTER TABLE salary_transfers + ADD COLUMN callsheet_date TIMESTAMP WITH TIME ZONE; + + COMMENT ON COLUMN salary_transfers.callsheet_date IS 'Date de génération de la feuille d''appel'; + END IF; +END $$; + +-- 4. Ajouter des commentaires pour clarifier la logique des colonnes existantes +COMMENT ON COLUMN salary_transfers.notification_sent IS 'Indique si la notification a été envoyée au client'; +COMMENT ON COLUMN salary_transfers.notification_ok IS 'Indique si le paiement du client a été reçu (ancien: statut de notification)'; +COMMENT ON COLUMN salary_transfers.client_wire_received_at IS 'Date de réception du virement client (optionnelle)'; + +-- 5. Créer un index sur salaires_payes pour améliorer les performances des filtres +CREATE INDEX IF NOT EXISTS idx_salary_transfers_salaires_payes +ON salary_transfers(salaires_payes); + +-- 6. Créer un index sur notification_ok pour les filtres +CREATE INDEX IF NOT EXISTS idx_salary_transfers_notification_ok +ON salary_transfers(notification_ok); + +-- 7. Mettre à jour les données existantes (optionnel) +-- Si vous voulez que tous les virements avec une date de réception aient notification_ok à true +UPDATE salary_transfers +SET notification_ok = true +WHERE client_wire_received_at IS NOT NULL +AND notification_ok IS NULL; + +-- Afficher un résumé +SELECT + COUNT(*) as total_transfers, + COUNT(CASE WHEN notification_sent = true THEN 1 END) as notifications_sent, + COUNT(CASE WHEN notification_ok = true THEN 1 END) as paiements_recus, + COUNT(CASE WHEN salaires_payes = true THEN 1 END) as salaires_payes, + COUNT(CASE WHEN client_wire_received_at IS NOT NULL THEN 1 END) as avec_date_reception +FROM salary_transfers; diff --git a/supabase/migrations/transpose_callsheet_to_num_appel.sql b/supabase/migrations/transpose_callsheet_to_num_appel.sql new file mode 100644 index 0000000..836b31f --- /dev/null +++ b/supabase/migrations/transpose_callsheet_to_num_appel.sql @@ -0,0 +1,23 @@ +-- Migration: Transposer callsheet_url vers num_appel quand ce sont des chiffres +-- Date: 2025-10-14 +-- Description: Copie les valeurs numériques de callsheet_url vers num_appel + +-- Mettre à jour num_appel avec les valeurs de callsheet_url qui sont uniquement des chiffres +UPDATE salary_transfers +SET num_appel = callsheet_url +WHERE + -- Vérifier que callsheet_url n'est pas null + callsheet_url IS NOT NULL + -- Vérifier que callsheet_url contient uniquement des chiffres (regex) + AND callsheet_url ~ '^[0-9]+$' + -- Optionnel: ne mettre à jour que si num_appel est vide + AND (num_appel IS NULL OR num_appel = ''); + +-- Afficher un résumé des enregistrements mis à jour +-- (Décommenter pour voir le résultat après exécution) +-- SELECT +-- COUNT(*) as total_updated, +-- STRING_AGG(DISTINCT id::text, ', ') as updated_ids +-- FROM salary_transfers +-- WHERE callsheet_url ~ '^[0-9]+$' +-- AND num_appel = callsheet_url;