fix: Duplikat-Schutz für Subtotals

- 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 <noreply@anthropic.com>
This commit is contained in:
Eduard Wisch 2026-03-03 14:26:31 +01:00
parent 244d4e8f67
commit 9a8f5431e1
3 changed files with 44 additions and 5 deletions

0
ajax/import_from_origin.php Normal file → Executable file
View file

View file

@ -70,6 +70,22 @@ if ($action == 'add') {
exit; 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 // AUTOMATISCHE REPARATUR: Wenn Section keine Subtotal-Zeile hat, erstelle sie
if ($line_type == 'section') { if ($line_type == 'section') {
$sql_check = "SELECT rowid FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $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)."'"; $sql_check .= " AND document_type = '".$db->escape($docType)."'";
$res_check = $db->query($sql_check); $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 // Keine Subtotal-Zeile vorhanden - automatisch erstellen
subtotaltitle_debug_log('⚠️ Section #'.$line_id.' hat keine Subtotal-Zeile - erstelle automatisch'); 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 = "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 .= " (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); $db->query($sql_subtotal);
subtotaltitle_debug_log('✅ Subtotal-Zeile automatisch erstellt für Section #'.$line_id); 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');
} }
} }

View file

@ -38,13 +38,26 @@ $sql .= " WHERE rowid = ".(int)$section_id;
$db->query($sql); $db->query($sql);
if ($show) { 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 = "SELECT rowid FROM ".MAIN_DB_PREFIX."facture_lines_manager";
$sql_check .= " WHERE parent_section = ".(int)$section_id; $sql_check .= " WHERE parent_section = ".(int)$section_id;
$sql_check .= " AND line_type = 'subtotal'"; $sql_check .= " AND line_type = 'subtotal'";
$sql_check .= " AND document_type = '".$db->escape($docType)."'";
$res_check = $db->query($sql_check); $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 // Berechne Zwischensumme
$sql_sum = "SELECT SUM(d.total_ht) as total"; $sql_sum = "SELECT SUM(d.total_ht) as total";
$sql_sum .= " FROM ".MAIN_DB_PREFIX."facture_lines_manager m"; $sql_sum .= " FROM ".MAIN_DB_PREFIX."facture_lines_manager m";