diff --git a/frontend/src/components/MatchReportApiDialog.vue b/frontend/src/components/MatchReportApiDialog.vue index 0f6fc938..4a4b1115 100755 --- a/frontend/src/components/MatchReportApiDialog.vue +++ b/frontend/src/components/MatchReportApiDialog.vue @@ -2081,6 +2081,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr }); const finalPayload = JSON.parse(JSON.stringify(normalizedByNuScore)); + this.applyBraunschweigerMatchFormations(finalPayload); this.applyCurrentLineupStateToPayload(finalPayload); this.updatePlayerPositions(finalPayload); @@ -2296,6 +2297,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr // Aufstellungen erneut in genau diesen Rückgabedatensatz // übernommen werden. Andernfalls werden die Doppel im // Berichtsbogen als "nicht anwesend" dargestellt. + this.applyBraunschweigerMatchFormations(submitPayload); this.applyCurrentLineupStateToPayload(submitPayload); this.updatePlayerPositions(submitPayload); submitPayload.isCompleted = true; @@ -2325,10 +2327,16 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr throw new Error(validationMessage || result.error || `HTTP ${response.status}: ${response.statusText}`); } - console.log('✅ Spielbericht erfolgreich abgesendet:', result); + const doubleVerification = await this.verifySubmittedDoubleAssignments(uuid); + console.log('✅ Spielbericht erfolgreich abgesendet:', result, doubleVerification); this.submitSucceeded = true; this.clearLocalDraft(); - alert('✅ Spielbericht erfolgreich abgesendet!'); + if (!doubleVerification.verified) { + alert(`⚠️ Der Spielbericht wurde von nuScore angenommen, aber die Doppel konnten nicht vollständig zurückgelesen werden.\n\n${doubleVerification.message}\n\nBitte die Doppel direkt in nuScore prüfen. Der Dialog bleibt zur Kontrolle geöffnet.`); + return; + } + + alert('✅ Spielbericht erfolgreich abgesendet und Doppelzuordnung von nuScore bestätigt!'); // Dialog schließen this.$emit('close'); @@ -2405,6 +2413,71 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr } }, + async verifySubmittedDoubleAssignments(uuid) { + try { + const response = await fetch(`${backendBaseUrl}/api/nuscore/meetingdetails/${uuid}`); + if (!response.ok) { + return { + verified: false, + message: `NuScore konnte nach dem Absenden nicht erneut gelesen werden (HTTP ${response.status}).` + }; + } + + const report = await response.json(); + const formations = report?.meetingPlayMode?.matchFormations || []; + const matches = report?.matches || []; + const doubleFormations = formations.filter(formation => + /^D\d+$/i.test(String(formation?.homeLineupPosition || '')) && + /^D\d+$/i.test(String(formation?.guestLineupPosition || '')) + ); + + if (doubleFormations.length === 0) { + return { verified: true, message: 'Das Spielsystem enthält keine Doppelbegegnung.' }; + } + + const incomplete = doubleFormations.filter(formation => { + const match = matches.find(item => Number(item?.matchNr) === Number(formation.matchNr)); + const hasTwoNamesInMatch = (side) => { + const first = this.formatMatchPlayerName(side === 'home' ? match?.playerA1 : match?.playerB1); + const second = this.formatMatchPlayerName(side === 'home' ? match?.playerA2 : match?.playerB2); + return Boolean(first && second); + }; + const hasSavedDoublePair = (side) => { + const teamKey = side === 'home' ? 'teamLineupHomePlayers' : 'teamLineupGuestPlayers'; + const position = Number(String(side === 'home' + ? formation.homeLineupPosition + : formation.guestLineupPosition).slice(1)); + return (report?.[teamKey] || []).filter(player => + Number(player?.nuLigaPersonId) > 0 && + (Number(player?.positionDouble) === position || + (player?.positions || []).includes(`D${position}`)) + ).length === 2; + }; + + // Manche nuScore-Antworten enthalten bei Doppeln keine + // playerA/B-Felder. In diesem Fall bestätigen die beiden + // gespeicherten D-Positionen die Paarung genauso sicher. + return !(hasTwoNamesInMatch('home') || hasSavedDoublePair('home')) || + !(hasTwoNamesInMatch('guest') || hasSavedDoublePair('guest')); + }); + + if (incomplete.length > 0) { + const matchNumbers = incomplete.map(formation => formation.matchNr).join(', '); + return { + verified: false, + message: `Unvollständige Doppelzuordnung in Spiel ${matchNumbers}.` + }; + } + + return { verified: true, message: 'Alle Doppel wurden von nuScore vollständig zurückgeliefert.' }; + } catch (error) { + return { + verified: false, + message: `Doppelzuordnung konnte nicht erneut geprüft werden: ${error.message}` + }; + } + }, + updateMatchData(matchData, options = {}) { try { console.log('🔄 updateMatchData: Verwende kompletten Original-Meeting-Daten...'); @@ -2777,6 +2850,58 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr .trim(); }, + isUsableMatchSide(name) { + const normalized = String(name || '') + .replace(/\s+/g, ' ') + .trim() + .toLocaleLowerCase('de-DE'); + + return Boolean(normalized) && ![ + 'nicht angegeben', + 'nicht angegeben / nicht angegeben', + 'not specified', + 'unknown', + 'unbekannt', + 'n/a', + '-' + ].includes(normalized); + }, + + getMatchFormationLabel(match, index) { + const formations = this.meetingDetails?.meetingPlayMode?.matchFormations; + const matchNumber = Number(match?.matchNr ?? match?.matchNumber); + const formation = Array.isArray(formations) + ? (formations.find(item => Number(item?.matchNr) === matchNumber) || formations[index]) + : null; + + if (formation) { + const homeLabel = this.formatLineupPositionLabel(formation.homeLineupPosition, 'home'); + const guestLabel = this.formatLineupPositionLabel(formation.guestLineupPosition, 'guest'); + if (homeLabel && guestLabel) { + return `${homeLabel} – ${guestLabel}`; + } + } + + return this.currentPlayModeMatrix?.[index] || ''; + }, + + resolveMatchSide(match, index, side) { + const assignedName = this.formatMeetingMatchSide(match, side); + if (this.isUsableMatchSide(assignedName)) { + return assignedName; + } + + const reportedName = side === 'home' ? match?.homePlayer : match?.guestPlayer; + if (this.isUsableMatchSide(reportedName)) { + return String(reportedName).trim(); + } + + // NuScore liefert vor allem bei Doppeln gelegentlich nur den + // Platzhalter „nicht angegeben“. Die Paarung wird dann aus der + // Formation und den aktuell gesetzten Doppelpositionen ermittelt. + return this.resolveSide(this.getMatchFormationLabel(match, index), side); + }, + formatMeetingMatchSide(match, side) { if (!match || typeof match !== 'object') { return ''; @@ -3127,11 +3252,10 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr ? this.meetingDetails.matches : []; if (serverMatches.length > 0) { - const matrix = this.currentPlayModeMatrix; this.results = serverMatches.map((match, index) => ({ - label: (typeof match?.name === 'string' && match.name.trim()) || matrix[index] || `Spiel ${index + 1}`, - homeName: this.formatMeetingMatchSide(match, 'home') || match?.homePlayer || this.resolveSide(matrix[index] || '', 'home'), - guestName: this.formatMeetingMatchSide(match, 'guest') || match?.guestPlayer || this.resolveSide(matrix[index] || '', 'guest'), + label: (typeof match?.name === 'string' && match.name.trim()) || this.getMatchFormationLabel(match, index) || `Spiel ${index + 1}`, + homeName: this.resolveMatchSide(match, index, 'home'), + guestName: this.resolveMatchSide(match, index, 'guest'), sets: ['', '', '', '', ''], completed: false, result: '1:0' @@ -3161,19 +3285,17 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr const src = sourceMatches[i] || {}; const dst = this.results[i]; - const resolvedHomeName = this.formatMeetingMatchSide(src, 'home'); - const resolvedGuestName = this.formatMeetingMatchSide(src, 'guest'); - const fallbackHomeName = typeof src.homePlayer === 'string' ? src.homePlayer.trim() : ''; - const fallbackGuestName = typeof src.guestPlayer === 'string' ? src.guestPlayer.trim() : ''; + const resolvedHomeName = this.resolveMatchSide(src, i, 'home'); + const resolvedGuestName = this.resolveMatchSide(src, i, 'guest'); if (typeof src.name === 'string' && src.name.trim() !== '') { dst.label = src.name.trim(); } - if (resolvedHomeName || fallbackHomeName) { - dst.homeName = resolvedHomeName || fallbackHomeName; + if (resolvedHomeName) { + dst.homeName = resolvedHomeName; } - if (resolvedGuestName || fallbackGuestName) { - dst.guestName = resolvedGuestName || fallbackGuestName; + if (resolvedGuestName) { + dst.guestName = resolvedGuestName; } if (!dst.sets || !Array.isArray(dst.sets)) {