diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..4a1edf1 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + "Bash(grep:*)" + ] + } +} diff --git a/ajax/create_textline.php b/ajax/create_textline.php index 7bb0c44..f98aad8 100755 --- a/ajax/create_textline.php +++ b/ajax/create_textline.php @@ -40,8 +40,8 @@ $fk_propal = ($docType === 'propal') ? (int)$facture_id : 'NULL'; $fk_commande = ($docType === 'order') ? (int)$facture_id : 'NULL'; $sql = "INSERT INTO ".MAIN_DB_PREFIX."facture_lines_manager"; -$sql .= " (fk_facture, fk_propal, fk_commande, document_type, line_type, title, line_order, date_creation)"; -$sql .= " VALUES (".$fk_facture.", ".$fk_propal.", ".$fk_commande.", '".$db->escape($docType)."', 'text', '".$db->escape($text)."', ".$next_order.", NOW())"; +$sql .= " (fk_facture, fk_propal, fk_commande, document_type, line_type, title, line_order, in_facturedet, date_creation)"; +$sql .= " VALUES (".$fk_facture.", ".$fk_propal.", ".$fk_commande.", '".$db->escape($docType)."', 'text', '".$db->escape($text)."', ".$next_order.", 1, NOW())"; if ($db->query($sql)) { $new_id = $db->last_insert_id(MAIN_DB_PREFIX."facture_lines_manager"); diff --git a/ajax/delete_section.php b/ajax/delete_section.php index b21a2a0..3b0cb3a 100755 --- a/ajax/delete_section.php +++ b/ajax/delete_section.php @@ -1,23 +1,31 @@ false, 'error' => 'Missing parameters']); exit; } +// Hole die richtigen Tabellennamen fuer diesen Dokumenttyp +$tables = DocumentTypeHelper::getTableNames($docType); +if (!$tables) { + echo json_encode(['success' => false, 'error' => 'Invalid document type']); + exit; +} + // 1. Hole Section-Info -$sql = "SELECT fk_facture FROM ".MAIN_DB_PREFIX."facture_lines_manager"; +$sql = "SELECT ".$tables['fk_parent']." as doc_id FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql .= " WHERE rowid = ".(int)$section_id; $sql .= " AND line_type = 'section'"; $resql = $db->query($sql); @@ -28,56 +36,62 @@ if (!$resql || $db->num_rows($resql) == 0) { } $section = $db->fetch_object($resql); -$facture_id = $section->fk_facture; +$document_id = $section->doc_id; -// 2. Prüfe Rechnungsstatus -$facture = new Facture($db); -$facture->fetch($facture_id); +// 2. Pruefe Dokumentstatus +$object = DocumentTypeHelper::loadDocument($docType, $document_id, $db); +if (!$object) { + echo json_encode(['success' => false, 'error' => 'Dokument nicht gefunden']); + exit; +} -if ($force && $facture->statut != Facture::STATUS_DRAFT) { - echo json_encode(['success' => false, 'error' => 'Rechnung ist nicht im Entwurf']); +$isDraft = DocumentTypeHelper::isDraft($object, $docType); +if ($force && !$isDraft) { + echo json_encode(['success' => false, 'error' => 'Dokument ist nicht im Entwurf']); exit; } // 3. Hole Produkt-IDs DIREKT aus DB $product_ids = []; -$sql_products = "SELECT fk_facturedet FROM ".MAIN_DB_PREFIX."facture_lines_manager"; +$sql_products = "SELECT ".$tables['fk_line']." as detail_id FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_products .= " WHERE parent_section = ".(int)$section_id; $sql_products .= " AND line_type = 'product'"; $res_products = $db->query($sql_products); while ($prod = $db->fetch_object($res_products)) { - $product_ids[] = (int)$prod->fk_facturedet; + if ($prod->detail_id) { + $product_ids[] = (int)$prod->detail_id; + } } $product_count = count($product_ids); -subtotaltitle_debug_log('🔍 Gefundene Produkte in Section: ' . implode(', ', $product_ids)); +subtotaltitle_debug_log('Gefundene Produkte in Section: ' . implode(', ', $product_ids)); $db->begin(); -// 4. Force-Delete: Produkte aus Rechnung löschen +// 4. Force-Delete: Produkte aus Dokument loeschen if ($force && $product_count > 0) { - subtotaltitle_debug_log('🗑️ Lösche ' . $product_count . ' Zeilen aus Rechnung...'); - + subtotaltitle_debug_log('Loesche ' . $product_count . ' Zeilen aus Dokument...'); + foreach ($product_ids as $line_id) { - $sql_del_line = "DELETE FROM ".MAIN_DB_PREFIX."facturedet WHERE rowid = ".(int)$line_id; + $sql_del_line = "DELETE FROM ".MAIN_DB_PREFIX.$tables['lines_table']." WHERE rowid = ".(int)$line_id; $res_del = $db->query($sql_del_line); - + if ($res_del) { - subtotaltitle_debug_log('✅ facturedet gelöscht: ' . $line_id); + subtotaltitle_debug_log('Detail geloescht: ' . $line_id); } else { - subtotaltitle_debug_log('❌ SQL Fehler: ' . $line_id . ' - ' . $db->lasterror()); + subtotaltitle_debug_log('SQL Fehler: ' . $line_id . ' - ' . $db->lasterror()); } } - - // Aus Manager-Tabelle löschen + + // Aus Manager-Tabelle loeschen $sql_del = "DELETE FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_del .= " WHERE parent_section = ".(int)$section_id; $sql_del .= " AND line_type = 'product'"; $db->query($sql_del); - - subtotaltitle_debug_log('🔴 Force-Delete abgeschlossen: ' . $product_count . ' Produkte'); - + + subtotaltitle_debug_log('Force-Delete abgeschlossen: ' . $product_count . ' Produkte'); + } else if (!$force) { // Ohne force: Produkte nur freigeben $sql = "UPDATE ".MAIN_DB_PREFIX."facture_lines_manager"; @@ -85,36 +99,35 @@ if ($force && $product_count > 0) { $sql .= " WHERE parent_section = ".(int)$section_id; $sql .= " AND line_type = 'product'"; $db->query($sql); - - subtotaltitle_debug_log('🔓 ' . $product_count . ' Produkte freigegeben'); + + subtotaltitle_debug_log($product_count . ' Produkte freigegeben'); } -// ========== NEU: SUBTOTAL LÖSCHEN ========== -// Hole Subtotal dieser Section (falls vorhanden) -$sql_subtotal = "SELECT rowid, fk_facturedet FROM ".MAIN_DB_PREFIX."facture_lines_manager"; +// ========== SUBTOTAL LOESCHEN ========== +$sql_subtotal = "SELECT rowid, ".$tables['fk_line']." as detail_id FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_subtotal .= " WHERE parent_section = ".(int)$section_id; $sql_subtotal .= " AND line_type = 'subtotal'"; $res_subtotal = $db->query($sql_subtotal); if ($obj_sub = $db->fetch_object($res_subtotal)) { - // Falls Subtotal in facturedet ist, dort auch löschen - if ($obj_sub->fk_facturedet > 0) { - $sql_del_fd = "DELETE FROM ".MAIN_DB_PREFIX."facturedet WHERE rowid = ".(int)$obj_sub->fk_facturedet; + // Falls Subtotal in Detail-Tabelle ist, dort auch loeschen + if ($obj_sub->detail_id > 0) { + $sql_del_fd = "DELETE FROM ".MAIN_DB_PREFIX.$tables['lines_table']." WHERE rowid = ".(int)$obj_sub->detail_id; $db->query($sql_del_fd); - subtotaltitle_debug_log('✅ Subtotal aus facturedet gelöscht: ' . $obj_sub->fk_facturedet); + subtotaltitle_debug_log('Subtotal aus Detail geloescht: ' . $obj_sub->detail_id); } - // Aus Manager-Tabelle löschen + // Aus Manager-Tabelle loeschen $sql_del_sub = "DELETE FROM ".MAIN_DB_PREFIX."facture_lines_manager WHERE rowid = ".(int)$obj_sub->rowid; $db->query($sql_del_sub); - subtotaltitle_debug_log('✅ Subtotal aus Manager gelöscht: ' . $obj_sub->rowid); + subtotaltitle_debug_log('Subtotal aus Manager geloescht: ' . $obj_sub->rowid); } -// ========== VERWAISTE SUBTOTALS AUFRÄUMEN ========== -// Finde alle Subtotals in dieser Rechnung, deren parent_section nicht mehr existiert -$sql_orphans = "SELECT s.rowid, s.fk_facturedet, s.parent_section +// ========== VERWAISTE SUBTOTALS AUFRAEUMEN ========== +$sql_orphans = "SELECT s.rowid, s.".$tables['fk_line']." as detail_id, s.parent_section FROM ".MAIN_DB_PREFIX."facture_lines_manager s - WHERE s.fk_facture = ".(int)$facture_id." + WHERE s.".$tables['fk_parent']." = ".(int)$document_id." + AND s.document_type = '".$db->escape($docType)."' AND s.line_type = 'subtotal' AND s.parent_section IS NOT NULL AND NOT EXISTS ( @@ -126,36 +139,33 @@ $res_orphans = $db->query($sql_orphans); $orphan_count = 0; while ($orphan = $db->fetch_object($res_orphans)) { - // Aus facturedet löschen (falls vorhanden) - if ($orphan->fk_facturedet > 0) { - $sql_del_orphan_fd = "DELETE FROM ".MAIN_DB_PREFIX."facturedet WHERE rowid = ".(int)$orphan->fk_facturedet; + if ($orphan->detail_id > 0) { + $sql_del_orphan_fd = "DELETE FROM ".MAIN_DB_PREFIX.$tables['lines_table']." WHERE rowid = ".(int)$orphan->detail_id; $db->query($sql_del_orphan_fd); - subtotaltitle_debug_log('🧹 Verwaistes Subtotal aus facturedet gelöscht: ' . $orphan->fk_facturedet . ' (parent_section=' . $orphan->parent_section . ')'); + subtotaltitle_debug_log('Verwaistes Subtotal aus Detail geloescht: ' . $orphan->detail_id); } - // Aus Manager-Tabelle löschen $sql_del_orphan = "DELETE FROM ".MAIN_DB_PREFIX."facture_lines_manager WHERE rowid = ".(int)$orphan->rowid; $db->query($sql_del_orphan); - subtotaltitle_debug_log('🧹 Verwaistes Subtotal aus Manager gelöscht: ' . $orphan->rowid); $orphan_count++; } if ($orphan_count > 0) { - subtotaltitle_debug_log('🧹 Aufgeräumt: ' . $orphan_count . ' verwaiste Subtotals entfernt'); + subtotaltitle_debug_log('Aufgeraeumt: ' . $orphan_count . ' verwaiste Subtotals entfernt'); } -// ========== ENDE VERWAISTE SUBTOTALS ========== -// 5. Section selbst löschen +// 5. Section selbst loeschen $sql = "DELETE FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql .= " WHERE rowid = ".(int)$section_id; $db->query($sql); -// Rechnungstotale neu berechnen (nach allen Löschungen) -$facture->update_price(1); +// Dokumenttotale neu berechnen +$object->update_price(1); // 6. Neuordnen $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."facture_lines_manager"; -$sql .= " WHERE fk_facture = ".(int)$facture_id; +$sql .= " WHERE ".$tables['fk_parent']." = ".(int)$document_id; +$sql .= " AND document_type = '".$db->escape($docType)."'"; $sql .= " ORDER BY line_order"; $resql = $db->query($sql); @@ -169,21 +179,24 @@ while ($obj = $db->fetch_object($resql)) { } // 7. Sync rang -$sql = "SELECT fk_facturedet FROM ".MAIN_DB_PREFIX."facture_lines_manager"; -$sql .= " WHERE fk_facture = ".(int)$facture_id; -$sql .= " AND line_type = 'product'"; +$sql = "SELECT ".$tables['fk_line']." as detail_id FROM ".MAIN_DB_PREFIX."facture_lines_manager"; +$sql .= " WHERE ".$tables['fk_parent']." = ".(int)$document_id; +$sql .= " AND document_type = '".$db->escape($docType)."'"; +$sql .= " AND ".$tables['fk_line']." IS NOT NULL"; $sql .= " ORDER BY line_order"; $resql = $db->query($sql); $rang = 1; while ($obj = $db->fetch_object($resql)) { - $sql_upd = "UPDATE ".MAIN_DB_PREFIX."facturedet"; - $sql_upd .= " SET rang = ".$rang; - $sql_upd .= " WHERE rowid = ".(int)$obj->fk_facturedet; - $db->query($sql_upd); - $rang++; + if ($obj->detail_id) { + $sql_upd = "UPDATE ".MAIN_DB_PREFIX.$tables['lines_table']; + $sql_upd .= " SET rang = ".$rang; + $sql_upd .= " WHERE rowid = ".(int)$obj->detail_id; + $db->query($sql_upd); + $rang++; + } } $db->commit(); -echo json_encode(['success' => true, 'deleted' => $force ? $product_count : 0]); \ No newline at end of file +echo json_encode(['success' => true, 'deleted' => $force ? $product_count : 0]); diff --git a/ajax/delete_textline.php b/ajax/delete_textline.php index 19e49ae..2871705 100755 --- a/ajax/delete_textline.php +++ b/ajax/delete_textline.php @@ -6,20 +6,42 @@ if (!$res && file_exists("../../../main.inc.php")) $res = @include "../../../mai if (!$res && file_exists("../../../../main.inc.php")) $res = @include "../../../../main.inc.php"; if (!$res) die("Include of main fails"); require_once __DIR__.'/../lib/subtotaltitle.lib.php'; +require_once __DIR__.'/../class/DocumentTypeHelper.class.php'; header('Content-Type: application/json'); $textline_id = GETPOST('textline_id', 'int'); +$docType = GETPOST('document_type', 'alpha'); -subtotaltitle_debug_log('🗑️ delete_textline: id=' . $textline_id); +// Fallback: Wenn kein docType, versuche aus der DB zu ermitteln +if (!$docType) { + $sql_type = "SELECT document_type FROM ".MAIN_DB_PREFIX."facture_lines_manager WHERE rowid = ".(int)$textline_id; + $res_type = $db->query($sql_type); + if ($res_type && $obj_type = $db->fetch_object($res_type)) { + $docType = $obj_type->document_type; + } +} + +if (!$docType) { + $docType = 'invoice'; // Fallback +} + +subtotaltitle_debug_log('delete_textline: id=' . $textline_id . ', docType=' . $docType); if (!$textline_id) { echo json_encode(array('success' => false, 'error' => 'Missing parameters')); exit; } -// 1. Hole facture_id BEVOR wir löschen -$sql = "SELECT fk_facture FROM ".MAIN_DB_PREFIX."facture_lines_manager"; +// Hole die richtigen Tabellennamen fuer diesen Dokumenttyp +$tables = DocumentTypeHelper::getTableNames($docType); +if (!$tables) { + echo json_encode(array('success' => false, 'error' => 'Invalid document type')); + exit; +} + +// 1. Hole document_id BEVOR wir loeschen +$sql = "SELECT ".$tables['fk_parent']." as doc_id FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql .= " WHERE rowid = ".(int)$textline_id; $resql = $db->query($sql); @@ -29,31 +51,54 @@ if (!$resql || $db->num_rows($resql) == 0) { } $obj = $db->fetch_object($resql); -$facture_id = $obj->fk_facture; +$document_id = $obj->doc_id; -// 2. DELETE ausführen +$db->begin(); + +// 2. DELETE ausfuehren $sql = "DELETE FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql .= " WHERE rowid = ".(int)$textline_id; $sql .= " AND line_type = 'text'"; if (!$db->query($sql)) { + $db->rollback(); echo json_encode(array('success' => false, 'error' => $db->lasterror())); exit; } -// 3. Lücken schließen -$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."facture_lines_manager - WHERE fk_facture = ".(int)$facture_id." - ORDER BY line_order"; +// 3. line_order neu durchnummerieren +$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."facture_lines_manager"; +$sql .= " WHERE ".$tables['fk_parent']." = ".(int)$document_id; +$sql .= " AND document_type = '".$db->escape($docType)."'"; +$sql .= " ORDER BY line_order"; $resql = $db->query($sql); $new_order = 1; while ($obj = $db->fetch_object($resql)) { - $sql_upd = "UPDATE ".MAIN_DB_PREFIX."facture_lines_manager - SET line_order = ".$new_order." - WHERE rowid = ".(int)$obj->rowid; + $sql_upd = "UPDATE ".MAIN_DB_PREFIX."facture_lines_manager SET line_order = ".$new_order." WHERE rowid = ".(int)$obj->rowid; $db->query($sql_upd); $new_order++; } -echo json_encode(array('success' => true)); \ No newline at end of file +// 4. rang in Detail-Tabelle synchronisieren +$sql = "SELECT ".$tables['fk_line']." as detail_id FROM ".MAIN_DB_PREFIX."facture_lines_manager"; +$sql .= " WHERE ".$tables['fk_parent']." = ".(int)$document_id; +$sql .= " AND document_type = '".$db->escape($docType)."'"; +$sql .= " AND ".$tables['fk_line']." IS NOT NULL"; +$sql .= " ORDER BY line_order"; +$resql = $db->query($sql); + +$rang = 1; +while ($obj = $db->fetch_object($resql)) { + if ($obj->detail_id) { + $sql_upd = "UPDATE ".MAIN_DB_PREFIX.$tables['lines_table']." SET rang = ".$rang." WHERE rowid = ".(int)$obj->detail_id; + $db->query($sql_upd); + $rang++; + } +} + +subtotaltitle_debug_log('delete_textline: rang synchronisiert, ' . ($rang - 1) . ' Zeilen'); + +$db->commit(); + +echo json_encode(array('success' => true)); diff --git a/ajax/edit_textline.php b/ajax/edit_textline.php index 31846ae..9275e35 100755 --- a/ajax/edit_textline.php +++ b/ajax/edit_textline.php @@ -6,26 +6,44 @@ if (!$res && file_exists("../../../main.inc.php")) $res = @include "../../../mai if (!$res && file_exists("../../../../main.inc.php")) $res = @include "../../../../main.inc.php"; if (!$res) die("Include of main fails"); require_once __DIR__.'/../lib/subtotaltitle.lib.php'; +require_once __DIR__.'/../class/DocumentTypeHelper.class.php'; header('Content-Type: application/json'); $textline_id = GETPOST('textline_id', 'int'); $text = GETPOST('text', 'restricthtml'); -subtotaltitle_debug_log('🔄 edit_textline: id=' . $textline_id); +subtotaltitle_debug_log('edit_textline: id=' . $textline_id); if (!$textline_id || !$text) { echo json_encode(array('success' => false, 'error' => 'Missing parameters')); exit; } -// Hole erst fk_facturedet (falls Textzeile in Rechnung ist) -$sql_get = "SELECT fk_facturedet FROM ".MAIN_DB_PREFIX."facture_lines_manager"; +// Hole erst document_type und FK zur Detail-Tabelle +$sql_get = "SELECT document_type, fk_facturedet, fk_propaldet, fk_commandedet FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_get .= " WHERE rowid = ".(int)$textline_id; $sql_get .= " AND line_type = 'text'"; $resql = $db->query($sql_get); $obj = $db->fetch_object($resql); -$fk_facturedet = $obj ? $obj->fk_facturedet : null; + +if (!$obj) { + echo json_encode(array('success' => false, 'error' => 'Textline not found')); + exit; +} + +$docType = $obj->document_type ?: 'invoice'; +$tables = DocumentTypeHelper::getTableNames($docType); + +// Ermittle FK zur Detail-Tabelle basierend auf Dokumenttyp +$fk_detail = null; +if ($docType == 'invoice' && $obj->fk_facturedet > 0) { + $fk_detail = $obj->fk_facturedet; +} elseif ($docType == 'propal' && $obj->fk_propaldet > 0) { + $fk_detail = $obj->fk_propaldet; +} elseif ($docType == 'order' && $obj->fk_commandedet > 0) { + $fk_detail = $obj->fk_commandedet; +} // Update Manager-Tabelle $sql = "UPDATE ".MAIN_DB_PREFIX."facture_lines_manager"; @@ -38,15 +56,15 @@ if (!$db->query($sql)) { exit; } -// Falls in facturedet vorhanden, dort auch updaten -if ($fk_facturedet > 0) { - $sql_fd = "UPDATE ".MAIN_DB_PREFIX."facturedet"; +// Falls in Detail-Tabelle vorhanden, dort auch updaten +if ($fk_detail > 0 && $tables) { + $sql_fd = "UPDATE ".MAIN_DB_PREFIX.$tables['lines_table']; $sql_fd .= " SET description = '".$db->escape($text)."'"; - $sql_fd .= " WHERE rowid = ".(int)$fk_facturedet; + $sql_fd .= " WHERE rowid = ".(int)$fk_detail; $db->query($sql_fd); - subtotaltitle_debug_log('✅ Textzeile + facturedet geändert'); + subtotaltitle_debug_log('Textzeile + Detail geaendert (docType='.$docType.')'); } else { - subtotaltitle_debug_log('✅ Textzeile geändert (nicht in facturedet)'); + subtotaltitle_debug_log('Textzeile geaendert (nicht in Detail-Tabelle)'); } -echo json_encode(array('success' => true, 'synced_facturedet' => ($fk_facturedet > 0))); +echo json_encode(array('success' => true, 'synced_detail' => ($fk_detail > 0))); diff --git a/ajax/mass_delete.php b/ajax/mass_delete.php index 691e9c2..bfcde9f 100755 --- a/ajax/mass_delete.php +++ b/ajax/mass_delete.php @@ -2,53 +2,66 @@ define('NOTOKENRENEWAL', 1); require '../../../main.inc.php'; require_once __DIR__.'/../lib/subtotaltitle.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; +require_once __DIR__.'/../class/DocumentTypeHelper.class.php'; $line_ids_json = GETPOST('line_ids', 'alpha'); -$facture_id = GETPOST('facture_id', 'int'); +$document_id = GETPOST('facture_id', 'int'); // Kompatibilitaet: facture_id wird auch fuer andere Typen verwendet +$docType = GETPOST('document_type', 'alpha'); $line_ids = json_decode($line_ids_json, true); -if (!is_array($line_ids) || count($line_ids) == 0 || !$facture_id) { +if (!is_array($line_ids) || count($line_ids) == 0 || !$document_id) { echo json_encode(['success' => false, 'error' => 'Missing parameters']); exit; } -// Prüfe Rechnungsstatus -$facture = new Facture($db); -$facture->fetch($facture_id); - -if ($facture->statut != Facture::STATUS_DRAFT) { - echo json_encode(['success' => false, 'error' => 'Rechnung ist nicht im Entwurf']); +// Hole die richtigen Tabellennamen fuer diesen Dokumenttyp +$tables = DocumentTypeHelper::getTableNames($docType); +if (!$tables) { + echo json_encode(['success' => false, 'error' => 'Invalid document type']); exit; } -subtotaltitle_debug_log('🗑️ Massenlöschung: ' . count($line_ids) . ' Zeilen'); +// Pruefe Dokumentstatus +$object = DocumentTypeHelper::loadDocument($docType, $document_id, $db); +if (!$object) { + echo json_encode(['success' => false, 'error' => 'Dokument nicht gefunden']); + exit; +} + +$isDraft = DocumentTypeHelper::isDraft($object, $docType); +if (!$isDraft) { + echo json_encode(['success' => false, 'error' => 'Dokument ist nicht im Entwurf']); + exit; +} + +subtotaltitle_debug_log('Massenloeschung: ' . count($line_ids) . ' Zeilen, docType=' . $docType); $db->begin(); $deleted = 0; foreach ($line_ids as $line_id) { $line_id = (int)$line_id; - - // Aus facturedet löschen - $sql = "DELETE FROM ".MAIN_DB_PREFIX."facturedet WHERE rowid = ".$line_id; + + // Aus Detail-Tabelle loeschen + $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tables['lines_table']." WHERE rowid = ".$line_id; if ($db->query($sql)) { $deleted++; - subtotaltitle_debug_log('✅ Zeile gelöscht: ' . $line_id); + subtotaltitle_debug_log('Zeile geloescht: ' . $line_id); } - - // Aus Manager-Tabelle löschen - $sql_manager = "DELETE FROM ".MAIN_DB_PREFIX."facture_lines_manager WHERE fk_facturedet = ".$line_id; + + // Aus Manager-Tabelle loeschen + $sql_manager = "DELETE FROM ".MAIN_DB_PREFIX."facture_lines_manager WHERE ".$tables['fk_line']." = ".$line_id; $db->query($sql_manager); } // Summen neu berechnen -$facture->update_price(1); +$object->update_price(1); // line_order neu durchnummerieren $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."facture_lines_manager"; -$sql .= " WHERE fk_facture = ".(int)$facture_id; +$sql .= " WHERE ".$tables['fk_parent']." = ".(int)$document_id; +$sql .= " AND document_type = '".$db->escape($docType)."'"; $sql .= " ORDER BY line_order"; $resql = $db->query($sql); @@ -60,21 +73,24 @@ while ($obj = $db->fetch_object($resql)) { } // rang synchronisieren -$sql = "SELECT fk_facturedet FROM ".MAIN_DB_PREFIX."facture_lines_manager"; -$sql .= " WHERE fk_facture = ".(int)$facture_id; -$sql .= " AND line_type = 'product'"; +$sql = "SELECT ".$tables['fk_line']." as detail_id FROM ".MAIN_DB_PREFIX."facture_lines_manager"; +$sql .= " WHERE ".$tables['fk_parent']." = ".(int)$document_id; +$sql .= " AND document_type = '".$db->escape($docType)."'"; +$sql .= " AND ".$tables['fk_line']." IS NOT NULL"; $sql .= " ORDER BY line_order"; $resql = $db->query($sql); $rang = 1; while ($obj = $db->fetch_object($resql)) { - $sql_upd = "UPDATE ".MAIN_DB_PREFIX."facturedet SET rang = ".$rang." WHERE rowid = ".(int)$obj->fk_facturedet; - $db->query($sql_upd); - $rang++; + if ($obj->detail_id) { + $sql_upd = "UPDATE ".MAIN_DB_PREFIX.$tables['lines_table']." SET rang = ".$rang." WHERE rowid = ".(int)$obj->detail_id; + $db->query($sql_upd); + $rang++; + } } $db->commit(); -subtotaltitle_debug_log('🗑️ Massenlöschung abgeschlossen: ' . $deleted . ' von ' . count($line_ids)); +subtotaltitle_debug_log('Massenloeschung abgeschlossen: ' . $deleted . ' von ' . count($line_ids)); -echo json_encode(['success' => true, 'deleted' => $deleted]); \ No newline at end of file +echo json_encode(['success' => true, 'deleted' => $deleted]); diff --git a/class/DocumentTypeHelper.class.php b/class/DocumentTypeHelper.class.php index af4c2ab..d6cfecd 100644 --- a/class/DocumentTypeHelper.class.php +++ b/class/DocumentTypeHelper.class.php @@ -122,4 +122,58 @@ class DocumentTypeHelper return isset($contexts[$type]) ? $contexts[$type] : ''; } + + /** + * Laedt ein Dokument basierend auf Typ und ID + * + * @param string $type Dokumenttyp ('invoice', 'propal', 'order') + * @param int $id Dokument-ID + * @param DoliDB $db Datenbankverbindung + * @return object|null Dolibarr Objekt oder null + */ + public static function loadDocument($type, $id, $db) + { + $object = null; + + if ($type == 'invoice') { + require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; + $object = new Facture($db); + } elseif ($type == 'propal') { + require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php'; + $object = new Propal($db); + } elseif ($type == 'order') { + require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php'; + $object = new Commande($db); + } + + if ($object && $object->fetch($id) > 0) { + return $object; + } + + return null; + } + + /** + * Prueft ob ein Dokument im Entwurfsstatus ist + * + * @param object $object Dolibarr Objekt + * @param string $type Dokumenttyp ('invoice', 'propal', 'order') + * @return bool true wenn Entwurf, sonst false + */ + public static function isDraft($object, $type) + { + if (!$object) { + return false; + } + + // Verschiedene Dokumenttypen haben unterschiedliche Status-Felder + if (isset($object->statut)) { + return ($object->statut == 0); + } + if (isset($object->status)) { + return ($object->status == 0); + } + + return false; + } } diff --git a/class/actions_subtotaltitle.class.php b/class/actions_subtotaltitle.class.php index e332e7b..3c210a2 100755 --- a/class/actions_subtotaltitle.class.php +++ b/class/actions_subtotaltitle.class.php @@ -54,7 +54,8 @@ class ActionsSubtotalTitle extends CommonHookActions global $db; $tables = DocumentTypeHelper::getTableNames($docType); if (!$tables) return ""; - return " WHERE ".$tableAlias.".".$tables['fk_parent']." = ".(int)$document_id." AND ".$tableAlias.".document_type = '".$db->escape($docType)."'"; + $prefix = $tableAlias ? $tableAlias."." : ""; + return " WHERE ".$prefix.$tables['fk_parent']." = ".(int)$document_id." AND ".$prefix."document_type = '".$db->escape($docType)."'"; } /** @@ -141,6 +142,21 @@ class ActionsSubtotalTitle extends CommonHookActions // Lade Übersetzungen $langs->load('subtotaltitle@subtotaltitle'); + // Prüfe ob Sections existieren (für Collapse-Buttons) + global $db; + $tables = DocumentTypeHelper::getTableNames($this->currentDocType); + $hasSections = false; + if ($tables && $object->id) { + $sql_sec = "SELECT COUNT(*) as cnt FROM ".MAIN_DB_PREFIX."facture_lines_manager"; + $sql_sec .= " WHERE ".$tables['fk_parent']." = ".(int)$object->id; + $sql_sec .= " AND document_type = '".$db->escape($this->currentDocType)."'"; + $sql_sec .= " AND line_type = 'section'"; + $res_sec = $db->query($sql_sec); + if ($res_sec && $obj_sec = $db->fetch_object($res_sec)) { + $hasSections = ($obj_sec->cnt > 0); + } + } + // CSS $cssPath = dol_buildpath('/custom/subtotaltitle/css/subtotaltitle.css', 1); echo ''."\n"; @@ -148,6 +164,9 @@ class ActionsSubtotalTitle extends CommonHookActions // Übersetzungen als JavaScript-Variablen bereitstellen echo '