| Server IP : 202.61.199.114 / Your IP : 216.73.217.139 Web Server : nginx/1.22.1 System : Linux de.arni-solutions.de 6.1.0-49-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64 User : web20 ( 1018) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /var/www/seaside.pacim.de/web/ |
Upload File : |
<?php
/**
* booking-hint-reply.php – Antwort auf den Kundenhinweis (booking_hint) einer Buchung.
*
* Nutzt dieselbe Vorlage und Mailing-Regel wie die Kontaktanfrage-Antwort (Template
* `contact_reply`). Es wird nur der Antworttext abgefragt; die fertige Mail kann vorab
* als Vorschau angesehen werden. Beim Senden wird die Antwort am Buchungssatz vermerkt
* (booking_hint_reply*) und – wie die übrige Mail-Pipeline – in pacim_messages protokolliert
* (erscheint im E-Mail-Tab der Buchung).
*
* Aktionen:
* preview POST invoice_id, reply -> { ok, to, subject, html }
* send POST invoice_id, reply -> { ok, message } (rendert serverseitig neu)
*
* Zugriff: nur eingeloggte Admins.
*/
require_once( __DIR__ . '/includes/config.inc.php' );
@ini_set( 'display_errors', '0' );
error_reporting( 0 );
ob_start();
function hr_json( $a ) { while ( ob_get_level() > 0 ) ob_end_clean(); header( 'Content-Type: application/json; charset=utf-8' ); echo json_encode( $a ); exit(); }
function hr_fail( $m ) { hr_json( array( 'ok' => false, 'error' => $m ) ); }
if ( ! ( function_exists( 'is_admin' ) && is_admin() ) ) { http_response_code( 403 ); hr_fail( 'forbidden' ); }
$db = new MysqliDb( MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DB );
$action = $_GET['action'] ?? ( $_POST['action'] ?? '' );
$invoice = (int) ( $_GET['invoice_id'] ?? ( $_POST['invoice_id'] ?? 0 ) );
$reply = trim( (string) ( $_POST['reply'] ?? ( $_GET['reply'] ?? '' ) ) );
if ( $invoice <= 0 ) hr_fail( 'Ungültige Buchung.' );
/** Erste Buchung der Rechnung mit nicht-leerem Kundenhinweis (sonst erste Buchung). */
function hr_hint_booking( $db, $invoice ) {
$b = $db->rawQueryOne( "SELECT booking_id, booking_no, booking_hint, booking_datetime FROM pacim_booking
WHERE booking_invoice = ? AND TRIM(COALESCE(booking_hint,'')) <> ''
ORDER BY booking_id ASC LIMIT 1", array( $invoice ) );
if ( ! $b ) $b = $db->rawQueryOne( "SELECT booking_id, booking_no, booking_hint, booking_datetime FROM pacim_booking WHERE booking_invoice = ? ORDER BY booking_id ASC LIMIT 1", array( $invoice ) );
return $b ?: null;
}
/** contact_reply-Vorlage. */
function hr_template( $db ) {
$t = $db->rawQueryOne( "SELECT * FROM pacim_templates WHERE template_key = 'contact_reply'" );
return $t ? (object) $t : null;
}
/** Render-Kontext für die Antwort aufbauen (Buchung + Antworttext in die request_*-Platzhalter). */
function hr_context( $db, $invoice, $reply ) {
$ctx = mailrenderer::context( $invoice );
$bk = hr_hint_booking( $db, $invoice );
$hint = $bk ? (string) $bk['booking_hint'] : '';
$staff = trim( (string) ( $_SESSION['user_name'] ?? 'Administrator' ) );
$fn = (string) ( $ctx['customer_firstname'] ?? '' );
$ln = (string) ( $ctx['customer_lastname'] ?? '' );
$name = trim( $fn . ' ' . $ln );
$ctx['request_decision_note'] = $reply; // [request_decision_note] = Antworttext
$ctx['request_message'] = $hint; // [request_message] = ursprünglicher Hinweis
$ctx['request_note'] = $hint;
$ctx['request_staff'] = $staff; // [request_staff]
if ( empty( $ctx['name'] ) ) $ctx['name'] = $name !== '' ? $name : ( $ctx['customer_email'] ?? '' );
if ( empty( $ctx['request_created'] ) ) {
$dt = $bk && ! empty( $bk['booking_datetime'] ) ? strtotime( $bk['booking_datetime'] ) : 0;
$ctx['request_created'] = $dt ? date( 'd.m.Y', $dt ) : '';
}
return $ctx;
}
/* ----------------------------------------------------------------- preview */
if ( $action === 'preview' ) {
if ( $reply === '' ) hr_fail( 'Bitte zuerst eine Antwort eingeben.' );
$tpl = hr_template( $db );
if ( ! $tpl ) hr_fail( 'Vorlage „Kontaktanfrage (Antwort)" nicht gefunden.' );
$ctx = hr_context( $db, $invoice, $reply );
$r = mailrenderer::render( $tpl, $ctx, false );
hr_json( array(
'ok' => true,
'to' => trim( (string) ( $ctx['customer_email'] ?? '' ) ),
'subject' => $r['subject'],
'html' => $r['html'],
) );
}
/* ----------------------------------------------------------------- send */
if ( $action === 'send' ) {
if ( $_SERVER['REQUEST_METHOD'] !== 'POST' ) hr_fail( 'method' );
if ( $reply === '' ) hr_fail( 'Bitte eine Antwort eingeben.' );
$tpl = hr_template( $db );
if ( ! $tpl ) hr_fail( 'Vorlage „Kontaktanfrage (Antwort)" nicht gefunden.' );
$tplId = (int) $tpl->template_id;
$ctx = hr_context( $db, $invoice, $reply );
$to = trim( (string) ( $ctx['customer_email'] ?? '' ) );
if ( ! filter_var( $to, FILTER_VALIDATE_EMAIL ) ) hr_fail( 'Für diesen Kunden ist keine gültige E-Mail-Adresse hinterlegt.' );
$r = mailrenderer::render( $tpl, $ctx, false ); // serverseitig rendern = identisch zur Vorschau
$subject = $r['subject'];
$html = $r['html'];
if ( trim( $html ) === '' ) hr_fail( 'Kein Inhalt zum Versenden.' );
$custRow = $db->rawQueryOne( "SELECT invoice_customer FROM pacim_invoice WHERE invoice_id=?", array( $invoice ) );
$bk = hr_hint_booking( $db, $invoice );
$bookingId = (int) ( $bk['booking_id'] ?? 0 );
// Konto & BCC aus DERSELBEN Mailing-Regel (contact_reply), sonst Firmenadresse als Kopie.
$opts = array(); $ruleAccount = 0; $ruleBcc = null;
$rule = $db->rawQueryOne( "SELECT rule_account, rule_bcc FROM pacim_mail_rules WHERE rule_template=? AND rule_active=1 ORDER BY rule_id ASC LIMIT 1", array( $tplId ) );
if ( $rule ) {
if ( (int) $rule['rule_account'] > 0 ) { $ruleAccount = (int) $rule['rule_account']; $opts['account'] = $ruleAccount; }
if ( trim( (string) $rule['rule_bcc'] ) !== '' ) $ruleBcc = trim( (string) $rule['rule_bcc'] );
}
$cfg = mailservice::config();
if ( $ruleBcc === null || $ruleBcc === '' ) {
$fb = trim( (string) $cfg['from_email'] );
if ( $fb !== '' && filter_var( $fb, FILTER_VALIDATE_EMAIL ) ) $ruleBcc = $fb;
}
if ( $ruleBcc !== null && $ruleBcc !== '' ) $opts['bcc'] = $ruleBcc;
$now = date( 'Y-m-d H:i:s' );
$live = mailservice::smtpEnabled();
// Protokoll-Snapshot (erscheint im E-Mail-Tab). message_rule NULL -> Queue prüft keine Regel.
$msgId = $db->insert( 'pacim_messages', array(
'message_status' => 5, 'message_type' => 'system', 'message_count' => 0,
'message_template' => $tplId, 'message_customer' => (int) ( $custRow['invoice_customer'] ?? 0 ),
'message_invoice' => $invoice, 'message_booking' => $bookingId,
'message_subject' => $subject, 'message_sender' => $cfg['from_email'], 'message_receiver' => $to,
'message_account' => $ruleAccount, 'message_bcc' => $ruleBcc,
'message_content' => $html, 'message_token' => pacim_sign( 'msg:' . uniqid( '', true ) ),
'message_datetime' => $now,
) );
// Antwort am Buchungssatz vermerken (alle Buchungen der Rechnung mit Hinweis).
$staff = trim( (string) ( $_SESSION['user_name'] ?? 'Administrator' ) );
$db->rawQuery( "UPDATE pacim_booking SET booking_hint_reply=?, booking_hint_reply_at=?, booking_hint_reply_by=?
WHERE booking_invoice=? AND TRIM(COALESCE(booking_hint,'')) <> ''",
array( $reply, $now, $staff, $invoice ) );
// Audit (still scheitern, falls apilog fehlt)
try {
if ( class_exists( 'apilog' ) && $bookingId > 0 ) {
apilog::record( array(
'key_name' => $staff, 'ip' => $_SERVER['REMOTE_ADDR'] ?? '', 'method' => 'POST',
'resource' => 'booking', 'action' => 'hint_reply', 'kind' => 'write',
'booking' => $bookingId, 'invoice' => $invoice,
'target' => 'Antwort auf Kundenhinweis gesendet (' . ( $bk['booking_no'] ?? '' ) . ')', 'changes' => null,
) );
}
} catch ( Exception $e ) {}
if ( ! $live ) {
hr_json( array( 'ok' => true, 'queued' => true, 'message' => 'Antwort gespeichert und in die Warteschlange gelegt – sie wird versendet, sobald der SMTP-Versand aktiv ist.' ) );
}
$res = mailservice::send( $to, '', $subject, $html, array(), $opts );
if ( ! empty( $res['ok'] ) ) {
$db->where( 'message_id', (int) $msgId );
$db->update( 'pacim_messages', array( 'message_status' => 1, 'message_count' => 1, 'message_error' => null ) );
hr_json( array( 'ok' => true, 'message' => 'Antwort an den Kunden gesendet.' ) );
}
$db->where( 'message_id', (int) $msgId );
$db->update( 'pacim_messages', array( 'message_count' => 1, 'message_error' => mb_substr( (string) ( $res['error'] ?? 'unbekannt' ), 0, 500 ) ) );
hr_fail( 'Versand fehlgeschlagen: ' . ( $res['error'] ?? 'unbekannt' ) . '. Die Antwort wurde vermerkt und bleibt in der Warteschlange.' );
}
hr_fail( 'unknown_action' );