From 06355552c675d2373cbd2ee9de14422a595cfd66 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Mon, 10 Aug 2026 15:52:28 +0200 Subject: [PATCH] PIN parser korrigiert. --- backend/services/pdfParserService.js | 99 +++++++++++++++------------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/backend/services/pdfParserService.js b/backend/services/pdfParserService.js index db3d37af..11cf3662 100755 --- a/backend/services/pdfParserService.js +++ b/backend/services/pdfParserService.js @@ -147,6 +147,9 @@ class PDFParserService { */ static parseStandardFormat(lines, clubId, lineEntries = null) { const matches = []; + // nuLiga PIN lists identify the team's club in the document header. The + // PIN in each row belongs to that team, not inherently to the guest. + const documentTeamName = lines[0]?.trim() || null; for (let i = 0; i < lines.length; i++) { @@ -218,6 +221,7 @@ class PDFParserService { const pinMatch = cleanLine3.match(/(\d{4})$/); let code = null; + let pin = null; let homePin = null; let guestPin = null; let teamsPart = cleanLine3; @@ -228,31 +232,8 @@ class PDFParserService { teamsPart = cleanLine3.substring(0, cleanLine3.length - code.length).trim(); } else if (pinMatch) { // PIN gefunden (4 Ziffern) - const pin = pinMatch[1]; + pin = pinMatch[1]; teamsPart = cleanLine3.substring(0, cleanLine3.length - pin.length).trim(); - - // Die PIN gehört immer zu "Harheimer TC" - // Prüfe, ob "Harheimer TC" am Anfang oder am Ende steht - const harheimerIndex = teamsPart.indexOf('Harheimer TC'); - - if (harheimerIndex >= 0) { - // "Harheimer TC" gefunden - let beforeHarheimer = teamsPart.substring(0, harheimerIndex).trim(); - - // Entferne führende Spielnummer (z.B. "1", "2", etc.) - beforeHarheimer = beforeHarheimer.replace(/^\d+/, '').trim(); - - if (beforeHarheimer && beforeHarheimer.length > 0) { - // Es gibt einen Team-Namen vor "Harheimer TC" → Harheimer ist Gastteam → guestPin - guestPin = pin; - } else { - // "Harheimer TC" steht am Anfang (nur Spielnummer davor) → Harheimer ist Heimteam → homePin - homePin = pin; - } - } else { - // "Harheimer TC" nicht gefunden → Standardlogik: PIN gehört zum Gastteam - guestPin = pin; - } } if (!code && codeFromColumns) { @@ -264,15 +245,22 @@ class PDFParserService { // columns unambiguously. Use them directly instead of trying // to reconstruct column boundaries from the flattened text. // This also preserves names with characters such as "ä". - if (code && homeFromColumns && guestFromColumns) { + if ((code || pin) && homeFromColumns && guestFromColumns) { + if (pin && documentTeamName) { + if (PDFParserService.namesRoughlyMatch(homeFromColumns, documentTeamName)) { + homePin = pin; + } else if (PDFParserService.namesRoughlyMatch(guestFromColumns, documentTeamName)) { + guestPin = pin; + } + } matches.push({ date: date, time: time, homeTeamName: homeFromColumns, guestTeamName: guestFromColumns, code: code, - homePin: null, - guestPin: null, + homePin: homePin, + guestPin: guestPin, clubId: clubId, rawLine: line }); @@ -329,17 +317,18 @@ class PDFParserService { } else { // Fallback: Versuche mit einzelnen Leerzeichen zu trennen - // Strategie 1: Suche nach "Harheimer TC" als Heimteam oder Gastteam - if (teamsPart.includes('Harheimer TC')) { - const harheimerIndex = teamsPart.indexOf('Harheimer TC'); - let beforeHarheimer = teamsPart.substring(0, harheimerIndex).trim(); - let afterHarheimer = teamsPart.substring(harheimerIndex + 'Harheimer TC'.length).trim(); + // Strategy 1: use the team named in the document header + // as the split point when PDF text lacks column spacing. + if (documentTeamName && teamsPart.includes(documentTeamName)) { + const documentTeamIndex = teamsPart.indexOf(documentTeamName); + let beforeDocumentTeam = teamsPart.substring(0, documentTeamIndex).trim(); + let afterDocumentTeam = teamsPart.substring(documentTeamIndex + documentTeamName.length).trim(); - beforeHarheimer = beforeHarheimer + beforeDocumentTeam = beforeDocumentTeam .replace(/^\(\d+\)/, '') .replace(/^\d+/, '') .trim(); - afterHarheimer = afterHarheimer + afterDocumentTeam = afterDocumentTeam .replace(/^\(\d+\)/, '') .replace(/^\d+/, '') .trim(); @@ -385,23 +374,23 @@ class PDFParserService { return { roman: null, tokens: tokensCopy }; }; - if (!beforeHarheimer && afterHarheimer) { - const tokens = afterHarheimer.split(/\s+/).filter(Boolean); + if (!beforeDocumentTeam && afterDocumentTeam) { + const tokens = afterDocumentTeam.split(/\s+/).filter(Boolean); const { roman: homeRoman, tokens: guestTokens } = extractLeadingRomanFromTokens(tokens); const homeSuffix = homeRoman ? ` ${homeRoman}` : ''; - homeTeamName = `Harheimer TC${homeSuffix}`; + homeTeamName = `${documentTeamName}${homeSuffix}`; guestTeamName = guestTokens.join(' ').trim(); - } else if (beforeHarheimer && !afterHarheimer) { - // "Harheimer TC" ist Gastteam ohne weitere Tokens - homeTeamName = beforeHarheimer.replace(/\([^)]*\)/g, '').trim(); - guestTeamName = 'Harheimer TC'; - } else if (beforeHarheimer && afterHarheimer) { - // "Harheimer TC" steht in der Mitte → Harheimer ist Gast, Tokens nach Harheimer gehören zu ihm - homeTeamName = beforeHarheimer.replace(/\([^)]*\)/g, '').trim(); - const tokens = afterHarheimer.split(/\s+/).filter(Boolean); + } else if (beforeDocumentTeam && !afterDocumentTeam) { + // The document team is the guest team without a suffix. + homeTeamName = beforeDocumentTeam.replace(/\([^)]*\)/g, '').trim(); + guestTeamName = documentTeamName; + } else if (beforeDocumentTeam && afterDocumentTeam) { + // The document team is guest; trailing tokens can contain its suffix. + homeTeamName = beforeDocumentTeam.replace(/\([^)]*\)/g, '').trim(); + const tokens = afterDocumentTeam.split(/\s+/).filter(Boolean); const { roman: guestRoman, tokens: remainingTokens } = extractLeadingRomanFromTokens(tokens); const guestSuffix = guestRoman ? ` ${guestRoman}` : ''; - guestTeamName = `Harheimer TC${guestSuffix}`; + guestTeamName = `${documentTeamName}${guestSuffix}`; if (remainingTokens.length > 0) { const trailingText = remainingTokens.join(' ').trim(); if (trailingText) { @@ -409,7 +398,7 @@ class PDFParserService { } } } else { - // Nur "Harheimer TC" ohne weitere Kontexte → überspringen + // Only the document team without opponent context → skip. continue; } @@ -437,6 +426,13 @@ class PDFParserService { } if (homeTeamName && guestTeamName) { + if (pin && !homePin && !guestPin && documentTeamName) { + if (PDFParserService.namesRoughlyMatch(homeTeamName, documentTeamName)) { + homePin = pin; + } else if (PDFParserService.namesRoughlyMatch(guestTeamName, documentTeamName)) { + guestPin = pin; + } + } matches.push({ date: date, @@ -723,9 +719,18 @@ class PDFParserService { } if (matchData.homePin) { updateData.homePin = matchData.homePin; + // Repair a PIN previously imported on the wrong side + // without touching an independently stored guest PIN. + if (matchingMatch.guestPin === matchData.homePin) { + updateData.guestPin = null; + } } if (matchData.guestPin) { updateData.guestPin = matchData.guestPin; + // Repair the corresponding inverse case. + if (matchingMatch.homePin === matchData.guestPin) { + updateData.homePin = null; + } } await matchingMatch.update(updateData);