db->fetch('SELECT * FROM orders WHERE id = ?', [$orderId]); if ($order === null) { throw new RuntimeException('Order not found.'); } $sendEmail = $sendEmail || $status === 'accepted'; $pdo = $this->db->pdo(); $pdo->beginTransaction(); try { $this->db->execute( 'UPDATE orders SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?', [$status, $orderId] ); $this->db->execute( 'INSERT INTO order_events (order_id, user_id, event_type, details, status, note, customer_visible, email_sent) VALUES (?, ?, ?, ?, ?, ?, ?, 0)', [ $orderId, $staffUserId, 'status_changed', 'Status changed to ' . order_status_label($status) . ($note !== '' ? '. ' . $note : '.'), $status, $note, $customerVisible && $note !== '' ? 1 : 0, ] ); $eventId = (int) $pdo->lastInsertId(); $pdo->commit(); } catch (\Throwable $error) { $pdo->rollBack(); throw $error; } $emailError = null; if ($sendEmail) { $order['status'] = $status; try { $this->sendStatusEmail($order, $status, $customerVisible ? $note : ''); $this->db->execute('UPDATE order_events SET email_sent = 1 WHERE id = ?', [$eventId]); } catch (\Throwable $error) { $emailError = $error->getMessage(); } } return ['email_requested' => $sendEmail, 'email_error' => $emailError]; } public function sendStatusEmail(array $order, string $status, string $visibleNote = ''): void { $subjectTemplate = (string) $this->config->get( 'order_emails.' . $status . '.subject', '{{business_name}} order {{order_number}}: {{status_label}}' ); $bodyTemplate = (string) $this->config->get( 'order_emails.' . $status . '.body', "Hi {{customer_name}},\n\nYour order {{order_number}} is now {{status_label}}.\n\n{{note}}\n\n{{tracking_url}}" ); $values = [ '{{business_name}}' => (string) $this->config->get('business.name', 'Lakeside Orders'), '{{customer_name}}' => (string) $order['customer_name'], '{{order_number}}' => (string) $order['order_number'], '{{status_label}}' => order_status_label($status), '{{note}}' => $visibleNote, '{{rejection_reason}}' => $status === 'declined' ? $visibleNote : '', '{{tracking_url}}' => $this->trackingUrl($order), ]; $subject = trim(strip_tags(strtr($subjectTemplate, $values))); $html = nl2br(strtr(htmlspecialchars($bodyTemplate, ENT_QUOTES, 'UTF-8'), [ '{{business_name}}' => e($values['{{business_name}}']), '{{customer_name}}' => e($values['{{customer_name}}']), '{{order_number}}' => e($values['{{order_number}}']), '{{status_label}}' => e($values['{{status_label}}']), '{{note}}' => e($values['{{note}}']), '{{rejection_reason}}' => e($values['{{rejection_reason}}']), '{{tracking_url}}' => 'View your order tracker', ])); $this->mailer->send((string) $order['customer_email'], $subject, '

' . $html . '

'); } public function trackingUrl(array $order): string { return rtrim((string) $this->config->get('app.url'), '/') . '/order/track?order=' . rawurlencode((string) $order['order_number']) . '&token=' . rawurlencode((string) $order['tracking_token']); } }