650 lines
27 KiB
PHP
650 lines
27 KiB
PHP
<?php
|
||
require_once __DIR__ . '/../includes/init_praticien.php';
|
||
require_once __DIR__ . '/../../push/push_endpoint.php';
|
||
|
||
|
||
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);
|
||
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 error: '.$e->getMessage().($m->ErrorInfo ? ' | '.$m->ErrorInfo : ''));
|
||
// retry sans ICS si c'est la PJ
|
||
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()];
|
||
}
|
||
}
|
||
|
||
// =================== Contrôles d’entrée ===================
|
||
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') out(false, 'Méthode invalide.');
|
||
|
||
$praticien_id = $_SESSION['reference_id'] ?? null;
|
||
$id = (int)($_POST['id'] ?? 0);
|
||
$statut = (string)($_POST['statut'] ?? '');
|
||
|
||
$whitelist = ['prévu','confirmé','annulé','terminé'];
|
||
if (!$praticien_id || $id <= 0 || !in_array($statut, $whitelist, true)) {
|
||
out(false, $translations['erreur_mise_a_jour'] ?? 'Erreur de mise à jour.');
|
||
}
|
||
|
||
// =================== Update DB sécurisé ===================
|
||
try {
|
||
|
||
// =========================
|
||
// SNAPSHOT AVANT UPDATE_STATUS (pour log)
|
||
// =========================
|
||
$beforeStmt = $pdo->prepare("
|
||
SELECT statut, date_rdv, duree_minutes, lieu
|
||
FROM rendez_vous
|
||
WHERE id = :id AND praticien_id = :pid
|
||
LIMIT 1
|
||
");
|
||
$beforeStmt->execute([':id'=>$id, ':pid'=>$praticien_id]);
|
||
$before = $beforeStmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
if (!$before) {
|
||
out(false, $translations['erreur_mise_a_jour'] ?? 'Erreur de mise à jour.');
|
||
}
|
||
|
||
// Mise à jour statut
|
||
|
||
$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
|
||
// =========================
|
||
$beforeJson = json_encode([
|
||
'statut' => $before['statut'],
|
||
'date_rdv' => $before['date_rdv'],
|
||
'duree_minutes' => (int)$before['duree_minutes'],
|
||
'lieu' => $before['lieu'],
|
||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||
|
||
$afterJson = json_encode([
|
||
'statut' => $statut,
|
||
'date_rdv' => $before['date_rdv'],
|
||
], 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
|
||
(:rid, 'update_status', :uid, 'praticien', :b, :a, NOW())
|
||
");
|
||
$logStmt->execute([
|
||
':rid' => (int)$id,
|
||
':uid' => (int)$praticien_id,
|
||
':b' => $beforeJson,
|
||
':a' => $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);
|
||
|
||
|
||
// AJOUT DE GARDE FOUS POUR CLIENT RENDEZ_VOUS
|
||
if (empty($rdv['client_id'])) {
|
||
error_log('changer_statut: client_id manquant pour RDV id='.$id);
|
||
}
|
||
// FIN DE GARDE FOUS
|
||
|
||
|
||
if (!$rdv) out(false, $translations['erreur_mise_a_jour'] ?? 'Erreur de mise à jour.');
|
||
|
||
// ✅ compte_id du client (pour compteur badge rdv)
|
||
$clientCompteId = 0;
|
||
if (!empty($rdv['client_id'])) {
|
||
$st = $pdo->prepare("SELECT id FROM compte WHERE role='client' AND reference_id=? LIMIT 1");
|
||
$st->execute([(int)$rdv['client_id']]);
|
||
$clientCompteId = (int)($st->fetchColumn() ?: 0);
|
||
}
|
||
|
||
// 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 = $rdv['prenom_client'].' '.$rdv['nom_client'];
|
||
$pratFull = $rdv['prenom_praticien'].' '.$rdv['nom_praticien'];
|
||
|
||
// ====== FORMAT (Онлайн/Очно + adresse si dispo) ======
|
||
$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 (Онлайн OU adresse, sinon Очно)
|
||
if ($lieu === 'online') {
|
||
$icsLocation = $onlineLabel; // "Онлайн"
|
||
} elseif ($lieu === 'offline') {
|
||
$icsLocation = $addr !== '' ? $addr : $cabinetLabel; // adresse si dispo, sinon "Очно"
|
||
} else {
|
||
$icsLocation = ''; // fallback neutre
|
||
}
|
||
// ======================================================
|
||
|
||
$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;
|
||
|
||
// ----- envois mails selon statut -----
|
||
$warnings=[];
|
||
|
||
// =====NOUVEAU BLOCK POUR CONTROLER LEZ RENDEZ-VOUS==============
|
||
//***************************************************************** */
|
||
// ===== HOOK SUIVI CLIENT — création/MAJ dossier + entrée auto =====
|
||
|
||
if ($statut === 'confirmé') {
|
||
// =========================
|
||
// PUSH RDV — confirmé (par praticien)
|
||
// =========================
|
||
try {
|
||
// ✅ Badge RDV : +1 pour le client (confirm)
|
||
if ($clientCompteId > 0) {
|
||
$pdo->prepare("
|
||
INSERT INTO push_badge_counters (compte_id, msg_unread, rdv_unread)
|
||
VALUES (:cid, 0, 1)
|
||
ON DUPLICATE KEY UPDATE
|
||
rdv_unread = LEAST(99, rdv_unread + 1),
|
||
updated_at = NOW()
|
||
")->execute([':cid' => $clientCompteId]);
|
||
}
|
||
|
||
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' => 'praticien',
|
||
'praticien_id' => (int)$praticien_id,
|
||
'date_rdv' => $rdv['date_rdv'],
|
||
]
|
||
);
|
||
|
||
} catch (Throwable $e) {
|
||
error_log('push rdv confirm failed: '.$e->getMessage());
|
||
}
|
||
|
||
try {
|
||
$pdo->beginTransaction();
|
||
|
||
// 1) 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();
|
||
}
|
||
|
||
// 2) Créer entrée de suivi automatique (une fois par RDV confirmé)
|
||
// On évite les doublons si reconfirmation
|
||
$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é) failed: '.$ex->getMessage());
|
||
// on n’interrompt pas le flux principal (mails etc.)
|
||
}
|
||
// ================================================
|
||
// ===== FIN HOOK INSERTION CLIENT POUR SUIVIE=====
|
||
//=================================================
|
||
|
||
// Client
|
||
$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.',
|
||
];
|
||
// Ajout format dans le corps du mail (intro)
|
||
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';
|
||
|
||
// Praticien
|
||
$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.',
|
||
];
|
||
// Ajout format pour le praticien
|
||
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 praticien)
|
||
// =========================
|
||
try {
|
||
// ✅ Badge RDV : +1 pour le client (cancel)
|
||
if ($clientCompteId > 0) {
|
||
$pdo->prepare("
|
||
INSERT INTO push_badge_counters (compte_id, msg_unread, rdv_unread)
|
||
VALUES (:cid, 0, 1)
|
||
ON DUPLICATE KEY UPDATE
|
||
rdv_unread = LEAST(99, rdv_unread + 1),
|
||
updated_at = NOW()
|
||
")->execute([':cid' => $clientCompteId]);
|
||
}
|
||
|
||
push_rdv_to_client(
|
||
$pdo,
|
||
(int)$rdv['client_id'],
|
||
$translations['push_rdv_cancel_praticien_title'] ?? 'Запись отменена',
|
||
$translations['push_rdv_cancel_praticien_body'] ?? 'Специалист отменил запись.',
|
||
'/public/client/mes_rendez_vous.php',
|
||
[
|
||
'rdv_id' => (int)$rdv['id'],
|
||
'action' => 'cancel',
|
||
'from_role' => 'praticien',
|
||
'praticien_id' => (int)$praticien_id,
|
||
'date_rdv' => $rdv['date_rdv'],
|
||
]
|
||
);
|
||
|
||
} catch (Throwable $e) {
|
||
error_log('push rdv cancel (praticien) 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 CANCEL — on met aussi la LOCATION correcte pour cohérence
|
||
$ics = !empty($rdv['uuid_ical'])
|
||
? ics_build($rdv['uuid_ical'],$start,$end,$tzStr,$summary,'',$icsLocation, $attBoth,'CANCEL','CANCELLED')
|
||
: null;
|
||
|
||
// Client
|
||
$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';
|
||
|
||
// Praticien
|
||
$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é
|
||
// =========================
|
||
try {
|
||
// ✅ Badge RDV : +1 pour le client (done)
|
||
if ($clientCompteId > 0) {
|
||
$pdo->prepare("
|
||
INSERT INTO push_badge_counters (compte_id, msg_unread, rdv_unread)
|
||
VALUES (:cid, 0, 1)
|
||
ON DUPLICATE KEY UPDATE
|
||
rdv_unread = LEAST(99, rdv_unread + 1),
|
||
updated_at = NOW()
|
||
")->execute([':cid' => $clientCompteId]);
|
||
}
|
||
|
||
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' => 'praticien',
|
||
'praticien_id' => (int)$praticien_id,
|
||
]
|
||
);
|
||
|
||
} catch (Throwable $e) {
|
||
error_log('push rdv done failed: '.$e->getMessage());
|
||
}
|
||
|
||
// Client
|
||
$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/avis.php?praticien_id='.(int)$praticien_id,
|
||
'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';
|
||
|
||
// Praticien
|
||
$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 fatal: '.$e->getMessage());
|
||
out(false, $translations['erreur_mise_a_jour'] ?? 'Erreur de mise à jour.');
|
||
}
|