feat(MatchReportApiDialog): implement double assignment verification and improve match side resolution
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 56s
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 56s
This commit is contained in:
@@ -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)) {
|
||||
|
||||
Reference in New Issue
Block a user