335 lines
15 KiB
PHP
335 lines
15 KiB
PHP
<?php
|
|
/**
|
|
* Actions AJAX reservees a la fiche de gestion des inscriptions (v2).
|
|
* N'altere pas ajax_promoteur.php ni enregistrer.php (legacy promoteur).
|
|
*/
|
|
session_start();
|
|
|
|
// MSIN-4401 — Traductions toujours au nom de compte.php (jamais ajax_inscr_gestion.php).
|
|
// Evite les doublons info_prg=ajax_*.php et les libelles manquants selon la page appelante
|
|
// (hub, compte, /mobile). Meme pattern que ajax_promoteur_hub / ajax_bib_range.
|
|
define('MS1_INSCR_GESTION_AJAX', true);
|
|
$_SERVER['PHP_SELF'] = '/compte.php';
|
|
$_SERVER['SCRIPT_NAME'] = '/compte.php';
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
// Filet de securite : transforme tout fatal (include manquant, fonction
|
|
// indefinie, parse error...) en reponse JSON exploitable au lieu d'un 500 muet.
|
|
$GLOBALS['__inscr_gestion_done'] = false;
|
|
register_shutdown_function(function () {
|
|
if (!empty($GLOBALS['__inscr_gestion_done'])) {
|
|
return;
|
|
}
|
|
$err = error_get_last();
|
|
$strDetail = ($err && in_array($err['type'], array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR), true))
|
|
? ($err['message'] . ' @ ' . $err['file'] . ':' . $err['line'])
|
|
: 'fatal inconnu';
|
|
error_log('[ajax_inscr_gestion] ' . $strDetail);
|
|
if (!headers_sent()) {
|
|
http_response_code(200);
|
|
}
|
|
echo json_encode(array('state' => 'error', 'message' => 'fatal', 'detail' => $strDetail));
|
|
});
|
|
|
|
require_once 'php/inc_fonctions.php';
|
|
require_once 'php/inc_fx_promoteur.php';
|
|
require_once 'php/inc_fx_inscriptions_gestion.php';
|
|
// Necessaire pour fxEveAccesResolveEveIdFromParId() appelee dans le case
|
|
// 'par_statut_course' : ce fichier n'est sinon charge que paresseusement
|
|
// (fxInscrGestionLoadEveAcces) APRES ce premier appel -> fatal undefined function.
|
|
require_once 'php/inc_fx_eve_acces.php';
|
|
|
|
$intComId = intval($_SESSION['com_id'] ?? 0);
|
|
$strAction = trim((string)($_REQUEST['a'] ?? ''));
|
|
$strLangue = trim((string)($_REQUEST['lang'] ?? 'fr'));
|
|
if ($strLangue === '' || !in_array($strLangue, array('fr', 'en'), true)) {
|
|
$strLangue = 'fr';
|
|
}
|
|
|
|
$vPage = 'compte.php';
|
|
$vtexte_page = obtenirTextepage($vPage, $strLangue, 2);
|
|
fxInscrGestionInitTextes($strLangue);
|
|
|
|
if ($intComId <= 0) {
|
|
$GLOBALS['__inscr_gestion_done'] = true;
|
|
// MSIN-4401 — code session : le client recharge pour afficher le login.
|
|
echo json_encode(array('state' => 'error', 'code' => 'session', 'message' => 'session'));
|
|
exit;
|
|
}
|
|
|
|
$tabRetour = array('state' => 'error');
|
|
|
|
try {
|
|
switch ($strAction) {
|
|
case 'par_statut_course':
|
|
$intParId = intval($_REQUEST['par_id'] ?? 0);
|
|
$strStatut = trim((string)($_REQUEST['par_statut_course'] ?? ''));
|
|
$intEveId = fxEveAccesResolveEveIdFromParId($intParId);
|
|
|
|
if ($intEveId <= 0 || !fxInscrGestionCanDo($intComId, $intEveId, 'inscriptions_gestion.statut_edit')) {
|
|
$tabRetour = array('state' => 'error', 'message' => 'unauthorized');
|
|
break;
|
|
}
|
|
|
|
$tabRetour = fxSetParStatutCourse($intParId, $strStatut, $strLangue);
|
|
break;
|
|
|
|
case 'refund_form':
|
|
// Charge le formulaire de remboursement mutualise pour la fiche de gestion (panneau repliable).
|
|
$strNoPanier = trim((string)($_REQUEST['no_panier'] ?? ''));
|
|
if ($strNoPanier === '') {
|
|
$tabRetour = array('state' => 'error', 'message' => 'no_panier_manquant');
|
|
break;
|
|
}
|
|
|
|
// Meme source que le Super Admin : la commande vient de inscriptions_panier_acheteurs.
|
|
$sqlCmd = "SELECT *, IF(a.com_id <> 0, (SELECT c.com_login FROM inscriptions_comptes c WHERE a.com_id = c.com_id), '') AS usager"
|
|
. " FROM inscriptions_panier_acheteurs a, inscriptions_panier_statuts s"
|
|
. " WHERE s.sta_id = a.sta_id AND a.no_panier = '" . $objDatabase->fxEscape($strNoPanier) . "'";
|
|
$recCommandes = $objDatabase->fxGetRow($sqlCmd);
|
|
if (!$recCommandes) {
|
|
$tabRetour = array('state' => 'error', 'message' => 'commande_introuvable');
|
|
break;
|
|
}
|
|
|
|
// L'eve_id de contexte = evenement de la fiche (participant), pas seulement celui du checkout.
|
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/superadm/php/inc_fx_paypal.php';
|
|
|
|
$intEveContext = intval($_REQUEST['eve_id'] ?? 0);
|
|
if ($intEveContext <= 0) {
|
|
$intEveContext = intval($recCommandes['eve_id']);
|
|
}
|
|
if ($intEveContext <= 0 || !fxInscrGestionCanDo($intComId, $intEveContext, 'inscriptions_gestion.refund')) {
|
|
$tabRetour = array('state' => 'error', 'message' => 'unauthorized');
|
|
break;
|
|
}
|
|
if (!fxRefundPanierLinkedToEvent($recCommandes['no_panier'], $intEveContext, intval($recCommandes['eve_id']))) {
|
|
$tabRetour = array('state' => 'error', 'message' => 'panier_evenement_incompatible');
|
|
break;
|
|
}
|
|
|
|
$strRefundToken = fxRefundEnsureToken();
|
|
$arrRefundCtx = fxRefundComputeContext($recCommandes, $intEveContext);
|
|
|
|
// Auteur = promoteur connecte (alimente l'encart "Ajoute par" du formulaire).
|
|
$strAuteur = trim((string)($_SESSION['com_info']['com_prenom'] ?? '') . ' '
|
|
. (string)($_SESSION['com_info']['com_nom'] ?? ''));
|
|
|
|
// Markup seul (sans <script>, dernier param false) car injecte via innerHTML :
|
|
// le comportement (submit AJAX) est gere par le handler delegue dans inc_footer_scripts.php.
|
|
// NB : le resume de la transaction n'est plus inclus ici (MSIN) ; il est desormais
|
|
// accessible via le bouton "Afficher la transaction" de la barre Gestion (action transaction_resume).
|
|
$strDocAside = '';
|
|
if (function_exists('fxDocRenderRefundPromoteurAside')) {
|
|
global $vPage;
|
|
$memPage = $vPage ?? null;
|
|
$vPage = 'compte.php';
|
|
$strDocAside = fxDocRenderRefundPromoteurAside();
|
|
$vPage = $memPage;
|
|
}
|
|
$strForm = fxRenderRefundForm($recCommandes, $strRefundToken, $arrRefundCtx, $vDomaine, $strAuteur, null, false, $strDocAside);
|
|
|
|
$tabRetour = array('state' => 'ok', 'html' => $strForm);
|
|
break;
|
|
|
|
case 'bib_ocr':
|
|
// MSIN-4401 — Lecture numero dossard via Google Cloud Vision.
|
|
// Image : fichier multipart (prefere) ou base64 POST (legacy).
|
|
if (!fxInscrGestionBibOcrCloudEnabled()) {
|
|
$tabRetour = array('state' => 'error', 'message' => 'ocr_not_configured');
|
|
break;
|
|
}
|
|
$strImage = '';
|
|
if (!empty($_FILES['image']['tmp_name']) && is_uploaded_file($_FILES['image']['tmp_name'])) {
|
|
if ((int)$_FILES['image']['error'] === UPLOAD_ERR_OK && (int)$_FILES['image']['size'] > 0
|
|
&& (int)$_FILES['image']['size'] <= 4 * 1024 * 1024) {
|
|
$strBin = (string)file_get_contents($_FILES['image']['tmp_name']);
|
|
$strImage = base64_encode($strBin);
|
|
} else {
|
|
$tabRetour = array('state' => 'error', 'message' => 'image_invalid');
|
|
break;
|
|
}
|
|
} else {
|
|
$strImage = (string)($_POST['image'] ?? '');
|
|
}
|
|
if ($strImage === '') {
|
|
$tabRetour = array('state' => 'error', 'message' => 'image_invalid');
|
|
break;
|
|
}
|
|
$arrOcr = fxInscrGestionVisionReadBibNumber($strImage);
|
|
if (!empty($arrOcr['ok']) && !empty($arrOcr['number'])) {
|
|
$tabRetour = array(
|
|
'state' => 'ok',
|
|
'number' => (string)$arrOcr['number'],
|
|
);
|
|
break;
|
|
}
|
|
$strSeen = '';
|
|
if (!empty($arrOcr['text'])) {
|
|
$strSeen = preg_replace('/\D+/', '', (string)$arrOcr['text']);
|
|
}
|
|
$tabRetour = array(
|
|
'state' => 'error',
|
|
'message' => (string)($arrOcr['message'] ?? 'no_number'),
|
|
'seen' => $strSeen,
|
|
'detail' => (string)($arrOcr['detail'] ?? ''),
|
|
);
|
|
break;
|
|
|
|
case 'bib_duplicate_check':
|
|
// Controle "dossard en double" dans l'evenement (avant enregistrement legacy).
|
|
// Ne modifie rien : renvoie qui porte deja ce dossard + la politique a appliquer.
|
|
$intParId = intval($_REQUEST['par_id'] ?? 0);
|
|
$strNoBib = trim((string)($_REQUEST['no_bib'] ?? ''));
|
|
$blnTeam = (trim((string)($_REQUEST['type'] ?? '')) === 'no_equipe');
|
|
|
|
$intEveId = fxEveAccesResolveEveIdFromParId($intParId);
|
|
if ($intEveId <= 0 || !fxInscrGestionCanDo($intComId, $intEveId, 'inscriptions_gestion.bib_edit')) {
|
|
$tabRetour = array('state' => 'error', 'message' => 'unauthorized');
|
|
break;
|
|
}
|
|
|
|
$strPolicy = fxInscrGestionBibDuplicatePolicy($intEveId);
|
|
|
|
if ($strNoBib === '') {
|
|
$tabRetour = array('state' => 'ok', 'duplicate' => false, 'policy' => $strPolicy, 'who' => array());
|
|
break;
|
|
}
|
|
|
|
// Commande courante a exclure du controle (un participant ne se "double" pas lui-meme).
|
|
$intPecOrig = intval($objDatabase->fxGetVar(
|
|
'SELECT e.pec_id_original FROM resultats_epreuves_commandees e, resultats_participants p'
|
|
. ' WHERE p.par_id = ' . $intParId . ' AND p.pec_id = e.pec_id_original LIMIT 1'
|
|
));
|
|
|
|
$intCurrentEprId = (int)$objDatabase->fxGetVar(
|
|
'SELECT epr_id FROM resultats_participants WHERE par_id = ' . $intParId . ' LIMIT 1'
|
|
);
|
|
|
|
$arrDup = fxInscrGestionFindBibDuplicates($intEveId, $strNoBib, $intPecOrig, $blnTeam, $intCurrentEprId, $intParId);
|
|
$arrWho = array();
|
|
foreach ($arrDup as $rowDup) {
|
|
$arrWho[] = array(
|
|
'name' => fxInscrGestionEsc(fxUnescape($rowDup['par_nom']) . ', ' . fxUnescape($rowDup['par_prenom'])),
|
|
'epreuve' => fxInscrGestionEsc(fxInscrGestionEpreuveLabel($rowDup['epr_id'], $strLangue)),
|
|
);
|
|
}
|
|
|
|
$tabRetour = array(
|
|
'state' => 'ok',
|
|
'duplicate' => count($arrWho) > 0,
|
|
'policy' => $strPolicy,
|
|
'who' => $arrWho,
|
|
);
|
|
break;
|
|
|
|
case 'transaction_resume':
|
|
// Resume de la transaction (meme rendu que facture.php cote promoteur) + detail des
|
|
// remboursements PayPal, affichable a tout moment via le panneau repliable de la barre Gestion.
|
|
$strNoPanier = trim((string)($_REQUEST['no_panier'] ?? ''));
|
|
if ($strNoPanier === '') {
|
|
$tabRetour = array('state' => 'error', 'message' => 'no_panier_manquant');
|
|
break;
|
|
}
|
|
|
|
// Commande complete (meme source que refund_form / Super Admin) : alimente fxRefundComputeContext.
|
|
$sqlCmd = "SELECT *, IF(a.com_id <> 0, (SELECT c.com_login FROM inscriptions_comptes c WHERE a.com_id = c.com_id), '') AS usager"
|
|
. " FROM inscriptions_panier_acheteurs a, inscriptions_panier_statuts s"
|
|
. " WHERE s.sta_id = a.sta_id AND a.no_panier = '" . $objDatabase->fxEscape($strNoPanier) . "'";
|
|
$recCommandes = $objDatabase->fxGetRow($sqlCmd);
|
|
if (!$recCommandes) {
|
|
$tabRetour = array('state' => 'error', 'message' => 'commande_introuvable');
|
|
break;
|
|
}
|
|
|
|
// L'eve_id fait foi cote serveur (jamais le client). Droit granulaire transaction.
|
|
$intEveId = intval($recCommandes['eve_id']);
|
|
if ($intEveId <= 0 || !fxInscrGestionCanDo($intComId, $intEveId, 'inscriptions_gestion.transaction')) {
|
|
$tabRetour = array('state' => 'error', 'message' => 'unauthorized');
|
|
break;
|
|
}
|
|
|
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/inc_fx_panier.php';
|
|
$strResume = fxShowPanier($strNoPanier, 0, 'web', 1, $strLangue);
|
|
|
|
// Sommaire des remboursements PayPal (vue compacte, pas la grande table Super Admin).
|
|
// N'apparait que s'il existe une capture PayPal exploitable ; sinon rien (les cas
|
|
// gratuits/non-PayPal sont deja couverts par la facture ci-dessus).
|
|
$strRefunds = '';
|
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/superadm/php/inc_fx_paypal.php';
|
|
$arrRefundCtx = fxRefundComputeContext($recCommandes);
|
|
if (!empty($arrRefundCtx['ok'])) {
|
|
// renderPayPalRefundSummaryHtml() renvoie '' s'il n'y a aucun remboursement :
|
|
// dans ce cas on n'ajoute meme pas le separateur/encadre (rien a montrer).
|
|
$strSummary = renderPayPalRefundSummaryHtml($arrRefundCtx['paypaldetails']);
|
|
if (trim($strSummary) !== '') {
|
|
$strRefunds = '<div style="margin-top:14px;border-top:1px solid #cfe0ff;padding-top:12px;">'
|
|
. $strSummary
|
|
. '</div>';
|
|
}
|
|
}
|
|
|
|
// Conteneur encadre (sans bascule interne : le bouton de la barre Gestion fait office de toggle).
|
|
$strHtml =
|
|
'<div class="ms1-transaction-resume"'
|
|
. ' style="border:1px solid #2d6cdf;border-radius:10px;background:#f4f7fc;overflow:hidden;box-shadow:0 1px 4px rgba(0,0,0,.12);">'
|
|
. '<div style="padding:14px;">' . $strResume . $strRefunds . '</div>'
|
|
. '</div>';
|
|
|
|
$tabRetour = array('state' => 'ok', 'html' => $strHtml);
|
|
break;
|
|
|
|
case 'fiche_audit_history':
|
|
// MSIN-4464 — derniers changements d'un champ whitelist (popup « ! » fiche).
|
|
require_once 'php/inc_fx_fiche_audit.php';
|
|
$intParId = intval($_REQUEST['par_id'] ?? 0);
|
|
$strField = preg_replace('/[^a-z0-9_]/i', '', (string)($_REQUEST['field'] ?? ''));
|
|
$intEveId = fxEveAccesResolveEveIdFromParId($intParId);
|
|
|
|
if ($intParId <= 0 || $strField === '' || $intEveId <= 0
|
|
|| !fxInscrGestionCanDo($intComId, $intEveId, 'inscriptions_gestion.audit_view')) {
|
|
$tabRetour = array('state' => 'error', 'message' => 'unauthorized');
|
|
break;
|
|
}
|
|
|
|
$arrItems = fxFicheAuditGetRecentForField($intParId, $strField);
|
|
$arrOut = array();
|
|
if (is_array($arrItems)) {
|
|
foreach ($arrItems as $row) {
|
|
if (!is_array($row)) {
|
|
continue;
|
|
}
|
|
$arrOut[] = array(
|
|
'when' => (string)($row['created_at'] ?? ''),
|
|
'who' => (string)($row['changed_by_label'] ?? ''),
|
|
'old' => (string)($row['old_value'] ?? ''),
|
|
'new' => (string)($row['new_value'] ?? ''),
|
|
'source' => (string)($row['source'] ?? ''),
|
|
);
|
|
}
|
|
}
|
|
|
|
$tabRetour = array(
|
|
'state' => 'success',
|
|
'field' => $strField,
|
|
'items' => $arrOut,
|
|
);
|
|
break;
|
|
|
|
default:
|
|
$tabRetour = array('state' => 'error', 'message' => 'invalid_action');
|
|
break;
|
|
}
|
|
} catch (\Throwable $e) {
|
|
error_log('[ajax_inscr_gestion] ' . $e->getMessage() . ' @ ' . $e->getFile() . ':' . $e->getLine());
|
|
$tabRetour = array(
|
|
'state' => 'error',
|
|
'message' => 'exception',
|
|
'detail' => $e->getMessage() . ' @ ' . $e->getFile() . ':' . $e->getLine(),
|
|
);
|
|
}
|
|
|
|
$GLOBALS['__inscr_gestion_done'] = true;
|
|
echo json_encode($tabRetour);
|