Scope stock table access by company warehouses
This commit is contained in:
@@ -36,6 +36,19 @@ class BarcodeManager {
|
|||||||
// ─────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
private function stockTableName(int $warehouse_id): string {
|
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;
|
return 'td_stock_' . $warehouse_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,10 +32,10 @@ class ContactManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a contact is referenced by any active stock transaction
|
* 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.
|
* 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.
|
* Table names from information_schema are backtick-quoted for safety.
|
||||||
*
|
*
|
||||||
* @param int $contact_id The md_contact.id to check.
|
* @param int $contact_id The md_contact.id to check.
|
||||||
@@ -44,11 +44,14 @@ class ContactManager {
|
|||||||
private function hasActiveStock(int $contact_id): bool {
|
private function hasActiveStock(int $contact_id): bool {
|
||||||
|
|
||||||
$sth = $this->pdo->prepare(
|
$sth = $this->pdo->prepare(
|
||||||
"SELECT table_name FROM information_schema.tables
|
"SELECT t.table_name
|
||||||
WHERE table_schema = DATABASE()
|
FROM md_warehouse w
|
||||||
AND table_name LIKE 'td_stock_%'"
|
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);
|
$tables = $sth->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
foreach ($tables as $table) {
|
foreach ($tables as $table) {
|
||||||
@@ -409,7 +412,7 @@ class ContactManager {
|
|||||||
* Soft-delete a contact by negating its company_id.
|
* Soft-delete a contact by negating its company_id.
|
||||||
*
|
*
|
||||||
* Blocks deletion if the contact is referenced in any active stock
|
* 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.
|
* broken foreign key references in transaction history.
|
||||||
*
|
*
|
||||||
* Must be called inside dbTransaction() by the caller.
|
* Must be called inside dbTransaction() by the caller.
|
||||||
|
|||||||
@@ -199,10 +199,14 @@ class EtlStockManager
|
|||||||
private function discoverStockTables(): array
|
private function discoverStockTables(): array
|
||||||
{
|
{
|
||||||
$sth = $this->pdo->prepare(
|
$sth = $this->pdo->prepare(
|
||||||
"SELECT table_name FROM information_schema.tables
|
"SELECT t.table_name
|
||||||
WHERE table_schema = DATABASE() AND table_name LIKE 'td_stock_%'"
|
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);
|
return $sth->fetchAll(PDO::FETCH_COLUMN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,9 +74,32 @@ class OrderManager {
|
|||||||
throw new Exception("Invalid warehouse id.");
|
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;
|
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
|
* FIFO bin pick — find the oldest approved stock-in row for a SKU
|
||||||
* in a given warehouse that is still bin-occupied.
|
* 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 ─
|
// ── Reverse approved stock-out side effects, then soft-delete rows ─
|
||||||
$sth = $this->pdo->prepare(
|
$tables = $this->getStockTables();
|
||||||
"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);
|
|
||||||
$whMgmt = new WarehouseManager($this->pdo, $this->company_id);
|
$whMgmt = new WarehouseManager($this->pdo, $this->company_id);
|
||||||
|
|
||||||
foreach ($tables as $table) {
|
foreach ($tables as $table) {
|
||||||
@@ -1017,12 +1034,7 @@ class OrderManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Block if any approved stock-out rows exist; user must reverse via ICS first
|
// Block if any approved stock-out rows exist; user must reverse via ICS first
|
||||||
$sth4 = $this->pdo->prepare(
|
$tables = $this->getStockTables();
|
||||||
"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);
|
|
||||||
|
|
||||||
foreach ($tables as $table) {
|
foreach ($tables as $table) {
|
||||||
$chk = $this->pdo->prepare(
|
$chk = $this->pdo->prepare(
|
||||||
|
|||||||
@@ -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.
|
* that references the given SKU.
|
||||||
*
|
*
|
||||||
* Used as a pre-delete guard on products: a product cannot be removed
|
* 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 {
|
private function findActiveStock(string $sku): ?string {
|
||||||
|
|
||||||
$sth = $this->pdo->prepare(
|
$sth = $this->pdo->prepare(
|
||||||
"SELECT table_name FROM information_schema.tables
|
"SELECT t.table_name
|
||||||
WHERE table_schema = DATABASE()
|
FROM md_warehouse w
|
||||||
AND table_name LIKE 'td_stock_%'"
|
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);
|
$tables = $sth->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
foreach ($tables as $table) {
|
foreach ($tables as $table) {
|
||||||
|
|||||||
@@ -97,13 +97,7 @@ class PurchaseOrderManager {
|
|||||||
}
|
}
|
||||||
if (!$has_any_stock_in) return 0;
|
if (!$has_any_stock_in) return 0;
|
||||||
|
|
||||||
$sth = $this->pdo->prepare(
|
$tables = $this->getStockTables();
|
||||||
"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);
|
|
||||||
|
|
||||||
$total = 0;
|
$total = 0;
|
||||||
$pending = 0;
|
$pending = 0;
|
||||||
@@ -143,9 +137,32 @@ class PurchaseOrderManager {
|
|||||||
throw new Exception("Invalid warehouse id.");
|
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;
|
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
|
private function syncPoItems(int $po_id, array $items): void
|
||||||
{
|
{
|
||||||
$this->pdo->prepare(
|
$this->pdo->prepare(
|
||||||
@@ -729,13 +746,7 @@ class PurchaseOrderManager {
|
|||||||
if ($status === -1) throw new Exception("PO is already cancelled.");
|
if ($status === -1) throw new Exception("PO is already cancelled.");
|
||||||
|
|
||||||
// Guard: block if any approved stock-in rows exist for this PO
|
// Guard: block if any approved stock-in rows exist for this PO
|
||||||
$sth = $this->pdo->prepare(
|
$tables = $this->getStockTables();
|
||||||
"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) {
|
foreach ($tables as $table) {
|
||||||
$sth = $this->pdo->prepare(
|
$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.');
|
throw new Exception('Cannot delete — this PO has linked supplier returns. Delete or cancel the returns first.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$sth4 = $this->pdo->prepare(
|
$tables = $this->getStockTables();
|
||||||
"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);
|
|
||||||
|
|
||||||
foreach ($tables as $table) {
|
foreach ($tables as $table) {
|
||||||
$sth5 = $this->pdo->prepare(
|
$sth5 = $this->pdo->prepare(
|
||||||
|
|||||||
@@ -68,6 +68,15 @@ class ReturnManager {
|
|||||||
throw new Exception("Invalid warehouse id.");
|
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;
|
return 'td_stock_' . $warehouse_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,11 +88,14 @@ class ReturnManager {
|
|||||||
private function deriveReceiptStatus(array $ret): int
|
private function deriveReceiptStatus(array $ret): int
|
||||||
{
|
{
|
||||||
$sth = $this->pdo->prepare(
|
$sth = $this->pdo->prepare(
|
||||||
"SELECT table_name FROM information_schema.tables
|
"SELECT t.table_name
|
||||||
WHERE table_schema = DATABASE()
|
FROM md_warehouse w
|
||||||
AND table_name LIKE 'td_stock_%'"
|
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);
|
$tables = $sth->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
foreach ($tables as $table) {
|
foreach ($tables as $table) {
|
||||||
@@ -737,12 +749,16 @@ class ReturnManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Block if any approved stock-in rows exist; user must reverse via ICS first
|
// Block if any approved stock-in rows exist; user must reverse via ICS first
|
||||||
$sth3 = $this->pdo->prepare(
|
$sth = $this->pdo->prepare(
|
||||||
"SELECT table_name FROM information_schema.tables
|
"SELECT t.table_name
|
||||||
WHERE table_schema = DATABASE() AND table_name LIKE 'td_stock_%'"
|
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();
|
$sth->execute([':company_id' => $this->company_id]);
|
||||||
$tables = $sth3->fetchAll(PDO::FETCH_COLUMN);
|
$tables = $sth->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
foreach ($tables as $table) {
|
foreach ($tables as $table) {
|
||||||
$chk = $this->pdo->prepare(
|
$chk = $this->pdo->prepare(
|
||||||
|
|||||||
@@ -44,6 +44,15 @@ class StockManager {
|
|||||||
throw new Exception("Invalid warehouse id.");
|
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;
|
return 'td_stock_' . $warehouse_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,12 +74,14 @@ class StockSourceManager
|
|||||||
private function getStockTables(): array
|
private function getStockTables(): array
|
||||||
{
|
{
|
||||||
$sth = $this->pdo->prepare(
|
$sth = $this->pdo->prepare(
|
||||||
"SELECT table_name
|
"SELECT t.table_name
|
||||||
FROM information_schema.tables
|
FROM md_warehouse w
|
||||||
WHERE table_schema = DATABASE()
|
JOIN information_schema.tables t
|
||||||
AND table_name LIKE 'td_stock_%'"
|
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);
|
return $sth->fetchAll(PDO::FETCH_COLUMN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,15 @@ class SupplierReturnManager {
|
|||||||
private function stockTableName(int $warehouse_id): string
|
private function stockTableName(int $warehouse_id): string
|
||||||
{
|
{
|
||||||
if ($warehouse_id <= 0) throw new Exception("Invalid warehouse id.");
|
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;
|
return 'td_stock_' . $warehouse_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,11 +70,14 @@ class SupplierReturnManager {
|
|||||||
private function deriveFulfillmentStatus(array $ret): int
|
private function deriveFulfillmentStatus(array $ret): int
|
||||||
{
|
{
|
||||||
$sth = $this->pdo->prepare(
|
$sth = $this->pdo->prepare(
|
||||||
"SELECT table_name FROM information_schema.tables
|
"SELECT t.table_name
|
||||||
WHERE table_schema = DATABASE()
|
FROM md_warehouse w
|
||||||
AND table_name LIKE 'td_stock_%'"
|
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);
|
$tables = $sth->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
$has_stock_out = false;
|
$has_stock_out = false;
|
||||||
@@ -638,12 +650,16 @@ class SupplierReturnManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Block if any approved stock-out rows exist; user must reverse via ICS first
|
// Block if any approved stock-out rows exist; user must reverse via ICS first
|
||||||
$sth3 = $this->pdo->prepare(
|
$sth = $this->pdo->prepare(
|
||||||
"SELECT table_name FROM information_schema.tables
|
"SELECT t.table_name
|
||||||
WHERE table_schema = DATABASE() AND table_name LIKE 'td_stock_%'"
|
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();
|
$sth->execute([':company_id' => $this->company_id]);
|
||||||
$tables = $sth3->fetchAll(PDO::FETCH_COLUMN);
|
$tables = $sth->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
foreach ($tables as $table) {
|
foreach ($tables as $table) {
|
||||||
$chk = $this->pdo->prepare(
|
$chk = $this->pdo->prepare(
|
||||||
|
|||||||
@@ -40,6 +40,15 @@ class WarehouseManager {
|
|||||||
throw new Exception("Invalid warehouse id.");
|
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;
|
return 'td_stock_' . $warehouse_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +252,7 @@ class WarehouseManager {
|
|||||||
/**
|
/**
|
||||||
* Validate that the given row is the globally latest transaction for its SKU.
|
* 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,
|
* 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.
|
* deletion is blocked to enforce LIFO (last-in-first-out) reversal order.
|
||||||
*
|
*
|
||||||
@@ -261,20 +270,23 @@ class WarehouseManager {
|
|||||||
string $product_name
|
string $product_name
|
||||||
): void {
|
): void {
|
||||||
|
|
||||||
// Discover all td_stock_* tables for this database
|
// Discover stock tables only for warehouses owned by this company.
|
||||||
$sth = $this->pdo->prepare(
|
$sth = $this->pdo->prepare(
|
||||||
"SELECT table_name FROM information_schema.tables
|
"SELECT t.table_name
|
||||||
WHERE table_schema = DATABASE()
|
FROM md_warehouse w
|
||||||
AND table_name LIKE 'td_stock_%'"
|
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);
|
$tables = $sth->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
if (empty($tables)) {
|
if (empty($tables)) {
|
||||||
return;
|
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(
|
$unions = implode(' UNION ALL ', array_map(
|
||||||
fn($t) => "SELECT `type`, `date` FROM `$t`
|
fn($t) => "SELECT `type`, `date` FROM `$t`
|
||||||
WHERE company_id = :company_id
|
WHERE company_id = :company_id
|
||||||
|
|||||||
Reference in New Issue
Block a user