From ea2609c66e4d09b4ae50a678e75b7f65e83b09d5 Mon Sep 17 00:00:00 2001 From: data Date: Tue, 3 Feb 2026 13:17:28 +0100 Subject: [PATCH] =?UTF-8?q?Fehler=20beseitig=20Verwaiste=20Zeilen=20=C3=BC?= =?UTF-8?q?ber=20Button=20entfernen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.json | 0 MIGRATION_MULTITYPE.md | 0 ajax/add_to_section.php | 0 ajax/check_subtotal.php | 0 ajax/cleanup_subtotals.php | 0 ajax/fix_section_hierarchy.php | 0 ajax/fix_sections.php | 0 ajax/repair_missing_subtotals.php | 0 ajax/sync_to_facturedet.php | 74 ++++++++++++++++++++++++++ class/DocumentTypeHelper.class.php | 0 class/actions_subtotaltitle.class.php | 21 ++++++-- debug_sections.php | 0 img/grip.png | Bin js/subtotaltitle.js | 41 +++++++++++--- js/subtotaltitle_sync.js | 73 +++++++++++-------------- langs/de_DE/subtotaltitle.lang | 6 +-- langs/en_US/subtotaltitle.lang | 8 +-- sql/llx_facture_lines_manager.sql | 0 18 files changed, 164 insertions(+), 59 deletions(-) mode change 100644 => 100755 .claude/settings.json mode change 100644 => 100755 MIGRATION_MULTITYPE.md mode change 100644 => 100755 ajax/add_to_section.php mode change 100644 => 100755 ajax/check_subtotal.php mode change 100644 => 100755 ajax/cleanup_subtotals.php mode change 100644 => 100755 ajax/fix_section_hierarchy.php mode change 100644 => 100755 ajax/fix_sections.php mode change 100644 => 100755 ajax/repair_missing_subtotals.php mode change 100644 => 100755 class/DocumentTypeHelper.class.php mode change 100644 => 100755 debug_sections.php mode change 100644 => 100755 img/grip.png mode change 100644 => 100755 sql/llx_facture_lines_manager.sql diff --git a/.claude/settings.json b/.claude/settings.json old mode 100644 new mode 100755 diff --git a/MIGRATION_MULTITYPE.md b/MIGRATION_MULTITYPE.md old mode 100644 new mode 100755 diff --git a/ajax/add_to_section.php b/ajax/add_to_section.php old mode 100644 new mode 100755 diff --git a/ajax/check_subtotal.php b/ajax/check_subtotal.php old mode 100644 new mode 100755 diff --git a/ajax/cleanup_subtotals.php b/ajax/cleanup_subtotals.php old mode 100644 new mode 100755 diff --git a/ajax/fix_section_hierarchy.php b/ajax/fix_section_hierarchy.php old mode 100644 new mode 100755 diff --git a/ajax/fix_sections.php b/ajax/fix_sections.php old mode 100644 new mode 100755 diff --git a/ajax/repair_missing_subtotals.php b/ajax/repair_missing_subtotals.php old mode 100644 new mode 100755 diff --git a/ajax/sync_to_facturedet.php b/ajax/sync_to_facturedet.php index 36bba2b..2aae2db 100755 --- a/ajax/sync_to_facturedet.php +++ b/ajax/sync_to_facturedet.php @@ -312,6 +312,80 @@ if ($action == 'add') { echo json_encode(array('success' => true, 'total_ht' => $total_ht)); +} elseif ($action == 'remove_all') { + // ========== ALLE SPEZIALZEILEN UND VERWAISTE EINTRÄGE ENTFERNEN ========== + + // document_id wird benötigt + $document_id = GETPOST('document_id', 'int'); + if (!$document_id) { + echo json_encode(array('success' => false, 'error' => 'Missing document_id')); + exit; + } + + $removed_count = 0; + $orphan_count = 0; + + // 1. Entferne ALLE Einträge mit special_code 100, 101, 102 aus der Detail-Tabelle + // (unabhängig davon ob sie noch in der Manager-Tabelle existieren) + $sql_delete_all = "DELETE FROM ".MAIN_DB_PREFIX.$tables['lines_table']; + $sql_delete_all .= " WHERE ".$tables['fk_parent']." = ".(int)$document_id; + $sql_delete_all .= " AND special_code IN (100, 101, 102)"; + + subtotaltitle_debug_log('🗑️ Remove ALL special lines: '.$sql_delete_all); + + if ($db->query($sql_delete_all)) { + $removed_count = $db->affected_rows($db->query("SELECT ROW_COUNT()")); + // Fallback: Zähle manuell wenn affected_rows nicht funktioniert + if ($removed_count === 0) { + // Zähle vorher + $sql_count = "SELECT COUNT(*) as cnt FROM ".MAIN_DB_PREFIX.$tables['lines_table']; + $sql_count .= " WHERE ".$tables['fk_parent']." = ".(int)$document_id; + $sql_count .= " AND special_code IN (100, 101, 102)"; + // Da wir schon gelöscht haben, ist es jetzt 0 + $removed_count = -1; // Unbekannt, aber erfolgreich + } + } + + // 2. Setze in_facturedet und fk_*det auf NULL für alle Manager-Einträge dieses Dokuments + $sql_reset = "UPDATE ".MAIN_DB_PREFIX."facture_lines_manager"; + $sql_reset .= " SET ".$tables['fk_line']." = NULL, in_facturedet = 0"; + $sql_reset .= " WHERE ".$tables['fk_parent']." = ".(int)$document_id; + $sql_reset .= " AND document_type = '".$db->escape($docType)."'"; + $sql_reset .= " AND line_type IN ('section', 'text', 'subtotal')"; + + subtotaltitle_debug_log('🔄 Reset manager entries: '.$sql_reset); + $db->query($sql_reset); + + // 3. Normalisiere die rang-Werte (schließe Lücken) + $sql_reorder = "SET @r = 0; UPDATE ".MAIN_DB_PREFIX.$tables['lines_table']; + $sql_reorder .= " SET rang = (@r := @r + 1)"; + $sql_reorder .= " WHERE ".$tables['fk_parent']." = ".(int)$document_id; + $sql_reorder .= " ORDER BY rang"; + + // MySQL erlaubt kein SET in einer Anweisung mit UPDATE, also manuell: + $sql_get_lines = "SELECT rowid FROM ".MAIN_DB_PREFIX.$tables['lines_table']; + $sql_get_lines .= " WHERE ".$tables['fk_parent']." = ".(int)$document_id; + $sql_get_lines .= " ORDER BY rang"; + $res_lines = $db->query($sql_get_lines); + + $new_rang = 1; + while ($obj = $db->fetch_object($res_lines)) { + $sql_upd_rang = "UPDATE ".MAIN_DB_PREFIX.$tables['lines_table']; + $sql_upd_rang .= " SET rang = ".(int)$new_rang; + $sql_upd_rang .= " WHERE rowid = ".(int)$obj->rowid; + $db->query($sql_upd_rang); + $new_rang++; + } + + subtotaltitle_debug_log('✅ Remove ALL completed: removed='.$removed_count); + + echo json_encode(array( + 'success' => true, + 'removed' => $removed_count, + 'orphans_cleaned' => $orphan_count, + 'message' => 'Alle Spezialzeilen wurden entfernt' + )); + } else { echo json_encode(array('success' => false, 'error' => 'Unknown action')); } diff --git a/class/DocumentTypeHelper.class.php b/class/DocumentTypeHelper.class.php old mode 100644 new mode 100755 diff --git a/class/actions_subtotaltitle.class.php b/class/actions_subtotaltitle.class.php index dc86d19..c4fa3ce 100755 --- a/class/actions_subtotaltitle.class.php +++ b/class/actions_subtotaltitle.class.php @@ -1437,15 +1437,30 @@ class ActionsSubtotalTitle extends CommonHookActions $html .= ' dropdown += \'\';'; $html .= ' $(\'#addproduct tr:last\').before(dropdown);'; - + $html .= ' $(\'form[name="addproduct"]\').append(\'\');'; - + + // Lade gespeicherte Auswahl aus sessionStorage (dokumentspezifisch) + $html .= ' var storageKey = \'subtotaltitle_section_\' + '.$document_id.';'; + $html .= ' var savedSection = sessionStorage.getItem(storageKey);'; + $html .= ' if (savedSection) {'; + $html .= ' $(\'#section_id_dropdown\').val(savedSection);'; + $html .= ' $(\'#section_id\').val(savedSection);'; + $html .= ' console.log(\'[SubtotalTitle] Gespeicherte Section geladen:\', savedSection);'; + $html .= ' }'; + $html .= ' $(document).on(\'change\', \'#section_id_dropdown\', function() {'; $html .= ' var val = $(this).val();'; $html .= ' console.log(\'Section selected:\', val);'; $html .= ' $(\'#section_id\').val(val);'; + // Speichere Auswahl in sessionStorage (dokumentspezifisch) + $html .= ' if (val) {'; + $html .= ' sessionStorage.setItem(storageKey, val);'; + $html .= ' } else {'; + $html .= ' sessionStorage.removeItem(storageKey);'; + $html .= ' }'; $html .= ' });'; - + $html .= '});'; $html .= ''; diff --git a/debug_sections.php b/debug_sections.php old mode 100644 new mode 100755 diff --git a/img/grip.png b/img/grip.png old mode 100644 new mode 100755 diff --git a/js/subtotaltitle.js b/js/subtotaltitle.js index 23fc84d..efd2d5a 100755 --- a/js/subtotaltitle.js +++ b/js/subtotaltitle.js @@ -345,8 +345,8 @@ function createNewSection() { } showInputDialog( - lang.sectionCreate || 'Positionsgruppe erstellen', - lang.sectionName || 'Name der Positionsgruppe:', + lang.sectionCreate || 'Produktgruppe erstellen', + lang.sectionName || 'Name der Produktgruppe:', '', function(title) { debugLog('Erstelle Section: ' + title + ' für ' + docInfo.type + ' ID ' + docInfo.id); @@ -414,8 +414,8 @@ function renameSection(sectionId, currentTitle) { var lang = (typeof subtotalTitleLang !== 'undefined') ? subtotalTitleLang : {}; showInputDialog( - lang.buttonEdit || 'Positionsgruppe umbenennen', - lang.sectionName || 'Name der Positionsgruppe:', + lang.sectionEdit || 'Produktgruppe umbenennen', + lang.sectionName || 'Name der Produktgruppe:', currentTitle || '', function(newTitle) { debugLog('✏️ Benenne Section ' + sectionId + ' um zu: ' + newTitle); @@ -820,15 +820,42 @@ function moveProductToSection(productId, sectionId, newLineOrder) { function addUnlinkColumn() { var $table = $('#tablelines'); if (!$table.length) return; - + + // NUR auf Dokumentdetailseiten ausführen (card), NICHT auf Listen + // Prüfe ob wir auf einer gültigen Seite sind (invoicecard, propalcard, ordercard) + var url = window.location.href; + var isDocumentCard = (url.indexOf('/facture/card.php') !== -1 || + url.indexOf('/propal/card.php') !== -1 || + url.indexOf('/commande/card.php') !== -1); + + // Zusätzliche Prüfung: Modul muss aktiv sein (subtotalTitleIsDraft wird von PHP gesetzt) + var isModuleActive = (typeof subtotalTitleIsDraft !== 'undefined'); + + if (!isDocumentCard) { + debugLog('🔗 Keine Dokumentdetailseite, Unlink-Spalte wird übersprungen'); + return; + } + + if (!isModuleActive) { + debugLog('🔗 SubtotalTitle Modul nicht aktiv auf dieser Seite, Unlink-Spalte wird übersprungen'); + return; + } + + // Prüfe ob mindestens eine Produktgruppe (Section) vorhanden ist + var hasSections = ($('tr.section-header').length > 0); + if (!hasSections) { + debugLog('🔗 Keine Produktgruppen vorhanden, Unlink-Spalte wird übersprungen'); + return; + } + // Prüfe ob schon ausgeführt if ($table.data('unlink-added')) { debugLog('🔗 Unlink-Spalte bereits vorhanden, überspringe'); return; } $table.data('unlink-added', true); - - debugLog('🔗 Füge Unlink-Spalte hinzu...'); + + debugLog('🔗 Füge Unlink-Spalte hinzu (Sections vorhanden)...'); // THEAD: Leere Spalte hinzufügen $table.find('thead tr').each(function() { diff --git a/js/subtotaltitle_sync.js b/js/subtotaltitle_sync.js index 3e38701..ec4aed7 100755 --- a/js/subtotaltitle_sync.js +++ b/js/subtotaltitle_sync.js @@ -144,6 +144,9 @@ function syncAllToFacturedet() { var errors = 0; var docType = getDocumentTypeForSync(); + // Zeige Loading-Hinweis + $('body').append('
Synchronisiere... Bitte warten.
'); + $unchecked.each(function() { var lineId = $(this).data('line-id'); var lineType = $(this).data('line-type'); @@ -155,16 +158,16 @@ function syncAllToFacturedet() { document_type: docType }, function(response) { done++; - if (response.success) { - updateSyncCheckbox(lineId, true); - } else { + if (!response.success) { errors++; } if (done >= total) { debugLog('✅ Sync abgeschlossen: ' + (total - errors) + ' erfolgreich, ' + errors + ' Fehler'); if (errors > 0) { + $('#sync-loading').remove(); showErrorAlert((total - errors) + ' von ' + total + ' Elementen hinzugefügt. ' + errors + ' Fehler aufgetreten.'); } else { + // Direkt reloaden ohne UI-Update safeReload(); } } @@ -181,56 +184,42 @@ function syncAllToFacturedet() { /** * Entfernt ALLE Sections/Textzeilen/Subtotals aus facturedet + * Inkl. verwaister Einträge die nicht mehr in der Manager-Tabelle existieren */ function removeAllFromFacturedet() { var lang = (typeof subtotalTitleLang !== 'undefined') ? subtotalTitleLang : {}; - var $checked = $('.sync-checkbox:checked'); - var total = $checked.length; - - if (total === 0) { - showErrorAlert(lang.noElementsInInvoice || 'Keine Elemente in der Rechnung vorhanden.'); - return; - } - showConfirmDialog( 'Alle aus Rechnung entfernen', - (lang.confirmRemoveAll || 'ALLE Positionsgruppen-Elemente aus der Rechnung entfernen?') + '

Die Elemente bleiben in der Verwaltung erhalten.', + (lang.confirmRemoveAll || 'ALLE Positionsgruppen-Elemente (Sections, Textzeilen, Zwischensummen) aus der Rechnung entfernen?') + + '

Inkl. verwaister Einträge. Die Elemente in der Verwaltung bleiben erhalten.', function() { - debugLog('📥 Remove ALL from facturedet...'); + debugLog('📥 Remove ALL from facturedet (server-side)...'); - var done = 0; - var errors = 0; var docType = getDocumentTypeForSync(); + var documentId = getFactureId(); - $checked.each(function() { - var lineId = $(this).data('line-id'); - var lineType = $(this).data('line-type'); + if (!documentId) { + showErrorAlert('Fehler: Keine Dokument-ID gefunden'); + return; + } - $.post(subtotaltitleAjaxUrl + 'sync_to_facturedet.php', { - action: 'remove', - line_id: lineId, - line_type: lineType, - document_type: docType - }, function(response) { - done++; - if (response.success) { - updateSyncCheckbox(lineId, false); - } else { - errors++; - } - if (done >= total) { - debugLog('✅ Remove abgeschlossen: ' + (total - errors) + ' erfolgreich, ' + errors + ' Fehler'); - if (errors > 0) { - showErrorAlert((total - errors) + ' von ' + total + ' Elementen entfernt. ' + errors + ' Fehler aufgetreten.'); - } else { - safeReload(); - } - } - }, 'json').fail(function() { - done++; - errors++; - }); + $.post(subtotaltitleAjaxUrl + 'sync_to_facturedet.php', { + action: 'remove_all', + line_id: 1, // Dummy, wird benötigt wegen Parameter-Check + document_id: documentId, + document_type: docType + }, function(response) { + debugLog('Remove ALL response: ' + JSON.stringify(response)); + if (response.success) { + debugLog('✅ Alle Spezialzeilen entfernt'); + safeReload(); + } else { + showErrorAlert((lang.errorSyncing || 'Fehler') + ': ' + (response.error || 'Unbekannter Fehler')); + } + }, 'json').fail(function(xhr, status, error) { + debugLog('AJAX Fehler: ' + status + ' ' + error); + showErrorAlert((lang.errorSyncing || 'Fehler') + ': ' + error); }); }, 'Ja, alle entfernen', diff --git a/langs/de_DE/subtotaltitle.lang b/langs/de_DE/subtotaltitle.lang index 4961bf7..23d4767 100755 --- a/langs/de_DE/subtotaltitle.lang +++ b/langs/de_DE/subtotaltitle.lang @@ -86,9 +86,9 @@ ButtonCancel = Abbrechen # UI Elements - Section Actions # SectionCreate = Produktgruppe erstellen -SectionEdit = Section bearbeiten -SectionDelete = Section löschen -SectionName = Section-Name +SectionEdit = Produktgruppe bearbeiten +SectionDelete = Produktgruppe löschen +SectionName = Name der Produktgruppe SectionSubtotal = Zwischensumme anzeigen ProductCount = Produkte diff --git a/langs/en_US/subtotaltitle.lang b/langs/en_US/subtotaltitle.lang index 70cf43b..904eb27 100755 --- a/langs/en_US/subtotaltitle.lang +++ b/langs/en_US/subtotaltitle.lang @@ -85,10 +85,10 @@ ButtonCancel = Cancel # # UI Elements - Section Actions # -SectionCreate = Create section -SectionEdit = Edit section -SectionDelete = Delete section -SectionName = Section name +SectionCreate = Create product group +SectionEdit = Edit product group +SectionDelete = Delete product group +SectionName = Product group name SectionSubtotal = Show subtotal ProductCount = Products diff --git a/sql/llx_facture_lines_manager.sql b/sql/llx_facture_lines_manager.sql old mode 100644 new mode 100755