false, 'error' => 'Missing parameters']); exit; } // Hole die richtigen Tabellennamen für diesen Dokumenttyp $tables = DocumentTypeHelper::getTableNames($docType); if (!$tables) { echo json_encode(['success' => false, 'error' => 'Invalid document type']); exit; } $db->begin(); // Hole das neueste Produkt dieses Dokuments (höchster rang) $sql = "SELECT rowid, rang FROM ".MAIN_DB_PREFIX.$tables['lines_table']; $sql .= " WHERE ".$tables['fk_parent']." = ".(int)$facture_id; $sql .= " ORDER BY rang DESC LIMIT 1"; $resql = $db->query($sql); if (!$resql || $db->num_rows($resql) == 0) { $db->rollback(); echo json_encode(['success' => false, 'error' => 'Kein Produkt gefunden']); exit; } $product = $db->fetch_object($resql); $product_id = $product->rowid; subtotaltitle_debug_log(' → Neustes Produkt: #' . $product_id . ' (rang=' . $product->rang . ')'); // Prüfe ob schon in Manager-Tabelle (anhand der Detail-FK-Spalte) $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql .= " WHERE ".$tables['fk_line']." = ".(int)$product_id; $resql = $db->query($sql); // Hole die line_order der Section (Produkt soll direkt danach kommen) $sql_section = "SELECT line_order FROM ".MAIN_DB_PREFIX."facture_lines_manager WHERE rowid = ".(int)$section_id; $resql_section = $db->query($sql_section); $section_order = 1; if ($obj_section = $db->fetch_object($resql_section)) { $section_order = $obj_section->line_order; } // Berechne neue line_order: Höchste line_order der Produkte in dieser Section + 1 // Oder Section line_order + 1 wenn keine Produkte vorhanden $sql_max_in_section = "SELECT MAX(line_order) as max_order FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_max_in_section .= " WHERE parent_section = ".(int)$section_id; $sql_max_in_section .= " AND line_type = 'product'"; $resql_max_section = $db->query($sql_max_in_section); $obj_max_section = $db->fetch_object($resql_max_section); if ($obj_max_section && $obj_max_section->max_order) { $new_line_order = $obj_max_section->max_order + 1; } else { $new_line_order = $section_order + 1; } subtotaltitle_debug_log(' → Section line_order='.$section_order.', neue Produkt line_order='.$new_line_order); // Verschiebe alle nachfolgenden Zeilen um 1 nach hinten $sql_shift = "UPDATE ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_shift .= " SET line_order = line_order + 1"; $sql_shift .= " WHERE ".$tables['fk_parent']." = ".(int)$facture_id; $sql_shift .= " AND document_type = '".$db->escape($docType)."'"; $sql_shift .= " AND line_order >= ".$new_line_order; $db->query($sql_shift); if ($db->num_rows($resql) == 0) { // Produkt fehlt - hinzufügen // Setze alle FK-Felder explizit (NULL für nicht genutzte) $fk_facture = ($docType === 'invoice') ? (int)$facture_id : 'NULL'; $fk_propal = ($docType === 'propal') ? (int)$facture_id : 'NULL'; $fk_commande = ($docType === 'order') ? (int)$facture_id : 'NULL'; $sql_ins = "INSERT INTO ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_ins .= " (fk_facture, fk_propal, fk_commande, document_type, line_type, ".$tables['fk_line'].", parent_section, line_order, date_creation)"; $sql_ins .= " VALUES (".$fk_facture.", ".$fk_propal.", ".$fk_commande.", '".$db->escape($docType)."', 'product', ".(int)$product_id.", ".(int)$section_id.", ".$new_line_order.", NOW())"; $db->query($sql_ins); subtotaltitle_debug_log(' → Produkt zu Manager-Tabelle hinzugefügt (line_order=' . $new_line_order . ')'); } else { // Produkt existiert - UPDATE parent_section UND line_order subtotaltitle_debug_log('🔵🔵🔵 assign_last_product: Produkt #'.$product_id.' → parent_section='.$section_id.', line_order='.$new_line_order); $sql_upd = "UPDATE ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_upd .= " SET parent_section = ".(int)$section_id; $sql_upd .= ", line_order = ".$new_line_order; $sql_upd .= " WHERE ".$tables['fk_line']." = ".(int)$product_id; $db->query($sql_upd); subtotaltitle_debug_log(' → parent_section und line_order updated'); } // Neu sortieren require_once DOL_DOCUMENT_ROOT.'/custom/subtotaltitle/class/actions_subtotaltitle.class.php'; $hook = new ActionsSubtotalTitle($db); $reflection = new ReflectionClass($hook); $method = $reflection->getMethod('reorderLines'); $method->setAccessible(true); $method->invoke($hook, $facture_id); $method = $reflection->getMethod('syncRangFromManager'); $method->setAccessible(true); $method->invoke($hook, $facture_id); $db->commit(); subtotaltitle_debug_log('✅ Assignment erfolgreich'); echo json_encode(['success' => true, 'product_id' => $product_id]);