686 lines
28 KiB
PHP
686 lines
28 KiB
PHP
<?php
|
||
require_once __DIR__ . '/../includes/init_secretaire.php';
|
||
|
||
|
||
require_once APP_HTDOCS . '/push/push_endpoint.php';
|
||
|
||
|
||
// PHPMailer (config centralisée)
|
||
|
||
|
||
use PHPMailer\PHPMailer\PHPMailer;
|
||
use PHPMailer\PHPMailer\Exception;
|
||
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
|
||
// ------- helpers JSON -------
|
||
function out(bool $ok, string $message, array $extra = []): never {
|
||
echo json_encode(['ok'=>$ok, 'message'=>$message] + $extra, JSON_UNESCAPED_UNICODE);
|
||
exit;
|
||
}
|
||
|
||
// ------- helpers e-mail HTML/TXT -------
|
||
function email_html_status(array $d, array $t): string {
|
||
$brand = '<img src="https://uslugi.fr/public/uploads/img/logo_mail.png" width="150" alt="USLUGI.FR">';
|
||
$brand_bas ='УСЛУГИ.ФР';
|
||
return '<!DOCTYPE html><html lang="ru"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>USLUGI.FR</title></head>
|
||
<body style="margin:0;padding:0;background:#f4f6f8;font-family:Arial,Helvetica,sans-serif;">
|
||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f4f6f8;padding:24px 0;"><tr><td align="center">
|
||
<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="background:#fff;border-radius:8px;overflow:hidden;">
|
||
<tr><td style="background:#38378B;color:#fff;padding:20px 24px;font-size:18px;font-weight:600;">'.$brand.'</td></tr>
|
||
<tr><td style="padding:24px 24px 8px 24px;color:#111;font-size:16px;line-height:1.5;">'.nl2br(htmlspecialchars($d['hello_line'])).'</td></tr>
|
||
<tr><td style="padding:8px 24px;color:#111;font-size:16px;line-height:1.6;">'.nl2br(htmlspecialchars($d['intro'])).'</td></tr>
|
||
<tr><td style="padding:16px 24px;">
|
||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border:1px solid #e5e7eb;border-radius:8px;">
|
||
<tr><td style="padding:14px 16px;font-size:14px;color:#6b7280;border-bottom:1px solid #e5e7eb;">'.htmlspecialchars($d['who_label']).'</td><td style="padding:14px 16px;font-size:14px;color:#111;">'.htmlspecialchars($d['who_value']).'</td></tr>
|
||
<tr><td style="padding:14px 16px;font-size:14px;color:#6b7280;">'.htmlspecialchars($d['date_label']).'</td><td style="padding:14px 16px;font-size:14px;color:#111;">'.htmlspecialchars($d['when']).'</td></tr>
|
||
<tr><td style="padding:14px 16px;font-size:14px;color:#6b7280;">'.htmlspecialchars($d['statut_label']).'</td><td style="padding:14px 16px;font-size:14px;color:#111;">'.htmlspecialchars($d['statut_value']).'</td></tr>
|
||
</table>
|
||
</td></tr>'.
|
||
(!empty($d['cta_url']) ? '<tr><td align="center" style="padding:8px 24px 24px 24px;"><a href="'.htmlspecialchars($d['cta_url']).'" target="_blank" style="display:inline-block;text-decoration:none;padding:12px 20px;border-radius:999px;background:#E03C3F;color:#fff;font-weight:600;">'.htmlspecialchars($d['cta_label']).'</a></td></tr>' : '').
|
||
'<tr><td style="padding:8px 24px 24px 24px;color:#111;font-size:16px;line-height:1.6;">'.nl2br(htmlspecialchars($d['footer'])).'</td></tr>
|
||
</table>
|
||
<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="margin-top:12px;"><tr><td style="text-align:center;color:#9ca3af;font-size:12px;padding:8px 0;">© '.date('Y').' '.$brand_bas.' — '.htmlspecialchars($t['merci_simple'] ?? 'Merci').'</td></tr></table>
|
||
</td></tr></table></body></html>';
|
||
}
|
||
function email_text_status(array $d, array $t): string {
|
||
$L=[]; $L[]=$d['hello_line']; $L[]=''; $L[]=$d['intro']; $L[]='';
|
||
$L[]=$d['who_label'].': '.$d['who_value']; $L[]=$d['date_label'].': '.$d['when']; $L[]=$d['statut_label'].': '.$d['statut_value'];
|
||
if (!empty($d['cta_url'])) { $L[]=''; $L[] = $d['cta_label'].': '.$d['cta_url']; }
|
||
$L[]=''; $L[]=$d['footer']; return implode("\n",$L);
|
||
}
|
||
|
||
// ------- ICS helpers -------
|
||
function ics_escape(string $s): string {
|
||
$s = str_replace("\\","\\\\",$s);
|
||
$s = str_replace(";","\;",$s);
|
||
$s = str_replace(",","\,",$s);
|
||
return preg_replace("/\r\n|\r|\n/","\\n",$s);
|
||
}
|
||
function ics_build(
|
||
string $uid, DateTime $start, DateTime $end, string $tz, string $summary, string $desc, string $loc,
|
||
array $attendees, string $method='REQUEST', string $status='CONFIRMED'
|
||
): string {
|
||
$L=[];
|
||
$L[]="BEGIN:VCALENDAR";
|
||
$L[]="VERSION:2.0";
|
||
$L[]="PRODID:-//УСЛУГИ.ФР//RDV//FR";
|
||
$L[]="CALSCALE:GREGORIAN";
|
||
$L[]="METHOD:{$method}";
|
||
$L[]="BEGIN:VEVENT";
|
||
$L[]="UID:{$uid}";
|
||
$L[]="DTSTAMP:".gmdate('Ymd\THis\Z');
|
||
$L[]="DTSTART;TZID={$tz}:".$start->format('Ymd\THis');
|
||
$L[]="DTEND;TZID={$tz}:".$end->format('Ymd\THis');
|
||
if ($summary) $L[]="SUMMARY:".ics_escape($summary);
|
||
if ($desc) $L[]="DESCRIPTION:".ics_escape($desc);
|
||
if ($loc) $L[]="LOCATION:".ics_escape($loc);
|
||
$L[]="ORGANIZER;CN=УСЛУГИ.ФР:mailto:rdv@uslugi.fr";
|
||
foreach ($attendees as $a) {
|
||
if (empty($a['email'])) continue;
|
||
$cn = ics_escape($a['cn'] ?? '');
|
||
$L[]="ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN={$cn};RSVP=TRUE:mailto:".$a['email'];
|
||
}
|
||
$L[]="STATUS:{$status}";
|
||
if ($method==='CANCEL') $L[]="SEQUENCE:2";
|
||
$L[]="END:VEVENT";
|
||
$L[]="END:VCALENDAR";
|
||
return implode("\r\n",$L)."\r\n";
|
||
}
|
||
|
||
// ------- SMTP send (PHPMailer) -------
|
||
function smtp_send(
|
||
string $to, string $subject, string $text, string $html,
|
||
?string $icsName=null, ?string $icsContent=null, ?string $icsMethod=null
|
||
): array {
|
||
global $MAILCFG;
|
||
$m = new PHPMailer(true);
|
||
try {
|
||
$m->isSMTP();
|
||
$m->Host = $MAILCFG['smtp']['host'];
|
||
$m->Port = $MAILCFG['smtp']['port'];
|
||
$m->SMTPSecure = $MAILCFG['smtp']['secure'];
|
||
$m->SMTPAuth = $MAILCFG['smtp']['auth'];
|
||
$m->Username = $MAILCFG['smtp']['username'];
|
||
$m->Password = $MAILCFG['smtp']['password'];
|
||
$m->CharSet = 'UTF-8';
|
||
$m->Encoding = 'base64';
|
||
$m->XMailer = '';
|
||
|
||
if (!empty($_GET['debug_mail'])) {
|
||
$m->SMTPDebug = 2;
|
||
$m->Debugoutput = function($s,$lvl){ error_log("SMTP[$lvl] ".$s); };
|
||
}
|
||
|
||
if (!empty($MAILCFG['bounce'])) $m->Sender = $MAILCFG['bounce'];
|
||
$from = $MAILCFG['from'] ?? ['no-reply@uslugi.fr','USLUGI.FR'];
|
||
$rpto = $MAILCFG['reply_to'] ?? ['contact@uslugi.fr','USLUGI.FR'];
|
||
$m->setFrom($from[0], $from[1] ?? '');
|
||
if (!empty($rpto[0])) $m->addReplyTo($rpto[0], $rpto[1] ?? '');
|
||
|
||
$m->addAddress($to);
|
||
if (!empty($MAILCFG['suppress_auto'])) {
|
||
$m->addCustomHeader('Auto-Submitted', 'auto-generated');
|
||
$m->addCustomHeader('X-Auto-Response-Suppress', 'All');
|
||
}
|
||
|
||
$m->isHTML(true);
|
||
$m->Subject = $subject;
|
||
$m->Body = $html;
|
||
$m->AltBody = $text;
|
||
|
||
if ($icsName && $icsContent !== null && $icsMethod) {
|
||
$m->addStringAttachment($icsContent, $icsName, 'base64', 'text/calendar; method='.$icsMethod.'; charset=UTF-8');
|
||
}
|
||
|
||
$m->send();
|
||
return [true, null];
|
||
|
||
} catch (Throwable $e) {
|
||
error_log('PHPMailer changer_statut (cabinet) error: '.$e->getMessage().($m->ErrorInfo ? ' | '.$m->ErrorInfo : ''));
|
||
// retry sans ICS
|
||
if ($icsName && $icsContent !== null && $icsMethod) {
|
||
try { $m->clearAttachments(); $m->send(); return [true, 'sent_without_ics']; }
|
||
catch(Throwable $e2){ error_log('PHPMailer fallback: '.$e2->getMessage()); }
|
||
}
|
||
return [false, $e->getMessage()];
|
||
}
|
||
}
|
||
// ===== Badge helpers =====
|
||
function get_compte_id(PDO $pdo, string $role, int $reference_id): int {
|
||
$st = $pdo->prepare("SELECT id FROM compte WHERE role = ? AND reference_id = ? LIMIT 1");
|
||
$st->execute([$role, $reference_id]);
|
||
return (int)($st->fetchColumn() ?: 0);
|
||
}
|
||
|
||
function badge_rdv_inc(PDO $pdo, int $compte_id, int $delta = 1): void {
|
||
if ($compte_id <= 0) return;
|
||
$pdo->prepare("
|
||
INSERT INTO push_badge_counters (compte_id, msg_unread, rdv_unread, updated_at)
|
||
VALUES (:cid, 0, :d, NOW())
|
||
ON DUPLICATE KEY UPDATE
|
||
rdv_unread = LEAST(99, GREATEST(0, rdv_unread + :d)),
|
||
updated_at = NOW()
|
||
")->execute([':cid' => $compte_id, ':d' => $delta]);
|
||
}
|
||
|
||
// =================== Contrôles d’entrée ===================
|
||
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') out(false, 'Méthode invalide.');
|
||
|
||
$compteId = (int)($_SESSION['compte_id'] ?? 0);
|
||
$userRole = strtolower((string)($_SESSION['role'] ?? ''));
|
||
$praticien_id = (int)($_POST['praticien_id'] ?? 0); // <<< IMPORTANT côté secrétaire
|
||
$id = (int)($_POST['id'] ?? 0);
|
||
$statut = (string)($_POST['statut'] ?? '');
|
||
|
||
$whitelist = ['prévu','confirmé','annulé','terminé'];
|
||
if ($userRole !== 'secretaire' || $compteId<=0 || $praticien_id<=0 || $id<=0 || !in_array($statut, $whitelist, true)) {
|
||
out(false, $translations['erreur_mise_a_jour'] ?? 'Erreur de mise à jour.');
|
||
}
|
||
|
||
// =================== Vérif pivot secrétaire ===================
|
||
$st = $pdo->prepare("
|
||
SELECT can_manage_rdv
|
||
FROM secretaire_praticien
|
||
WHERE secretaire_user_id = :u
|
||
AND praticien_id = :p
|
||
AND is_active = 1
|
||
LIMIT 1
|
||
");
|
||
$st->execute(['u'=>$compteId,'p'=>$praticien_id]);
|
||
$pivot = $st->fetch(PDO::FETCH_ASSOC);
|
||
if (!$pivot || (int)$pivot['can_manage_rdv'] !== 1) {
|
||
out(false, 'Forbidden');
|
||
}
|
||
|
||
// =================== Update DB sécurisé ===================
|
||
try {
|
||
// =========================
|
||
// SNAPSHOT AVANT UPDATE (secrétaire)
|
||
// =========================
|
||
$beforeStmt = $pdo->prepare("
|
||
SELECT statut, date_rdv, duree_minutes, lieu, praticien_id, client_id
|
||
FROM rendez_vous
|
||
WHERE id = :id AND praticien_id = :pid
|
||
LIMIT 1
|
||
");
|
||
$beforeStmt->execute([':id'=>$id, ':pid'=>$praticien_id]);
|
||
$beforeRow = $beforeStmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
if (!$beforeRow) {
|
||
out(false, $translations['erreur_mise_a_jour'] ?? 'Erreur de mise à jour.');
|
||
}
|
||
|
||
// bornage au praticien cible
|
||
$upd = $pdo->prepare("UPDATE rendez_vous SET statut = :s WHERE id = :id AND praticien_id = :pid");
|
||
$upd->execute([':s'=>$statut, ':id'=>$id, ':pid'=>$praticien_id]);
|
||
if ($upd->rowCount() === 0) {
|
||
out(false, $translations['aucune_modification'] ?? 'Aucune modification.');
|
||
}
|
||
// =========================
|
||
// LOG RDV (rdv_actions_log) : update_status (secrétaire)
|
||
// =========================
|
||
$beforeJson = json_encode([
|
||
'statut' => $beforeRow['statut'],
|
||
'date_rdv' => $beforeRow['date_rdv'],
|
||
'duree_minutes' => (int)$beforeRow['duree_minutes'],
|
||
'lieu' => $beforeRow['lieu'],
|
||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||
|
||
$afterJson = json_encode([
|
||
'statut' => $statut,
|
||
'date_rdv' => $beforeRow['date_rdv'], // date inchangée ici
|
||
'duree_minutes' => (int)$beforeRow['duree_minutes'], // idem
|
||
'lieu' => $beforeRow['lieu'],
|
||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||
|
||
$logStmt = $pdo->prepare("
|
||
INSERT INTO rdv_actions_log
|
||
(rdv_id, action, by_user_id, by_role, before_json, after_json, created_at)
|
||
VALUES
|
||
(?, 'update_status', ?, 'secretaire', ?, ?, NOW())
|
||
");
|
||
$logStmt->execute([(int)$id, (int)$compteId, $beforeJson, $afterJson]);
|
||
|
||
// Récup info RDV (+ lieu + adresse praticien)
|
||
$q = $pdo->prepare("
|
||
SELECT rv.id, rv.client_id, rv.date_rdv, rv.duree_minutes, rv.uuid_ical, rv.lieu,
|
||
p.email AS email_praticien, p.prenom AS prenom_praticien, p.nom AS nom_praticien,
|
||
p.adresse AS p_adresse, p.ville AS p_ville, p.code_postal AS p_cp,
|
||
c.email AS email_client, c.prenom AS prenom_client, c.nom AS nom_client
|
||
FROM rendez_vous rv
|
||
JOIN praticien p ON rv.praticien_id = p.id
|
||
JOIN client c ON rv.client_id = c.id
|
||
WHERE rv.id = :id AND rv.praticien_id = :pid
|
||
");
|
||
$q->execute([':id'=>$id, ':pid'=>$praticien_id]);
|
||
$rdv = $q->fetch(PDO::FETCH_ASSOC);
|
||
if (!$rdv) out(false, $translations['erreur_mise_a_jour'] ?? 'Erreur de mise à jour.');
|
||
// ✅ compte_id pour badges
|
||
$clientCompteId = get_compte_id($pdo, 'client', (int)$rdv['client_id']);
|
||
$praticienCompteId = get_compte_id($pdo, 'praticien', (int)$praticien_id);
|
||
|
||
|
||
// Données communes
|
||
$tzStr='Europe/Paris'; $tz=new DateTimeZone($tzStr);
|
||
$start=new DateTime($rdv['date_rdv'], $tz);
|
||
$end=(clone $start)->modify('+'.(int)($rdv['duree_minutes'] ?? 60).' minutes');
|
||
$when=$start->format('d/m/Y H:i');
|
||
$when_a=$start->format('d/m/Y') . ' ' . ($translations['a'] ?? 'в') . ' ' . $start->format('H:i');
|
||
|
||
$cliFull = trim(($rdv['prenom_client'] ?? '').' '.($rdv['nom_client'] ?? ''));
|
||
$pratFull = trim(($rdv['prenom_praticien'] ?? '').' '.($rdv['nom_praticien'] ?? ''));
|
||
|
||
// ====== FORMAT (online/offline + adresse) ======
|
||
$lieu = strtolower(trim((string)($rdv['lieu'] ?? '')));
|
||
$addrParts = array_filter([
|
||
trim((string)($rdv['p_adresse'] ?? '')),
|
||
trim((string)($rdv['p_cp'] ?? '') . ' ' . (string)($rdv['p_ville'] ?? ''))
|
||
], fn($x) => $x !== '' && $x !== ' ');
|
||
$addr = trim(implode(', ', $addrParts));
|
||
|
||
$formatLabel = $translations['format_label'] ?? 'Формат';
|
||
$onlineLabel = $translations['option_online'] ?? 'Онлайн';
|
||
$cabinetLabel = $translations['option_cabinet'] ?? 'Очно';
|
||
$addressLabel = $translations['adresse_label'] ?? 'Адрес';
|
||
|
||
$formatLine = '';
|
||
if ($lieu === 'online') {
|
||
$formatLine = "\n\n{$formatLabel}: {$onlineLabel}";
|
||
} elseif ($lieu === 'offline') {
|
||
$formatLine = "\n\n{$formatLabel}: {$cabinetLabel}" . ($addr ? "\n{$addressLabel}: {$addr}" : '');
|
||
}
|
||
|
||
// LOCATION pour ICS
|
||
if ($lieu === 'online') {
|
||
$icsLocation = $onlineLabel;
|
||
} elseif ($lieu === 'offline') {
|
||
$icsLocation = $addr !== '' ? $addr : $cabinetLabel;
|
||
} else {
|
||
$icsLocation = '';
|
||
}
|
||
|
||
$cta_client_url='https://uslugi.fr/public/client/mes_rendez_vous.php';
|
||
$cta_prat_url ='https://uslugi.fr/public/praticien/agenda.php';
|
||
$cta_label =$translations['consultez_espace_pour_details'] ?? 'Voir les détails';
|
||
$statutLabelMap=[
|
||
'confirmé'=>$translations['statut_confirme'] ?? 'Confirmé',
|
||
'annulé' =>$translations['statut_annule'] ?? 'Annulé',
|
||
'prévu' =>$translations['statut_prevu'] ?? 'Prévu',
|
||
'terminé' =>$translations['statut_termine'] ?? 'Terminé',
|
||
];
|
||
$statutLibelle=$statutLabelMap[$statut] ?? $statut;
|
||
|
||
$warnings=[];
|
||
|
||
// ===== HOOK SUIVI CLIENT — création/MAJ dossier + entrée auto (sur confirmation) =====
|
||
if ($statut === 'confirmé') {
|
||
// =========================
|
||
// PUSH RDV — confirmé (par secrétaire)
|
||
// =========================
|
||
try {
|
||
// 🔔 Praticien : confirmé par secrétaire
|
||
push_rdv_to_praticien(
|
||
$pdo,
|
||
(int)$praticien_id,
|
||
$translations['push_rdv_confirmed_praticien_title'] ?? 'Запись подтверждена',
|
||
$translations['push_rdv_confirmed_praticien_body'] ?? 'Секретарь подтвердил(а) запись.',
|
||
'/public/praticien/agenda.php',
|
||
[
|
||
'rdv_id' => (int)$rdv['id'],
|
||
'action' => 'confirm',
|
||
'from_role' => 'secretaire',
|
||
'secretaire_id' => (int)$compteId,
|
||
'client_id' => (int)$rdv['client_id'],
|
||
'praticien_id' => (int)$praticien_id,
|
||
'date_rdv' => $rdv['date_rdv'],
|
||
]
|
||
);
|
||
|
||
// ✅ badges
|
||
badge_rdv_inc($pdo, $clientCompteId, 1);
|
||
badge_rdv_inc($pdo, $praticienCompteId, 1);
|
||
|
||
push_rdv_to_client(
|
||
$pdo,
|
||
(int)$rdv['client_id'],
|
||
$translations['push_rdv_confirmed_title'] ?? 'Запись подтверждена',
|
||
$translations['push_rdv_confirmed_body'] ?? 'Ваша запись подтверждена администратором.',
|
||
'/public/client/mes_rendez_vous.php',
|
||
[
|
||
'rdv_id' => (int)$rdv['id'],
|
||
'action' => 'confirm',
|
||
'from_role' => 'secretaire',
|
||
'praticien_id' => (int)$praticien_id,
|
||
'date_rdv' => $rdv['date_rdv'],
|
||
]
|
||
);
|
||
|
||
} catch (Throwable $e) {
|
||
error_log('push rdv confirm (secretaire) failed: '.$e->getMessage());
|
||
}
|
||
|
||
try {
|
||
$pdo->beginTransaction();
|
||
|
||
// Upsert dossier praticien↔client
|
||
$upsert = $pdo->prepare("
|
||
INSERT INTO client_dossiers (praticien_id, client_id, first_rdv_id)
|
||
VALUES (:p, :c, :r)
|
||
ON DUPLICATE KEY UPDATE updated_at = CURRENT_TIMESTAMP
|
||
");
|
||
$upsert->execute([
|
||
':p' => (int)$praticien_id,
|
||
':c' => (int)$rdv['client_id'],
|
||
':r' => (int)$rdv['id']
|
||
]);
|
||
|
||
// Récup id dossier (si existed déjà, lastInsertId = 0)
|
||
$dossierId = (int)$pdo->lastInsertId();
|
||
if ($dossierId === 0) {
|
||
$getId = $pdo->prepare("SELECT id FROM client_dossiers WHERE praticien_id=:p AND client_id=:c LIMIT 1");
|
||
$getId->execute([':p'=>(int)$praticien_id, ':c'=>(int)$rdv['client_id']]);
|
||
$dossierId = (int)$getId->fetchColumn();
|
||
}
|
||
|
||
// Éviter doublon côté suivi
|
||
$exists = $pdo->prepare("SELECT 1 FROM client_suivi WHERE dossier_id=:d AND rdv_id=:r LIMIT 1");
|
||
$exists->execute([':d'=>$dossierId, ':r'=>(int)$rdv['id']]);
|
||
if (!$exists->fetchColumn()) {
|
||
$type = ($lieu === 'online') ? 'online' : (($lieu === 'offline') ? 'cabinet' : 'autre');
|
||
$ins = $pdo->prepare("
|
||
INSERT INTO client_suivi (dossier_id, rdv_id, date_rdv, type_rdv, titre, notes, prive, duree_minutes)
|
||
VALUES (:d, :r, :dt, :ty, :ti, :no, 1, :du)
|
||
");
|
||
$ins->execute([
|
||
':d' => $dossierId,
|
||
':r' => (int)$rdv['id'],
|
||
':dt' => $start->format('Y-m-d H:i:s'),
|
||
':ty' => $type,
|
||
':ti' => $translations['suivi_rdv_confirme_titre'] ?? 'RDV confirmé',
|
||
':no' => ($translations['suivi_rdv_confirme_note'] ?? 'Créé automatiquement suite à la confirmation du rendez-vous.'),
|
||
':du' => (int)($rdv['duree_minutes'] ?? 60),
|
||
]);
|
||
}
|
||
|
||
$pdo->commit();
|
||
} catch (Throwable $ex) {
|
||
if ($pdo->inTransaction()) $pdo->rollBack();
|
||
error_log('Suivi auto (confirmé/cabinet) failed: '.$ex->getMessage());
|
||
// on continue quand même (mails, etc.)
|
||
}
|
||
|
||
// E-mails confirmation
|
||
$d_c = [
|
||
'hello_line'=>sprintf($translations['mail_valid_bonjour_nom'] ?? 'Bonjour %s',$cliFull),
|
||
'intro'=>($translations['votre_rdv_confirme'] ?? 'Votre rendez-vous est confirmé').' '.
|
||
($translations['email_avec'] ?? 'avec').' '.$pratFull.'.',
|
||
'who_label'=>$translations['praticien_label'] ?? 'Praticien',
|
||
'who_value'=>$pratFull,
|
||
'date_label'=>$translations['date_label'] ?? 'Date',
|
||
'when'=>$when_a,
|
||
'statut_label'=>$translations['statut_label'] ?? 'Statut',
|
||
'statut_value'=>$statutLibelle,
|
||
'cta_url'=>$cta_client_url,
|
||
'cta_label'=>$cta_label,
|
||
'footer'=>$translations['merci_simple'] ?? 'Merci.',
|
||
];
|
||
if ($formatLine !== '') $d_c['intro'] .= $formatLine;
|
||
|
||
$icsC = !empty($rdv['uuid_ical'])
|
||
? ics_build($rdv['uuid_ical'],$start,$end,$tzStr,
|
||
($translations['rdv_avec'] ?? 'Rendez-vous avec').' '.$pratFull,
|
||
'', $icsLocation,
|
||
[['cn'=>$pratFull,'email'=>$rdv['email_praticien']],['cn'=>$cliFull,'email'=>$rdv['email_client']]],
|
||
'REQUEST','CONFIRMED')
|
||
: null;
|
||
[$okC,$errC] = smtp_send(
|
||
$rdv['email_client'],
|
||
$translations['email_confirmation_rdv_sujet'] ?? 'Confirmation de rendez-vous',
|
||
email_text_status($d_c,$translations),
|
||
email_html_status($d_c,$translations),
|
||
$icsC?('rdv_'.$rdv['uuid_ical'].'_client.ics'):null,
|
||
$icsC,
|
||
$icsC?'REQUEST':null
|
||
);
|
||
if (!$okC) $warnings[]='mail_client';
|
||
|
||
$d_p = [
|
||
'hello_line'=>($translations['email_bonjour_dr'] ?? 'Bonjour').' '.$pratFull,
|
||
'intro'=>$translations['nouveau_rdv_pris_avec'] ?? 'Un nouveau rendez-vous a été confirmé.',
|
||
'who_label'=>$translations['client_label'] ?? 'Client',
|
||
'who_value'=>$cliFull,
|
||
'date_label'=>$translations['date_label'] ?? 'Date',
|
||
'when'=>$when_a,
|
||
'statut_label'=>$translations['statut_label'] ?? 'Statut',
|
||
'statut_value'=>$statutLibelle,
|
||
'cta_url'=>$cta_prat_url,
|
||
'cta_label'=>$cta_label,
|
||
'footer'=>$translations['merci_simple'] ?? 'Merci.',
|
||
];
|
||
if ($formatLine !== '') $d_p['intro'] .= $formatLine;
|
||
|
||
$icsP = !empty($rdv['uuid_ical'])
|
||
? ics_build($rdv['uuid_ical'],$start,$end,$tzStr,
|
||
($translations['rdv_avec'] ?? 'Rendez-vous avec').' '.$cliFull,
|
||
'', $icsLocation,
|
||
[['cn'=>$cliFull,'email'=>$rdv['email_client']],['cn'=>$pratFull,'email'=>$rdv['email_praticien']]],
|
||
'REQUEST','CONFIRMED')
|
||
: null;
|
||
[$okP,$errP] = smtp_send(
|
||
$rdv['email_praticien'],
|
||
($translations['rdv_confirme_pour'] ?? 'RDV confirmé pour').' '.$cliFull,
|
||
email_text_status($d_p,$translations),
|
||
email_html_status($d_p,$translations),
|
||
$icsP?('rdv_'.$rdv['uuid_ical'].'_praticien.ics'):null,
|
||
$icsP,
|
||
$icsP?'REQUEST':null
|
||
);
|
||
if (!$okP) $warnings[]='mail_praticien';
|
||
}
|
||
|
||
if ($statut === 'annulé') {
|
||
// =========================
|
||
// PUSH RDV — annulé (par secrétaire)
|
||
// =========================
|
||
try {
|
||
// 🔔 Praticien : annulé par secrétaire
|
||
push_rdv_to_praticien(
|
||
$pdo,
|
||
(int)$praticien_id,
|
||
$translations['push_rdv_cancel_praticien_title'] ?? 'Запись отменена',
|
||
$translations['push_rdv_cancel_praticien_body'] ?? 'Секретарь отменил(а) запись.',
|
||
'/public/praticien/rendez_vous.php',
|
||
[
|
||
'rdv_id' => (int)$rdv['id'],
|
||
'action' => 'cancel',
|
||
'from_role' => 'secretaire',
|
||
'secretaire_id' => (int)$compteId,
|
||
'client_id' => (int)$rdv['client_id'],
|
||
'praticien_id' => (int)$praticien_id,
|
||
'date_rdv' => $rdv['date_rdv'],
|
||
]
|
||
);
|
||
|
||
// ✅ badges
|
||
badge_rdv_inc($pdo, $clientCompteId, 1);
|
||
badge_rdv_inc($pdo, $praticienCompteId, 1);
|
||
|
||
push_rdv_to_client(
|
||
$pdo,
|
||
(int)$rdv['client_id'],
|
||
$translations['push_rdv_cancel_secretaire_title'] ?? 'Запись отменена',
|
||
$translations['push_rdv_cancel_secretaire_body'] ?? 'Администратор отменил запись.',
|
||
'/public/client/mes_rendez_vous.php',
|
||
[
|
||
'rdv_id' => (int)$rdv['id'],
|
||
'action' => 'cancel',
|
||
'from_role' => 'secretaire',
|
||
'praticien_id' => (int)$praticien_id,
|
||
'date_rdv' => $rdv['date_rdv'],
|
||
]
|
||
);
|
||
|
||
} catch (Throwable $e) {
|
||
error_log('push rdv cancel (secretaire) failed: '.$e->getMessage());
|
||
}
|
||
|
||
$summary = ($translations['rdv_avec'] ?? 'Rendez-vous avec').' '.$pratFull;
|
||
$attBoth = [
|
||
['cn'=>$cliFull,'email'=>$rdv['email_client']],
|
||
['cn'=>$pratFull,'email'=>$rdv['email_praticien']]
|
||
];
|
||
$ics = !empty($rdv['uuid_ical'])
|
||
? ics_build($rdv['uuid_ical'],$start,$end,$tzStr,$summary,'',$icsLocation, $attBoth,'CANCEL','CANCELLED')
|
||
: null;
|
||
|
||
$d_c = [
|
||
'hello_line'=>sprintf($translations['mail_valid_bonjour_nom'] ?? 'Bonjour %s',$cliFull),
|
||
'intro'=>($translations['email_votre_rdv_du'] ?? 'Votre rendez-vous du').' '.$when_a.' '.
|
||
($translations['email_avec'] ?? 'avec').' '.$pratFull.' '.
|
||
($translations['email_a_ete_annule'] ?? 'a été annulé.'),
|
||
'who_label'=>$translations['praticien_label'] ?? 'Praticien',
|
||
'who_value'=>$pratFull,
|
||
'date_label'=>$translations['date_label'] ?? 'Date',
|
||
'when'=>$when_a,
|
||
'statut_label'=>$translations['statut_label'] ?? 'Statut',
|
||
'statut_value'=>$statutLibelle,
|
||
'cta_url'=>'https://uslugi.fr/public/client/mes_rendez_vous.php',
|
||
'cta_label'=>$translations['consultez_espace_pour_details'] ?? 'Voir les détails',
|
||
'footer'=>$translations['merci_simple'] ?? 'Merci.',
|
||
];
|
||
[$okC,$errC] = smtp_send(
|
||
$rdv['email_client'],
|
||
$translations['email_annulation_client_sujet'] ?? 'Confirmation d’annulation de votre rendez-vous',
|
||
email_text_status($d_c,$translations),
|
||
email_html_status($d_c,$translations),
|
||
$ics?('rdv_cancel_'.$rdv['uuid_ical'].'_client.ics'):null,
|
||
$ics,
|
||
$ics?'CANCEL':null
|
||
);
|
||
if (!$okC) $warnings[]='mail_client';
|
||
|
||
$d_p = [
|
||
'hello_line'=>($translations['email_bonjour_dr'] ?? 'Bonjour').' '.$pratFull,
|
||
'intro'=>($translations['email_rdv_du'] ?? 'Le rendez-vous du').' '.$when_a.' '.
|
||
($translations['email_avec'] ?? 'avec').' '.$cliFull.' '.
|
||
($translations['email_annule_par_praticien'] ?? 'a été annulé.'),
|
||
'who_label'=>$translations['client_label'] ?? 'Client',
|
||
'who_value'=>$cliFull,
|
||
'date_label'=>$translations['date_label'] ?? 'Date',
|
||
'when'=>$when_a,
|
||
'statut_label'=>$translations['statut_label'] ?? 'Statut',
|
||
'statut_value'=>$statutLibelle,
|
||
'cta_url'=>'https://uslugi.fr/public/praticien/agenda.php',
|
||
'cta_label'=>$translations['consultez_espace_pour_details'] ?? 'Voir les détails',
|
||
'footer'=>$translations['merci_simple'] ?? 'Merci.',
|
||
];
|
||
[$okP,$errP] = smtp_send(
|
||
$rdv['email_praticien'],
|
||
$translations['email_annulation_sujet'] ?? 'Rendez-vous annulé',
|
||
email_text_status($d_p,$translations),
|
||
email_html_status($d_p,$translations),
|
||
$ics?('rdv_cancel_'.$rdv['uuid_ical'].'_praticien.ics'):null,
|
||
$ics,
|
||
$ics?'CANCEL':null
|
||
);
|
||
if (!$okP) $warnings[]='mail_praticien';
|
||
}
|
||
|
||
if ($statut === 'terminé') {
|
||
// =========================
|
||
// PUSH RDV — terminé (par secrétaire)
|
||
// =========================
|
||
try {
|
||
// 🔔 Praticien : marqué terminé par secrétaire
|
||
push_rdv_to_praticien(
|
||
$pdo,
|
||
(int)$praticien_id,
|
||
$translations['push_rdv_done_praticien_title'] ?? 'Приём завершён',
|
||
$translations['push_rdv_done_praticien_body'] ?? 'Секретарь отметил(а) приём как завершённый.',
|
||
'/public/praticien/agenda.php',
|
||
[
|
||
'rdv_id' => (int)$rdv['id'],
|
||
'action' => 'done',
|
||
'from_role' => 'secretaire',
|
||
'secretaire_id' => (int)$compteId,
|
||
'client_id' => (int)$rdv['client_id'],
|
||
'praticien_id' => (int)$praticien_id,
|
||
'date_rdv' => $rdv['date_rdv'],
|
||
]
|
||
);
|
||
|
||
// ✅ badges
|
||
badge_rdv_inc($pdo, $clientCompteId, 1);
|
||
badge_rdv_inc($pdo, $praticienCompteId, 1);
|
||
|
||
push_rdv_to_client(
|
||
$pdo,
|
||
(int)$rdv['client_id'],
|
||
$translations['push_rdv_done_title'] ?? 'Приём завершён',
|
||
$translations['push_rdv_done_body'] ?? 'Ваш визит завершён.',
|
||
'/public/client/mes_rendez_vous.php',
|
||
[
|
||
'rdv_id' => (int)$rdv['id'],
|
||
'action' => 'done',
|
||
'from_role' => 'secretaire',
|
||
'praticien_id' => (int)$praticien_id,
|
||
]
|
||
);
|
||
|
||
} catch (Throwable $e) {
|
||
error_log('push rdv done (secretaire) failed: '.$e->getMessage());
|
||
}
|
||
|
||
$d_c = [
|
||
'hello_line'=>sprintf($translations['mail_valid_bonjour_nom'] ?? 'Bonjour %s',$cliFull),
|
||
'intro'=>$translations['email_rdv_termine_message_client'] ?? 'Votre rendez-vous est marqué comme terminé. Merci pour votre confiance.',
|
||
'who_label'=>$translations['praticien_label'] ?? 'Praticien',
|
||
'who_value'=>$pratFull,
|
||
'date_label'=>$translations['date_label'] ?? 'Date',
|
||
'when'=>$when_a,
|
||
'statut_label'=>$translations['statut_label'] ?? 'Statut',
|
||
'statut_value'=>$statutLibelle,
|
||
'cta_url' => 'https://uslugi.fr/public/client/mes_rendez_vous.php',
|
||
'cta_label'=>$translations['avis_suite_consultation'] ?? 'Voir mes rendez-vous',
|
||
'footer'=>$translations['merci_simple'] ?? 'Merci.',
|
||
];
|
||
[$okC,$errC] = smtp_send(
|
||
$rdv['email_client'],
|
||
$translations['email_rdv_termine_client_sujet'] ?? 'Merci de votre visite',
|
||
email_text_status($d_c,$translations),
|
||
email_html_status($d_c,$translations)
|
||
);
|
||
if (!$okC) $warnings[]='mail_client';
|
||
|
||
$d_p = [
|
||
'hello_line'=>($translations['email_bonjour_dr'] ?? 'Bonjour').' '.$pratFull,
|
||
'intro'=>$translations['email_rdv_termine_message_praticien'] ?? 'Vous avez marqué ce rendez-vous comme terminé.',
|
||
'who_label'=>$translations['client_label'] ?? 'Client',
|
||
'who_value'=>$cliFull,
|
||
'date_label'=>$translations['date_label'] ?? 'Date',
|
||
'when'=>$when_a,
|
||
'statut_label'=>$translations['statut_label'] ?? 'Statut',
|
||
'statut_value'=>$statutLibelle,
|
||
'cta_url'=>'https://uslugi.fr/public/praticien/agenda.php',
|
||
'cta_label'=>$translations['consultez_espace_pour_details'] ?? 'Voir les détails',
|
||
'footer'=>$translations['merci_simple'] ?? 'Merci.',
|
||
];
|
||
[$okP,$errP] = smtp_send(
|
||
$rdv['email_praticien'],
|
||
$translations['email_rdv_termine_praticien_sujet'] ?? 'Rendez-vous marqué terminé',
|
||
email_text_status($d_p,$translations),
|
||
email_html_status($d_p,$translations)
|
||
);
|
||
if (!$okP) $warnings[]='mail_praticien';
|
||
}
|
||
|
||
$msg = $translations['statut_mis_a_jour'] ?? 'Statut mis à jour.';
|
||
if ($warnings) $msg .= ' (mail: '.implode(',',$warnings).')';
|
||
out(true, $msg, ['status'=>$statut]);
|
||
|
||
} catch (Throwable $e) {
|
||
error_log('changer_statut (cabinet) fatal: '.$e->getMessage());
|
||
out(false, $translations['erreur_mise_a_jour'] ?? 'Erreur de mise à jour.');
|
||
}
|