conn = $conn; $this->primaryWarehouse = self::PRIMARY_WAREHOUSE_CODE; $this->warehouseConfig = $this->loadWarehouseStructure(); } /** * Fő generáló metódus */ public static function generate($conn, $inputData, $coderclass = null) { $generator = new self($conn); return $generator->processPickingList($inputData, $coderclass); } /** * Picking list feldolgozás */ private function processPickingList($inputData, $coderclass) { $orders = []; $reason = ''; $orderDate = ''; // Input típus meghatározása if (isset($inputData['manual']) && is_array($inputData['manual'])) { $orders = $this->buildManualOrders($inputData['manual']); } else { if ($coderclass === null) { return ['result' => 'error', 'message' => 'CoderClass szükséges reason alapú lekérdezéshez']; } $encodedData = htmlspecialchars($inputData['reason'] ?? ''); $decodedData = $coderclass->decode($encodedData, 'SZ4TUN4'); $dataParts = explode('|', $decodedData); if (count($dataParts) != 2) { return ['result' => 'error', 'message' => 'Érvénytelen kiszedési lista azonosító']; } $reason = $dataParts[0]; $orderDate = $dataParts[1]; $orders = $this->loadOrders($reason, $orderDate); } // Készletek betöltése $boxStock = $this->loadBoxStock(); $foilStock = $this->loadFoilStock(); // Picking listák generálása $allPickingLists = []; foreach ($orders as $key => $orderData) { $localBoxStock = $boxStock; $localFoilStock = $foilStock; $orderPrimaryWarehouse = $orderData['primary_warehouse'] ?? $this->primaryWarehouse; $this->prioritizeStock($localBoxStock, $orderPrimaryWarehouse); $this->prioritizeFoilStock($localFoilStock, $orderPrimaryWarehouse); $pickingList = $this->generatePickingList($orderData, $localBoxStock, $localFoilStock); $this->formatPositionForSort($pickingList); $this->sortPickingList($pickingList); $icon = $this->determineIcon($pickingList); if (!empty($icon)) { $icon = $orderData['overal_order_id'] . ' ' . $icon; } $allPickingLists[] = [ 'overal_order_id' => $orderData['overal_order_id'], 'order_id' => $orderData['order_id'], 'order_name' => $orderData['order_name'], 'order_mail' => $orderData['order_mail'], 'receipt_method' => $orderData['receipt_method'], 'order_note' => $orderData['order_note'], 'picking_list' => $pickingList ]; } $this->sortOrders($allPickingLists); return [ 'result' => 'ok', 'reason' => $reason, 'date' => $orderDate, 'all_picking_lists' => $allPickingLists ]; } // ======================================================================== // ADATBETÖLTŐ METÓDUSOK // ======================================================================== private function loadWarehouseStructure() { $config = []; $sql = "SELECT warehouse_id, location, name, code FROM warehouse_structure WHERE status=1"; $result = $this->conn->query($sql); if ($result) { while ($row = $result->fetch_assoc()) { $config[intval($row['warehouse_id'])] = [ 'location' => $row['location'], 'name' => $row['name'], 'code' => $row['code'] ]; } } return $config; } private function buildManualOrders($manualData) { $manualItems = []; foreach ($manualData as $entry) { $itemId = $entry['item_id'] ?? ''; $amount = intval($entry['amount'] ?? 0); $primarySource = intval($entry['primary_source'] ?? 0); $amountType = intval($entry['amount_type'] ?? 0); if (empty($itemId) || $amount <= 0) continue; $key = $itemId . '_' . $primarySource . '_' . $amountType; if (!isset($manualItems[$key])) { $manualItems[$key] = [ 'item_id' => $itemId, 'amount' => 0, 'primary_source' => $primarySource, 'amount_type' => $amountType ]; } $manualItems[$key]['amount'] += $amount; } $orders = []; $orders['manual'] = [ 'overal_order_id' => 'Manuális kiszedés', 'order_id' => '', 'order_name' => 'Manuális kiszedés', 'order_mail' => '', 'receipt_method' => '', 'order_note' => '', 'items' => [], 'totalneed' => 0, 'primary_warehouse' => $this->primaryWarehouse ]; foreach ($manualItems as $item) { $orders['manual']['items'][] = [ 'item_id' => $item['item_id'], 'need' => $item['amount'], 'taken_out' => 0, 'ordered' => $item['amount'], 'amount_type' => $item['amount_type'], 'primary_source' => $item['primary_source'] ]; $orders['manual']['totalneed'] += $item['amount']; } return $orders; } private function loadOrders($reason, $orderDate) { $orders = []; $sql = "SELECT item_id, SUM(amount) AS ordered, SUM(taken_out) AS taken_out, SUM(amount) - SUM(taken_out) AS need, IFNULL(order_id, '') AS order_id, IFNULL(order_name, '') AS order_name, IFNULL(order_mail, '') AS order_mail, MAX(reason) AS reason, primary_source, primary_warehouse, MAX(amount_type) AS amount_type, GROUP_CONCAT(DISTINCT IFNULL(note, '') SEPARATOR ' ') AS note, GROUP_CONCAT(DISTINCT IFNULL(receipt_method, '') SEPARATOR ' ') AS receipt_method FROM warehouse_reservation WHERE reason = ? AND DATE(FROM_UNIXTIME(date_create)) = ? AND is_active = 1 GROUP BY item_id, order_id, order_name, order_mail, primary_source, amount_type, primary_warehouse ORDER BY amount_type DESC, item_id"; $stmt = $this->conn->prepare($sql); $stmt->bind_param("ss", $reason, $orderDate); $stmt->execute(); $result = $stmt->get_result(); while ($row = $result->fetch_assoc()) { $hasOrder = !empty($row['order_id']) || !empty($row['order_name']) || !empty($row['order_mail']); if ($hasOrder) { $key = $row['order_id'] . '~' . $row['order_name'] . '~' . $row['order_mail']; $label = $row['order_name'] . ' - (' . $row['order_id'] . ')'; } else { $key = 'maradek'; $label = '!&maradek&! '; } if (!isset($orders[$key])) { $orders[$key] = [ 'overal_order_id' => $label, 'order_id' => strval($row['order_id']), 'order_name' => $row['order_name'], 'order_mail' => $row['order_mail'], 'receipt_method' => '', 'order_note' => '', 'items' => [], 'totalneed' => 0, 'primary_warehouse' => $row['primary_warehouse'] ?? $this->primaryWarehouse ]; } if (!empty($row['receipt_method'])) { if (empty($orders[$key]['receipt_method']) || strpos($orders[$key]['receipt_method'], $row['receipt_method']) === false) { $orders[$key]['receipt_method'] = trim($orders[$key]['receipt_method'] . ' ' . $row['receipt_method']); } } if (!empty($row['note'])) { if (empty($orders[$key]['order_note']) || strpos($orders[$key]['order_note'], $row['note']) === false) { $orders[$key]['order_note'] = trim($orders[$key]['order_note'] . ' ' . $row['note']); } } $need = intval($row['need']); $orders[$key]['items'][] = [ 'item_id' => $row['item_id'], 'need' => $need, 'taken_out' => intval($row['taken_out']), 'ordered' => intval($row['ordered']), 'amount_type' => intval($row['amount_type']), 'primary_source' => intval($row['primary_source']) ]; $orders[$key]['totalneed'] += max(0, $need); } $stmt->close(); return $orders; } private function loadBoxStock() { $stock = []; $sql = "SELECT item_id, wid, position AS pos, amount, warehouse_id FROM warehouse WHERE amount > 0 ORDER BY item_id, CAST(SUBSTRING_INDEX(position, ':', 1) AS UNSIGNED) DESC, CAST(SUBSTRING_INDEX(position, ':', -1) AS UNSIGNED) ASC, amount ASC"; $result = $this->conn->query($sql); if ($result) { while ($row = $result->fetch_assoc()) { $stock[$row['item_id']][] = [ 'wid' => intval($row['wid']), 'pos' => $row['pos'], 'amount' => intval($row['amount']), 'warehouse_id' => intval($row['warehouse_id']) ]; } } return $stock; } private function loadFoilStock() { $stock = []; $sql = "SELECT item_id, wid, place AS pos, right_db, left_db FROM warehouse_foil WHERE (right_db > 0 OR left_db > 0) AND is_active = 1 ORDER BY item_id, right_db ASC"; $result = $this->conn->query($sql); if ($result) { while ($row = $result->fetch_assoc()) { $stock[$row['item_id']][] = [ 'wid' => 'F' . intval($row['wid']), 'pos' => $row['pos'], 'amount' => min(intval($row['right_db']), intval($row['left_db'])), 'warehouse_id' => 'FOIL', 'right_db' => intval($row['right_db']), 'left_db' => intval($row['left_db']) ]; } } return $stock; } /** * Dobozos raktár priorizálása rendelésenként */ private function prioritizeStock(&$stock, $primaryWarehouse = null) { $primary = $primaryWarehouse ?? $this->primaryWarehouse; foreach ($stock as $itemId => &$bins) { usort($bins, function($a, $b) use ($primary) { $aCode = $this->warehouseConfig[$a['warehouse_id']]['code'] ?? ''; $bCode = $this->warehouseConfig[$b['warehouse_id']]['code'] ?? ''; $aIsMain = (strpos($aCode, $primary) === 0) ? 1 : 0; $bIsMain = (strpos($bCode, $primary) === 0) ? 1 : 0; if ($aIsMain !== $bIsMain) { return $bIsMain - $aIsMain; } return 0; }); } unset($bins); } /** * Fóliás raktár priorizálása rendelésenként */ private function prioritizeFoilStock(&$stock, $primaryWarehouse = null) { $primary = $primaryWarehouse ?? $this->primaryWarehouse; foreach ($stock as $itemId => &$bins) { usort($bins, function($a, $b) use ($primary) { $aIsMain = (strpos($a['pos'], $primary) === 0) ? 1 : 0; $bIsMain = (strpos($b['pos'], $primary) === 0) ? 1 : 0; if ($aIsMain !== $bIsMain) { return $bIsMain - $aIsMain; } return 0; }); } unset($bins); } // ======================================================================== // KISZEDÉSI LOGIKA // ======================================================================== private function generatePickingList($orderData, $boxStock, $foilStock) { $pickingList = []; $localBoxStock = $boxStock; $localFoilStock = $foilStock; foreach ($orderData['items'] as $item) { $itemId = $item['item_id']; $need = $item['need']; $takenOut = $item['taken_out']; $ordered = $item['ordered']; $amountType = $item['amount_type']; $primarySource = $item['primary_source']; if ($takenOut > 0) { $pickingList[] = [ 'wid' => 0, 'item_id' => $itemId, 'pos' => '00', 'code' => '', 'amount' => $takenOut . ' / ' . $ordered, 'amount_type' => $amountType, 'location' => '', 'type' => 'taken' ]; } if ($need <= 0) continue; $setInfo = $this->splitSetItemId($itemId); if ($setInfo === false) { $picks = $this->pickSimpleItem($itemId, $need, $amountType, $primarySource, $localBoxStock, $localFoilStock); $pickingList = array_merge($pickingList, $picks); } else { if ($this->isCLSet($itemId)) { $picks = $this->pickCLSet($itemId, $setInfo, $need, $amountType, $localBoxStock); $pickingList = array_merge($pickingList, $picks); } else { $picks = $this->pickNormalSet($itemId, $setInfo, $need, $amountType, $primarySource, $localBoxStock, $localFoilStock); $pickingList = array_merge($pickingList, $picks); } } if ($need > 0) { $pickingList[] = [ 'wid' => -1, 'item_id' => $itemId, 'pos' => '00', 'code' => '', 'amount' => $need, 'amount_type' => $amountType, 'location' => '', 'type' => 'missing' ]; } } return $pickingList; } private function pickSimpleItem($itemId, &$need, $amountType, $primarySource, &$boxStock, &$foilStock) { $picks = []; $sources = ($primarySource === 1) ? ['foil', 'box'] : ['box', 'foil']; foreach ($sources as $sourceType) { if ($need <= 0) break; if ($amountType !== 0 && $sourceType !== 'foil') { continue; } if ($sourceType === 'box' && isset($boxStock[$itemId])) { $picks = array_merge($picks, $this->pickFromBoxStock($itemId, $need, $amountType, $boxStock)); } if ($sourceType === 'foil' && isset($foilStock[$itemId])) { $picks = array_merge($picks, $this->pickFromFoilStock($itemId, $need, $amountType, $foilStock)); } } return $picks; } private function pickFromBoxStock($itemId, &$need, $amountType, &$stock) { $picks = []; while ($need > 0 && isset($stock[$itemId]) && !empty($stock[$itemId])) { $bin = &$stock[$itemId][0]; $take = min($bin['amount'], $need); $picks[] = [ 'wid' => $bin['wid'], 'item_id' => $itemId, 'pos' => $this->formatWarehousePosition($bin['pos'], $bin['warehouse_id']), 'code' => $this->warehouseConfig[$bin['warehouse_id']]['code'] ?? '', 'amount' => $take, 'amount_type' => $amountType, 'location' => ($this->warehouseConfig[$bin['warehouse_id']]['location'] ?? '') . ' / ' . ($this->warehouseConfig[$bin['warehouse_id']]['name'] ?? ''), 'type' => 'stock' ]; $bin['amount'] -= $take; $need -= $take; if ($bin['amount'] <= 0) { array_shift($stock[$itemId]); } } return $picks; } private function pickFromFoilStock($itemId, &$need, $amountType, &$stock) { $picks = []; while ($need > 0 && isset($stock[$itemId]) && !empty($stock[$itemId])) { $bin = &$stock[$itemId][0]; if ($amountType === 0) { $take = min($bin['amount'], $need); } elseif ($amountType === 1) { $take = min($bin['right_db'], $need); } elseif ($amountType === 2) { $take = min($bin['left_db'], $need); } else { $take = 0; } if ($take <= 0) { array_shift($stock[$itemId]); continue; } $picks[] = [ 'wid' => $bin['wid'], 'item_id' => $itemId, 'pos' => $bin['pos'], 'code' => '', 'amount' => $take, 'amount_type' => $amountType, 'location' => self::FOIL_WAREHOUSE_LABEL, 'type' => 'foil' ]; if ($amountType === 0) { $bin['right_db'] -= $take; $bin['left_db'] -= $take; } elseif ($amountType === 1) { $bin['right_db'] -= $take; } elseif ($amountType === 2) { $bin['left_db'] -= $take; } $bin['amount'] = min($bin['right_db'], $bin['left_db']); $need -= $take; if ($bin['right_db'] <= 0 && $bin['left_db'] <= 0) { array_shift($stock[$itemId]); } } return $picks; } private function pickCLSet($itemId, $setInfo, &$need, $amountType, &$boxStock) { $picks = []; $item1 = $setInfo['item1']; $item2 = $setInfo['item2']; $available1 = 0; $available2 = 0; if (isset($boxStock[$item1])) { foreach ($boxStock[$item1] as $bin) { $available1 += $bin['amount']; } } if (isset($boxStock[$item2])) { foreach ($boxStock[$item2] as $bin) { $available2 += $bin['amount']; } } $maxCanTake = min($available1, $available2, $need); if ($maxCanTake > 0) { $tempNeed = $maxCanTake; while ($tempNeed > 0 && !empty($boxStock[$item1]) && !empty($boxStock[$item2])) { $bin1 = &$boxStock[$item1][0]; $bin2 = &$boxStock[$item2][0]; $take = min($bin1['amount'], $bin2['amount'], $tempNeed); $pos1 = $this->formatWarehousePosition($bin1['pos'], $bin1['warehouse_id']); $pos2 = $this->formatWarehousePosition($bin2['pos'], $bin2['warehouse_id']); $picks[] = [ 'wid' => $bin1['wid'] . '/' . $bin2['wid'], 'item_id' => $itemId, 'pos' => $pos1 . '
' . $pos2, // + helyett
'code' => '', 'amount' => $take, 'amount_type' => $amountType, 'location' => ($this->warehouseConfig[$bin1['warehouse_id']]['location'] ?? '') . ' / ' . ($this->warehouseConfig[$bin1['warehouse_id']]['name'] ?? '') . '
' . ($this->warehouseConfig[$bin2['warehouse_id']]['location'] ?? '') . ' / ' . ($this->warehouseConfig[$bin2['warehouse_id']]['name'] ?? ''), 'type' => 'stock' ]; $bin1['amount'] -= $take; $bin2['amount'] -= $take; $tempNeed -= $take; if ($bin1['amount'] <= 0) { array_shift($boxStock[$item1]); } if ($bin2['amount'] <= 0) { array_shift($boxStock[$item2]); } } $need -= $maxCanTake; } return $picks; } private function pickNormalSet($itemId, $setInfo, &$need, $amountType, $primarySource, &$boxStock, &$foilStock) { $picks = []; $item1 = $setInfo['item1']; $item2 = $setInfo['item2']; $sources = ($primarySource === 1) ? ['foil', 'box', 'hybrid'] : ['box', 'foil', 'hybrid']; foreach ($sources as $sourceType) { if ($need <= 0) break; if ($sourceType === 'box' && isset($boxStock[$itemId])) { while ($need > 0 && !empty($boxStock[$itemId])) { $bin = &$boxStock[$itemId][0]; $take = min($bin['amount'], $need); $picks[] = [ 'wid' => $bin['wid'], 'item_id' => $itemId, 'pos' => $this->formatWarehousePosition($bin['pos'], $bin['warehouse_id']), 'code' => $this->warehouseConfig[$bin['warehouse_id']]['code'] ?? '', 'amount' => $take, 'amount_type' => $amountType, 'location' => ($this->warehouseConfig[$bin['warehouse_id']]['location'] ?? '') . ' / ' . ($this->warehouseConfig[$bin['warehouse_id']]['name'] ?? ''), 'type' => 'stock' ]; $bin['amount'] -= $take; $need -= $take; if ($bin['amount'] <= 0) { array_shift($boxStock[$itemId]); } } } if ($sourceType === 'foil' && $need > 0) { $picks = array_merge($picks, $this->pickSetFromFoil($itemId, $item1, $item2, $need, $amountType, $foilStock)); } if ($sourceType === 'hybrid' && $need > 0) { $picks = array_merge($picks, $this->pickSetHybrid($itemId, $item1, $item2, $need, $amountType, $boxStock, $foilStock)); } } return $picks; } private function pickSetFromFoil($itemId, $item1, $item2, &$need, $amountType, &$foilStock) { $picks = []; $available1 = 0; $available2 = 0; if (isset($foilStock[$item1])) { foreach ($foilStock[$item1] as $bin) { $available1 += $bin['amount']; } } if (isset($foilStock[$item2])) { foreach ($foilStock[$item2] as $bin) { $available2 += $bin['amount']; } } $maxCanTake = min($available1, $available2, $need); if ($maxCanTake > 0) { $tempNeed = $maxCanTake; while ($tempNeed > 0 && !empty($foilStock[$item1]) && !empty($foilStock[$item2])) { $bin1 = &$foilStock[$item1][0]; $bin2 = &$foilStock[$item2][0]; $take = min($bin1['amount'], $bin2['amount'], $tempNeed); if ($take > 0) { $picks[] = [ 'wid' => $bin1['wid'] . '/' . $bin2['wid'], 'item_id' => $itemId, 'pos' => $bin1['pos'] . '
' . $bin2['pos'], // + helyett
'code' => '', 'amount' => $take, 'amount_type' => $amountType, 'location' => self::FOIL_WAREHOUSE_LABEL, 'type' => 'foil' ]; $bin1['amount'] -= $take; $bin2['amount'] -= $take; $bin1['right_db'] -= $take; $bin1['left_db'] -= $take; $bin2['right_db'] -= $take; $bin2['left_db'] -= $take; $tempNeed -= $take; } if ($bin1['amount'] <= 0) { array_shift($foilStock[$item1]); } if ($bin2['amount'] <= 0) { array_shift($foilStock[$item2]); } } $need -= $maxCanTake; } return $picks; } private function pickSetHybrid($itemId, $item1, $item2, &$need, $amountType, &$boxStock, &$foilStock) { $picks = []; // item1 dobozból, item2 fóliásból $availableBox1 = 0; $availableFoil2 = 0; if (isset($boxStock[$item1])) { foreach ($boxStock[$item1] as $bin) { $availableBox1 += $bin['amount']; } } if (isset($foilStock[$item2])) { foreach ($foilStock[$item2] as $bin) { $availableFoil2 += $bin['amount']; } } $maxCanTake1 = min($availableBox1, $availableFoil2, $need); if ($maxCanTake1 > 0) { $tempNeed = $maxCanTake1; while ($tempNeed > 0 && !empty($boxStock[$item1]) && !empty($foilStock[$item2])) { $bin1 = &$boxStock[$item1][0]; $bin2 = &$foilStock[$item2][0]; $take = min($bin1['amount'], $bin2['amount'], $tempNeed); if ($take > 0) { $pos1 = $this->formatWarehousePosition($bin1['pos'], $bin1['warehouse_id']); $picks[] = [ 'wid' => $bin1['wid'] . '/' . $bin2['wid'], 'item_id' => $itemId, 'pos' => $pos1 . '
' . $bin2['pos'], // + helyett
'code' => '', 'amount' => $take, 'amount_type' => $amountType, 'location' => ($this->warehouseConfig[$bin1['warehouse_id']]['location'] ?? '') . ' / ' . ($this->warehouseConfig[$bin1['warehouse_id']]['name'] ?? '') . '
' . self::FOIL_WAREHOUSE_LABEL, 'type' => 'hybrid' ]; $bin1['amount'] -= $take; $bin2['amount'] -= $take; $bin2['right_db'] -= $take; $bin2['left_db'] -= $take; $tempNeed -= $take; } if ($bin1['amount'] <= 0) { array_shift($boxStock[$item1]); } if ($bin2['amount'] <= 0) { array_shift($foilStock[$item2]); } } $need -= $maxCanTake1; } // item1 fóliásból, item2 dobozból if ($need > 0) { $availableFoil1 = 0; $availableBox2 = 0; if (isset($foilStock[$item1])) { foreach ($foilStock[$item1] as $bin) { $availableFoil1 += $bin['amount']; } } if (isset($boxStock[$item2])) { foreach ($boxStock[$item2] as $bin) { $availableBox2 += $bin['amount']; } } $maxCanTake2 = min($availableFoil1, $availableBox2, $need); if ($maxCanTake2 > 0) { $tempNeed = $maxCanTake2; while ($tempNeed > 0 && !empty($foilStock[$item1]) && !empty($boxStock[$item2])) { $bin1 = &$foilStock[$item1][0]; $bin2 = &$boxStock[$item2][0]; $take = min($bin1['amount'], $bin2['amount'], $tempNeed); if ($take > 0) { $pos2 = $this->formatWarehousePosition($bin2['pos'], $bin2['warehouse_id']); $picks[] = [ 'wid' => $bin1['wid'] . '/' . $bin2['wid'], 'item_id' => $itemId, 'pos' => $bin1['pos'] . '
' . $pos2, // + helyett
'code' => '', 'amount' => $take, 'amount_type' => $amountType, 'location' => self::FOIL_WAREHOUSE_LABEL . '
' . ($this->warehouseConfig[$bin2['warehouse_id']]['location'] ?? '') . ' / ' . ($this->warehouseConfig[$bin2['warehouse_id']]['name'] ?? ''), 'type' => 'hybrid' ]; $bin1['amount'] -= $take; $bin1['right_db'] -= $take; $bin1['left_db'] -= $take; $bin2['amount'] -= $take; $tempNeed -= $take; } if ($bin1['amount'] <= 0) { array_shift($foilStock[$item1]); } if ($bin2['amount'] <= 0) { array_shift($boxStock[$item2]); } } $need -= $maxCanTake2; } } return $picks; } // ======================================================================== // SEGÉD METÓDUSOK // ======================================================================== /** * Szett item_id szétbontása * Ha van prefix (betű), akkor vezető nullák maradnak * Ha nincs prefix, akkor vezető nullák eltávolítása */ private function splitSetItemId($itemId) { // Ellenőrzés: tartalmaz-e '+' jelet if (strpos($itemId, '+') === false) { return false; } $parts = explode('+', $itemId, 2); $first = trim($parts[0]); $second = trim($parts[1]); // Prefix detektálás (nem számjegy karakterek az elején) $prefix = ''; $firstNumeric = $first; if (preg_match('/^([^0-9]{1,2})(.*)$/', $first, $matches)) { // Van prefix (pl. CL, FR, LR) $prefix = $matches[1]; $firstNumeric = $matches[2]; // Prefix esetén vezető nullák MARADNAK $item1 = $prefix . $firstNumeric; $item2 = $prefix . $second; } else { // Nincs prefix - csak számok // Vezető nullák ELTÁVOLÍTÁSA $item1 = (string)intval($first); $item2 = (string)intval($second); } return ['item1' => $item1, 'item2' => $item2]; } private function isCLSet($itemId) { return substr($itemId, 0, 2) === self::CL_SET_PREFIX; } private function formatWarehousePosition($position, $warehouseId) { $parts = explode(':', $position); if (count($parts) === 2) { $letter = $this->numberToLetter(intval($parts[0])); $number = $this->padZero($parts[1]); $code = $this->warehouseConfig[$warehouseId]['code'] ?? ''; return $code . $letter . $number; } return $position; } private function numberToLetter($n) { if ($n < 1 || $n > 26) return null; return chr(64 + $n); } private function padZero($str) { return str_pad(strval($str), 2, '0', STR_PAD_LEFT); } private function formatPositionForSort(&$pickingList) { foreach ($pickingList as &$entry) { if (strpos($entry['pos'], ',') !== false) { list($row, $col) = explode(',', $entry['pos']); $entry['pos'] = $col . '~' . $row; } } unset($entry); } private function sortPickingList(&$pickingList) { $typeOrder = [ 'foil' => 1, 'stock' => 2, 'hybrid' => 2, 'taken' => 3, 'missing' => 4 ]; usort($pickingList, function($a, $b) use ($typeOrder) { $aType = $a['type'] ?? 'stock'; $bType = $b['type'] ?? 'stock'; $aPriority = $typeOrder[$aType] ?? 3; $bPriority = $typeOrder[$bType] ?? 3; if ($aPriority != $bPriority) { return $aPriority - $bPriority; } if ($aType === 'taken' || $aType === 'missing') { return strcmp($a['item_id'], $b['item_id']); } // Lokáció természetes rendezés $locCmp = strnatcmp($a['location'], $b['location']); if ($locCmp != 0) { return $locCmp; } // Pozíció természetes rendezés if (in_array($aType, ['stock', 'hybrid'])) { if (strpos($a['pos'], '~') !== false && strpos($b['pos'], '~') !== false) { list($aCol, $aRow) = explode('~', $a['pos']); list($bCol, $bRow) = explode('~', $b['pos']); if (intval($aRow) != intval($bRow)) { return intval($aRow) - intval($bRow); } if (intval($aCol) != intval($bCol)) { return intval($aCol) - intval($bCol); } } } // Természetes rendezés pozíción (FŐ01, FŐ02, ... FŐ10) $posCmp = strnatcmp($a['pos'], $b['pos']); if ($posCmp != 0) { return $posCmp; } return strcmp($a['item_id'], $b['item_id']); }); } private function sortOrders(&$orders) { $typeOrder = [ 'foil' => 1, 'stock' => 2, 'hybrid' => 2, 'taken' => 3, 'missing' => 4 ]; usort($orders, function($a, $b) use ($typeOrder) { if (empty($a['picking_list']) || empty($b['picking_list'])) { return 0; } $aFirst = $a['picking_list'][0]; $bFirst = $b['picking_list'][0]; $aType = $aFirst['type'] ?? 'stock'; $bType = $bFirst['type'] ?? 'stock'; $aPriority = $typeOrder[$aType] ?? 3; $bPriority = $typeOrder[$bType] ?? 3; if ($aPriority != $bPriority) { return $aPriority - $bPriority; } // Természetes rendezés lokáción $locCmp = strnatcmp($aFirst['location'], $bFirst['location']); if ($locCmp != 0) { return $locCmp; } // Pozíció rendezés if (in_array($aType, ['stock', 'hybrid'])) { if (strpos($aFirst['pos'], '~') !== false && strpos($bFirst['pos'], '~') !== false) { list($aCol, $aRow) = explode('~', $aFirst['pos']); list($bCol, $bRow) = explode('~', $bFirst['pos']); if (intval($aRow) != intval($bRow)) { return intval($aRow) - intval($bRow); } if (intval($aCol) != intval($bCol)) { return intval($aCol) - intval($bCol); } } } // Természetes rendezés pozíción return strnatcmp($aFirst['pos'], $bFirst['pos']); }); } private function determineIcon($pickingList) { foreach ($pickingList as $entry) { if ($entry['wid'] != 0) { return '✓'; } } return ''; } } /** * Raktárból Eltávolítás Osztály * * Funkció: Kiszedett termékek eltávolítása a raktárból és lefoglalás frissítése * */ class WarehouseRemover { private $conn; /** * Konstruktor */ public function __construct($conn) { $this->conn = $conn; } /** * Statikus hívás */ public static function remove($conn, $inputData, $coderclass) { $remover = new self($conn); return $remover->processRemoval($inputData, $coderclass); } /** * Eltávolítás feldolgozás */ private function processRemoval($inputData, $coderclass) { // Input adatok kinyerése és tisztítása $wid = htmlspecialchars($inputData['wid'] ?? ''); $amount = htmlspecialchars($inputData['amount'] ?? ''); $item_id = htmlspecialchars(str_replace(' ', '+', $inputData['item_id'] ?? '')); $corrigate = htmlspecialchars($inputData['corrigate'] ?? 'false'); $overal_order_id = $inputData['overal_order_id'] ?? ''; $encoded_reason = htmlspecialchars($inputData['reason'] ?? ''); // Order ID és order name feldolgozása $order_info = $this->parseOrderInfo($overal_order_id); $order_name = $order_info['order_name']; $order_id = $order_info['order_id']; // Reason dekódolás $decoded_data = $coderclass->decode($encoded_reason, 'SZ4TUN4'); $data_parts = explode('|', $decoded_data); if (count($data_parts) != 2) { return ['result' => 'error', 'message' => 'Érvénytelen kiszedési lista azonosító']; } $reason = $data_parts[0]; $order_date = $data_parts[1]; // Amount type feldolgozása $amount_info = $this->parseAmount($amount); $amount_type = $amount_info['amount_type']; $amount_value = $amount_info['amount']; // WID lista feldolgozása és szétválogatása $wid_info = $this->parseWids($wid); $foil_wids = $wid_info['foil_wids']; $box_wids = $wid_info['box_wids']; // Raktár ellenőrzés $warehouse_check = $this->checkWarehouseStock($foil_wids, $box_wids, $amount_value, $amount_type); if (!$warehouse_check['success']) { return $warehouse_check; } $warehouse_foil_list = $warehouse_check['foil_list']; $warehouse_box_list = $warehouse_check['box_list']; // Lefoglalás ellenőrzés $reservation_check = $this->checkReservation($item_id, $order_date, $reason, $order_name, $order_id, $amount_type, $amount_value); if (!$reservation_check['success']) { return $reservation_check; } $reserv_id = $reservation_check['reserv_id']; $new_taken_out = $reservation_check['new_taken_out']; // Lefoglalás frissítése $this->updateReservation($reserv_id, $new_taken_out); // Raktár frissítése $update_result = $this->updateWarehouseStock( $foil_wids, $box_wids, $warehouse_foil_list, $warehouse_box_list, $amount_value, $amount_type, $corrigate ); return [ 'result' => 'ok', 'reset' => $update_result['reset'], 'message' => $update_result['message'] ]; } /** * Order info feldolgozás */ private function parseOrderInfo($overal_order_id) { $order_name = ''; $order_id = ''; if ($overal_order_id != '!&maradek&! ') { $last_dash_pos = strrpos($overal_order_id, ' - '); if ($last_dash_pos !== false) { $order_name = htmlspecialchars(substr($overal_order_id, 0, $last_dash_pos)); $order_part = substr($overal_order_id, $last_dash_pos + 3); $order_id = substr($order_part, 1, -1); } else { $order_name = htmlspecialchars($overal_order_id); } } return ['order_name' => $order_name, 'order_id' => $order_id]; } /** * Amount feldolgozás (J/B prefix kezelése) */ private function parseAmount($amount) { $amount_type = 0; if (substr($amount, 0, 1) == "J") { $amount_type = 1; $amount = substr($amount, 1); } else if (substr($amount, 0, 1) == "B") { $amount_type = 2; $amount = substr($amount, 1); } return ['amount_type' => $amount_type, 'amount' => intval($amount)]; } /** * WID lista feldolgozása és szétválogatása */ private function parseWids($wid) { $wid_list = explode('/', $wid); $foil_wids = []; $box_wids = []; foreach ($wid_list as $single_wid) { if (substr($single_wid, 0, 1) == "F") { $foil_wids[] = substr($single_wid, 1); } else { $box_wids[] = $single_wid; } } return ['foil_wids' => $foil_wids, 'box_wids' => $box_wids]; } /** * Raktár készlet ellenőrzés */ private function checkWarehouseStock($foil_wids, $box_wids, $amount, $amount_type) { $warehouse_foil_list = []; $warehouse_box_list = []; // Fóliás raktár ellenőrzés foreach ($foil_wids as $foil_wid) { $stmt = $this->conn->prepare("SELECT * FROM warehouse_foil WHERE wid = ? AND is_active = 1"); $stmt->bind_param("i", $foil_wid); $stmt->execute(); $result = $stmt->get_result(); $warehouse_foil = $result->fetch_assoc(); $stmt->close(); if ($warehouse_foil == null) { return [ 'success' => false, 'result' => 'error', 'reset' => 'true', 'message' => 'Érvénytelen fóliás raktár azonosító (WID: F' . $foil_wid . ')' ]; } // Mennyiség ellenőrzés if ($amount_type == 1) { $inWarehouse = intval($warehouse_foil['right_db']); } else if ($amount_type == 2) { $inWarehouse = intval($warehouse_foil['left_db']); } else { $inWarehouse = min(intval($warehouse_foil['right_db']), intval($warehouse_foil['left_db'])); } if ($inWarehouse < $amount) { return [ 'success' => false, 'result' => 'error', 'reset' => 'true', 'message' => 'Érvénytelen raktár bejegyzés, a fóliás raktár szerint nincs ennyi termék a polcon (van: ' . $inWarehouse . ', kell: ' . $amount . ')' ]; } $warehouse_foil_list[] = $warehouse_foil; } // Dobozos raktár ellenőrzés foreach ($box_wids as $box_wid) { $stmt = $this->conn->prepare("SELECT * FROM warehouse WHERE wid = ?"); $stmt->bind_param("i", $box_wid); $stmt->execute(); $result = $stmt->get_result(); $warehouse_box = $result->fetch_assoc(); $stmt->close(); if ($warehouse_box == null) { return [ 'success' => false, 'result' => 'error', 'reset' => 'true', 'message' => 'Érvénytelen dobozos raktár azonosító (WID: ' . $box_wid . ')' ]; } if (intval($warehouse_box['amount']) < $amount) { return [ 'success' => false, 'result' => 'error', 'reset' => 'true', 'message' => 'Érvénytelen raktár bejegyzés, a dobozos raktár szerint nincs ennyi termék a polcon (van: ' . $warehouse_box['amount'] . ', kell: ' . $amount . ')' ]; } $warehouse_box_list[] = $warehouse_box; } return [ 'success' => true, 'foil_list' => $warehouse_foil_list, 'box_list' => $warehouse_box_list ]; } /** * Lefoglalás ellenőrzés */ private function checkReservation($item_id, $order_date, $reason, $order_name, $order_id, $amount_type, $amount) { $stmt = $this->conn->prepare("SELECT * FROM warehouse_reservation WHERE DATE(FROM_UNIXTIME(date_create)) = ? AND item_id = ? AND reason = ? AND order_name = ? AND order_id = ? AND is_active = 1 AND amount_type = ?"); $stmt->bind_param("sssssi", $order_date, $item_id, $reason, $order_name, $order_id, $amount_type); $stmt->execute(); $result = $stmt->get_result(); $reservation_sql = $result->fetch_assoc(); $stmt->close(); if ($reservation_sql == null) { return [ 'success' => false, 'result' => 'error', 'reset' => 'true', 'message' => 'Érvénytelen rendelési azonosító (item: ' . $item_id . ', order: ' . $order_name . ', date: ' . $order_date . ')' ]; } $reserv_id = $reservation_sql['reserv_id']; $current_taken_out = intval($reservation_sql['taken_out']); $ordered_amount = intval($reservation_sql['amount']); $new_taken_out = $current_taken_out + $amount; if ($new_taken_out > $ordered_amount) { return [ 'success' => false, 'result' => 'error', 'reset' => 'true', 'message' => 'Hiba történt a mennyiségek megadásakor! Több terméket akart kiszedni mint amennyi a rendelésben szerepel. (Rendelt: ' . $ordered_amount . ', már kiszedve: ' . $current_taken_out . ', most kiszedni kívánt: ' . $amount . ')' ]; } if ($new_taken_out < 0) { return [ 'success' => false, 'result' => 'error', 'reset' => 'true', 'message' => 'Hiba történt a mennyiségek megadásakor, negatív érték nem megengedett!' ]; } return [ 'success' => true, 'reserv_id' => $reserv_id, 'new_taken_out' => $new_taken_out ]; } /** * Lefoglalás frissítése */ private function updateReservation($reserv_id, $new_taken_out) { $stmt = $this->conn->prepare("UPDATE warehouse_reservation SET taken_out = ? WHERE reserv_id = ?"); $stmt->bind_param("ii", $new_taken_out, $reserv_id); $stmt->execute(); $stmt->close(); } /** * Raktár készlet frissítése */ private function updateWarehouseStock($foil_wids, $box_wids, $warehouse_foil_list, $warehouse_box_list, $amount, $amount_type, $corrigate) { $reset = 'false'; $message = ''; // Fóliás raktár frissítése foreach ($foil_wids as $idx => $foil_wid) { $warehouse_foil = $warehouse_foil_list[$idx]; // Mennyiség számítás if ($amount_type == 1) { $left_db = 0; $right_db = -$amount; } else if ($amount_type == 2) { $left_db = -$amount; $right_db = 0; } else { $left_db = -$amount; $right_db = -$amount; } // Korrigálás if ($corrigate == 'true') { if ($amount_type == 1) { $left_db = 0; $right_db = -intval($warehouse_foil['right_db']); } else if ($amount_type == 2) { $left_db = -intval($warehouse_foil['left_db']); $right_db = 0; } else { if (intval($warehouse_foil['right_db']) > intval($warehouse_foil['left_db'])) { $left_db = -intval($warehouse_foil['left_db']); $right_db = -intval($warehouse_foil['left_db']); } else { $left_db = -intval($warehouse_foil['right_db']); $right_db = -intval($warehouse_foil['right_db']); } } $reset = 'true'; $message = 'A raktár megfelelő polcán a mennyiség nullázva lett'; } // Frissítés $stmt = $this->conn->prepare("UPDATE warehouse_foil SET left_db = CASE WHEN left_db + ? < 0 THEN 0 ELSE left_db + ? END, right_db = CASE WHEN right_db + ? < 0 THEN 0 ELSE right_db + ? END WHERE wid = ?"); $stmt->bind_param("iiiii", $left_db, $left_db, $right_db, $right_db, $foil_wid); $stmt->execute(); $stmt->close(); } // Dobozos raktár frissítése foreach ($box_wids as $idx => $box_wid) { $warehouse_box = $warehouse_box_list[$idx]; $new_amount_in_warehouse = 0; if ($corrigate == 'true') { $new_amount_in_warehouse = 0; $reset = 'true'; $message = 'A raktár megfelelő polcán a mennyiség nullázva lett'; } else { $new_amount_in_warehouse = intval($warehouse_box['amount']) - $amount; } $stmt = $this->conn->prepare("UPDATE warehouse SET amount = ? WHERE wid = ?"); $stmt->bind_param("ii", $new_amount_in_warehouse, $box_wid); $stmt->execute(); $stmt->close(); } return ['reset' => $reset, 'message' => $message]; } } ?>