espace-paie-odentas/scripts/migrate-employee-docuseal-slugs.ts
odentas 542e0e963d feat: Stocker et utiliser employee_docuseal_slug pour signature-salarie
- Ajout colonne employee_docuseal_slug dans cddu_contracts
- Stockage automatique du slug lors de création signature DocuSeal
- Recherche directe par slug (+ rapide et fiable)
- Bypass mode maintenance en localhost
- Scripts de migration pour contrats existants (92 contrats migrés)
- Logs détaillés dans verify-birthdate et check-status

Fixes: Erreur 'Document introuvable' pour contrats anciens
Performance: O(n) -> O(1) avec index sur employee_docuseal_slug
2025-10-22 17:35:50 +02:00

146 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Script de migration pour récupérer les slugs DocuSeal des salariés
* et les stocker dans cddu_contracts.employee_docuseal_slug
*
* Usage: npx tsx scripts/migrate-employee-docuseal-slugs.ts
*/
import { createClient } from '@supabase/supabase-js';
import { config } from 'dotenv';
import { resolve } from 'path';
// Charger les variables d'environnement depuis .env.local
config({ path: resolve(process.cwd(), '.env.local') });
const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL;
const SUPABASE_SERVICE_ROLE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY;
const DOCUSEAL_TOKEN = process.env.DOCUSEAL_TOKEN;
// Vérification des variables d'environnement
if (!SUPABASE_URL || !SUPABASE_SERVICE_ROLE_KEY || !DOCUSEAL_TOKEN) {
console.error('❌ Variables d\'environnement manquantes:');
if (!SUPABASE_URL) console.error(' - NEXT_PUBLIC_SUPABASE_URL');
if (!SUPABASE_SERVICE_ROLE_KEY) console.error(' - SUPABASE_SERVICE_ROLE_KEY');
if (!DOCUSEAL_TOKEN) console.error(' - DOCUSEAL_TOKEN');
process.exit(1);
}
const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY);
interface Contract {
id: number;
contract_number: string;
docuseal_submission_id: string;
employee_docuseal_slug: string | null;
}
async function getEmployeeSlugFromDocuSeal(submissionId: string): Promise<string | null> {
if (!DOCUSEAL_TOKEN) {
console.error('❌ DOCUSEAL_TOKEN non défini');
return null;
}
try {
const response = await fetch(`https://api.docuseal.eu/submissions/${submissionId}`, {
method: 'GET',
headers: {
'X-Auth-Token': DOCUSEAL_TOKEN,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
console.error(`❌ DocuSeal API error for submission ${submissionId}:`, response.status);
return null;
}
const data = await response.json();
const submitters = data.submitters || [];
const employeeSubmitter = submitters.find((s: any) => s.role === 'Salarié');
return employeeSubmitter?.slug || null;
} catch (error) {
console.error(`❌ Error fetching DocuSeal submission ${submissionId}:`, error);
return null;
}
}
async function migrateEmployeeDocusealSlugs() {
console.log('🚀 Début de la migration des slugs DocuSeal des salariés...\n');
// 1. Récupérer tous les contrats qui ont un docuseal_submission_id mais pas de employee_docuseal_slug
const { data: contracts, error } = await supabase
.from('cddu_contracts')
.select('id, contract_number, docuseal_submission_id, employee_docuseal_slug')
.not('docuseal_submission_id', 'is', null)
.is('employee_docuseal_slug', null);
if (error) {
console.error('❌ Erreur lors de la récupération des contrats:', error);
return;
}
if (!contracts || contracts.length === 0) {
console.log('✅ Aucun contrat à migrer (tous les slugs sont déjà renseignés)');
return;
}
console.log(`📋 ${contracts.length} contrats à migrer\n`);
let successCount = 0;
let errorCount = 0;
let notFoundCount = 0;
// 2. Pour chaque contrat, récupérer le slug depuis DocuSeal
for (const contract of contracts as Contract[]) {
console.log(`🔄 Traitement du contrat ${contract.contract_number}...`);
const employeeSlug = await getEmployeeSlugFromDocuSeal(contract.docuseal_submission_id);
if (!employeeSlug) {
console.log(`⚠️ Slug non trouvé pour le contrat ${contract.contract_number}`);
notFoundCount++;
continue;
}
// 3. Mettre à jour le contrat avec le slug
const { error: updateError } = await supabase
.from('cddu_contracts')
.update({ employee_docuseal_slug: employeeSlug })
.eq('id', contract.id);
if (updateError) {
console.error(`❌ Erreur lors de la mise à jour du contrat ${contract.contract_number}:`, updateError);
errorCount++;
} else {
console.log(`✅ Contrat ${contract.contract_number} mis à jour avec le slug: ${employeeSlug}`);
successCount++;
}
// Pause pour éviter de surcharger l'API DocuSeal
await new Promise(resolve => setTimeout(resolve, 100));
}
// 4. Résumé
console.log('\n📊 Résumé de la migration:');
console.log(` ✅ Succès: ${successCount}`);
console.log(` ⚠️ Non trouvés: ${notFoundCount}`);
console.log(` ❌ Erreurs: ${errorCount}`);
console.log(` 📋 Total: ${contracts.length}`);
if (successCount === contracts.length) {
console.log('\n🎉 Migration terminée avec succès !');
} else if (successCount > 0) {
console.log('\n⚠ Migration partiellement réussie');
} else {
console.log('\n❌ Migration échouée');
}
}
// Exécuter la migration
migrateEmployeeDocusealSlugs()
.then(() => process.exit(0))
.catch((error) => {
console.error('❌ Erreur fatale:', error);
process.exit(1);
});