pdo = $pdo; $this->company_id = $company_id; } // ───────────────────────────────────────────────────────────── // Private helpers // ───────────────────────────────────────────────────────────── /** * Check if a SKU has any active stock across all td_stock_* tables. * Returns the first blocking table name found, or null if clear. */ 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_%'" ); $sth->execute(); $tables = $sth->fetchAll(PDO::FETCH_COLUMN); foreach ($tables as $table) { $sth = $this->pdo->prepare( "SELECT COUNT(*) FROM `$table` WHERE company_id = :company_id AND product_sku = :sku" ); $sth->execute([ ':company_id' => $this->company_id, ':sku' => $sku, ]); if ($sth->fetchColumn() > 0) { return $table; } } return null; } /** * Build a log entry array for audit context. */ private function buildLogEntry(string $action): array { return [ 'user_id' => $_SESSION['login_user_id'] ?? null, 'dt' => date('Y-m-d H:i:s'), 'login' => isset($_SESSION['otpTime']) ? date('Y-m-d H:i:s', $_SESSION['otpTime']) : null, 'action' => $action, ]; } // ───────────────────────────────────────────────────────────── // Product category // ───────────────────────────────────────────────────────────── /** * Soft-delete a product category. * * Blocks if any md_product references this category. * * @throws Exception if category not found or has dependent products */ public function deleteCategory(int $category_id): void { $sth = $this->pdo->prepare( "SELECT id, category, `log` FROM md_product_category WHERE company_id = :company_id AND id = :id" ); $sth->execute([ ':company_id' => $this->company_id, ':id' => $category_id, ]); $row = $sth->fetch(PDO::FETCH_ASSOC); if (!$row) { throw new Exception("Product category not found."); } // Block if any product references this category $sth = $this->pdo->prepare( "SELECT COUNT(*) FROM md_product WHERE company_id = :company_id AND category = :category_id" ); $sth->execute([ ':company_id' => $this->company_id, ':category_id' => $category_id, ]); if ($sth->fetchColumn() > 0) { throw new Exception( "Cannot delete — category \"{$row['category']}\" " . "still has products assigned to it." ); } // Append delete event to log $log = json_decode($row['log'] ?? '[]', true) ?: []; $log[] = $this->buildLogEntry('delete'); // Soft-delete $this->pdo->prepare( "UPDATE md_product_category SET company_id = company_id * -1, `log` = :log WHERE id = :id AND company_id = :company_id" )->execute([ ':log' => json_encode($log), ':id' => $category_id, ':company_id' => $this->company_id, ]); } // ───────────────────────────────────────────────────────────── // Product // ───────────────────────────────────────────────────────────── /** * Soft-delete a product. * * Blocks if the product SKU has any active stock across any td_stock_* table. * * @throws Exception if product not found or has active stock */ public function deleteProduct(int $product_id): void { $sth = $this->pdo->prepare( "SELECT id, product_name, sku, `log` FROM md_product WHERE company_id = :company_id AND id = :id" ); $sth->execute([ ':company_id' => $this->company_id, ':id' => $product_id, ]); $row = $sth->fetch(PDO::FETCH_ASSOC); if (!$row) { throw new Exception("Product not found."); } // Block if active stock exists for this SKU in any warehouse $blocking_table = $this->findActiveStock($row['sku']); if ($blocking_table !== null) { throw new Exception( "Cannot delete — \"{$row['product_name']}\" " . "still has active stock in the system." ); } // Append delete event to log $log = json_decode($row['log'] ?? '[]', true) ?: []; $log[] = $this->buildLogEntry('delete'); // Soft-delete $this->pdo->prepare( "UPDATE md_product SET company_id = company_id * -1, `log` = :log WHERE id = :id AND company_id = :company_id" )->execute([ ':log' => json_encode($log), ':id' => $product_id, ':company_id' => $this->company_id, ]); } }