false, 'error' => 'Missing parameters']); exit; } $tables = DocumentTypeHelper::getTableNames($docType); if (!$tables) { echo json_encode(['success' => false, 'error' => 'Invalid document type']); exit; } $db->begin(); // 1. Hole die Manager-Zeile des Produkts $sql = "SELECT rowid, line_order FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql .= " WHERE ".$tables['fk_line']." = ".(int)$line_id; $sql .= " AND ".$tables['fk_parent']." = ".(int)$document_id; $sql .= " AND document_type = '".$db->escape($docType)."'"; $sql .= " AND line_type = 'product'"; $resql = $db->query($sql); if (!$resql || $db->num_rows($resql) == 0) { $db->rollback(); echo json_encode(['success' => false, 'error' => 'Product not found in manager table']); exit; } $product = $db->fetch_object($resql); $manager_id = $product->rowid; $current_line_order = $product->line_order; // 2. Hole die line_order der Section $sql_section = "SELECT line_order FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_section .= " WHERE rowid = ".(int)$section_id; $sql_section .= " AND line_type = 'section'"; $sql_section .= " AND document_type = '".$db->escape($docType)."'"; $resql_section = $db->query($sql_section); if (!$resql_section || $db->num_rows($resql_section) == 0) { $db->rollback(); echo json_encode(['success' => false, 'error' => 'Section not found']); exit; } $section = $db->fetch_object($resql_section); $section_line_order = $section->line_order; // 3. Finde die neue line_order für das Produkt // Das Produkt soll IMMER als LETZTES Produkt der Section eingefügt werden // (nach allen bestehenden Produkten dieser Section, aber vor der nächsten Section oder dem Subtotal) // Suche das letzte Produkt/Text dieser Section $sql_last = "SELECT MAX(line_order) as max_order FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_last .= " WHERE ".$tables['fk_parent']." = ".(int)$document_id; $sql_last .= " AND document_type = '".$db->escape($docType)."'"; $sql_last .= " AND parent_section = ".(int)$section_id; $sql_last .= " AND line_type IN ('product', 'text')"; $sql_last .= " AND rowid != ".(int)$manager_id; // Nicht das aktuelle Produkt selbst $resql_last = $db->query($sql_last); $obj_last = $db->fetch_object($resql_last); if ($obj_last && $obj_last->max_order) { // Es gibt bereits Produkte in dieser Section → füge NACH dem letzten ein $new_line_order = (int)$obj_last->max_order + 1; } else { // Keine anderen Produkte in der Section → füge direkt nach der Section ein $new_line_order = $section_line_order + 1; } subtotaltitle_debug_log(' current_line_order='.$current_line_order.', section_line_order='.$section_line_order.', new_line_order='.$new_line_order); // 4. Verschiebe Zeilen // WICHTIG: Zuerst das Produkt "entfernen" (temporär auf -1 setzen), dann verschieben, dann einfügen $sql_temp = "UPDATE ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_temp .= " SET line_order = -1"; $sql_temp .= " WHERE rowid = ".(int)$manager_id; $db->query($sql_temp); if ($current_line_order < $new_line_order) { // Produkt wird nach hinten verschoben // Schließe die Lücke: alle Zeilen zwischen current+1 und new_line_order um -1 verschieben $sql_shift = "UPDATE ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_shift .= " SET line_order = line_order - 1"; $sql_shift .= " WHERE ".$tables['fk_parent']." = ".(int)$document_id; $sql_shift .= " AND document_type = '".$db->escape($docType)."'"; $sql_shift .= " AND line_order > ".(int)$current_line_order; $sql_shift .= " AND line_order <= ".(int)$new_line_order; $db->query($sql_shift); // Korrigiere new_line_order (weil wir verschoben haben) $new_line_order = $new_line_order - 1; subtotaltitle_debug_log(' Nach hinten: Lücke geschlossen, new_line_order korrigiert auf '.$new_line_order); } elseif ($current_line_order > $new_line_order) { // Produkt wird nach vorne verschoben // Mache Platz: alle Zeilen ab new_line_order bis current-1 um +1 verschieben $sql_shift = "UPDATE ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_shift .= " SET line_order = line_order + 1"; $sql_shift .= " WHERE ".$tables['fk_parent']." = ".(int)$document_id; $sql_shift .= " AND document_type = '".$db->escape($docType)."'"; $sql_shift .= " AND line_order >= ".(int)$new_line_order; $sql_shift .= " AND line_order < ".(int)$current_line_order; $db->query($sql_shift); subtotaltitle_debug_log(' Nach vorne: Platz gemacht ab position '.$new_line_order); } // 5. Update das Produkt: setze parent_section und neue line_order $sql_update = "UPDATE ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_update .= " SET parent_section = ".(int)$section_id; $sql_update .= ", line_order = ".(int)$new_line_order; $sql_update .= " WHERE rowid = ".(int)$manager_id; $db->query($sql_update); subtotaltitle_debug_log('✅ Produkt #'.$line_id.' zu Section #'.$section_id.' hinzugefügt mit line_order='.$new_line_order); // 6. Normalisiere alle line_order (keine Lücken, keine Duplikate) $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, rowid"; // Bei Gleichstand nach rowid sortieren $resql = $db->query($sql); $new_order = 1; while ($obj = $db->fetch_object($resql)) { $sql_upd = "UPDATE ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_upd .= " SET line_order = ".$new_order; $sql_upd .= " WHERE rowid = ".(int)$obj->rowid; $db->query($sql_upd); $new_order++; } subtotaltitle_debug_log(' line_order normalisiert: '.($new_order-1).' Zeilen'); // 7. Sync rang in Detail-Tabelle subtotaltitle_debug_log(' Starte rang-Synchronisation für docType='.$docType.' doc_id='.$document_id); $sql = "SELECT rowid, line_type, ".$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)) { $detail_id = $obj->detail_id; if ($detail_id) { $sql_upd = "UPDATE ".MAIN_DB_PREFIX.$tables['lines_table']; $sql_upd .= " SET rang = ".$rang; $sql_upd .= " WHERE rowid = ".(int)$detail_id; $db->query($sql_upd); subtotaltitle_debug_log(' Sync rang: '.$obj->line_type.' manager#'.$obj->rowid.' detail#'.$detail_id.' → rang='.$rang); $rang++; } } subtotaltitle_debug_log(' rang-Synchronisation abgeschlossen, '.$rang.' Zeilen synchronisiert'); $db->commit(); echo json_encode(['success' => true]);