diff --git a/php/chronotrack_api/fx_chronotrack_admin.php b/php/chronotrack_api/fx_chronotrack_admin.php index 221ad54..dcfbebd 100644 --- a/php/chronotrack_api/fx_chronotrack_admin.php +++ b/php/chronotrack_api/fx_chronotrack_admin.php @@ -223,9 +223,9 @@ function fxChronotrackApiAdminRenderPushPanel($intEveId, $blnClosed = false) { if ($blnClosed) { echo '
Événement fermé — push manuel autorisé pour tests ou rattrapage.
'; } - echo '

Lit resultats_participants (comme le rapport chrono). '; - echo 'External ID = par_id_original. Dossard obligatoire ; sexe vide ou « n » (non défini) → NOT SPECIFIED côté CT ; '; - echo 'doublon de dossard MS1 = aucun des inscrits concernés n\'est transféré.

'; + echo '

Push manuel — pas de sync automatique. Lit resultats_participants '; + echo '(comme le rapport chrono). External ID = par_id_original. '; + echo 'Après une correction MS1 : Actualiser puis Pousser.

'; echo '
' . 'Cliquez sur Actualiser pour calculer le diff MS1 ↔ ChronoTrack.
'; echo '
'; @@ -754,46 +754,60 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed } var intBlocked = res.blocked_count || 0; - var intTransfer = res.transferable || 0; + var intEligible = res.transferable || 0; var intMs1 = res.ms1_total || 0; - var intAlreadyCt = res.already_in_ct || 0; + var intWillUpdate = res.will_update || res.already_in_ct || 0; var intWillCreate = res.will_create || res.to_create || 0; var intCtTotal = res.ct_total || 0; + var intOrphans = res.ct_orphans || 0; - var html = '

Avant le prochain push — lecture MS1 + ChronoTrack Live :

'; - html += '
'; - html += '
' + intMs1 + 'inscrits MS1
'; - html += '
' + intTransfer + 'seront envoyés
'; - html += '
'; - html += '' + intBlocked + 'exclus
'; - html += '
' + intAlreadyCt + 'déjà sur CT Live
'; - html += '
' + intCtTotal + 'entries CT (total)
'; - html += '
'; + var html = '

État actuel (MS1 + ChronoTrack Live) :

'; - if (intTransfer > 0) { - html += '

Parmi les ' + intTransfer + ' envoyés : '; - html += '' + intWillCreate + ' création(s) · '; - html += '' + intAlreadyCt + ' mise(s) à jour.

'; + html += '
'; + html += '

MS1' + intMs1 + ' inscrits : '; + html += '' + intEligible + ' éligibles'; + if (intBlocked > 0) { + html += ' · ' + intBlocked + ' exclus'; } + html += ' (équipe, dossard, sexe, etc.)

'; + html += '

ChronoTrack' + intCtTotal + ' entries'; + if (intEligible > 0 || intOrphans > 0) { + html += ' ('; + if (intWillUpdate > 0 || intWillCreate > 0) { + html += intWillUpdate + ' déjà liées à un éligible MS1'; + if (intWillCreate > 0) { + html += ', ' + intWillCreate + ' éligible(s) pas encore sur CT'; + } + } + if (intOrphans > 0) { + html += (intWillUpdate > 0 || intWillCreate > 0 ? ', ' : '') + intOrphans + ' sans correspondant MS1'; + } + html += ')'; + } + html += '

'; - html += '

Un deuxième push renvoie les mêmes transférables : ChronoTrack fait une mise à jour (upsert par external_id), pas de doublon.

'; + html += '

Au prochain push — renvoie les ' + intEligible + + ' éligibles avec les données MS1 actuelles '; + html += '(' + intWillCreate + ' création · ' + intWillUpdate + ' mise à jour). '; + html += 'Ce n\'est pas une sync auto : corrigez dans MS1 → ActualiserPousser.

'; - if (intCtTotal > 0 && (res.ct_orphans || 0) > 0) { - html += '

' + res.ct_orphans + ' entry(s) CT sans correspondant MS1 transférable.

'; + if (intOrphans > 0) { + html += '

' + intOrphans + + ' entry(s) CT sans correspondant MS1 éligible (tests, anciennes données).

'; } if (intBlocked > 0) { html += '

'; - html += 'Les ' + intBlocked + ' exclus ne seront pas envoyés tant que non corrigés dans MS1. '; + html += 'Les ' + intBlocked + ' exclus ne partent pas tant que non corrigés dans MS1. '; if (res.blocked_team > 0) { - html += '' + res.blocked_team + ' sont des inscriptions équipe (section dédiée). '; + html += '' + res.blocked_team + ' sont des inscriptions équipe. '; } - html += 'Détail dans la zone ci-dessous.

'; + html += 'Détail ci-dessous.

'; } $sum.html(html); renderBlockedPanel(res); - $btn.prop('disabled', intTransfer <= 0); + $btn.prop('disabled', intEligible <= 0); } function renderPushResult(res, strTitle) { @@ -904,7 +918,11 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed }); $('#ct_api_btn_push').on('click', function () { - if (!window.confirm('Envoyer les participants éligibles vers ChronoTrack ?\n\nMS1 ne sera pas modifié.')) { + var intEligible = (objPushPreview && objPushPreview.transferable) ? objPushPreview.transferable : 0; + var strConfirm = 'Push manuel — renvoyer les ' + intEligible + ' participants éligibles vers ChronoTrack ?\n\n'; + strConfirm += 'Données MS1 actuelles (upsert par external_id). Pas de sync automatique.\n'; + strConfirm += 'MS1 ne sera pas modifié.'; + if (!window.confirm(strConfirm)) { return; } var $btn = $(this); diff --git a/php/chronotrack_api/fx_chronotrack_sync.php b/php/chronotrack_api/fx_chronotrack_sync.php index bd51c44..b755c99 100644 --- a/php/chronotrack_api/fx_chronotrack_sync.php +++ b/php/chronotrack_api/fx_chronotrack_sync.php @@ -485,9 +485,25 @@ function fxChronotrackApiSyncBuildEntryPayload(array $arrRow, array $arrRaceMap, $arrEntry['country_code'] = $strCountry; } + $arrEntry['entry_status'] = fxChronotrackApiSyncMapEntryStatus( + $arrRow['par_statut_course'] ?? 'CONF', + intval($arrRow['is_cancelled'] ?? 0) === 1 + ); + return $arrEntry; } +function fxChronotrackApiSyncEnrichPayloadWithCtEntry(array $arrPayload, $arrCtEntity) { + if (!is_array($arrCtEntity)) { + return $arrPayload; + } + $strEntryId = fxChronotrackApiEntityId($arrCtEntity); + if ($strEntryId !== '') { + $arrPayload['entry_id'] = $strEntryId; + } + return $arrPayload; +} + function fxChronotrackApiSyncPostEntries($intCtEventId, array $tabEntries) { $intCtEventId = intval($intCtEventId); if ($intCtEventId <= 0 || count($tabEntries) === 0) { @@ -770,10 +786,20 @@ function fxChronotrackApiSyncPushEvent($intEveId) { ); } + $tabCtEntries = array(); + $arrCtFetch = fxChronotrackApiSyncFetchCtEntriesIndexed($intCtEventId); + if ($arrCtFetch['state'] === 'ok') { + $tabCtEntries = $arrCtFetch['entries']; + } + $tabBatch = array(); $tabMeta = array(); foreach ($arrClass['transferable'] as $arrItem) { - $tabBatch[] = $arrItem['payload']; + $arrPayload = fxChronotrackApiSyncEnrichPayloadWithCtEntry( + $arrItem['payload'], + $tabCtEntries[$arrItem['payload']['external_id']] ?? null + ); + $tabBatch[] = $arrPayload; $tabMeta[] = array( 'par_id' => intval($arrItem['row']['par_id'] ?? 0), 'external_id' => $arrItem['payload']['external_id'], @@ -860,7 +886,7 @@ function fxChronotrackApiSyncPushEvent($intEveId) { $intBlockedCount = $arrClass['blocked_count']; $strMessage = ($intErr === 0) - ? ($intOk . ' participant(s) envoyé(s) vers ChronoTrack.') + ? ($intOk . ' éligible(s) (re)envoyé(s) vers ChronoTrack (données MS1 actuelles, upsert par external_id).') : ($intOk . ' OK, ' . $intErr . ' erreur(s) API.'); return array(