order.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. <?php
  2. require_once __DIR__ . "/../config.php";
  3. require_once __DIR__ . "/../includes/functions.php";
  4. if (empty($_SESSION['admin_logged_in'])) {
  5. header("Location: login.php");
  6. exit();
  7. }
  8. expirePendingOrders();
  9. $message = "";
  10. $messageType = "";
  11. if (
  12. $_SERVER['REQUEST_METHOD'] === "POST" &&
  13. isset($_POST['toggle_item_backorder'])
  14. ) {
  15. if (!validateCsrfToken($_POST['csrf_token'] ?? "")) {
  16. $message = "Ungültiges Token. Bitte versuchen Sie es erneut.";
  17. $messageType = "error";
  18. } else {
  19. $result = toggleOrderItemBackorder(
  20. $_POST['order_id'] ?? "",
  21. (int) ($_POST['item_index'] ?? -1),
  22. );
  23. $message = $result["success"]
  24. ? "Nachbestellstatus wurde aktualisiert."
  25. : $result["message"];
  26. $messageType = $result["success"] ? "success" : "error";
  27. if ($result["success"]) {
  28. logAccess("Admin toggled order item backorder", [
  29. "admin" => $_SESSION['admin_username'] ?? "unknown",
  30. "order_id" => $_POST['order_id'] ?? "",
  31. "item_index" => $_POST['item_index'] ?? -1,
  32. ]);
  33. }
  34. }
  35. }
  36. if (
  37. $_SERVER['REQUEST_METHOD'] === "POST" &&
  38. isset($_POST['toggle_item_processed'])
  39. ) {
  40. if (!validateCsrfToken($_POST['csrf_token'] ?? "")) {
  41. $message = "Ungültiges Token. Bitte versuchen Sie es erneut.";
  42. $messageType = "error";
  43. } else {
  44. $result = toggleOrderItemProcessed(
  45. $_POST['order_id'] ?? "",
  46. (int) ($_POST['item_index'] ?? -1),
  47. );
  48. $message = $result["success"]
  49. ? "Position wurde aktualisiert."
  50. : $result["message"];
  51. $messageType = $result["success"] ? "success" : "error";
  52. if ($result["success"]) {
  53. logAccess("Admin toggled order item", [
  54. "admin" => $_SESSION['admin_username'] ?? "unknown",
  55. "order_id" => $_POST['order_id'] ?? "",
  56. "item_index" => $_POST['item_index'] ?? -1,
  57. ]);
  58. }
  59. }
  60. }
  61. if ($_SERVER['REQUEST_METHOD'] === "POST" && isset($_POST['cancel_order'])) {
  62. if (!validateCsrfToken($_POST['csrf_token'] ?? "")) {
  63. $message = "Ungültiges Token. Bitte versuchen Sie es erneut.";
  64. $messageType = "error";
  65. } else {
  66. $adminUsername = $_SESSION['admin_username'] ?? "";
  67. $result = cancelOrder(
  68. $_POST['order_id'] ?? "",
  69. $adminUsername,
  70. $_POST['cancellation_reason'] ?? "",
  71. );
  72. $message = $result["success"]
  73. ? "Bestellung wurde storniert."
  74. : $result["message"];
  75. $messageType = $result["success"] ? "success" : "error";
  76. if ($result["success"]) {
  77. logAccess("Admin cancelled order", [
  78. "admin" => $adminUsername,
  79. "order_id" => $_POST['order_id'] ?? "",
  80. ]);
  81. }
  82. }
  83. }
  84. if ($_SERVER['REQUEST_METHOD'] === "POST" && isset($_POST['uncancel_order'])) {
  85. if (!validateCsrfToken($_POST['csrf_token'] ?? "")) {
  86. $message = "Ungültiges Token. Bitte versuchen Sie es erneut.";
  87. $messageType = "error";
  88. } else {
  89. $adminUsername = $_SESSION['admin_username'] ?? "";
  90. $result = uncancelOrder($_POST['order_id'] ?? "");
  91. $message = $result["success"]
  92. ? "Stornierung wurde aufgehoben."
  93. : $result["message"];
  94. $messageType = $result["success"] ? "success" : "error";
  95. if ($result["success"]) {
  96. logAccess("Admin uncancelled order", [
  97. "admin" => $adminUsername,
  98. "order_id" => $_POST['order_id'] ?? "",
  99. ]);
  100. }
  101. }
  102. }
  103. $orderId = trim((string) ($_GET['id'] ?? $_POST['order_id'] ?? ""));
  104. $order = $orderId !== "" ? getOrderById($orderId) : null;
  105. $pageTitle =
  106. $order !== null
  107. ? "Bestellung " . $order["id"]
  108. : ($orderId !== ""
  109. ? "Bestellung nicht gefunden"
  110. : "Bestellung");
  111. $bodyClass = "admin-page";
  112. include __DIR__ . "/../includes/header.php";
  113. ?>
  114. <div class="admin-header">
  115. <h2><?php echo $order !== null
  116. ? "Bestellung " . escape($order["id"])
  117. : "Bestellung"; ?></h2>
  118. <div class="admin-dashboard-actions">
  119. <a href="index.php" class="btn btn-secondary">Zurück zum Dashboard</a>
  120. <a href="orders.php" class="btn">Zurück zur Bestellliste</a>
  121. </div>
  122. </div>
  123. <?php if ($message !== ""): ?>
  124. <div class="alert alert-<?php echo escape($messageType); ?>">
  125. <?php echo escape($message); ?>
  126. </div>
  127. <?php endif; ?>
  128. <?php if ($order === null): ?>
  129. <div class="alert alert-info">
  130. <p><?php echo $orderId !== ""
  131. ? "Die Bestellung wurde nicht gefunden."
  132. : "Keine Bestellnummer angegeben."; ?></p>
  133. </div>
  134. <?php else: ?>
  135. <div class="panel">
  136. <p><strong>Status:</strong> <span class="status <?php echo escape(
  137. getOrderStatusClass($order),
  138. ); ?>"><?php echo escape(
  139. getOrderStatusLabel($order),
  140. ); ?></span>
  141. <?php if (orderHasBackorder($order)): ?>
  142. <span class="status status-backorder">Nachbestellung</span>
  143. <?php endif; ?>
  144. </p>
  145. <p><strong>Name:</strong> <?php echo escape(
  146. $order["customer_name"],
  147. ); ?></p>
  148. <p><strong>E-Mail:</strong> <?php echo escape(
  149. $order["customer_email"],
  150. ); ?></p>
  151. <p><strong>Organisation:</strong> <?php echo escape(
  152. $order["organization_label"],
  153. ); ?></p>
  154. <p><strong>Erstellt:</strong> <?php echo escape(
  155. formatDate($order["created_at"]),
  156. ); ?></p>
  157. <?php if ($order["confirmed_at"] !== ""): ?>
  158. <p><strong>Bestätigt:</strong> <?php echo escape(
  159. formatDate($order["confirmed_at"]),
  160. ); ?></p>
  161. <?php endif; ?>
  162. <?php if ($order["confirmation_status"] === "pending"): ?>
  163. <p><strong>Bestätigung offen bis:</strong> <?php echo escape(
  164. formatDate($order["confirmation_expires_at"]),
  165. ); ?></p>
  166. <?php endif; ?>
  167. <?php if ($order["admin_notified_at"] !== ""): ?>
  168. <p><strong>Intern weitergeleitet:</strong> <?php echo escape(
  169. formatDate($order["admin_notified_at"]),
  170. ); ?></p>
  171. <?php endif; ?>
  172. <p><strong>Kommentar:</strong><br><?php echo $order[
  173. "comment"
  174. ] !== ""
  175. ? nl2br(escape($order["comment"]))
  176. : "Kein Kommentar"; ?></p>
  177. <?php if ($order["status"] === "cancelled"): ?>
  178. <div class="alert alert-warning">
  179. <p><strong>Storniert am:</strong> <?php echo escape(
  180. formatDate($order["cancelled_at"]),
  181. ); ?></p>
  182. <p><strong>Storniert durch:</strong> <?php echo escape(
  183. $order["cancelled_by"],
  184. ); ?></p>
  185. <p><strong>Stornogrund:</strong><br><?php echo $order[
  186. "cancellation_reason"
  187. ] !== ""
  188. ? nl2br(escape($order["cancellation_reason"]))
  189. : "Kein Grund angegeben"; ?></p>
  190. </div>
  191. <form
  192. method="POST"
  193. class="inline-form"
  194. onsubmit="return confirm('Stornierung wirklich aufheben? Die Bestellung kann danach wieder bearbeitet werden.');"
  195. >
  196. <?php echo csrfField(); ?>
  197. <input type="hidden" name="order_id" value="<?php echo escape(
  198. $order["id"],
  199. ); ?>">
  200. <button type="submit" name="uncancel_order" class="btn btn-small">
  201. Stornierung aufheben
  202. </button>
  203. </form>
  204. <?php endif; ?>
  205. <h4>Positionen</h4>
  206. <div class="table-responsive">
  207. <table class="responsive-table table-compact">
  208. <thead>
  209. <tr>
  210. <th>Artikel</th>
  211. <th>Größe</th>
  212. <th>Lieferhinweis</th>
  213. <th>Bearbeitet</th>
  214. <th>Nachbestellung</th>
  215. <th>Aktion</th>
  216. </tr>
  217. </thead>
  218. <tbody>
  219. <?php foreach ($order["items"] as $index => $item): ?>
  220. <tr>
  221. <td data-label="Artikel"><?php echo escape(
  222. $item["product_name"],
  223. ); ?></td>
  224. <td data-label="Größe"><?php echo $item["size"] !==
  225. ""
  226. ? escape($item["size"])
  227. : "-"; ?></td>
  228. <td data-label="Lieferhinweis"><?php echo $item[
  229. "availability_label"
  230. ] !== ""
  231. ? escape($item["availability_label"])
  232. : "-"; ?></td>
  233. <td data-label="Bearbeitet">
  234. <span class="status <?php echo !empty(
  235. $item["is_processed"]
  236. )
  237. ? "status-processed"
  238. : "status-open"; ?>">
  239. <?php echo !empty($item["is_processed"])
  240. ? "Ja"
  241. : "Nein"; ?>
  242. </span>
  243. </td>
  244. <td data-label="Nachbestellung">
  245. <?php
  246. $backorderStatus = (string) ($item["backorder_status"] ?? "");
  247. if ($backorderStatus !== ""): ?>
  248. <span class="status <?php echo escape(
  249. getBackorderStatusClass($backorderStatus),
  250. ); ?>"><?php echo escape(
  251. getBackorderStatusLabel($backorderStatus),
  252. ); ?></span>
  253. <?php else: ?>
  254. -
  255. <?php endif; ?>
  256. </td>
  257. <td data-label="Aktionen">
  258. <?php if (
  259. $order["status"] !== "cancelled" &&
  260. $order["confirmation_status"] !==
  261. "pending" &&
  262. $order["confirmation_status"] !==
  263. "expired"
  264. ): ?>
  265. <form method="POST" class="inline-form">
  266. <?php echo csrfField(); ?>
  267. <input type="hidden" name="order_id" value="<?php echo escape(
  268. $order["id"],
  269. ); ?>">
  270. <input type="hidden" name="item_index" value="<?php echo (int) $index; ?>">
  271. <button type="submit" name="toggle_item_processed" class="btn btn-small">
  272. <?php echo !empty(
  273. $item["is_processed"]
  274. )
  275. ? "Als offen markieren"
  276. : "Als bearbeitet markieren"; ?>
  277. </button>
  278. </form>
  279. <?php
  280. $canToggleBackorder =
  281. $backorderStatus === "to_be_backordered" ||
  282. ($backorderStatus === "" &&
  283. empty($item["is_processed"]));
  284. if ($canToggleBackorder): ?>
  285. <form method="POST" class="inline-form">
  286. <?php echo csrfField(); ?>
  287. <input type="hidden" name="order_id" value="<?php echo escape(
  288. $order["id"],
  289. ); ?>">
  290. <input type="hidden" name="item_index" value="<?php echo (int) $index; ?>">
  291. <button type="submit" name="toggle_item_backorder" class="btn btn-small btn-secondary">
  292. <?php echo $backorderStatus === "to_be_backordered"
  293. ? "Nachbestellung aufheben"
  294. : "Als Nachbestellung markieren"; ?>
  295. </button>
  296. </form>
  297. <?php endif; ?>
  298. <?php else: ?>
  299. -
  300. <?php endif; ?>
  301. </td>
  302. </tr>
  303. <?php endforeach; ?>
  304. </tbody>
  305. </table>
  306. </div>
  307. <?php if (
  308. $order["status"] !== "cancelled" &&
  309. $order["status"] !== "processed"
  310. ): ?>
  311. <button
  312. type="button"
  313. class="btn btn-secondary btn-small"
  314. id="cancel-order-open"
  315. >
  316. Bestellung stornieren
  317. </button>
  318. <div
  319. id="cancel-order-modal"
  320. class="modal"
  321. role="dialog"
  322. aria-labelledby="cancel-order-title"
  323. aria-hidden="true"
  324. >
  325. <div class="modal-content modal-content-compact">
  326. <button
  327. type="button"
  328. class="modal-close btn btn-secondary btn-small"
  329. id="cancel-order-close"
  330. aria-label="Schließen"
  331. >
  332. &times;
  333. </button>
  334. <h4 id="cancel-order-title">Bestellung stornieren</h4>
  335. <form method="POST" id="cancel-order-form">
  336. <?php echo csrfField(); ?>
  337. <input type="hidden" name="order_id" value="<?php echo escape(
  338. $order["id"],
  339. ); ?>">
  340. <div class="form-group">
  341. <label for="cancellation_reason">Stornogrund</label>
  342. <textarea
  343. id="cancellation_reason"
  344. name="cancellation_reason"
  345. rows="3"
  346. placeholder="Optionaler Grund"
  347. ></textarea>
  348. </div>
  349. <button type="submit" name="cancel_order" class="btn">
  350. Stornierung bestätigen
  351. </button>
  352. </form>
  353. </div>
  354. </div>
  355. <script>
  356. (function () {
  357. const modal = document.getElementById("cancel-order-modal");
  358. const openBtn = document.getElementById("cancel-order-open");
  359. const closeBtn = document.getElementById("cancel-order-close");
  360. if (!modal || !openBtn || !closeBtn) {
  361. return;
  362. }
  363. function openModal() {
  364. modal.classList.add("is-open");
  365. modal.setAttribute("aria-hidden", "false");
  366. const reason = document.getElementById("cancellation_reason");
  367. if (reason) {
  368. reason.focus();
  369. }
  370. }
  371. function closeModal() {
  372. modal.classList.remove("is-open");
  373. modal.setAttribute("aria-hidden", "true");
  374. }
  375. openBtn.addEventListener("click", openModal);
  376. closeBtn.addEventListener("click", closeModal);
  377. modal.addEventListener("click", function (event) {
  378. if (event.target === modal) {
  379. closeModal();
  380. }
  381. });
  382. document.addEventListener("keydown", function (event) {
  383. if (event.key === "Escape" && modal.classList.contains("is-open")) {
  384. closeModal();
  385. }
  386. });
  387. })();
  388. </script>
  389. <?php endif; ?>
  390. </div>
  391. <?php endif; ?>
  392. <?php include __DIR__ . "/../includes/footer.php"; ?>