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; } // Hole Section-Info $sql = "SELECT fk_facture, fk_propal, fk_commande, title, document_type FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql .= " WHERE rowid = ".(int)$section_id; $resql = $db->query($sql); $section = $db->fetch_object($resql); $facture_id = $section->{$tables['fk_parent']}; $db->begin(); // Update show_subtotal Flag $sql = "UPDATE ".MAIN_DB_PREFIX."facture_lines_manager"; $sql .= " SET show_subtotal = ".(int)$show; $sql .= " WHERE rowid = ".(int)$section_id; $db->query($sql); if ($show) { // Prüfe ob Subtotal-Zeile schon existiert $sql_check = "SELECT rowid FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_check .= " WHERE parent_section = ".(int)$section_id; $sql_check .= " AND line_type = 'subtotal'"; $res_check = $db->query($sql_check); if ($db->num_rows($res_check) == 0) { // Berechne Zwischensumme $sql_sum = "SELECT SUM(d.total_ht) as total"; $sql_sum .= " FROM ".MAIN_DB_PREFIX."facture_lines_manager m"; $sql_sum .= " INNER JOIN ".MAIN_DB_PREFIX.$tables['lines_table']." d ON d.rowid = m.".$tables['fk_line']; $sql_sum .= " WHERE m.parent_section = ".(int)$section_id; $sql_sum .= " AND m.document_type = '".$db->escape($docType)."'"; $sql_sum .= " AND m.line_type = 'product'"; $res_sum = $db->query($sql_sum); $obj_sum = $db->fetch_object($res_sum); $subtotal_ht = $obj_sum->total ? (float)$obj_sum->total : 0; // Finde line_order des letzten Produkts dieser Section $sql_last = "SELECT MAX(line_order) as max_order FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_last .= " WHERE parent_section = ".(int)$section_id; $sql_last .= " AND line_type = 'product'"; $res_last = $db->query($sql_last); $obj_last = $db->fetch_object($res_last); $last_order = $obj_last->max_order ? $obj_last->max_order : 0; // Wenn keine Produkte in der Section, nimm die line_order der Section selbst if ($last_order == 0) { $sql_sec = "SELECT line_order FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_sec .= " WHERE rowid = ".(int)$section_id; $res_sec = $db->query($sql_sec); $obj_sec = $db->fetch_object($res_sec); $last_order = $obj_sec->line_order ? $obj_sec->line_order : 0; } // Neue line_order = nach letztem Produkt (oder nach der Section selbst wenn leer) $new_order = $last_order + 1; // Alle nachfolgenden Zeilen +1 $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_order; $db->query($sql_shift); // Bestimme FK-Felder $fk_facture = ($docType === 'invoice') ? (int)$facture_id : 'NULL'; $fk_propal = ($docType === 'propal') ? (int)$facture_id : 'NULL'; $fk_commande = ($docType === 'order') ? (int)$facture_id : 'NULL'; // Subtotal-Zeile in Manager-Tabelle einfügen $sql_ins = "INSERT INTO ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_ins .= " (fk_facture, fk_propal, fk_commande, document_type, line_type, parent_section, title, line_order, date_creation)"; $sql_ins .= " VALUES (".$fk_facture.", ".$fk_propal.", ".$fk_commande.", '".$db->escape($docType)."', 'subtotal', ".(int)$section_id.", 'Zwischensumme: ".addslashes($section->title)."', ".$new_order.", NOW())"; $db->query($sql_ins); $subtotal_manager_id = $db->last_insert_id(MAIN_DB_PREFIX."facture_lines_manager"); // Subtotal-Zeile auch direkt in Detail-Tabelle einfügen // Bestimme rang basierend auf line_order (EINHEITLICH für alle Zeilentypen) // Finde den höchsten rang aller Zeilen, die VOR dieser neuen Subtotal-Zeile liegen $sql_rang = "SELECT MAX(d.rang) as max_rang"; $sql_rang .= " FROM ".MAIN_DB_PREFIX."facture_lines_manager m"; $sql_rang .= " INNER JOIN ".MAIN_DB_PREFIX.$tables['lines_table']." d ON d.rowid = m.".$tables['fk_line']; $sql_rang .= " WHERE m.".$tables['fk_parent']." = ".(int)$facture_id; $sql_rang .= " AND m.document_type = '".$db->escape($docType)."'"; $sql_rang .= " AND m.line_order < ".(int)$new_order; $res_rang = $db->query($sql_rang); $obj_rang = $db->fetch_object($res_rang); $new_rang = ($obj_rang && $obj_rang->max_rang) ? $obj_rang->max_rang + 1 : 1; // Verschiebe nachfolgende Zeilen $sql_shift_fd = "UPDATE ".MAIN_DB_PREFIX.$tables['lines_table']; $sql_shift_fd .= " SET rang = rang + 1"; $sql_shift_fd .= " WHERE ".$tables['fk_parent']." = ".(int)$facture_id; $sql_shift_fd .= " AND rang >= ".(int)$new_rang; $db->query($sql_shift_fd); // Füge Subtotal in Detail-Tabelle ein (special_code = 102) $sql_ins_fd = "INSERT INTO ".MAIN_DB_PREFIX.$tables['lines_table']; $sql_ins_fd .= " (".$tables['fk_parent'].", description, qty, subprice, total_ht, total_tva, total_ttc,"; $sql_ins_fd .= " tva_tx, product_type, special_code, rang, info_bits,"; $sql_ins_fd .= " multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc)"; $sql_ins_fd .= " VALUES ("; $sql_ins_fd .= (int)$facture_id.", "; $sql_ins_fd .= "'".$db->escape('Zwischensumme: '.$section->title)."', "; $sql_ins_fd .= "1, "; // qty $sql_ins_fd .= (float)$subtotal_ht.", "; // subprice $sql_ins_fd .= (float)$subtotal_ht.", "; // total_ht $sql_ins_fd .= "0, "; // total_tva $sql_ins_fd .= (float)$subtotal_ht.", "; // total_ttc $sql_ins_fd .= "0, "; // tva_tx $sql_ins_fd .= "9, "; // product_type = 9 (Titel/Kommentar) $sql_ins_fd .= "102, "; // special_code = 102 (Subtotal) $sql_ins_fd .= (int)$new_rang.", "; $sql_ins_fd .= "0, "; // info_bits $sql_ins_fd .= (float)$subtotal_ht.", "; // multicurrency_subprice $sql_ins_fd .= (float)$subtotal_ht.", "; // multicurrency_total_ht $sql_ins_fd .= "0, "; // multicurrency_total_tva $sql_ins_fd .= (float)$subtotal_ht.")"; $db->query($sql_ins_fd); $new_detail_id = $db->last_insert_id(MAIN_DB_PREFIX.$tables['lines_table']); // Verknüpfe Manager-Eintrag mit Detail-Tabelle $sql_link = "UPDATE ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_link .= " SET ".$tables['fk_line']." = ".(int)$new_detail_id; $sql_link .= ", in_facturedet = 1"; $sql_link .= " WHERE rowid = ".(int)$subtotal_manager_id; $db->query($sql_link); subtotaltitle_debug_log('✅ Subtotal-Zeile erstellt für Section ' . $section_id . ' mit Summe ' . $subtotal_ht . ' ('.$tables['lines_table'].' #' . $new_detail_id . ', special_code=102)'); } } else { // Hole erst fk_facturedet bevor wir löschen $sql_get = "SELECT fk_facturedet FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_get .= " WHERE parent_section = ".(int)$section_id; $sql_get .= " AND line_type = 'subtotal'"; $res_get = $db->query($sql_get); $obj_get = $db->fetch_object($res_get); $fk_facturedet = $obj_get ? $obj_get->fk_facturedet : null; // Aus facturedet löschen (falls vorhanden) if ($fk_facturedet > 0) { $sql_del_fd = "DELETE FROM ".MAIN_DB_PREFIX."facturedet WHERE rowid = ".(int)$fk_facturedet; $db->query($sql_del_fd); subtotaltitle_debug_log('🗑️ Subtotal aus facturedet gelöscht: #' . $fk_facturedet); } // Subtotal-Zeile aus Manager löschen $sql_del = "DELETE FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_del .= " WHERE parent_section = ".(int)$section_id; $sql_del .= " AND line_type = 'subtotal'"; $db->query($sql_del); subtotaltitle_debug_log('🗑️ Subtotal-Zeile gelöscht für Section ' . $section_id); // line_order neu durchnummerieren $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."facture_lines_manager"; $sql .= " WHERE fk_facture = ".(int)$facture_id; $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"; $sql_upd .= " SET line_order = ".$new_order; $sql_upd .= " WHERE rowid = ".(int)$obj->rowid; $db->query($sql_upd); $new_order++; } } $db->commit(); echo json_encode([ 'success' => true, 'show' => $show, 'reload' => true ]);