prepare( "SELECT sku, product_name, uom FROM md_product WHERE company_id = :company_id AND sku = :sku AND status > 0 LIMIT 1" ); $sth->execute([':company_id' => $company_id, ':sku' => $sku]); return $sth->fetch(PDO::FETCH_ASSOC); } function validateLotSerial(PDO $pdo, int $company_id, string $sku, string $lot, string $serial): void { if ($serial === '') return; $warehouses = $pdo->prepare( "SELECT id FROM md_warehouse WHERE company_id = :company_id AND status = 1" ); $warehouses->execute([':company_id' => $company_id]); foreach ($warehouses->fetchAll(PDO::FETCH_COLUMN) as $warehouse_id) { $table = 'td_stock_' . (int)$warehouse_id; $sth = $pdo->prepare( "SELECT lot_number FROM `{$table}` WHERE company_id = :company_id AND product_sku = :sku AND serial_number = :serial AND status != -1 LIMIT 1" ); $sth->execute([':company_id' => $company_id, ':sku' => $sku, ':serial' => $serial]); $existing_lot = $sth->fetchColumn(); if ($existing_lot !== false && (string)$existing_lot !== $lot) { throw new Exception('Serial number belongs to a different lot.'); } } } function findPreparedLabel(PDO $pdo, int $company_id, string $barcode, string $type): array|false { $sth = $pdo->prepare( "SELECT * FROM md_barcode WHERE company_id = :company_id AND barcode = :barcode AND barcode_type = :barcode_type LIMIT 1" ); $sth->execute([ ':company_id' => $company_id, ':barcode' => $barcode, ':barcode_type' => $type, ]); $row = $sth->fetch(PDO::FETCH_ASSOC); if ($row && (int)$row['status'] !== 1) { throw new Exception('Barcode is disabled.'); } return $row ?: false; } function advancedLocation(PDO $pdo, int $company_id): bool { $sth = $pdo->prepare( "SELECT value FROM company_setting WHERE company_id = :company_id AND setting_key = 'advanced_location' LIMIT 1" ); $sth->execute([':company_id' => $company_id]); $value = $sth->fetchColumn(); return (int)($value === false ? 0 : $value) === 1; } function validateLocation(PDO $pdo, int $company_id, int $warehouse_id, string $zone, string $aisle, string $rack): void { $sth = $pdo->prepare( "SELECT 1 FROM md_rack WHERE company_id = :company_id AND warehouse = :warehouse AND zone = :zone AND aisle = :aisle AND rack = :rack LIMIT 1" ); $sth->execute([ ':company_id' => $company_id, ':warehouse' => $warehouse_id, ':zone' => $zone, ':aisle' => $aisle, ':rack' => $rack, ]); if (!$sth->fetchColumn()) throw new Exception('Location not found.'); } $parts = explode('|', $barcode); $label_type = strtoupper($parts[0] ?? ''); try { if ($label_type === 'SKU') { if (count($parts) !== 4) throw new Exception('Invalid SKU label format.'); $sku = trim($parts[1]); $lot = trim($parts[2]); $serial = trim($parts[3]); if ($sku === '' || $lot === '' || $serial === '') throw new Exception('SKU, lot, and serial are required.'); $label = findPreparedLabel($pdo2, $company_id, $barcode, 'sku'); if (!$label) throw new Exception('SKU barcode label was not prepared.'); if ($label['product_sku'] !== $sku || $label['lot_number'] !== $lot || $label['serial_number'] !== $serial) { throw new Exception('SKU barcode label does not match its registry.'); } $product = findProduct($pdo2, $company_id, $sku); if (!$product) throw new Exception('SKU not found.'); validateLotSerial($pdo2, $company_id, $sku, $lot, $serial); $answer['success'] = 1; $answer['type'] = 'sku'; $answer['sku'] = $product['sku']; $answer['product_name'] = $product['product_name']; $answer['uom'] = $product['uom'] ?? 'pcs'; $answer['lot_number'] = $lot; $answer['serial_number'] = $serial; exit(json_encode($answer)); } if ($label_type === 'LOC') { $advanced = advancedLocation($pdo1, $company_id); if ($advanced && count($parts) !== 5) throw new Exception('Invalid advanced location label format.'); if (!$advanced && count($parts) !== 3) throw new Exception('Invalid location label format.'); $warehouse_id = (int)($parts[1] ?? 0); if ($advanced) { $zone = trim($parts[2]); $aisle = trim($parts[3]); $rack = trim($parts[4]); } else { $rack = trim($parts[2]); $zone = $rack; $aisle = $rack; } if (!$warehouse_id || $zone === '' || $aisle === '' || $rack === '') throw new Exception('Location label is incomplete.'); $label = findPreparedLabel($pdo2, $company_id, $barcode, 'loc'); if (!$label) throw new Exception('Location barcode label was not prepared.'); if ((int)$label['warehouse_id'] !== $warehouse_id || $label['zone'] !== $zone || $label['aisle'] !== $aisle || $label['rack'] !== $rack) { throw new Exception('Location barcode label does not match its registry.'); } validateLocation($pdo2, $company_id, $warehouse_id, $zone, $aisle, $rack); $answer['success'] = 1; $answer['type'] = 'loc'; $answer['warehouse_id'] = $warehouse_id; $answer['zone'] = $zone; $answer['aisle'] = $aisle; $answer['rack'] = $rack; exit(json_encode($answer)); } } catch (Exception $e) { $answer['message'] = $e->getMessage(); http_response_code(400); exit(json_encode($answer)); } $sth = $pdo2->prepare( "SELECT sku, product_name, uom FROM md_product WHERE company_id = :company_id AND (barcode = :barcode OR sku = :sku) AND status > 0 ORDER BY (barcode = :barcode_order) DESC LIMIT 1" ); $sth->execute([ ':company_id' => $company_id, ':barcode' => $barcode, ':sku' => $barcode, ':barcode_order' => $barcode, ]); $product = $sth->fetch(PDO::FETCH_ASSOC); if (!$product) { $answer['message'] = 'Barcode not found.'; http_response_code(404); exit(json_encode($answer)); } $answer['success'] = 1; $answer['type'] = 'raw'; $answer['sku'] = $product['sku']; $answer['product_name'] = $product['product_name']; $answer['uom'] = $product['uom'] ?? 'pcs'; exit(json_encode($answer)); ?>