diff --git a/app/assets/utils/classes/BarcodeManager.php b/app/assets/utils/classes/BarcodeManager.php index 7730375..eee6bd9 100644 --- a/app/assets/utils/classes/BarcodeManager.php +++ b/app/assets/utils/classes/BarcodeManager.php @@ -36,6 +36,19 @@ class BarcodeManager { // ───────────────────────────────────────────────────────────── private function stockTableName(int $warehouse_id): string { + if ($warehouse_id <= 0) { + throw new Exception("Invalid warehouse id."); + } + + $sth = $this->pdo->prepare( + "SELECT id FROM md_warehouse + WHERE company_id = :company_id AND id = :id" + ); + $sth->execute([':company_id' => $this->company_id, ':id' => $warehouse_id]); + if (!$sth->fetchColumn()) { + throw new Exception("Warehouse ID {$warehouse_id} not found."); + } + return 'td_stock_' . $warehouse_id; } diff --git a/app/assets/utils/classes/ContactManager.php b/app/assets/utils/classes/ContactManager.php index 1fd61d6..f06cd95 100644 --- a/app/assets/utils/classes/ContactManager.php +++ b/app/assets/utils/classes/ContactManager.php @@ -32,10 +32,10 @@ class ContactManager { /** * Check whether a contact is referenced by any active stock transaction - * across all td_stock_* warehouse tables. + * across this company's td_stock_* warehouse tables. * * Used as a pre-delete guard to prevent orphaning stock records. - * Scans information_schema to discover all td_stock_* tables dynamically. + * Discovers existing td_stock_* tables through md_warehouse scoped to this company. * Table names from information_schema are backtick-quoted for safety. * * @param int $contact_id The md_contact.id to check. @@ -44,11 +44,14 @@ class ContactManager { private function hasActiveStock(int $contact_id): bool { $sth = $this->pdo->prepare( - "SELECT table_name FROM information_schema.tables - WHERE table_schema = DATABASE() - AND table_name LIKE 'td_stock_%'" + "SELECT t.table_name + FROM md_warehouse w + JOIN information_schema.tables t + ON t.table_schema = DATABASE() + AND t.table_name = CONCAT('td_stock_', w.id) + WHERE w.company_id = :company_id" ); - $sth->execute(); + $sth->execute([':company_id' => $this->company_id]); $tables = $sth->fetchAll(PDO::FETCH_COLUMN); foreach ($tables as $table) { @@ -409,7 +412,7 @@ class ContactManager { * Soft-delete a contact by negating its company_id. * * Blocks deletion if the contact is referenced in any active stock - * transaction across all td_stock_* warehouse tables, preventing + * transaction across this company's td_stock_* warehouse tables, preventing * broken foreign key references in transaction history. * * Must be called inside dbTransaction() by the caller. diff --git a/app/assets/utils/classes/EtlStockManager.php b/app/assets/utils/classes/EtlStockManager.php index 3bdba9d..69a762d 100644 --- a/app/assets/utils/classes/EtlStockManager.php +++ b/app/assets/utils/classes/EtlStockManager.php @@ -199,10 +199,14 @@ class EtlStockManager private function discoverStockTables(): array { $sth = $this->pdo->prepare( - "SELECT table_name FROM information_schema.tables - WHERE table_schema = DATABASE() AND table_name LIKE 'td_stock_%'" + "SELECT t.table_name + FROM md_warehouse w + JOIN information_schema.tables t + ON t.table_schema = DATABASE() + AND t.table_name = CONCAT('td_stock_', w.id) + WHERE w.company_id = :cid" ); - $sth->execute(); + $sth->execute([':cid' => $this->companyId]); return $sth->fetchAll(PDO::FETCH_COLUMN); } diff --git a/app/assets/utils/classes/OrderManager.php b/app/assets/utils/classes/OrderManager.php index b07e94f..56c8dba 100644 --- a/app/assets/utils/classes/OrderManager.php +++ b/app/assets/utils/classes/OrderManager.php @@ -74,9 +74,32 @@ class OrderManager { throw new Exception("Invalid warehouse id."); } + $sth = $this->pdo->prepare( + "SELECT id FROM md_warehouse + WHERE company_id = :company_id AND id = :id" + ); + $sth->execute([':company_id' => $this->company_id, ':id' => $warehouse_id]); + if (!$sth->fetchColumn()) { + throw new Exception("Warehouse ID {$warehouse_id} not found."); + } + return 'td_stock_' . $warehouse_id; } + private function getStockTables(): array + { + $sth = $this->pdo->prepare( + "SELECT t.table_name + FROM md_warehouse w + JOIN information_schema.tables t + ON t.table_schema = DATABASE() + AND t.table_name = CONCAT('td_stock_', w.id) + WHERE w.company_id = :company_id" + ); + $sth->execute([':company_id' => $this->company_id]); + return $sth->fetchAll(PDO::FETCH_COLUMN); + } + /** * FIFO bin pick — find the oldest approved stock-in row for a SKU * in a given warehouse that is still bin-occupied. @@ -865,13 +888,7 @@ class OrderManager { } // ── Reverse approved stock-out side effects, then soft-delete rows ─ - $sth = $this->pdo->prepare( - "SELECT table_name FROM information_schema.tables - WHERE table_schema = DATABASE() - AND table_name LIKE 'td_stock_%'" - ); - $sth->execute(); - $tables = $sth->fetchAll(PDO::FETCH_COLUMN); + $tables = $this->getStockTables(); $whMgmt = new WarehouseManager($this->pdo, $this->company_id); foreach ($tables as $table) { @@ -1017,12 +1034,7 @@ class OrderManager { } // Block if any approved stock-out rows exist; user must reverse via ICS first - $sth4 = $this->pdo->prepare( - "SELECT table_name FROM information_schema.tables - WHERE table_schema = DATABASE() AND table_name LIKE 'td_stock_%'" - ); - $sth4->execute(); - $tables = $sth4->fetchAll(PDO::FETCH_COLUMN); + $tables = $this->getStockTables(); foreach ($tables as $table) { $chk = $this->pdo->prepare( diff --git a/app/assets/utils/classes/ProductManager.php b/app/assets/utils/classes/ProductManager.php index faa6a04..46291e3 100644 --- a/app/assets/utils/classes/ProductManager.php +++ b/app/assets/utils/classes/ProductManager.php @@ -31,7 +31,7 @@ class ProductManager { // ───────────────────────────────────────────────────────────── /** - * Scan all td_stock_* warehouse tables for any active stock row + * Scan this company's td_stock_* warehouse tables for any active stock row * that references the given SKU. * * Used as a pre-delete guard on products: a product cannot be removed @@ -45,11 +45,14 @@ class ProductManager { private function findActiveStock(string $sku): ?string { $sth = $this->pdo->prepare( - "SELECT table_name FROM information_schema.tables - WHERE table_schema = DATABASE() - AND table_name LIKE 'td_stock_%'" + "SELECT t.table_name + FROM md_warehouse w + JOIN information_schema.tables t + ON t.table_schema = DATABASE() + AND t.table_name = CONCAT('td_stock_', w.id) + WHERE w.company_id = :company_id" ); - $sth->execute(); + $sth->execute([':company_id' => $this->company_id]); $tables = $sth->fetchAll(PDO::FETCH_COLUMN); foreach ($tables as $table) { diff --git a/app/assets/utils/classes/PurchaseOrderManager.php b/app/assets/utils/classes/PurchaseOrderManager.php index c45317a..b890b49 100644 --- a/app/assets/utils/classes/PurchaseOrderManager.php +++ b/app/assets/utils/classes/PurchaseOrderManager.php @@ -97,13 +97,7 @@ class PurchaseOrderManager { } if (!$has_any_stock_in) return 0; - $sth = $this->pdo->prepare( - "SELECT table_name FROM information_schema.tables - WHERE table_schema = DATABASE() - AND table_name LIKE 'td_stock_%'" - ); - $sth->execute(); - $tables = $sth->fetchAll(PDO::FETCH_COLUMN); + $tables = $this->getStockTables(); $total = 0; $pending = 0; @@ -143,9 +137,32 @@ class PurchaseOrderManager { throw new Exception("Invalid warehouse id."); } + $sth = $this->pdo->prepare( + "SELECT id FROM md_warehouse + WHERE company_id = :company_id AND id = :id" + ); + $sth->execute([':company_id' => $this->company_id, ':id' => $warehouse_id]); + if (!$sth->fetchColumn()) { + throw new Exception("Warehouse ID {$warehouse_id} not found."); + } + return 'td_stock_' . $warehouse_id; } + private function getStockTables(): array + { + $sth = $this->pdo->prepare( + "SELECT t.table_name + FROM md_warehouse w + JOIN information_schema.tables t + ON t.table_schema = DATABASE() + AND t.table_name = CONCAT('td_stock_', w.id) + WHERE w.company_id = :company_id" + ); + $sth->execute([':company_id' => $this->company_id]); + return $sth->fetchAll(PDO::FETCH_COLUMN); + } + private function syncPoItems(int $po_id, array $items): void { $this->pdo->prepare( @@ -729,13 +746,7 @@ class PurchaseOrderManager { if ($status === -1) throw new Exception("PO is already cancelled."); // Guard: block if any approved stock-in rows exist for this PO - $sth = $this->pdo->prepare( - "SELECT table_name FROM information_schema.tables - WHERE table_schema = DATABASE() - AND table_name LIKE 'td_stock_%'" - ); - $sth->execute(); - $tables = $sth->fetchAll(PDO::FETCH_COLUMN); + $tables = $this->getStockTables(); foreach ($tables as $table) { $sth = $this->pdo->prepare( @@ -876,12 +887,7 @@ class PurchaseOrderManager { throw new Exception('Cannot delete — this PO has linked supplier returns. Delete or cancel the returns first.'); } - $sth4 = $this->pdo->prepare( - "SELECT table_name FROM information_schema.tables - WHERE table_schema = DATABASE() AND table_name LIKE 'td_stock_%'" - ); - $sth4->execute(); - $tables = $sth4->fetchAll(PDO::FETCH_COLUMN); + $tables = $this->getStockTables(); foreach ($tables as $table) { $sth5 = $this->pdo->prepare( diff --git a/app/assets/utils/classes/ReturnManager.php b/app/assets/utils/classes/ReturnManager.php index cc88d81..87218fc 100644 --- a/app/assets/utils/classes/ReturnManager.php +++ b/app/assets/utils/classes/ReturnManager.php @@ -68,6 +68,15 @@ class ReturnManager { throw new Exception("Invalid warehouse id."); } + $sth = $this->pdo->prepare( + "SELECT id FROM md_warehouse + WHERE company_id = :company_id AND id = :id" + ); + $sth->execute([':company_id' => $this->company_id, ':id' => $warehouse_id]); + if (!$sth->fetchColumn()) { + throw new Exception("Warehouse ID {$warehouse_id} not found."); + } + return 'td_stock_' . $warehouse_id; } @@ -79,11 +88,14 @@ class ReturnManager { private function deriveReceiptStatus(array $ret): int { $sth = $this->pdo->prepare( - "SELECT table_name FROM information_schema.tables - WHERE table_schema = DATABASE() - AND table_name LIKE 'td_stock_%'" + "SELECT t.table_name + FROM md_warehouse w + JOIN information_schema.tables t + ON t.table_schema = DATABASE() + AND t.table_name = CONCAT('td_stock_', w.id) + WHERE w.company_id = :company_id" ); - $sth->execute(); + $sth->execute([':company_id' => $this->company_id]); $tables = $sth->fetchAll(PDO::FETCH_COLUMN); foreach ($tables as $table) { @@ -737,12 +749,16 @@ class ReturnManager { } // Block if any approved stock-in rows exist; user must reverse via ICS first - $sth3 = $this->pdo->prepare( - "SELECT table_name FROM information_schema.tables - WHERE table_schema = DATABASE() AND table_name LIKE 'td_stock_%'" + $sth = $this->pdo->prepare( + "SELECT t.table_name + FROM md_warehouse w + JOIN information_schema.tables t + ON t.table_schema = DATABASE() + AND t.table_name = CONCAT('td_stock_', w.id) + WHERE w.company_id = :company_id" ); - $sth3->execute(); - $tables = $sth3->fetchAll(PDO::FETCH_COLUMN); + $sth->execute([':company_id' => $this->company_id]); + $tables = $sth->fetchAll(PDO::FETCH_COLUMN); foreach ($tables as $table) { $chk = $this->pdo->prepare( diff --git a/app/assets/utils/classes/StockManager.php b/app/assets/utils/classes/StockManager.php index ce8a88d..bc3d6b5 100644 --- a/app/assets/utils/classes/StockManager.php +++ b/app/assets/utils/classes/StockManager.php @@ -44,6 +44,15 @@ class StockManager { throw new Exception("Invalid warehouse id."); } + $sth = $this->pdo->prepare( + "SELECT id FROM md_warehouse + WHERE company_id = :company_id AND id = :id" + ); + $sth->execute([':company_id' => $this->company_id, ':id' => $warehouse_id]); + if (!$sth->fetchColumn()) { + throw new Exception("Warehouse ID {$warehouse_id} not found."); + } + return 'td_stock_' . $warehouse_id; } diff --git a/app/assets/utils/classes/StockSourceManager.php b/app/assets/utils/classes/StockSourceManager.php index 673f8b5..1eaede7 100644 --- a/app/assets/utils/classes/StockSourceManager.php +++ b/app/assets/utils/classes/StockSourceManager.php @@ -74,12 +74,14 @@ class StockSourceManager private function getStockTables(): array { $sth = $this->pdo->prepare( - "SELECT table_name - FROM information_schema.tables - WHERE table_schema = DATABASE() - AND table_name LIKE 'td_stock_%'" + "SELECT t.table_name + FROM md_warehouse w + JOIN information_schema.tables t + ON t.table_schema = DATABASE() + AND t.table_name = CONCAT('td_stock_', w.id) + WHERE w.company_id = :company_id" ); - $sth->execute(); + $sth->execute([':company_id' => $this->companyId]); return $sth->fetchAll(PDO::FETCH_COLUMN); } } diff --git a/app/assets/utils/classes/SupplierReturnManager.php b/app/assets/utils/classes/SupplierReturnManager.php index a747ec5..80f72ac 100644 --- a/app/assets/utils/classes/SupplierReturnManager.php +++ b/app/assets/utils/classes/SupplierReturnManager.php @@ -48,6 +48,15 @@ class SupplierReturnManager { private function stockTableName(int $warehouse_id): string { if ($warehouse_id <= 0) throw new Exception("Invalid warehouse id."); + $sth = $this->pdo->prepare( + "SELECT id FROM md_warehouse + WHERE company_id = :company_id AND id = :id" + ); + $sth->execute([':company_id' => $this->company_id, ':id' => $warehouse_id]); + if (!$sth->fetchColumn()) { + throw new Exception("Warehouse ID {$warehouse_id} not found."); + } + return 'td_stock_' . $warehouse_id; } @@ -61,11 +70,14 @@ class SupplierReturnManager { private function deriveFulfillmentStatus(array $ret): int { $sth = $this->pdo->prepare( - "SELECT table_name FROM information_schema.tables - WHERE table_schema = DATABASE() - AND table_name LIKE 'td_stock_%'" + "SELECT t.table_name + FROM md_warehouse w + JOIN information_schema.tables t + ON t.table_schema = DATABASE() + AND t.table_name = CONCAT('td_stock_', w.id) + WHERE w.company_id = :company_id" ); - $sth->execute(); + $sth->execute([':company_id' => $this->company_id]); $tables = $sth->fetchAll(PDO::FETCH_COLUMN); $has_stock_out = false; @@ -638,12 +650,16 @@ class SupplierReturnManager { } // Block if any approved stock-out rows exist; user must reverse via ICS first - $sth3 = $this->pdo->prepare( - "SELECT table_name FROM information_schema.tables - WHERE table_schema = DATABASE() AND table_name LIKE 'td_stock_%'" + $sth = $this->pdo->prepare( + "SELECT t.table_name + FROM md_warehouse w + JOIN information_schema.tables t + ON t.table_schema = DATABASE() + AND t.table_name = CONCAT('td_stock_', w.id) + WHERE w.company_id = :company_id" ); - $sth3->execute(); - $tables = $sth3->fetchAll(PDO::FETCH_COLUMN); + $sth->execute([':company_id' => $this->company_id]); + $tables = $sth->fetchAll(PDO::FETCH_COLUMN); foreach ($tables as $table) { $chk = $this->pdo->prepare( diff --git a/app/assets/utils/classes/WarehouseManager.php b/app/assets/utils/classes/WarehouseManager.php index 29991a5..cb5dd67 100644 --- a/app/assets/utils/classes/WarehouseManager.php +++ b/app/assets/utils/classes/WarehouseManager.php @@ -40,6 +40,15 @@ class WarehouseManager { throw new Exception("Invalid warehouse id."); } + $sth = $this->pdo->prepare( + "SELECT id FROM md_warehouse + WHERE company_id = :company_id AND id = :id" + ); + $sth->execute([':company_id' => $this->company_id, ':id' => $warehouse_id]); + if (!$sth->fetchColumn()) { + throw new Exception("Warehouse ID {$warehouse_id} not found."); + } + return 'td_stock_' . $warehouse_id; } @@ -243,7 +252,7 @@ class WarehouseManager { /** * Validate that the given row is the globally latest transaction for its SKU. * - * Scans all td_stock_* tables via UNION ALL to find the single most recent + * Scans this company's td_stock_* tables via UNION ALL to find the single most recent * transaction date across all warehouses for the SKU. If a newer row exists, * deletion is blocked to enforce LIFO (last-in-first-out) reversal order. * @@ -261,20 +270,23 @@ class WarehouseManager { string $product_name ): void { - // Discover all td_stock_* tables for this database + // Discover stock tables only for warehouses owned by this company. $sth = $this->pdo->prepare( - "SELECT table_name FROM information_schema.tables - WHERE table_schema = DATABASE() - AND table_name LIKE 'td_stock_%'" + "SELECT t.table_name + FROM md_warehouse w + JOIN information_schema.tables t + ON t.table_schema = DATABASE() + AND t.table_name = CONCAT('td_stock_', w.id) + WHERE w.company_id = :company_id" ); - $sth->execute(); + $sth->execute([':company_id' => $this->company_id]); $tables = $sth->fetchAll(PDO::FETCH_COLUMN); if (empty($tables)) { return; } - // Build UNION across all td_stock_* tables to find the global latest date + // Build UNION across this company's td_stock_* tables to find the global latest date $unions = implode(' UNION ALL ', array_map( fn($t) => "SELECT `type`, `date` FROM `$t` WHERE company_id = :company_id