Fügt die Unterstützung für Teilnahmegebühren in officialTournamentController.js hinzu, einschließlich der Extraktion von Gebühren aus dem Turniertext. Aktualisiert das Datenmodell in OfficialTournament.js, um die Teilnahmegebühren zu speichern. Passt die Benutzeroberfläche in OfficialTournaments.vue an, um die Teilnahmegebühren anzuzeigen, und aktualisiert PDFGenerator.js, um die Gebühren im PDF-Dokument darzustellen.
This commit is contained in:
@@ -29,6 +29,7 @@ export const uploadTournamentPdf = async (req, res) => {
|
||||
venues: JSON.stringify(parsed.austragungsorte || []),
|
||||
competitionTypes: JSON.stringify(parsed.konkurrenztypen || []),
|
||||
registrationDeadlines: JSON.stringify(parsed.meldeschluesse || []),
|
||||
entryFees: JSON.stringify(parsed.entryFees || {}),
|
||||
});
|
||||
// competitions persistieren
|
||||
for (const c of parsed.competitions || []) {
|
||||
@@ -111,6 +112,7 @@ export const getParsedTournament = async (req, res) => {
|
||||
austragungsorte: JSON.parse(t.venues || '[]'),
|
||||
konkurrenztypen: JSON.parse(t.competitionTypes || '[]'),
|
||||
meldeschluesse: JSON.parse(t.registrationDeadlines || '[]'),
|
||||
entryFees: JSON.parse(t.entryFees || '{}'),
|
||||
competitions,
|
||||
},
|
||||
participation: entries.map(e => ({
|
||||
@@ -365,6 +367,67 @@ function parseTournamentText(text) {
|
||||
return idx >= 0 ? normLines[idx] : null;
|
||||
};
|
||||
|
||||
// Neue Funktion: Teilnahmegebühren pro Spielklasse extrahieren
|
||||
const extractEntryFees = () => {
|
||||
const entryFees = {};
|
||||
|
||||
// Verschiedene Patterns für Teilnahmegebühren suchen
|
||||
const feePatterns = [
|
||||
// Pattern 1: "Startgeld: U12: 5€, U14: 7€, U16: 10€"
|
||||
/startgeld\s*:?\s*(.+)/i,
|
||||
// Pattern 2: "Teilnahmegebühr: U12: 5€, U14: 7€"
|
||||
/teilnahmegebühr\s*:?\s*(.+)/i,
|
||||
// Pattern 3: "Gebühr: U12: 5€, U14: 7€"
|
||||
/gebühr\s*:?\s*(.+)/i,
|
||||
// Pattern 4: "Einschreibegebühr: U12: 5€, U14: 7€"
|
||||
/einschreibegebühr\s*:?\s*(.+)/i,
|
||||
// Pattern 5: "Anmeldegebühr: U12: 5€, U14: 7€"
|
||||
/anmeldegebühr\s*:?\s*(.+)/i
|
||||
];
|
||||
|
||||
for (const pattern of feePatterns) {
|
||||
for (let i = 0; i < normLines.length; i++) {
|
||||
const line = normLines[i];
|
||||
const match = line.match(pattern);
|
||||
if (match) {
|
||||
const feeText = match[1];
|
||||
|
||||
// Extrahiere Gebühren aus dem Text
|
||||
// Unterstützt verschiedene Formate:
|
||||
// "U12: 5€, U14: 7€, U16: 10€"
|
||||
// "U12: 5 Euro, U14: 7 Euro"
|
||||
// "U12 5€, U14 7€"
|
||||
// "U12: 5,00€, U14: 7,00€"
|
||||
const feeMatches = feeText.matchAll(/(U\d+|AK\s*\d+)\s*:?\s*(\d+(?:[,.]\d+)?)\s*(?:€|Euro|EUR)?/gi);
|
||||
|
||||
for (const feeMatch of feeMatches) {
|
||||
const ageClass = feeMatch[1].toUpperCase().replace(/\s+/g, '');
|
||||
const amount = feeMatch[2].replace(',', '.');
|
||||
const numericAmount = parseFloat(amount);
|
||||
|
||||
if (!isNaN(numericAmount)) {
|
||||
entryFees[ageClass] = {
|
||||
amount: numericAmount,
|
||||
currency: '€',
|
||||
rawText: feeMatch[0]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Wenn wir Gebühren gefunden haben, brechen wir ab
|
||||
if (Object.keys(entryFees).length > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Object.keys(entryFees).length > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return entryFees;
|
||||
};
|
||||
|
||||
const extractBlockAfter = (labels, multiline = false) => {
|
||||
const idx = normLines.findIndex(l => labels.some(lb => l.toLowerCase().startsWith(lb)));
|
||||
if (idx === -1) return multiline ? [] : null;
|
||||
@@ -467,7 +530,26 @@ function parseTournamentText(text) {
|
||||
else if (key.startsWith('austragungssys. vorrunde')) comp.vorrunde = val;
|
||||
else if (key.startsWith('austragungssys. endrunde')) comp.endrunde = val;
|
||||
else if (key.startsWith('max. teilnehmerzahl')) comp.maxTeilnehmer = val;
|
||||
else if (key === 'startgeld') comp.startgeld = val;
|
||||
else if (key === 'startgeld') {
|
||||
comp.startgeld = val;
|
||||
// Versuche auch spezifische Gebühren für diese Altersklasse zu extrahieren
|
||||
const ageClassMatch = comp.altersklasseWettbewerb?.match(/(U\d+|AK\s*\d+)/i);
|
||||
if (ageClassMatch) {
|
||||
const ageClass = ageClassMatch[1].toUpperCase().replace(/\s+/g, '');
|
||||
const feeMatch = val.match(/(\d+(?:[,.]\d+)?)\s*(?:€|Euro|EUR)?/);
|
||||
if (feeMatch) {
|
||||
const amount = feeMatch[1].replace(',', '.');
|
||||
const numericAmount = parseFloat(amount);
|
||||
if (!isNaN(numericAmount)) {
|
||||
comp.entryFeeDetails = {
|
||||
amount: numericAmount,
|
||||
currency: '€',
|
||||
ageClass: ageClass
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
@@ -515,6 +597,9 @@ function parseTournamentText(text) {
|
||||
}
|
||||
}
|
||||
|
||||
// Extrahiere Teilnahmegebühren
|
||||
const entryFees = extractEntryFees();
|
||||
|
||||
return {
|
||||
title,
|
||||
termin,
|
||||
@@ -526,6 +611,7 @@ function parseTournamentText(text) {
|
||||
startzeiten: {},
|
||||
competitions,
|
||||
entries,
|
||||
entryFees, // Neue: Teilnahmegebühren pro Spielklasse
|
||||
debug: { normLines },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ const OfficialTournament = sequelize.define('OfficialTournament', {
|
||||
venues: { type: DataTypes.TEXT, allowNull: true }, // JSON.stringify(Array)
|
||||
competitionTypes: { type: DataTypes.TEXT, allowNull: true }, // JSON.stringify(Array)
|
||||
registrationDeadlines: { type: DataTypes.TEXT, allowNull: true }, // JSON.stringify(Array)
|
||||
entryFees: { type: DataTypes.TEXT, allowNull: true }, // JSON.stringify(Object) - Teilnahmegebühren pro Spielklasse
|
||||
}, {
|
||||
tableName: 'official_tournaments',
|
||||
timestamps: true,
|
||||
|
||||
@@ -354,13 +354,15 @@ class PDFGenerator {
|
||||
this.pdf.setFont('helvetica', 'bold');
|
||||
this.pdf.setFontSize(12);
|
||||
this.pdf.text('Wettbewerb', this.margin, y);
|
||||
this.pdf.text('Datum', this.margin + 110, y);
|
||||
this.pdf.text('Startzeit', this.margin + 150, y);
|
||||
this.pdf.text('Datum', this.margin + 80, y);
|
||||
this.pdf.text('Startzeit', this.margin + 120, y);
|
||||
this.pdf.text('Gebühr', this.margin + 160, y);
|
||||
y += 7;
|
||||
for (const r of recommendedRows) {
|
||||
this.pdf.text(r.name || '', this.margin, y);
|
||||
this.pdf.text(r.date || '–', this.margin + 110, y);
|
||||
this.pdf.text(r.time || '–', this.margin + 150, y);
|
||||
this.pdf.text(r.date || '–', this.margin + 80, y);
|
||||
this.pdf.text(r.time || '–', this.margin + 120, y);
|
||||
this.pdf.text(r.entryFee || '–', this.margin + 160, y);
|
||||
y += 7;
|
||||
if (y > this.pageHeight) {
|
||||
this.addNewPage();
|
||||
@@ -381,8 +383,9 @@ class PDFGenerator {
|
||||
this.pdf.setFontSize(12);
|
||||
for (const r of otherRows) {
|
||||
this.pdf.text(r.name || '', this.margin, y);
|
||||
this.pdf.text(r.date || '–', this.margin + 110, y);
|
||||
this.pdf.text(r.time || '–', this.margin + 150, y);
|
||||
this.pdf.text(r.date || '–', this.margin + 80, y);
|
||||
this.pdf.text(r.time || '–', this.margin + 120, y);
|
||||
this.pdf.text(r.entryFee || '–', this.margin + 160, y);
|
||||
y += 7;
|
||||
if (y > this.pageHeight) {
|
||||
this.addNewPage();
|
||||
|
||||
@@ -43,6 +43,15 @@
|
||||
{{ ak }}: {{ arr.join(', ') }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="parsed.parsedData.entryFees && Object.keys(parsed.parsedData.entryFees).length">
|
||||
<strong>Teilnahmegebühren:</strong>
|
||||
<div class="entry-fees">
|
||||
<div v-for="(fee, ageClass) in parsed.parsedData.entryFees" :key="ageClass" class="fee-item">
|
||||
<span class="age-class">{{ ageClass }}:</span>
|
||||
<span class="fee-amount">{{ fee.amount }}{{ fee.currency }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- ehemals 'Erkannte Einträge' entfernt -->
|
||||
@@ -898,13 +907,40 @@ export default {
|
||||
},
|
||||
competitionsForMember(member) {
|
||||
const comps = (this.parsed && this.parsed.parsedData && this.parsed.parsedData.competitions) ? this.parsed.parsedData.competitions : [];
|
||||
const entryFees = (this.parsed && this.parsed.parsedData && this.parsed.parsedData.entryFees) ? this.parsed.parsedData.entryFees : {};
|
||||
const rows = [];
|
||||
for (let idx = 0; idx < comps.length; idx++) {
|
||||
const c = comps[idx];
|
||||
if (this.isEligibleForCompetition(member, c)) {
|
||||
const title = c.ageClassCompetition || c.altersklasseWettbewerb || '';
|
||||
const st = this.splitDateTime(c.startTime || c.startzeit || '');
|
||||
rows.push({ key: String(idx), name: title, date: st.date, time: st.time, raw: c });
|
||||
|
||||
// Bestimme die Teilnahmegebühr für diese Altersklasse
|
||||
let entryFee = '';
|
||||
const ageClassMatch = title.match(/(U\d+|AK\s*\d+)/i);
|
||||
if (ageClassMatch) {
|
||||
const ageClass = ageClassMatch[1].toUpperCase().replace(/\s+/g, '');
|
||||
if (entryFees[ageClass]) {
|
||||
entryFee = `${entryFees[ageClass].amount}${entryFees[ageClass].currency}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: Verwende startgeld aus der Konkurrenz
|
||||
if (!entryFee && c.startgeld) {
|
||||
const feeMatch = c.startgeld.match(/(\d+(?:[,.]\d+)?)\s*(?:€|Euro|EUR)?/);
|
||||
if (feeMatch) {
|
||||
entryFee = `${feeMatch[1]}€`;
|
||||
}
|
||||
}
|
||||
|
||||
rows.push({
|
||||
key: String(idx),
|
||||
name: title,
|
||||
date: st.date,
|
||||
time: st.time,
|
||||
entryFee: entryFee || '–',
|
||||
raw: c
|
||||
});
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
@@ -1261,6 +1297,35 @@ th, td { border-bottom: 1px solid var(--border-color); padding: 0.5rem; text-ali
|
||||
font-style: italic;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* Entry Fees Styling */
|
||||
.entry-fees {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.fee-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.age-class {
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.fee-amount {
|
||||
font-weight: 700;
|
||||
color: #28a745;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user