functions.php 24 KB

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