|
|
@@ -1156,6 +1156,12 @@ function normalizeOrderItem($item)
|
|
|
$size = "";
|
|
|
}
|
|
|
|
|
|
+ $backorderStatus = trim((string) ($item["backorder_status"] ?? ""));
|
|
|
+ $allowedBackorderStatuses = ["", "to_be_backordered", "ordered"];
|
|
|
+ if (!in_array($backorderStatus, $allowedBackorderStatuses, true)) {
|
|
|
+ $backorderStatus = "";
|
|
|
+ }
|
|
|
+
|
|
|
return [
|
|
|
"product_id" => $productId,
|
|
|
"product_name" => $product["name"],
|
|
|
@@ -1163,6 +1169,9 @@ function normalizeOrderItem($item)
|
|
|
"availability_label" =>
|
|
|
$size !== "" ? getAvailabilityLabel($product, $size) : "",
|
|
|
"is_processed" => !empty($item["is_processed"]),
|
|
|
+ "backorder_status" => $backorderStatus,
|
|
|
+ "backordered_at" => trim((string) ($item["backordered_at"] ?? "")),
|
|
|
+ "ordered_at" => trim((string) ($item["ordered_at"] ?? "")),
|
|
|
];
|
|
|
}
|
|
|
|
|
|
@@ -1746,6 +1755,349 @@ function cancelOrder($orderId, $adminUsername, $reason = "")
|
|
|
return ["success" => false, "message" => "Bestellung nicht gefunden."];
|
|
|
}
|
|
|
|
|
|
+function orderItemCanBeManaged($order)
|
|
|
+{
|
|
|
+ if (($order["status"] ?? "") === "cancelled") {
|
|
|
+ return [
|
|
|
+ "success" => false,
|
|
|
+ "message" =>
|
|
|
+ "Stornierte Bestellungen können nicht mehr bearbeitet werden.",
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ if (($order["confirmation_status"] ?? "") === "pending") {
|
|
|
+ return [
|
|
|
+ "success" => false,
|
|
|
+ "message" =>
|
|
|
+ "Unbestätigte Bestellungen können noch nicht bearbeitet werden.",
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ if (($order["confirmation_status"] ?? "") === "expired") {
|
|
|
+ return [
|
|
|
+ "success" => false,
|
|
|
+ "message" =>
|
|
|
+ "Abgelaufene unbestätigte Bestellungen können nicht bearbeitet werden.",
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ return ["success" => true];
|
|
|
+}
|
|
|
+
|
|
|
+function setOrderItemBackorderStatus($orderId, $itemIndex, $status)
|
|
|
+{
|
|
|
+ $allowedStatuses = ["", "to_be_backordered"];
|
|
|
+ if (!in_array($status, $allowedStatuses, true)) {
|
|
|
+ return ["success" => false, "message" => "Ungültiger Nachbestellstatus."];
|
|
|
+ }
|
|
|
+
|
|
|
+ $orders = getOrders();
|
|
|
+ $now = date("Y-m-d H:i:s");
|
|
|
+
|
|
|
+ foreach ($orders as &$order) {
|
|
|
+ if ($order["id"] !== $orderId) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ $guard = orderItemCanBeManaged($order);
|
|
|
+ if (!$guard["success"]) {
|
|
|
+ return $guard;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!isset($order["items"][$itemIndex])) {
|
|
|
+ return [
|
|
|
+ "success" => false,
|
|
|
+ "message" => "Position nicht gefunden.",
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($status === "to_be_backordered") {
|
|
|
+ $order["items"][$itemIndex]["backorder_status"] = "to_be_backordered";
|
|
|
+ $order["items"][$itemIndex]["backordered_at"] = $now;
|
|
|
+ $order["items"][$itemIndex]["ordered_at"] = "";
|
|
|
+ } else {
|
|
|
+ $order["items"][$itemIndex]["backorder_status"] = "";
|
|
|
+ $order["items"][$itemIndex]["backordered_at"] = "";
|
|
|
+ $order["items"][$itemIndex]["ordered_at"] = "";
|
|
|
+ }
|
|
|
+
|
|
|
+ $order["updated_at"] = $now;
|
|
|
+ saveOrders($orders);
|
|
|
+
|
|
|
+ return ["success" => true, "order" => $order];
|
|
|
+ }
|
|
|
+ unset($order);
|
|
|
+
|
|
|
+ return ["success" => false, "message" => "Bestellung nicht gefunden."];
|
|
|
+}
|
|
|
+
|
|
|
+function toggleOrderItemBackorder($orderId, $itemIndex)
|
|
|
+{
|
|
|
+ $orders = getOrders();
|
|
|
+
|
|
|
+ foreach ($orders as $order) {
|
|
|
+ if ($order["id"] !== $orderId) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!isset($order["items"][$itemIndex])) {
|
|
|
+ return [
|
|
|
+ "success" => false,
|
|
|
+ "message" => "Position nicht gefunden.",
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ $current = (string) ($order["items"][$itemIndex]["backorder_status"] ?? "");
|
|
|
+ $newStatus = $current === "to_be_backordered" ? "" : "to_be_backordered";
|
|
|
+
|
|
|
+ return setOrderItemBackorderStatus($orderId, $itemIndex, $newStatus);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ["success" => false, "message" => "Bestellung nicht gefunden."];
|
|
|
+}
|
|
|
+
|
|
|
+function collectBackorderItemRefs()
|
|
|
+{
|
|
|
+ $refs = [];
|
|
|
+
|
|
|
+ foreach (getOrders() as $order) {
|
|
|
+ if (($order["status"] ?? "") === "cancelled") {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (in_array($order["confirmation_status"] ?? "", ["pending", "expired"], true)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach ($order["items"] as $itemIndex => $item) {
|
|
|
+ $status = (string) ($item["backorder_status"] ?? "");
|
|
|
+ if ($status === "") {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ $refs[] = [
|
|
|
+ "order_id" => $order["id"],
|
|
|
+ "item_index" => (int) $itemIndex,
|
|
|
+ "product_id" => (int) $item["product_id"],
|
|
|
+ "product_name" => $item["product_name"],
|
|
|
+ "size" => $item["size"],
|
|
|
+ "backorder_status" => $status,
|
|
|
+ "created_at" => $order["created_at"],
|
|
|
+ "sort_at" =>
|
|
|
+ $status === "ordered"
|
|
|
+ ? ($item["ordered_at"] !== ""
|
|
|
+ ? $item["ordered_at"]
|
|
|
+ : $order["created_at"])
|
|
|
+ : ($item["backordered_at"] !== ""
|
|
|
+ ? $item["backordered_at"]
|
|
|
+ : $order["created_at"]),
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return $refs;
|
|
|
+}
|
|
|
+
|
|
|
+function getBackorderGroups()
|
|
|
+{
|
|
|
+ $refs = collectBackorderItemRefs();
|
|
|
+ $groups = [];
|
|
|
+
|
|
|
+ foreach ($refs as $ref) {
|
|
|
+ $key = $ref["product_id"] . "|" . $ref["size"];
|
|
|
+ if (!isset($groups[$key])) {
|
|
|
+ $groups[$key] = [
|
|
|
+ "product_id" => $ref["product_id"],
|
|
|
+ "product_name" => $ref["product_name"],
|
|
|
+ "size" => $ref["size"],
|
|
|
+ "to_be_backordered" => 0,
|
|
|
+ "ordered" => 0,
|
|
|
+ "items_to_be_backordered" => [],
|
|
|
+ "items_ordered" => [],
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($ref["backorder_status"] === "to_be_backordered") {
|
|
|
+ $groups[$key]["to_be_backordered"]++;
|
|
|
+ $groups[$key]["items_to_be_backordered"][] = $ref;
|
|
|
+ } elseif ($ref["backorder_status"] === "ordered") {
|
|
|
+ $groups[$key]["ordered"]++;
|
|
|
+ $groups[$key]["items_ordered"][] = $ref;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach ($groups as &$group) {
|
|
|
+ usort($group["items_to_be_backordered"], function ($left, $right) {
|
|
|
+ $cmp = strcmp($left["created_at"], $right["created_at"]);
|
|
|
+ if ($cmp !== 0) {
|
|
|
+ return $cmp;
|
|
|
+ }
|
|
|
+ return strcmp($left["order_id"], $right["order_id"]);
|
|
|
+ });
|
|
|
+ usort($group["items_ordered"], function ($left, $right) {
|
|
|
+ $cmp = strcmp($left["sort_at"], $right["sort_at"]);
|
|
|
+ if ($cmp !== 0) {
|
|
|
+ return $cmp;
|
|
|
+ }
|
|
|
+ return strcmp($left["order_id"], $right["order_id"]);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ unset($group);
|
|
|
+
|
|
|
+ $result = array_values($groups);
|
|
|
+ usort($result, function ($left, $right) {
|
|
|
+ $cmp = strcmp($left["product_name"], $right["product_name"]);
|
|
|
+ if ($cmp !== 0) {
|
|
|
+ return $cmp;
|
|
|
+ }
|
|
|
+ return strcmp($left["size"], $right["size"]);
|
|
|
+ });
|
|
|
+
|
|
|
+ return $result;
|
|
|
+}
|
|
|
+
|
|
|
+function applyBackorderBulkUpdate($productId, $size, $fromStatus, $toStatus, $quantity)
|
|
|
+{
|
|
|
+ $productId = (int) $productId;
|
|
|
+ $size = trim((string) $size);
|
|
|
+ $quantity = (int) $quantity;
|
|
|
+
|
|
|
+ if ($productId <= 0 || $quantity <= 0) {
|
|
|
+ return [
|
|
|
+ "success" => false,
|
|
|
+ "message" => "Ungültige Menge oder Artikel.",
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ $refs = collectBackorderItemRefs();
|
|
|
+ $candidates = [];
|
|
|
+
|
|
|
+ foreach ($refs as $ref) {
|
|
|
+ if (
|
|
|
+ $ref["product_id"] !== $productId ||
|
|
|
+ $ref["size"] !== $size ||
|
|
|
+ $ref["backorder_status"] !== $fromStatus
|
|
|
+ ) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $candidates[] = $ref;
|
|
|
+ }
|
|
|
+
|
|
|
+ usort($candidates, function ($left, $right) {
|
|
|
+ $cmp = strcmp($left["created_at"], $right["created_at"]);
|
|
|
+ if ($cmp !== 0) {
|
|
|
+ return $cmp;
|
|
|
+ }
|
|
|
+ return strcmp($left["order_id"], $right["order_id"]);
|
|
|
+ });
|
|
|
+
|
|
|
+ if ($quantity > count($candidates)) {
|
|
|
+ return [
|
|
|
+ "success" => false,
|
|
|
+ "message" =>
|
|
|
+ "Nur " .
|
|
|
+ count($candidates) .
|
|
|
+ " Position(en) verfügbar, " .
|
|
|
+ $quantity .
|
|
|
+ " angefordert.",
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ $targets = array_slice($candidates, 0, $quantity);
|
|
|
+ $orders = getOrders();
|
|
|
+ $now = date("Y-m-d H:i:s");
|
|
|
+ $updated = 0;
|
|
|
+
|
|
|
+ foreach ($targets as $target) {
|
|
|
+ foreach ($orders as &$order) {
|
|
|
+ if ($order["id"] !== $target["order_id"]) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ $itemIndex = $target["item_index"];
|
|
|
+ if (!isset($order["items"][$itemIndex])) {
|
|
|
+ continue 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($toStatus === "ordered") {
|
|
|
+ $order["items"][$itemIndex]["backorder_status"] = "ordered";
|
|
|
+ $order["items"][$itemIndex]["ordered_at"] = $now;
|
|
|
+ } else {
|
|
|
+ $order["items"][$itemIndex]["backorder_status"] = "";
|
|
|
+ $order["items"][$itemIndex]["ordered_at"] = "";
|
|
|
+ }
|
|
|
+
|
|
|
+ $order["updated_at"] = $now;
|
|
|
+ $updated++;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ unset($order);
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($updated === 0) {
|
|
|
+ return [
|
|
|
+ "success" => false,
|
|
|
+ "message" => "Keine Positionen aktualisiert.",
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ saveOrders($orders);
|
|
|
+
|
|
|
+ return ["success" => true, "updated" => $updated];
|
|
|
+}
|
|
|
+
|
|
|
+function markBackorderItemsOrdered($productId, $size, $quantity)
|
|
|
+{
|
|
|
+ return applyBackorderBulkUpdate(
|
|
|
+ $productId,
|
|
|
+ $size,
|
|
|
+ "to_be_backordered",
|
|
|
+ "ordered",
|
|
|
+ $quantity,
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+function markBackorderItemsDelivered($productId, $size, $quantity)
|
|
|
+{
|
|
|
+ return applyBackorderBulkUpdate($productId, $size, "ordered", "", $quantity);
|
|
|
+}
|
|
|
+
|
|
|
+function orderHasBackorder($order)
|
|
|
+{
|
|
|
+ if (!is_array($order["items"] ?? null)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach ($order["items"] as $item) {
|
|
|
+ if (($item["backorder_status"] ?? "") !== "") {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+function getBackorderStatusLabel($status)
|
|
|
+{
|
|
|
+ switch ((string) $status) {
|
|
|
+ case "to_be_backordered":
|
|
|
+ return "Nachzubestellen";
|
|
|
+ case "ordered":
|
|
|
+ return "Wartet auf Lieferung";
|
|
|
+ default:
|
|
|
+ return "-";
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function getBackorderStatusClass($status)
|
|
|
+{
|
|
|
+ switch ((string) $status) {
|
|
|
+ case "to_be_backordered":
|
|
|
+ return "status-backorder";
|
|
|
+ case "ordered":
|
|
|
+ return "status-backorder-waiting";
|
|
|
+ default:
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
function getOrderStatusLabel($order)
|
|
|
{
|
|
|
if (($order["status"] ?? "") === "cancelled") {
|