warehouse layers [switchable]

This commit is contained in:
Thanakorn S
2026-05-04 13:43:32 +07:00
parent a90975d9f1
commit 414f1cd81d
26 changed files with 2022 additions and 584 deletions
+90 -10
View File
@@ -78,7 +78,12 @@ class WarehouseManager {
$from = trim($from);
$to = trim($to);
// Numeric range
// Same value — single location, any format allowed (e.g. "Shelf-A", "Bin-3")
if ($from === $to) {
return [$from];
}
// Numeric range e.g. 1 → 10
if (is_numeric($from) && is_numeric($to)) {
$f = (int) $from;
$t = (int) $to;
@@ -86,15 +91,62 @@ class WarehouseManager {
return array_map('strval', range($f, $t));
}
// Single-letter alpha range
// Single-letter alpha range e.g. A → Z
if (ctype_alpha($from) && ctype_alpha($to)
&& strlen($from) === 1 && strlen($to) === 1) {
if (ord($from) > ord($to)) throw new Exception("Range start ($from) must be <= end ($to)");
return range($from, $to); // PHP range() handles 'A' → 'Z' natively
if (ord(strtoupper($from)) > ord(strtoupper($to))) {
throw new Exception("Range start ($from) must be <= end ($to)");
}
return range(strtoupper($from), strtoupper($to));
}
// Prefix + suffix range e.g. Shelf-A → Shelf-E or Bin-1 → Bin-5
// Split on the last sequence of digits or single letter at the end
$pattern = '/^(.*?)([A-Za-z]|[0-9]+)$/';
if (preg_match($pattern, $from, $mf) && preg_match($pattern, $to, $mt)) {
$prefix_f = $mf[1];
$prefix_t = $mt[1];
$suffix_f = $mf[2];
$suffix_t = $mt[2];
// Prefixes must match
if ($prefix_f !== $prefix_t) {
throw new Exception(
"Location prefix mismatch: '$prefix_f' vs '$prefix_t'. " .
"From and To must share the same prefix (e.g. Shelf-A → Shelf-E)."
);
}
$prefix = $prefix_f;
// Suffix: numeric range
if (is_numeric($suffix_f) && is_numeric($suffix_t)) {
$f = (int) $suffix_f;
$t = (int) $suffix_t;
if ($f > $t) throw new Exception("Range start ($from) must be <= end ($to)");
return array_map(fn($n) => $prefix . $n, range($f, $t));
}
// Suffix: single-letter range
if (ctype_alpha($suffix_f) && ctype_alpha($suffix_t)
&& strlen($suffix_f) === 1 && strlen($suffix_t) === 1) {
$sf = strtoupper($suffix_f);
$st = strtoupper($suffix_t);
if (ord($sf) > ord($st)) {
throw new Exception("Range start ($from) must be <= end ($to)");
}
return array_map(fn($c) => $prefix . $c, range($sf, $st));
}
throw new Exception(
"Location suffix must be a number (e.g. 1–99) or single letter (e.g. A–Z). " .
"Got: '$suffix_f' → '$suffix_t'"
);
}
throw new Exception(
"Aisle/rack values must be either numbers (e.g. 1–99) or single uppercase letters (e.g. A–Z). Got: '$from' → '$to'"
"Invalid location range: '$from' → '$to'. " .
"Use formats like: 1–10, A–Z, Shelf-1–Shelf-10, or Shelf-A–Shelf-Z."
);
}
@@ -848,6 +900,16 @@ class WarehouseManager {
*/
public function getRacksAll(int $warehouse_id, string $zone, string $aisle): array
{
if ($zone === '' || $aisle === '') {
$sth = $this->pdo->prepare(
"SELECT DISTINCT rack FROM md_rack
WHERE company_id = :company_id AND warehouse = :warehouse
ORDER BY CAST(rack AS UNSIGNED), rack"
);
$sth->execute([':company_id' => $this->company_id, ':warehouse' => $warehouse_id]);
return array_column($sth->fetchAll(PDO::FETCH_ASSOC), 'rack');
}
$sth = $this->pdo->prepare(
"SELECT DISTINCT rack FROM md_rack
WHERE company_id = :company_id AND warehouse = :warehouse AND zone = :zone AND aisle = :aisle
@@ -1703,6 +1765,21 @@ class WarehouseManager {
*/
public function getRacksIn(int $warehouse_id, string $zone, string $aisle): array
{
if ($zone === '' || $aisle === '') {
$sth = $this->pdo->prepare(
"SELECT DISTINCT rack FROM md_rack
WHERE company_id = :company_id
AND warehouse = :warehouse
AND product_sku IS NULL
ORDER BY CAST(rack AS UNSIGNED), rack"
);
$sth->execute([
':company_id' => $this->company_id,
':warehouse' => $warehouse_id,
]);
return array_column($sth->fetchAll(PDO::FETCH_ASSOC), 'rack');
}
$sth = $this->pdo->prepare(
"SELECT DISTINCT rack FROM md_rack
WHERE company_id = :company_id
@@ -1746,17 +1823,20 @@ class WarehouseManager {
[$lot_cond, $serial_cond, $params] = $this->buildLotSerialCondition(
$warehouse_id, $product_sku, $lot_number, $serial_number
);
$params[':zone'] = $zone;
$params[':aisle'] = $aisle;
$location_cond = '';
if ($zone !== '' && $aisle !== '') {
$location_cond = "AND r.zone = :zone AND r.aisle = :aisle";
$params[':zone'] = $zone;
$params[':aisle'] = $aisle;
}
$sth = $this->pdo->prepare(
"SELECT DISTINCT r.rack
FROM md_rack r
WHERE r.company_id = :company_id
AND r.warehouse = :warehouse
AND r.zone = :zone
AND r.aisle = :aisle
AND r.product_sku = :product_sku
{$location_cond}
AND r.td_stock_id IN (
SELECT s.id FROM `{$table}` s
WHERE s.company_id = :company_id2
@@ -2055,4 +2135,4 @@ class WarehouseManager {
$sth->execute($params);
return $sth->fetchAll(PDO::FETCH_ASSOC);
}
}
}