From 9a8f5431e1cdf33033e05701de2c17ea5814292e Mon Sep 17 00:00:00 2001 From: data Date: Tue, 3 Mar 2026 14:26:31 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20Duplikat-Schutz=20f=C3=BCr=20Subtotals?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Prüft in toggle_subtotal.php und sync_to_facturedet.php ob Subtotal bereits in Detail-Tabelle existiert - Verhindert mehrfache Subtotal-Zeilen für dieselbe Section - addslashes() durch $db->escape() ersetzt (SQL-Sicherheit) - LIKE durch exakten Match ersetzt (Präzision) Co-Authored-By: Claude Opus 4.5 --- ajax/import_from_origin.php | 0 ajax/sync_to_facturedet.php | 30 ++++++++++++++++++++++++++++-- ajax/toggle_subtotal.php | 19 ++++++++++++++++--- 3 files changed, 44 insertions(+), 5 deletions(-) mode change 100644 => 100755 ajax/import_from_origin.php diff --git a/ajax/import_from_origin.php b/ajax/import_from_origin.php old mode 100644 new mode 100755 diff --git a/ajax/sync_to_facturedet.php b/ajax/sync_to_facturedet.php index 2aae2db..2b29a2a 100755 --- a/ajax/sync_to_facturedet.php +++ b/ajax/sync_to_facturedet.php @@ -70,6 +70,22 @@ if ($action == 'add') { exit; } + // ZUSÄTZLICHE PRÜFUNG: Existiert bereits ein Eintrag mit gleichem special_code und ähnlicher Beschreibung? + $special_code_check = isset($special_codes[$line_type]) ? $special_codes[$line_type] : 0; + if ($special_code_check > 0) { + $title_to_check = ($line_type == 'subtotal') ? 'Zwischensumme: '.$line->section_title : $line->title; + $sql_dup_check = "SELECT rowid FROM ".MAIN_DB_PREFIX.$tables['lines_table']; + $sql_dup_check .= " WHERE ".$tables['fk_parent']." = ".(int)$document_id; + $sql_dup_check .= " AND special_code = ".(int)$special_code_check; + $sql_dup_check .= " AND description = '".$db->escape($title_to_check)."'"; + $res_dup_check = $db->query($sql_dup_check); + if ($db->num_rows($res_dup_check) > 0) { + subtotaltitle_debug_log('⚠️ Duplikat gefunden: '.$line_type.' "'.$title_to_check.'" existiert bereits in Detail-Tabelle'); + echo json_encode(array('success' => false, 'error' => 'Entry already exists in detail table')); + exit; + } + } + // AUTOMATISCHE REPARATUR: Wenn Section keine Subtotal-Zeile hat, erstelle sie if ($line_type == 'section') { $sql_check = "SELECT rowid FROM ".MAIN_DB_PREFIX."facture_lines_manager"; @@ -78,7 +94,15 @@ if ($action == 'add') { $sql_check .= " AND document_type = '".$db->escape($docType)."'"; $res_check = $db->query($sql_check); - if ($db->num_rows($res_check) == 0) { + // Prüfe AUCH ob Subtotal schon in Detail-Tabelle existiert + $sql_check_detail = "SELECT rowid FROM ".MAIN_DB_PREFIX.$tables['lines_table']; + $sql_check_detail .= " WHERE ".$tables['fk_parent']." = ".(int)$document_id; + $sql_check_detail .= " AND special_code = 102"; + $sql_check_detail .= " AND description = '".$db->escape('Zwischensumme: '.$line->title)."'"; + $res_check_detail = $db->query($sql_check_detail); + $subtotal_exists_in_detail = ($db->num_rows($res_check_detail) > 0); + + if ($db->num_rows($res_check) == 0 && !$subtotal_exists_in_detail) { // Keine Subtotal-Zeile vorhanden - automatisch erstellen subtotaltitle_debug_log('⚠️ Section #'.$line_id.' hat keine Subtotal-Zeile - erstelle automatisch'); @@ -95,10 +119,12 @@ if ($action == 'add') { $sql_subtotal = "INSERT INTO ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_subtotal .= " (fk_facture, fk_propal, fk_commande, document_type, line_type, title, parent_section, line_order, date_creation)"; - $sql_subtotal .= " VALUES (".$fk_facture.", ".$fk_propal.", ".$fk_commande.", '".$db->escape($docType)."', 'subtotal', 'Zwischensumme', ".(int)$line_id.", ".$next_order.", NOW())"; + $sql_subtotal .= " VALUES (".$fk_facture.", ".$fk_propal.", ".$fk_commande.", '".$db->escape($docType)."', 'subtotal', 'Zwischensumme: ".$db->escape($line->title)."', ".(int)$line_id.", ".$next_order.", NOW())"; $db->query($sql_subtotal); subtotaltitle_debug_log('✅ Subtotal-Zeile automatisch erstellt für Section #'.$line_id); + } elseif ($subtotal_exists_in_detail) { + subtotaltitle_debug_log('⚠️ Subtotal für Section "'.$line->title.'" existiert bereits in Detail-Tabelle - überspringe automatische Erstellung'); } } diff --git a/ajax/toggle_subtotal.php b/ajax/toggle_subtotal.php index a1f26a8..003aff0 100755 --- a/ajax/toggle_subtotal.php +++ b/ajax/toggle_subtotal.php @@ -38,13 +38,26 @@ $sql .= " WHERE rowid = ".(int)$section_id; $db->query($sql); if ($show) { - // Prüfe ob Subtotal-Zeile schon existiert + // Prüfe ob Subtotal-Zeile schon in Manager-Tabelle existiert $sql_check = "SELECT rowid FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_check .= " WHERE parent_section = ".(int)$section_id; $sql_check .= " AND line_type = 'subtotal'"; + $sql_check .= " AND document_type = '".$db->escape($docType)."'"; $res_check = $db->query($sql_check); - - if ($db->num_rows($res_check) == 0) { + + // Prüfe AUCH ob Subtotal schon in Detail-Tabelle existiert (special_code = 102) + $sql_check_detail = "SELECT rowid FROM ".MAIN_DB_PREFIX.$tables['lines_table']; + $sql_check_detail .= " WHERE ".$tables['fk_parent']." = ".(int)$facture_id; + $sql_check_detail .= " AND special_code = 102"; + $sql_check_detail .= " AND description = '".$db->escape('Zwischensumme: '.$section->title)."'"; + $res_check_detail = $db->query($sql_check_detail); + $subtotal_exists_in_detail = ($db->num_rows($res_check_detail) > 0); + + if ($subtotal_exists_in_detail) { + subtotaltitle_debug_log('⚠️ Subtotal für "'.$section->title.'" existiert bereits in Detail-Tabelle - überspringe'); + } + + if ($db->num_rows($res_check) == 0 && !$subtotal_exists_in_detail) { // Berechne Zwischensumme $sql_sum = "SELECT SUM(d.total_ht) as total"; $sql_sum .= " FROM ".MAIN_DB_PREFIX."facture_lines_manager m";