diff --git a/backend/services/officialTournamentService.js b/backend/services/officialTournamentService.js index a7c420e2..6783c9ae 100755 --- a/backend/services/officialTournamentService.js +++ b/backend/services/officialTournamentService.js @@ -55,6 +55,17 @@ class OfficialTournamentService { const source = new URL(sourceUrl); if (!['www.mytischtennis.de', 'mytischtennis.de'].includes(source.hostname)) return 0; const response = await axios.get(sourceUrl, { timeout: 30000, headers: { 'User-Agent': 'Trainingstagebuch/1.0 (+turnierdetails)' } }); + const venues = this.extractJsonArray(response.data, 'tournament_locations') + .map((location) => [location.location_name, location.location_street, [location.location_zip, location.location_city].filter(Boolean).join(' ')].filter(Boolean).join(', ')) + .filter(Boolean); + let repaired = 0; + if (venues.length) { + const storedVenues = JSON.parse(tournament.venues || '[]'); + if (JSON.stringify(storedVenues) !== JSON.stringify(venues)) { + await tournament.update({ venues: JSON.stringify(venues) }); + repaired += 1; + } + } const competitions = this.extractJsonArray(response.data, 'competition_infos') .filter((competition) => competition?.competition_name) .map((competition) => ({ @@ -69,11 +80,10 @@ class OfficialTournamentService { maxParticipants: competition.competition_player_capacity_total == null ? null : String(competition.competition_player_capacity_total), entryFee: competition.competition_fee == null ? null : `${competition.competition_fee}${competition.competition_fee_currency === 'euro' ? '€' : ''}`, })); - if (!competitions.length) return 0; + if (!competitions.length) return repaired; const existing = await OfficialCompetition.findAll({ where: { tournamentId: tournament.id } }); const byName = new Map(existing.map((competition) => [this.normalizeCompetitionName(competition.ageClassCompetition), competition])); - let repaired = 0; for (const competition of competitions) { const existingCompetition = byName.get(this.normalizeCompetitionName(competition.ageClassCompetition)); if (existingCompetition) { @@ -197,7 +207,9 @@ class OfficialTournamentService { if (!t) return null; let comps = await OfficialCompetition.findAll({ where: { tournamentId: id } }); - const needsRepair = !comps.length || comps.some((competition) => !competition.startTime || competition.entryFee == null); + const venues = JSON.parse(t.venues || '[]'); + const needsVenueRepair = !venues.length || venues.every((venue) => !String(venue).includes(',')); + const needsRepair = needsVenueRepair || !comps.length || comps.some((competition) => !competition.startTime || competition.entryFee == null); if (needsRepair) { try { await this.repairTournamentFromSource(clubId, t); diff --git a/frontend/src/components/PDFGenerator.js b/frontend/src/components/PDFGenerator.js index d3d27585..adcda8a9 100755 --- a/frontend/src/components/PDFGenerator.js +++ b/frontend/src/components/PDFGenerator.js @@ -580,6 +580,15 @@ class PDFGenerator { this.pdf.text(lines, 105, y + 1, { align: 'center' }); y += boxHeight + 4; } + const venueLines = Array.isArray(venues) ? venues.filter(Boolean) : []; + if (venueLines.length) { + const venueText = `${this.t('pdfGenerator.venue')}: ${venueLines.join(' · ')}`; + const lines = this.pdf.splitTextToSize(venueText, 210 - this.margin * 2); + this.pdf.setFont('helvetica', 'bold'); + this.pdf.setFontSize(11); + this.pdf.text(lines, this.margin, y); + y += lines.length * 5 + 3; + } this.pdf.setFont('helvetica', 'bold'); this.pdf.setFontSize(12); this.pdf.text(`${this.t('pdfGenerator.member')} ${memberName}`, this.margin, y); @@ -632,37 +641,6 @@ class PDFGenerator { } } } - // Austragungsort(e) direkt vor den Hinweisen - const venueLines = Array.isArray(venues) ? venues.filter(Boolean) : []; - if (venueLines.length) { - const heading = venueLines.length === 1 ? this.t('pdfGenerator.venue') : this.t('pdfGenerator.venues'); - const maxWidth = 210 - this.margin * 2; - if (y + 20 + venueLines.length * 6 > this.pageHeight) { - this.addNewPage(); - y = this.margin; - } else { - y += 6; - } - this.pdf.setFont('helvetica', 'bold'); - this.pdf.setFontSize(13); - this.pdf.text(`${heading}:`, this.margin, y); - y += 7; - this.pdf.setFont('helvetica', 'normal'); - this.pdf.setFontSize(12); - for (const v of venueLines) { - const wrapped = this.pdf.splitTextToSize(String(v), maxWidth); - for (const line of wrapped) { - this.pdf.text(line, this.margin, y); - y += 6; - if (y > this.pageHeight) { - this.addNewPage(); - y = this.margin; - this.pdf.setFont('helvetica', 'normal'); - this.pdf.setFontSize(12); - } - } - } - } // Hinweise-Sektion const remainingForHints = 60; // Platz für Überschrift + Liste abschätzen if (y + remainingForHints > this.pageHeight) {