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 '