false, 'error' => 'Missing section_id']); exit; } // Hole Section-Info $sql = "SELECT fk_facture, title 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->fk_facture; $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."facturedet d ON d.rowid = m.fk_facturedet"; $sql_sum .= " WHERE m.parent_section = ".(int)$section_id; $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; // Neue line_order = nach letztem Produkt $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 fk_facture = ".(int)$facture_id; $sql_shift .= " AND line_order >= ".$new_order; $db->query($sql_shift); // Subtotal-Zeile in Manager-Tabelle einfügen $sql_ins = "INSERT INTO ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_ins .= " (fk_facture, line_type, parent_section, title, line_order, date_creation)"; $sql_ins .= " VALUES (".(int)$facture_id.", '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 facturedet einfügen // Bestimme rang (nach letztem Produkt der Section) $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."facturedet d ON d.rowid = m.fk_facturedet"; $sql_rang .= " WHERE m.parent_section = ".(int)$section_id; $sql_rang .= " AND m.line_type = 'product'"; $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."facturedet"; $sql_shift_fd .= " SET rang = rang + 1"; $sql_shift_fd .= " WHERE fk_facture = ".(int)$facture_id; $sql_shift_fd .= " AND rang >= ".(int)$new_rang; $db->query($sql_shift_fd); // Füge Subtotal in facturedet ein (special_code = 102) $sql_ins_fd = "INSERT INTO ".MAIN_DB_PREFIX."facturedet"; $sql_ins_fd .= " (fk_facture, description, qty, subprice, total_ht, total_tva, total_ttc,"; $sql_ins_fd .= " tva_tx, product_type, special_code, rang, info_bits)"; $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)"; $db->query($sql_ins_fd); $new_facturedet_id = $db->last_insert_id(MAIN_DB_PREFIX."facturedet"); // Verknüpfe Manager-Eintrag mit facturedet $sql_link = "UPDATE ".MAIN_DB_PREFIX."facture_lines_manager"; $sql_link .= " SET fk_facturedet = ".(int)$new_facturedet_id; $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 . ' (facturedet #' . $new_facturedet_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 ]);