functions.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  1. <?php
  2. require_once __DIR__ . '/../config.php';
  3. /**
  4. * Read JSON file and return decoded data
  5. */
  6. function readJsonFile($file) {
  7. if (!file_exists($file)) {
  8. return [];
  9. }
  10. $content = file_get_contents($file);
  11. if (empty($content)) {
  12. return [];
  13. }
  14. $data = json_decode($content, true);
  15. return $data ? $data : [];
  16. }
  17. /**
  18. * Write data to JSON file
  19. */
  20. function writeJsonFile($file, $data) {
  21. $dir = dirname($file);
  22. if (!is_dir($dir)) {
  23. mkdir($dir, 0755, true);
  24. }
  25. file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
  26. }
  27. /**
  28. * Get all products
  29. */
  30. function getProducts() {
  31. $data = readJsonFile(PRODUCTS_FILE);
  32. return isset($data['products']) ? $data['products'] : [];
  33. }
  34. /**
  35. * Get product by ID
  36. */
  37. function getProductById($id) {
  38. $products = getProducts();
  39. foreach ($products as $product) {
  40. if ($product['id'] == $id) {
  41. return $product;
  42. }
  43. }
  44. return null;
  45. }
  46. /**
  47. * Save products
  48. */
  49. function saveProducts($products) {
  50. $data = ['products' => $products];
  51. writeJsonFile(PRODUCTS_FILE, $data);
  52. }
  53. /**
  54. * Get all reservations
  55. */
  56. function getReservations() {
  57. $data = readJsonFile(RESERVATIONS_FILE);
  58. return isset($data['reservations']) ? $data['reservations'] : [];
  59. }
  60. /**
  61. * Get reservation by order number
  62. */
  63. function getReservationByOrderNumber($orderNumber) {
  64. $reservations = getReservations();
  65. foreach ($reservations as $reservation) {
  66. if (isset($reservation['id']) && $reservation['id'] === $orderNumber && !isReservationHidden($reservation)) {
  67. return $reservation;
  68. }
  69. }
  70. return null;
  71. }
  72. /**
  73. * Check if reservation is hidden (spam/deleted)
  74. */
  75. function isReservationHidden($reservation) {
  76. return isset($reservation['is_hidden']) && $reservation['is_hidden'] === true;
  77. }
  78. /**
  79. * Save reservations
  80. */
  81. function saveReservations($reservations) {
  82. $data = ['reservations' => $reservations];
  83. writeJsonFile(RESERVATIONS_FILE, $data);
  84. }
  85. /**
  86. * Generate order number
  87. * Pattern: PREFIX-YEAR-SEQ
  88. */
  89. function generateReservationId() {
  90. $reservations = getReservations();
  91. $year = date('Y');
  92. $prefix = defined('ORDER_PREFIX') ? ORDER_PREFIX : 'ORD';
  93. $max = 0;
  94. $pattern = '/^' . preg_quote($prefix, '/') . '-\\d{4}-(\\d+)$/';
  95. foreach ($reservations as $reservation) {
  96. if (!isset($reservation['id'])) {
  97. continue;
  98. }
  99. if (preg_match($pattern, $reservation['id'], $matches)) {
  100. $num = (int)$matches[1];
  101. if ($num > $max) {
  102. $max = $num;
  103. }
  104. }
  105. }
  106. $next = $max + 1;
  107. return sprintf('%s-%s-%03d', $prefix, $year, $next);
  108. }
  109. /**
  110. * Check if product has enough stock
  111. * For apparel: checks stock for specific size
  112. * For merch: checks general stock
  113. */
  114. function checkStock($productId, $quantity, $size = null) {
  115. $product = getProductById($productId);
  116. if (!$product) {
  117. return false;
  118. }
  119. // For apparel with sizes, check stock per size
  120. if ($product['category'] === 'apparel' && !empty($product['sizes']) && $size !== null) {
  121. if (!isset($product['stock_by_size']) || !is_array($product['stock_by_size'])) {
  122. return false;
  123. }
  124. $sizeStock = isset($product['stock_by_size'][$size]) ? (int)$product['stock_by_size'][$size] : 0;
  125. return $sizeStock >= $quantity;
  126. }
  127. // For merch or apparel without size-specific stock, use general stock
  128. $stock = isset($product['stock']) ? (int)$product['stock'] : 0;
  129. return $stock >= $quantity;
  130. }
  131. /**
  132. * Get stock for a product (per size for apparel, general for merch)
  133. */
  134. function getStock($product, $size = null) {
  135. if ($product['category'] === 'apparel' && !empty($product['sizes']) && $size !== null) {
  136. if (isset($product['stock_by_size']) && is_array($product['stock_by_size'])) {
  137. return isset($product['stock_by_size'][$size]) ? (int)$product['stock_by_size'][$size] : 0;
  138. }
  139. }
  140. return isset($product['stock']) ? (int)$product['stock'] : 0;
  141. }
  142. /**
  143. * Get total stock for a product (sum of all sizes for apparel)
  144. */
  145. function getTotalStock($product) {
  146. if ($product['category'] === 'apparel' && isset($product['stock_by_size']) && is_array($product['stock_by_size'])) {
  147. return array_sum($product['stock_by_size']);
  148. }
  149. return isset($product['stock']) ? (int)$product['stock'] : 0;
  150. }
  151. /**
  152. * Allocate stock for reservation
  153. */
  154. function allocateStock($productId, $quantity, $size = null) {
  155. $products = getProducts();
  156. foreach ($products as &$product) {
  157. if ($product['id'] == $productId) {
  158. // For apparel with sizes, allocate per size
  159. if ($product['category'] === 'apparel' && !empty($product['sizes']) && $size !== null) {
  160. if (!isset($product['stock_by_size']) || !is_array($product['stock_by_size'])) {
  161. $product['stock_by_size'] = [];
  162. }
  163. if (!isset($product['stock_by_size'][$size])) {
  164. $product['stock_by_size'][$size] = 0;
  165. }
  166. $product['stock_by_size'][$size] -= $quantity;
  167. if ($product['stock_by_size'][$size] < 0) {
  168. $product['stock_by_size'][$size] = 0;
  169. }
  170. } else {
  171. // For merch or general stock
  172. if (!isset($product['stock'])) {
  173. $product['stock'] = 0;
  174. }
  175. $product['stock'] -= $quantity;
  176. if ($product['stock'] < 0) {
  177. $product['stock'] = 0;
  178. }
  179. }
  180. break;
  181. }
  182. }
  183. saveProducts($products);
  184. }
  185. /**
  186. * Release stock from reservation
  187. */
  188. function releaseStock($productId, $quantity, $size = null) {
  189. $products = getProducts();
  190. foreach ($products as &$product) {
  191. if ($product['id'] == $productId) {
  192. // For apparel with sizes, release per size
  193. if ($product['category'] === 'apparel' && !empty($product['sizes']) && $size !== null) {
  194. if (!isset($product['stock_by_size']) || !is_array($product['stock_by_size'])) {
  195. $product['stock_by_size'] = [];
  196. }
  197. if (!isset($product['stock_by_size'][$size])) {
  198. $product['stock_by_size'][$size] = 0;
  199. }
  200. $product['stock_by_size'][$size] += $quantity;
  201. } else {
  202. // For merch or general stock
  203. if (!isset($product['stock'])) {
  204. $product['stock'] = 0;
  205. }
  206. $product['stock'] += $quantity;
  207. }
  208. break;
  209. }
  210. }
  211. saveProducts($products);
  212. }
  213. /**
  214. * Create new reservation
  215. */
  216. function createReservation($customerName, $customerEmail, $items) {
  217. $reservations = getReservations();
  218. // Validate stock for all items
  219. foreach ($items as $item) {
  220. $size = isset($item['size']) ? $item['size'] : null;
  221. if (!checkStock($item['product_id'], $item['quantity'], $size)) {
  222. $product = getProductById($item['product_id']);
  223. $productName = $product ? $product['name'] : 'Produkt';
  224. $sizeInfo = $size ? " (Größe: $size)" : '';
  225. return ['success' => false, 'message' => "Nicht genügend Lagerbestand für: $productName$sizeInfo"];
  226. }
  227. }
  228. // Allocate stock
  229. foreach ($items as $item) {
  230. $size = isset($item['size']) ? $item['size'] : null;
  231. allocateStock($item['product_id'], $item['quantity'], $size);
  232. }
  233. // Create reservation
  234. $now = new DateTime();
  235. $expires = clone $now;
  236. $expires->modify('+' . RESERVATION_EXPIRY_DAYS . ' days');
  237. $reservation = [
  238. 'id' => generateReservationId(),
  239. 'customer_name' => $customerName,
  240. 'customer_email' => $customerEmail,
  241. 'items' => $items,
  242. 'created' => $now->format('Y-m-d H:i:s'),
  243. 'expires' => $expires->format('Y-m-d H:i:s'),
  244. 'status' => 'open',
  245. 'picked_up' => false,
  246. 'type' => 'regular',
  247. 'is_hidden' => false
  248. ];
  249. $reservations[] = $reservation;
  250. saveReservations($reservations);
  251. // Send confirmation emails
  252. sendReservationEmails($reservation);
  253. return ['success' => true, 'reservation' => $reservation];
  254. }
  255. /**
  256. * Create new backorder reservation
  257. */
  258. function createBackorderReservation($customerName, $customerEmail, $items) {
  259. $reservations = getReservations();
  260. $now = new DateTime();
  261. $reservation = [
  262. 'id' => generateReservationId(),
  263. 'customer_name' => $customerName,
  264. 'customer_email' => $customerEmail,
  265. 'items' => $items,
  266. 'created' => $now->format('Y-m-d H:i:s'),
  267. 'expires' => '',
  268. 'status' => 'open',
  269. 'picked_up' => false,
  270. 'type' => 'backorder',
  271. 'backorder_status' => 'pending',
  272. 'is_hidden' => false
  273. ];
  274. $reservations[] = $reservation;
  275. saveReservations($reservations);
  276. // Send confirmation emails
  277. sendBackorderEmails($reservation);
  278. return ['success' => true, 'reservation' => $reservation];
  279. }
  280. /**
  281. * Mark reservation as picked up
  282. */
  283. function markReservationPickedUp($reservationId) {
  284. $reservations = getReservations();
  285. foreach ($reservations as &$reservation) {
  286. if ($reservation['id'] === $reservationId) {
  287. if (isReservationHidden($reservation)) {
  288. break;
  289. }
  290. $reservation['picked_up'] = true;
  291. $reservation['status'] = 'picked_up';
  292. break;
  293. }
  294. }
  295. saveReservations($reservations);
  296. }
  297. /**
  298. * Mark reservation/backorder as spam/deleted and hide it from non-admin views.
  299. * For open regular reservations we release stock, because the order is discarded.
  300. */
  301. function markReservationHidden($reservationId) {
  302. $reservations = getReservations();
  303. foreach ($reservations as &$reservation) {
  304. if ($reservation['id'] !== $reservationId) {
  305. continue;
  306. }
  307. if (isReservationHidden($reservation)) {
  308. return ['success' => false, 'message' => 'Bestellung ist bereits als Spam/Gelöscht markiert.'];
  309. }
  310. $isBackorder = isset($reservation['type']) && $reservation['type'] === 'backorder';
  311. if (!$isBackorder && isset($reservation['status']) && $reservation['status'] === 'open' && empty($reservation['picked_up'])) {
  312. foreach ($reservation['items'] as $item) {
  313. $size = isset($item['size']) ? $item['size'] : null;
  314. releaseStock($item['product_id'], $item['quantity'], $size);
  315. }
  316. $reservation['status'] = 'deleted';
  317. }
  318. $reservation['is_hidden'] = true;
  319. $reservation['hidden_at'] = date('Y-m-d H:i:s');
  320. $reservation['hidden_reason'] = 'spam_deleted';
  321. saveReservations($reservations);
  322. return ['success' => true];
  323. }
  324. return ['success' => false, 'message' => 'Bestellung nicht gefunden.'];
  325. }
  326. /**
  327. * Check and expire old reservations
  328. */
  329. function expireOldReservations() {
  330. $reservations = getReservations();
  331. $now = new DateTime();
  332. $changed = false;
  333. foreach ($reservations as &$reservation) {
  334. if (isReservationHidden($reservation)) {
  335. continue;
  336. }
  337. if ($reservation['status'] === 'open' && !$reservation['picked_up']) {
  338. if (isset($reservation['type']) && $reservation['type'] === 'backorder') {
  339. continue;
  340. }
  341. if (empty($reservation['expires'])) {
  342. continue;
  343. }
  344. $expires = new DateTime($reservation['expires']);
  345. if ($now > $expires) {
  346. $reservation['status'] = 'expired';
  347. // Release stock
  348. foreach ($reservation['items'] as $item) {
  349. $size = isset($item['size']) ? $item['size'] : null;
  350. releaseStock($item['product_id'], $item['quantity'], $size);
  351. }
  352. $changed = true;
  353. }
  354. }
  355. }
  356. if ($changed) {
  357. saveReservations($reservations);
  358. }
  359. }
  360. /**
  361. * Check if all items are in stock
  362. */
  363. function canFulfillReservationItems($items) {
  364. foreach ($items as $item) {
  365. $size = isset($item['size']) ? $item['size'] : null;
  366. if (!checkStock($item['product_id'], $item['quantity'], $size)) {
  367. return false;
  368. }
  369. }
  370. return true;
  371. }
  372. /**
  373. * Mark backorder as available
  374. */
  375. function markBackorderAvailable($reservationId) {
  376. $reservations = getReservations();
  377. foreach ($reservations as &$reservation) {
  378. if ($reservation['id'] === $reservationId) {
  379. if (isReservationHidden($reservation)) {
  380. return ['success' => false, 'message' => 'Diese Vorbestellung ist als Spam/Gelöscht markiert.'];
  381. }
  382. if (!isset($reservation['type']) || $reservation['type'] !== 'backorder') {
  383. return ['success' => false, 'message' => 'Diese Vorbestellung wurde bereits in eine Bestellung umgewandelt.'];
  384. }
  385. if (isset($reservation['backorder_status']) && $reservation['backorder_status'] === 'notified') {
  386. return ['success' => false, 'message' => 'Diese Vorbestellung wurde bereits informiert.'];
  387. }
  388. if (!canFulfillReservationItems($reservation['items'])) {
  389. return ['success' => false, 'message' => 'Nicht alle Artikel sind verfügbar.'];
  390. }
  391. foreach ($reservation['items'] as $item) {
  392. $size = isset($item['size']) ? $item['size'] : null;
  393. allocateStock($item['product_id'], $item['quantity'], $size);
  394. }
  395. $now = new DateTime();
  396. $expires = clone $now;
  397. $expires->modify('+' . RESERVATION_EXPIRY_DAYS . ' days');
  398. $reservation['type'] = 'regular';
  399. $reservation['status'] = 'open';
  400. $reservation['picked_up'] = false;
  401. $reservation['expires'] = $expires->format('Y-m-d H:i:s');
  402. if (isset($reservation['backorder_status'])) {
  403. unset($reservation['backorder_status']);
  404. }
  405. saveReservations($reservations);
  406. sendBackorderAvailableEmail($reservation);
  407. return ['success' => true, 'reservation' => $reservation];
  408. }
  409. }
  410. return ['success' => false, 'message' => 'Vorbestellung nicht gefunden.'];
  411. }
  412. /**
  413. * Sanitize input
  414. */
  415. function sanitize($input) {
  416. return htmlspecialchars(strip_tags(trim($input)), ENT_QUOTES, 'UTF-8');
  417. }
  418. /**
  419. * Format price
  420. */
  421. function formatPrice($price) {
  422. return number_format($price, 2, ',', '.') . ' €';
  423. }
  424. /**
  425. * Format date
  426. */
  427. function formatDate($dateString) {
  428. $date = new DateTime($dateString);
  429. return $date->format('d.m.Y H:i');
  430. }
  431. /**
  432. * Send email
  433. */
  434. function sendEmail($to, $subject, $message, $isHtml = true) {
  435. $headers = [];
  436. $headers[] = 'From: ' . FROM_NAME . ' <' . FROM_EMAIL . '>';
  437. $headers[] = 'Reply-To: ' . FROM_EMAIL;
  438. $headers[] = 'X-Mailer: PHP/' . phpversion();
  439. if ($isHtml) {
  440. $headers[] = 'MIME-Version: 1.0';
  441. $headers[] = 'Content-type: text/html; charset=UTF-8';
  442. }
  443. return mail($to, $subject, $message, implode("\r\n", $headers));
  444. }
  445. /**
  446. * Send reservation confirmation emails
  447. */
  448. function sendReservationEmails($reservation) {
  449. $products = getProducts();
  450. // Build items list
  451. $itemsHtml = '<ul style="list-style: none; margin: 0; padding: 0;">';
  452. foreach ($reservation['items'] as $item) {
  453. $product = getProductById($item['product_id']);
  454. if ($product) {
  455. $sizeInfo = '';
  456. if (isset($item['size']) && !empty($item['size'])) {
  457. $sizeInfo = ' - Größe: ' . htmlspecialchars($item['size']);
  458. }
  459. $itemsHtml .= '<li style="margin: 0 0 0.6rem 0; padding: 0.75rem 0.9rem; background: #28292a; border: 1px solid #4a5263; border-radius: 6px;"><strong style="color: #cac300;">' . htmlspecialchars($product['name']) . '</strong>' . $sizeInfo . ' - Menge: <strong>' . (int) $item['quantity'] . '</strong></li>';
  460. }
  461. }
  462. $itemsHtml .= '</ul>';
  463. // Customer email
  464. $customerSubject = 'Ihre Reservierung bei ' . SITE_NAME;
  465. $customerMessage = '
  466. <html>
  467. <head>
  468. <meta charset="UTF-8">
  469. </head>
  470. <body style="font-family: Arial, sans-serif; line-height: 1.6; color: #f5f7fb; background: #28292a; padding: 1.5rem;">
  471. <div style="max-width: 640px; margin: 0 auto; background: #2f3541; padding: 1.5rem 2rem; border-radius: 10px; border: 1px solid #3b4252;">
  472. <h2 style="color: #cac300; margin-top: 0;">Reservierung bestätigt</h2>
  473. <p>Sehr geehrte/r ' . htmlspecialchars($reservation['customer_name']) . ',</p>
  474. <p>vielen Dank für Ihre Reservierung bei ' . SITE_NAME . '.</p>
  475. <div style="background: #28292a; border: 2px solid #cac300; padding: 1.5rem; margin: 1.5rem 0; border-radius: 8px; text-align: center;">
  476. <h3 style="margin-top: 0; color: #f5f7fb;">Ihre Bestellnummer:</h3>
  477. <p style="margin: 0; color: #cac300; font-family: monospace;">' . htmlspecialchars($reservation['id']) . '</p>
  478. </div>
  479. <h3>Reservierungsdetails:</h3>
  480. <p><strong>Bestellnummer:</strong> ' . htmlspecialchars($reservation['id']) . '</p>
  481. <p><strong>Erstellt am:</strong> ' . formatDate($reservation['created']) . '</p>
  482. <p><strong>Gültig bis:</strong> ' . formatDate($reservation['expires']) . '</p>
  483. <h3>Reservierte Artikel:</h3>
  484. <div style="background: #303745; border: 1px solid #4a5263; border-left: 4px solid #cac300; border-radius: 8px; padding: 1rem;">' . $itemsHtml . '</div>
  485. <p><strong>Wichtig:</strong> Bitte nennen Sie diese Bestellnummer bei der Abholung. Die Reservierung ist bis zum ' . formatDate($reservation['expires']) . ' gültig.</p>
  486. <p>Mit freundlichen Grüßen<br>' . SITE_NAME . '</p>
  487. </div>
  488. </body>
  489. </html>';
  490. sendEmail($reservation['customer_email'], $customerSubject, $customerMessage);
  491. // Admin email
  492. $adminSubject = 'Neue Reservierung: ' . $reservation['id'];
  493. $adminMessage = '
  494. <html>
  495. <head>
  496. <meta charset="UTF-8">
  497. </head>
  498. <body style="font-family: Arial, sans-serif; line-height: 1.6; color: #f5f7fb; background: #28292a; padding: 1.5rem;">
  499. <div style="max-width: 640px; margin: 0 auto; background: #2f3541; padding: 1.5rem 2rem; border-radius: 10px; border: 1px solid #3b4252;">
  500. <h2 style="color: #cac300; margin-top: 0;">Neue Reservierung</h2>
  501. <p>Eine neue Reservierung wurde erstellt:</p>
  502. <div style="background: #28292a; border: 2px solid #cac300; padding: 1.5rem; margin: 1.5rem 0; border-radius: 8px;">
  503. <h3 style="margin-top: 0;">Bestellnummer:</h3>
  504. <p style="margin: 0; color: #cac300; font-family: monospace;">' . htmlspecialchars($reservation['id']) . '</p>
  505. </div>
  506. <h3>Kundendaten:</h3>
  507. <p><strong>Name:</strong> ' . htmlspecialchars($reservation['customer_name']) . '</p>
  508. <p><strong>E-Mail:</strong> ' . htmlspecialchars($reservation['customer_email']) . '</p>
  509. <h3>Reservierungsdetails:</h3>
  510. <p><strong>Bestellnummer:</strong> ' . htmlspecialchars($reservation['id']) . '</p>
  511. <p><strong>Erstellt am:</strong> ' . formatDate($reservation['created']) . '</p>
  512. <p><strong>Gültig bis:</strong> ' . formatDate($reservation['expires']) . '</p>
  513. <h3>Reservierte Artikel:</h3>
  514. <div style="background: #303745; border: 1px solid #4a5263; border-left: 4px solid #cac300; border-radius: 8px; padding: 1rem;">' . $itemsHtml . '</div>
  515. </div>
  516. </body>
  517. </html>';
  518. sendEmail(ADMIN_EMAIL, $adminSubject, $adminMessage);
  519. }
  520. /**
  521. * Send backorder confirmation emails
  522. */
  523. function sendBackorderEmails($reservation) {
  524. // Build items list
  525. $itemsHtml = '<ul style="list-style: none; margin: 0; padding: 0;">';
  526. foreach ($reservation['items'] as $item) {
  527. $product = getProductById($item['product_id']);
  528. if ($product) {
  529. $sizeInfo = '';
  530. if (isset($item['size']) && !empty($item['size'])) {
  531. $sizeInfo = ' - Größe: ' . htmlspecialchars($item['size']);
  532. }
  533. $itemsHtml .= '<li style="margin: 0 0 0.6rem 0; padding: 0.75rem 0.9rem; background: #28292a; border: 1px solid #4a5263; border-radius: 6px;"><strong style="color: #cac300;">' . htmlspecialchars($product['name']) . '</strong>' . $sizeInfo . ' - Menge: <strong>' . (int) $item['quantity'] . '</strong></li>';
  534. }
  535. }
  536. $itemsHtml .= '</ul>';
  537. // Customer email
  538. $customerSubject = 'Vorbestellung bei ' . SITE_NAME;
  539. $customerMessage = '
  540. <html>
  541. <head>
  542. <meta charset="UTF-8">
  543. </head>
  544. <body style="font-family: Arial, sans-serif; line-height: 1.6; color: #f5f7fb; background: #28292a; padding: 1.5rem;">
  545. <div style="max-width: 640px; margin: 0 auto; background: #2f3541; padding: 1.5rem 2rem; border-radius: 10px; border: 1px solid #3b4252;">
  546. <h2 style="color: #cac300; margin-top: 0;">Vorbestellung bestätigt</h2>
  547. <p>Sehr geehrte/r ' . htmlspecialchars($reservation['customer_name']) . ',</p>
  548. <p>vielen Dank für Ihre Vorbestellung bei ' . SITE_NAME . '.</p>
  549. <div style="background: #28292a; border: 2px solid #cac300; padding: 1.5rem; margin: 1.5rem 0; border-radius: 8px; text-align: center;">
  550. <h3 style="margin-top: 0; color: #f5f7fb;">Ihre Bestellnummer:</h3>
  551. <p style="margin: 0; color: #cac300; font-family: monospace;">' . htmlspecialchars($reservation['id']) . '</p>
  552. </div>
  553. <h3>Vorbestellungsdetails:</h3>
  554. <p><strong>Bestellnummer:</strong> ' . htmlspecialchars($reservation['id']) . '</p>
  555. <p><strong>Erstellt am:</strong> ' . formatDate($reservation['created']) . '</p>
  556. <h3>Vorbestellte Artikel:</h3>
  557. <div style="background: #303745; border: 1px solid #4a5263; border-left: 4px solid #cac300; border-radius: 8px; padding: 1rem;">' . $itemsHtml . '</div>
  558. <div style="background: #28292a; border: 2px solid #cf2e2e; padding: 1.5rem; margin: 1.5rem 0; border-radius: 8px;">
  559. <strong>Hinweis:</strong> Die Lieferzeiten sind nicht bekannt, da die Bestellung in Chargen erfolgt.
  560. </div>
  561. <p>Wir informieren Sie, sobald die komplette Vorbestellung zur Abholung bereit ist.</p>
  562. <p>Mit freundlichen Grüßen<br>' . SITE_NAME . '</p>
  563. </div>
  564. </body>
  565. </html>';
  566. sendEmail($reservation['customer_email'], $customerSubject, $customerMessage);
  567. // Admin email
  568. $adminSubject = 'Neue Vorbestellung: ' . $reservation['id'];
  569. $adminMessage = '
  570. <html>
  571. <head>
  572. <meta charset="UTF-8">
  573. </head>
  574. <body style="font-family: Arial, sans-serif; line-height: 1.6; color: #f5f7fb; background: #28292a; padding: 1.5rem;">
  575. <div style="max-width: 640px; margin: 0 auto; background: #2f3541; padding: 1.5rem 2rem; border-radius: 10px; border: 1px solid #3b4252;">
  576. <h2 style="color: #cac300; margin-top: 0;">Neue Vorbestellung</h2>
  577. <p>Eine neue Vorbestellung wurde erstellt:</p>
  578. <div style="background: #28292a; border: 2px solid #cac300; padding: 1.5rem; margin: 1.5rem 0; border-radius: 8px;">
  579. <h3 style="margin-top: 0;">Bestellnummer:</h3>
  580. <p style="margin: 0; color: #cac300; font-family: monospace;">' . htmlspecialchars($reservation['id']) . '</p>
  581. </div>
  582. <h3>Kundendaten:</h3>
  583. <p><strong>Name:</strong> ' . htmlspecialchars($reservation['customer_name']) . '</p>
  584. <p><strong>E-Mail:</strong> ' . htmlspecialchars($reservation['customer_email']) . '</p>
  585. <h3>Vorbestellungsdetails:</h3>
  586. <p><strong>Bestellnummer:</strong> ' . htmlspecialchars($reservation['id']) . '</p>
  587. <p><strong>Erstellt am:</strong> ' . formatDate($reservation['created']) . '</p>
  588. <h3>Vorbestellte Artikel:</h3>
  589. <div style="background: #303745; border: 1px solid #4a5263; border-left: 4px solid #cac300; border-radius: 8px; padding: 1rem;">' . $itemsHtml . '</div>
  590. </div>
  591. </body>
  592. </html>';
  593. sendEmail(ADMIN_EMAIL, $adminSubject, $adminMessage);
  594. }
  595. /**
  596. * Send backorder availability email
  597. */
  598. function sendBackorderAvailableEmail($reservation) {
  599. $itemsHtml = '<ul>';
  600. foreach ($reservation['items'] as $item) {
  601. $product = getProductById($item['product_id']);
  602. if ($product) {
  603. $sizeInfo = '';
  604. if (isset($item['size']) && !empty($item['size'])) {
  605. $sizeInfo = ' - Größe: ' . htmlspecialchars($item['size']);
  606. }
  607. $itemsHtml .= '<li>' . htmlspecialchars($product['name']) . $sizeInfo . ' - Menge: ' . $item['quantity'] . '</li>';
  608. }
  609. }
  610. $itemsHtml .= '</ul>';
  611. $subject = 'Ihre Vorbestellung ist zur Abholung bereit';
  612. $message = '
  613. <html>
  614. <head>
  615. <meta charset="UTF-8">
  616. </head>
  617. <body style="font-family: Arial, sans-serif; line-height: 1.6; color: #f5f7fb; background: #28292a; padding: 1.5rem;">
  618. <div style="max-width: 640px; margin: 0 auto; background: #2f3541; padding: 1.5rem 2rem; border-radius: 10px; border: 1px solid #3b4252;">
  619. <h2 style="color: #cac300; margin-top: 0;">Vorbestellung zur Abholung bereit</h2>
  620. <p>Sehr geehrte/r ' . htmlspecialchars($reservation['customer_name']) . ',</p>
  621. <p>Ihre komplette Vorbestellung ist jetzt zur Abholung bereit.</p>
  622. <div style="background: #28292a; border: 2px solid #cac300; padding: 1.5rem; margin: 1.5rem 0; border-radius: 8px; text-align: center;">
  623. <h3 style="margin-top: 0; color: #f5f7fb;">Ihre Bestellnummer:</h3>
  624. <h2 style="font-size: 2rem; letter-spacing: 0.2rem; color: #cac300; font-family: monospace;">' . htmlspecialchars($reservation['id']) . '</h2>
  625. </div>
  626. <h3>Bereitliegende Artikel:</h3>
  627. ' . $itemsHtml . '
  628. <p>Bitte nennen Sie die Bestellnummer bei der Abholung.</p>
  629. <p>Mit freundlichen Grüßen<br>' . SITE_NAME . '</p>
  630. </div>
  631. </body>
  632. </html>';
  633. sendEmail($reservation['customer_email'], $subject, $message);
  634. }