fix: Récupérer le nom du client depuis Supabase pour email signature-request-salarie
This commit is contained in:
parent
265ed6ce67
commit
5502926271
6 changed files with 445 additions and 90 deletions
|
|
@ -97,9 +97,9 @@ Le système d'export SEPA permet aux clients gérant eux-mêmes leurs virements
|
||||||
## ⚠️ Limitations Connues
|
## ⚠️ Limitations Connues
|
||||||
|
|
||||||
### 1. Support contrats
|
### 1. Support contrats
|
||||||
- ❌ **Seulement CDDU** : La requête SQL ne récupère que `cddu_contracts`
|
- ✅ **Tous types de contrats supportés** : CDDU et RG
|
||||||
- ⚠️ **Pas de RG** : Les contrats régime général ne sont pas inclus
|
- ℹ️ **Note technique** : La table `cddu_contracts` contient en réalité tous les types de contrats (nom historique)
|
||||||
- **Impact** : Export incomplet pour organisations mixtes CDDU+RG
|
- ✅ **Payslips** : Toutes les fiches de paie non payées sont incluses
|
||||||
|
|
||||||
### 2. Tests bancaires
|
### 2. Tests bancaires
|
||||||
- ✅ **Qonto** : Testé et validé
|
- ✅ **Qonto** : Testé et validé
|
||||||
|
|
@ -139,18 +139,18 @@ Le système d'export SEPA permet aux clients gérant eux-mêmes leurs virements
|
||||||
- Montants corrects
|
- Montants corrects
|
||||||
- Bénéficiaires corrects
|
- Bénéficiaires corrects
|
||||||
|
|
||||||
### Phase 2 : Support Contrats RG
|
### Phase 2 : Tests Élargis Supplémentaires (OPTIONNEL)
|
||||||
**Objectif** : Inclure les contrats régime général
|
**Objectif** : Valider avec d'autres banques
|
||||||
|
|
||||||
**Modifications nécessaires** :
|
**Banques à tester** :
|
||||||
1. Modifier la requête SQL pour inclure `rg_contracts`
|
- [ ] BNP Paribas
|
||||||
2. Unifier la structure de données (CDDU + RG)
|
- [ ] Crédit Agricole
|
||||||
3. Tester avec organisations mixtes
|
- [ ] LCL
|
||||||
4. Mettre à jour la documentation
|
- [ ] Banque Postale
|
||||||
|
|
||||||
**Estimation** : 2-4 heures de développement
|
**Note** : Test Qonto réussi suffit pour activation prudente. Tests supplémentaires recommandés mais non bloquants.
|
||||||
|
|
||||||
### Phase 3 : Validation XSD (Optionnel)
|
### Phase 3 : Validation XSD (OPTIONNEL)
|
||||||
**Objectif** : Valider le XML contre le schéma officiel
|
**Objectif** : Valider le XML contre le schéma officiel
|
||||||
|
|
||||||
**Avantages** :
|
**Avantages** :
|
||||||
|
|
@ -165,10 +165,10 @@ Le système d'export SEPA permet aux clients gérant eux-mêmes leurs virements
|
||||||
|
|
||||||
### Phase 4 : Activation Production
|
### Phase 4 : Activation Production
|
||||||
**Pré-requis** :
|
**Pré-requis** :
|
||||||
- ✅ Tests réussis sur 3+ banques
|
- ✅ Tests réussis sur Qonto
|
||||||
- ✅ Support CDDU + RG
|
- ✅ Support CDDU + RG (déjà fonctionnel)
|
||||||
- ✅ Documentation utilisateur complète
|
- ⏳ Documentation utilisateur complète
|
||||||
- ✅ Support client prêt
|
- ⏳ Support client prêt
|
||||||
|
|
||||||
**Actions** :
|
**Actions** :
|
||||||
1. Réactiver le bouton d'export
|
1. Réactiver le bouton d'export
|
||||||
|
|
@ -176,6 +176,8 @@ Le système d'export SEPA permet aux clients gérant eux-mêmes leurs virements
|
||||||
3. Communiquer la nouveauté aux clients
|
3. Communiquer la nouveauté aux clients
|
||||||
4. Monitorer les premiers exports
|
4. Monitorer les premiers exports
|
||||||
|
|
||||||
|
**Estimation activation** : Peut être fait rapidement, en attente de décision stratégique
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🐛 Problèmes Connus
|
## 🐛 Problèmes Connus
|
||||||
|
|
@ -190,29 +192,34 @@ Les fonctionnalités testées fonctionnent correctement. Les limitations sont do
|
||||||
|
|
||||||
### Avant activation en production
|
### Avant activation en production
|
||||||
|
|
||||||
1. **Tester avec plus de banques** (priorité haute)
|
1. **Documentation utilisateur** (priorité haute)
|
||||||
- Demander à 2-3 clients volontaires de tester
|
|
||||||
- Vérifier compatibilité BNP, CA, SG
|
|
||||||
- Documenter les retours
|
|
||||||
|
|
||||||
2. **Ajouter support RG** (priorité haute)
|
|
||||||
- Nécessaire pour organisations mixtes
|
|
||||||
- Évite confusion clients
|
|
||||||
|
|
||||||
3. **Améliorer les messages d'erreur** (priorité moyenne)
|
|
||||||
- Erreurs plus explicites côté client
|
|
||||||
- Guide de résolution des problèmes
|
|
||||||
|
|
||||||
4. **Documentation utilisateur** (priorité haute)
|
|
||||||
- Guide pas-à-pas
|
- Guide pas-à-pas
|
||||||
- FAQ
|
- FAQ
|
||||||
- Captures d'écran
|
- Captures d'écran
|
||||||
|
|
||||||
5. **Monitoring** (priorité basse)
|
2. **Tester avec plus de banques** (priorité moyenne)
|
||||||
|
- Demander à 1-2 clients volontaires de tester
|
||||||
|
- Vérifier compatibilité BNP, CA, SG
|
||||||
|
- Documenter les retours
|
||||||
|
|
||||||
|
3. **Améliorer les messages d'erreur** (priorité basse)
|
||||||
|
- Erreurs plus explicites côté client
|
||||||
|
- Guide de résolution des problèmes
|
||||||
|
|
||||||
|
4. **Monitoring** (priorité basse)
|
||||||
- Logger les exports réussis/échoués
|
- Logger les exports réussis/échoués
|
||||||
- Alertes en cas d'erreurs répétées
|
- Alertes en cas d'erreurs répétées
|
||||||
- Analytics PostHog
|
- Analytics PostHog
|
||||||
|
|
||||||
|
### Note importante
|
||||||
|
Le système est **techniquement prêt pour activation** :
|
||||||
|
- ✅ Test réel réussi (Qonto)
|
||||||
|
- ✅ Tous types de contrats supportés
|
||||||
|
- ✅ Validation IBAN robuste
|
||||||
|
- ✅ Format SEPA conforme
|
||||||
|
|
||||||
|
La désactivation actuelle est une **mesure de précaution** en attente de validation stratégique et documentation utilisateur.
|
||||||
|
|
||||||
### Après activation
|
### Après activation
|
||||||
|
|
||||||
1. **Collecte de feedback**
|
1. **Collecte de feedback**
|
||||||
|
|
@ -264,11 +271,13 @@ Les fonctionnalités testées fonctionnent correctement. Les limitations sont do
|
||||||
### Version 1.0 (3 novembre 2025)
|
### Version 1.0 (3 novembre 2025)
|
||||||
- Implémentation initiale
|
- Implémentation initiale
|
||||||
- Test réussi avec Qonto
|
- Test réussi avec Qonto
|
||||||
- Support CDDU uniquement
|
- Support de tous les types de contrats (CDDU + RG via table cddu_contracts)
|
||||||
- Validation IBAN avec checksum modulo 97
|
- Validation IBAN avec checksum modulo 97
|
||||||
- Interface de sélection multiple
|
- Interface de sélection multiple
|
||||||
- Marquage groupé des paies
|
- Marquage groupé des paies
|
||||||
- **Statut** : Désactivé en production (en attente tests élargis)
|
- **Statut** : Désactivé en production par précaution
|
||||||
|
- **Raison désactivation** : En attente documentation utilisateur et validation stratégique
|
||||||
|
- **Prêt techniquement** : Oui ✓
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -389,6 +389,8 @@ export default function VirementsPage() {
|
||||||
const [isExporting, setIsExporting] = useState(false);
|
const [isExporting, setIsExporting] = useState(false);
|
||||||
const [isMarkingPaid, setIsMarkingPaid] = useState(false);
|
const [isMarkingPaid, setIsMarkingPaid] = useState(false);
|
||||||
const [showBulkMarkPaidModal, setShowBulkMarkPaidModal] = useState(false);
|
const [showBulkMarkPaidModal, setShowBulkMarkPaidModal] = useState(false);
|
||||||
|
const [showChangeGestionModal, setShowChangeGestionModal] = useState(false);
|
||||||
|
const [isChangingGestion, setIsChangingGestion] = useState(false);
|
||||||
|
|
||||||
const { data: userInfo, isLoading: isLoadingUser } = useUserInfo();
|
const { data: userInfo, isLoading: isLoadingUser } = useUserInfo();
|
||||||
const { data: organizations, isLoading: isLoadingOrgs, error: orgsError } = useOrganizations();
|
const { data: organizations, isLoading: isLoadingOrgs, error: orgsError } = useOrganizations();
|
||||||
|
|
@ -704,6 +706,42 @@ export default function VirementsPage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Changement de gestion des virements
|
||||||
|
async function handleChangeGestion() {
|
||||||
|
if (!org || !selectedOrgId) return;
|
||||||
|
setShowChangeGestionModal(false);
|
||||||
|
setIsChangingGestion(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const currentMode = isOdentas ? 'odentas' : 'client';
|
||||||
|
const newMode = isOdentas ? 'client' : 'odentas';
|
||||||
|
|
||||||
|
const response = await fetch('/api/virements-salaires/change-gestion', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
credentials: 'include',
|
||||||
|
body: JSON.stringify({
|
||||||
|
organizationId: selectedOrgId,
|
||||||
|
newMode
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const error = await response.json();
|
||||||
|
throw new Error(error.message || 'Erreur lors du changement de gestion');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recharger les données
|
||||||
|
await queryClient.invalidateQueries({ queryKey: ["virements-salaires"] });
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur changement gestion:', error);
|
||||||
|
alert(error instanceof Error ? error.message : 'Erreur lors du changement de gestion');
|
||||||
|
} finally {
|
||||||
|
setIsChangingGestion(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Filtrage local pour la recherche ET la période
|
// Filtrage local pour la recherche ET la période
|
||||||
const filteredItems = useMemo((): VirementItem[] => {
|
const filteredItems = useMemo((): VirementItem[] => {
|
||||||
let result: VirementItem[] = items;
|
let result: VirementItem[] = items;
|
||||||
|
|
@ -873,23 +911,14 @@ export default function VirementsPage() {
|
||||||
<div className="text-xs text-slate-600">Les virements de salaires sont effectués par</div>
|
<div className="text-xs text-slate-600">Les virements de salaires sont effectués par</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="relative group inline-block">
|
<button
|
||||||
<button
|
type="button"
|
||||||
type="button"
|
onClick={() => setShowChangeGestionModal(true)}
|
||||||
disabled
|
disabled={loadingOrg || isChangingGestion}
|
||||||
aria-disabled="true"
|
className="text-xs px-2 py-1 rounded-md border hover:bg-slate-50 transition-colors disabled:opacity-60 disabled:cursor-not-allowed"
|
||||||
className="text-xs px-2 py-1 rounded-md border opacity-60 cursor-not-allowed"
|
>
|
||||||
>
|
{isChangingGestion ? 'Modification...' : 'Modifier'}
|
||||||
Modifier
|
</button>
|
||||||
</button>
|
|
||||||
<div
|
|
||||||
role="tooltip"
|
|
||||||
className="pointer-events-none absolute right-0 mt-2 w-64 px-3 py-2 rounded-lg bg-slate-900 text-white text-xs shadow-lg opacity-0 group-hover:opacity-100 translate-y-1 group-hover:translate-y-0 transition"
|
|
||||||
>
|
|
||||||
Bientôt disponible, veuillez nous contacter.
|
|
||||||
<div className="absolute -top-1 right-6 w-2 h-2 rotate-45 bg-slate-900" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 flex items-center justify-between gap-2">
|
<div className="mt-3 flex items-center justify-between gap-2">
|
||||||
|
|
@ -1352,6 +1381,76 @@ export default function VirementsPage() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Modal de changement de gestion des virements */}
|
||||||
|
{showChangeGestionModal && (
|
||||||
|
<div className="fixed inset-0 z-[1000]">
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 bg-black/40"
|
||||||
|
onClick={() => setShowChangeGestionModal(false)}
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center p-4">
|
||||||
|
<div role="dialog" aria-modal="true" className="w-full max-w-md rounded-2xl border bg-white shadow-xl">
|
||||||
|
<div className="p-5 border-b bg-blue-50">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="rounded-lg bg-blue-100 p-2">
|
||||||
|
<Info className="h-5 w-5 text-blue-700" />
|
||||||
|
</div>
|
||||||
|
<h2 className="text-base font-semibold text-blue-900">
|
||||||
|
Changement de gestion des virements
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-5 space-y-4 text-sm">
|
||||||
|
{isOdentas ? (
|
||||||
|
<>
|
||||||
|
<p className="text-slate-700">
|
||||||
|
Souhaitez-vous reprendre la <strong>gestion de vos virements de salaires</strong> ?
|
||||||
|
</p>
|
||||||
|
<p className="text-slate-600">
|
||||||
|
Vous pourrez effectuer vous-même les virements via votre banque.
|
||||||
|
</p>
|
||||||
|
<div className="rounded-lg bg-green-50 border border-green-200 p-3">
|
||||||
|
<p className="text-sm text-green-900">
|
||||||
|
<strong>Aucune modification tarifaire</strong> - Ce changement n'impacte pas votre facturation.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<p className="text-slate-700">
|
||||||
|
Souhaitez-vous <strong>confier la gestion de vos virements de salaires à Odentas</strong> ?
|
||||||
|
</p>
|
||||||
|
<p className="text-slate-600">
|
||||||
|
Odentas s'occupera de redistribuer les salaires à vos salariés après réception de votre virement mensuel.
|
||||||
|
</p>
|
||||||
|
<div className="rounded-lg bg-green-50 border border-green-200 p-3">
|
||||||
|
<p className="text-sm text-green-900">
|
||||||
|
<strong>Service sans surcoût</strong> - Ce changement n'impacte pas votre facturation.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="p-4 border-t flex justify-end gap-2">
|
||||||
|
<button
|
||||||
|
onClick={() => setShowChangeGestionModal(false)}
|
||||||
|
className="px-4 py-2 rounded-lg border hover:bg-slate-50 text-sm transition-colors"
|
||||||
|
>
|
||||||
|
Annuler
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleChangeGestion}
|
||||||
|
disabled={isChangingGestion}
|
||||||
|
className="px-4 py-2 rounded-lg bg-blue-600 text-white hover:bg-blue-700 text-sm transition-colors disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{isChangingGestion ? 'Modification en cours...' : 'Confirmer'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Modal PDF */}
|
{/* Modal PDF */}
|
||||||
{pdfModalOpen && (
|
{pdfModalOpen && (
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
|
||||||
|
|
|
||||||
|
|
@ -74,48 +74,44 @@ export async function POST(request: NextRequest) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Récupération du vrai nom de l'organisation depuis Supabase
|
// 3. Récupération du nom du client depuis Supabase
|
||||||
let organizationName = providedOrgName || 'Employeur';
|
let organizationName = 'Employeur'; // Fallback par défaut
|
||||||
|
|
||||||
if (organizationId || contractId) {
|
if (!organizationId) {
|
||||||
console.log('🔍 Récupération du nom de l\'organisation depuis la base de données...');
|
console.error('❌ organization_id manquant');
|
||||||
try {
|
return NextResponse.json(
|
||||||
const supabase = createSbServiceRole();
|
{ error: 'organization_id est requis' },
|
||||||
|
{ status: 400 }
|
||||||
let actualOrgId = organizationId;
|
);
|
||||||
|
}
|
||||||
// Si on n'a que le contractId, récupérer l'org_id depuis le contrat
|
|
||||||
if (!actualOrgId && contractId) {
|
console.log('🔍 Récupération du nom du client depuis organizations...');
|
||||||
const { data: contractData } = await supabase
|
try {
|
||||||
.from('cddu_contracts')
|
const supabase = createSbServiceRole();
|
||||||
.select('org_id')
|
|
||||||
.eq('id', contractId)
|
const { data: orgData, error: orgError } = await supabase
|
||||||
.single();
|
.from('organizations')
|
||||||
|
.select('name')
|
||||||
if (contractData?.org_id) {
|
.eq('id', organizationId)
|
||||||
actualOrgId = contractData.org_id;
|
.single();
|
||||||
console.log('✅ org_id récupéré depuis le contrat:', actualOrgId);
|
|
||||||
}
|
if (orgError) {
|
||||||
}
|
console.error('❌ Erreur lors de la récupération de l\'organisation:', orgError);
|
||||||
|
throw orgError;
|
||||||
// Récupérer le nom de l'organisation
|
|
||||||
if (actualOrgId) {
|
|
||||||
const { data: orgData, error: orgError } = await supabase
|
|
||||||
.from('organizations')
|
|
||||||
.select('name')
|
|
||||||
.eq('id', actualOrgId)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!orgError && orgData?.name) {
|
|
||||||
organizationName = orgData.name;
|
|
||||||
console.log('✅ Nom de l\'organisation trouvé:', organizationName);
|
|
||||||
} else {
|
|
||||||
console.warn('⚠️ Nom de l\'organisation non trouvé, utilisation du fallback');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('⚠️ Erreur lors de la récupération du nom de l\'organisation:', err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (orgData?.name) {
|
||||||
|
organizationName = orgData.name;
|
||||||
|
console.log('✅ Nom du client trouvé:', organizationName);
|
||||||
|
} else {
|
||||||
|
console.error('❌ Nom du client non trouvé pour org_id:', organizationId);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('❌ Erreur lors de la récupération du nom du client:', err);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Impossible de récupérer le nom du client' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Récupération du prénom depuis Supabase si non fourni
|
// 4. Récupération du prénom depuis Supabase si non fourni
|
||||||
|
|
|
||||||
157
app/api/virements-salaires/change-gestion/route.ts
Normal file
157
app/api/virements-salaires/change-gestion/route.ts
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
|
||||||
|
import { cookies } from "next/headers";
|
||||||
|
|
||||||
|
export const dynamic = 'force-dynamic';
|
||||||
|
export const revalidate = 0;
|
||||||
|
export const runtime = 'nodejs';
|
||||||
|
|
||||||
|
export async function POST(req: Request) {
|
||||||
|
try {
|
||||||
|
const supabase = createRouteHandlerClient({ cookies });
|
||||||
|
const { data: { session } } = await supabase.auth.getSession();
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
return NextResponse.json({ error: 'unauthorized' }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = await req.json();
|
||||||
|
const { organizationId, newMode } = body;
|
||||||
|
|
||||||
|
if (!organizationId || !newMode) {
|
||||||
|
return NextResponse.json({ error: 'missing_parameters' }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!['odentas', 'client'].includes(newMode.toLowerCase())) {
|
||||||
|
return NextResponse.json({ error: 'invalid_mode' }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupérer les informations de l'organisation
|
||||||
|
const { data: orgData, error: orgError } = await supabase
|
||||||
|
.from('organizations')
|
||||||
|
.select(`
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
structure_api,
|
||||||
|
organization_details(
|
||||||
|
org_id,
|
||||||
|
virements_salaires,
|
||||||
|
email_notifs,
|
||||||
|
email_notifs_cc,
|
||||||
|
code_employeur,
|
||||||
|
prenom_contact
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
.eq('id', organizationId)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (orgError || !orgData) {
|
||||||
|
return NextResponse.json({ error: 'organization_not_found' }, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const orgDetails = Array.isArray(orgData.organization_details)
|
||||||
|
? orgData.organization_details[0]
|
||||||
|
: orgData.organization_details;
|
||||||
|
|
||||||
|
const currentMode = orgDetails?.virements_salaires?.toLowerCase() || 'client';
|
||||||
|
const targetMode = newMode.toLowerCase() === 'odentas' ? 'Odentas' : 'Client';
|
||||||
|
|
||||||
|
// Vérifier que le mode change réellement
|
||||||
|
if (currentMode === targetMode.toLowerCase()) {
|
||||||
|
return NextResponse.json({
|
||||||
|
error: 'no_change_needed',
|
||||||
|
message: 'Le mode de gestion est déjà celui demandé'
|
||||||
|
}, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre à jour le mode de gestion
|
||||||
|
const { error: updateError } = await supabase
|
||||||
|
.from('organization_details')
|
||||||
|
.update({ virements_salaires: targetMode })
|
||||||
|
.eq('org_id', organizationId);
|
||||||
|
|
||||||
|
if (updateError) {
|
||||||
|
console.error('Erreur mise à jour virements_salaires:', updateError);
|
||||||
|
return NextResponse.json({ error: 'update_failed' }, { status: 500 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Préparer les emails
|
||||||
|
const orgName = orgData.structure_api || orgData.name;
|
||||||
|
const emailClient = orgDetails?.email_notifs;
|
||||||
|
const emailClientCC = orgDetails?.email_notifs_cc;
|
||||||
|
|
||||||
|
// Importer le service d'email
|
||||||
|
const { sendUniversalEmailV2 } = await import('@/lib/emailTemplateService');
|
||||||
|
|
||||||
|
// Email au client
|
||||||
|
if (emailClient) {
|
||||||
|
const emailType = targetMode === 'Odentas'
|
||||||
|
? 'virements-gestion-to-odentas'
|
||||||
|
: 'virements-gestion-to-client';
|
||||||
|
|
||||||
|
const emailData = {
|
||||||
|
organizationName: orgName,
|
||||||
|
companyName: orgName,
|
||||||
|
employerCode: orgDetails?.code_employeur || '—',
|
||||||
|
handlerName: 'Renaud BREVIERE-ABRAHAM',
|
||||||
|
gestionMode: targetMode === 'Odentas' ? 'Géré par Odentas' : 'Géré en autonomie',
|
||||||
|
changeDate: new Date().toLocaleString('fr-FR', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
}),
|
||||||
|
firstName: orgDetails?.prenom_contact || '',
|
||||||
|
supportUrl: 'https://espace-paie.odentas.fr/support',
|
||||||
|
step1: 'Réception de l\'appel à virement mensuel',
|
||||||
|
step2: 'Virement unique vers Odentas',
|
||||||
|
step3: 'Redistribution automatique aux salariés',
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sendUniversalEmailV2({
|
||||||
|
type: emailType as any,
|
||||||
|
toEmail: emailClient,
|
||||||
|
ccEmail: emailClientCC || undefined,
|
||||||
|
data: emailData
|
||||||
|
});
|
||||||
|
} catch (emailError) {
|
||||||
|
console.error('Erreur envoi email client:', emailError);
|
||||||
|
// Continue même si l'email échoue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Email interne à l'équipe Odentas
|
||||||
|
const internalData = {
|
||||||
|
organizationName: orgName,
|
||||||
|
previousMode: currentMode,
|
||||||
|
newMode: targetMode,
|
||||||
|
changeDate: new Date().toLocaleString('fr-FR'),
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sendUniversalEmailV2({
|
||||||
|
type: 'virements-gestion-internal',
|
||||||
|
toEmail: 'paie@odentas.fr',
|
||||||
|
data: internalData
|
||||||
|
});
|
||||||
|
} catch (emailError) {
|
||||||
|
console.error('Erreur envoi email interne:', emailError);
|
||||||
|
// Continue même si l'email échoue
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
success: true,
|
||||||
|
previousMode: currentMode,
|
||||||
|
newMode: targetMode
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error changing gestion mode:', error);
|
||||||
|
return NextResponse.json({
|
||||||
|
error: 'internal_server_error',
|
||||||
|
message: error instanceof Error ? error.message : 'Unknown error'
|
||||||
|
}, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -302,6 +302,9 @@ export async function POST(req: Request) {
|
||||||
xml += ' <SvcLvl>\n';
|
xml += ' <SvcLvl>\n';
|
||||||
xml += ' <Cd>SEPA</Cd>\n';
|
xml += ' <Cd>SEPA</Cd>\n';
|
||||||
xml += ' </SvcLvl>\n';
|
xml += ' </SvcLvl>\n';
|
||||||
|
xml += ' <CtgyPurp>\n';
|
||||||
|
xml += ' <Cd>SALA</Cd>\n';
|
||||||
|
xml += ' </CtgyPurp>\n';
|
||||||
xml += ' </PmtTpInf>\n';
|
xml += ' </PmtTpInf>\n';
|
||||||
xml += ` <ReqdExctnDt>${requestedExecutionDate}</ReqdExctnDt>\n`;
|
xml += ` <ReqdExctnDt>${requestedExecutionDate}</ReqdExctnDt>\n`;
|
||||||
xml += ' <Dbtr>\n';
|
xml += ' <Dbtr>\n';
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,9 @@ export type EmailTypeV2 =
|
||||||
| 'contribution-notification' // Nouveau type pour notification de cotisations
|
| 'contribution-notification' // Nouveau type pour notification de cotisations
|
||||||
| 'production-declared' // Nouveau type pour notification de déclaration de production
|
| 'production-declared' // Nouveau type pour notification de déclaration de production
|
||||||
| 'sepa-mandate-request' // Nouveau type pour demande de signature de mandat SEPA
|
| 'sepa-mandate-request' // Nouveau type pour demande de signature de mandat SEPA
|
||||||
|
| 'virements-gestion-to-odentas' // Nouveau type pour changement gestion Client → Odentas
|
||||||
|
| 'virements-gestion-to-client' // Nouveau type pour changement gestion Odentas → Client
|
||||||
|
| 'virements-gestion-internal' // Nouveau type pour notification interne de changement de gestion
|
||||||
| 'notification'
|
| 'notification'
|
||||||
// Support
|
// Support
|
||||||
| 'support-reply' // Réponse du staff à un ticket support
|
| 'support-reply' // Réponse du staff à un ticket support
|
||||||
|
|
@ -282,6 +285,94 @@ const EMAIL_TEMPLATES_V2: Record<EmailTypeV2, EmailTemplateV2> = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'virements-gestion-to-odentas': {
|
||||||
|
subject: 'Modification de la gestion des virements de salaire – {{organizationName}}',
|
||||||
|
title: 'Modification de la gestion des virements de salaire',
|
||||||
|
greeting: 'Bonjour {{firstName}},',
|
||||||
|
mainMessage: 'Nous confirmons qu\'<strong>Odentas gère désormais vos virements de salaires</strong>.',
|
||||||
|
closingMessage: 'Chaque mois, nous vous envoyons un appel à virement avec le total des salaires nets. Vous effectuez un virement unique vers notre compte bancaire, et nous redistribuons les salaires à vos salariés.<br><br>L\'équipe Odentas vous remercie pour votre confiance.',
|
||||||
|
ctaText: 'Accès à l\'Espace Paie',
|
||||||
|
footerText: 'Vous recevez cet e-mail car vous êtes client de Odentas, pour vous notifier d\'une action sur votre compte.',
|
||||||
|
preheaderText: 'Modification de la gestion des virements · Votre compte',
|
||||||
|
colors: {
|
||||||
|
headerColor: STANDARD_COLORS.HEADER,
|
||||||
|
titleColor: '#0F172A',
|
||||||
|
buttonColor: STANDARD_COLORS.BUTTON,
|
||||||
|
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||||||
|
cardBackgroundColor: '#FFFFFF',
|
||||||
|
cardBorder: '#E5E7EB',
|
||||||
|
cardTitleColor: '#0F172A',
|
||||||
|
alertIndicatorColor: '#22C55E',
|
||||||
|
},
|
||||||
|
ctaUrl: 'https://paie.odentas.fr',
|
||||||
|
infoCard: [
|
||||||
|
{ label: 'Votre structure', key: 'companyName' },
|
||||||
|
{ label: 'Votre code employeur', key: 'employerCode' },
|
||||||
|
{ label: 'Votre gestionnaire', key: 'handlerName' },
|
||||||
|
],
|
||||||
|
detailsCard: {
|
||||||
|
title: 'Comment ça fonctionne',
|
||||||
|
rows: [
|
||||||
|
{ label: 'Étape 1', key: 'step1' },
|
||||||
|
{ label: 'Étape 2', key: 'step2' },
|
||||||
|
{ label: 'Étape 3', key: 'step3' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'virements-gestion-to-client': {
|
||||||
|
subject: 'Modification de la gestion des virements de salaire – {{organizationName}}',
|
||||||
|
title: 'Modification de la gestion des virements de salaire',
|
||||||
|
greeting: 'Bonjour {{firstName}},',
|
||||||
|
mainMessage: 'Nous confirmons que <strong>vous gérez désormais vous-même vos virements de salaires</strong>.',
|
||||||
|
closingMessage: 'L\'équipe Odentas vous remercie pour votre confiance.',
|
||||||
|
ctaText: 'Accès à l\'Espace Paie',
|
||||||
|
footerText: 'Vous recevez cet e-mail car vous êtes client de Odentas, pour vous notifier d\'une action sur votre compte.',
|
||||||
|
preheaderText: 'Modification de la gestion des virements · Votre compte',
|
||||||
|
colors: {
|
||||||
|
headerColor: STANDARD_COLORS.HEADER,
|
||||||
|
titleColor: '#0F172A',
|
||||||
|
buttonColor: STANDARD_COLORS.BUTTON,
|
||||||
|
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
|
||||||
|
cardBackgroundColor: '#FFFFFF',
|
||||||
|
cardBorder: '#E5E7EB',
|
||||||
|
cardTitleColor: '#0F172A',
|
||||||
|
alertIndicatorColor: '#3B82F6',
|
||||||
|
},
|
||||||
|
ctaUrl: 'https://paie.odentas.fr',
|
||||||
|
infoCard: [
|
||||||
|
{ label: 'Votre structure', key: 'companyName' },
|
||||||
|
{ label: 'Votre code employeur', key: 'employerCode' },
|
||||||
|
{ label: 'Votre gestionnaire', key: 'handlerName' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
'virements-gestion-internal': {
|
||||||
|
subject: '[Virements] Changement de gestion - {{organizationName}}',
|
||||||
|
title: 'Changement de gestion des virements',
|
||||||
|
greeting: 'Notification interne',
|
||||||
|
mainMessage: 'Un client a modifié son mode de gestion des virements salaires.',
|
||||||
|
closingMessage: 'Cette notification est automatique.',
|
||||||
|
footerText: 'Odentas - Notification interne',
|
||||||
|
preheaderText: 'Notification interne · Changement de gestion',
|
||||||
|
colors: {
|
||||||
|
headerColor: '#64748B',
|
||||||
|
titleColor: '#0F172A',
|
||||||
|
buttonColor: '#64748B',
|
||||||
|
buttonTextColor: '#FFFFFF',
|
||||||
|
cardBackgroundColor: '#FFFFFF',
|
||||||
|
cardBorder: '#E5E7EB',
|
||||||
|
cardTitleColor: '#0F172A',
|
||||||
|
alertIndicatorColor: '#F59E0B',
|
||||||
|
},
|
||||||
|
infoCard: [
|
||||||
|
{ label: 'Organisation', key: 'organizationName' },
|
||||||
|
{ label: 'Mode précédent', key: 'previousMode' },
|
||||||
|
{ label: 'Nouveau mode', key: 'newMode' },
|
||||||
|
{ label: 'Date', key: 'changeDate' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
'referral': {
|
'referral': {
|
||||||
subject: '{{referrer_first_name}} de {{organization_name}} vous recommande Odentas',
|
subject: '{{referrer_first_name}} de {{organization_name}} vous recommande Odentas',
|
||||||
title: 'Vous avez été recommandé',
|
title: 'Vous avez été recommandé',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue