Change 'Rack' to 'Bin'
This commit is contained in:
@@ -15,7 +15,7 @@ require_once __DIR__ . '/../notify_node.php';
|
||||
* getTransferById, saveStockIn, saveStockOut, saveStockTransfer
|
||||
* Report basis → (none — reporting is handled by ReportManager)
|
||||
*
|
||||
* Write operations delegate rack and balance side-effects to WarehouseManager.
|
||||
* Write operations delegate bin and balance side-effects to WarehouseManager.
|
||||
* Delete operations are handled directly in WarehouseManager (deleteStockIn, etc.).
|
||||
*
|
||||
* Note: Write methods do NOT manage their own DB transactions.
|
||||
@@ -228,16 +228,16 @@ class StockManager {
|
||||
* Insert flow (id = 0):
|
||||
* 1. Upserts md_lot if lot_number + expiry_date are provided.
|
||||
* 2. Inserts the td_stock_<warehouse_id> row.
|
||||
* 3. Calls WarehouseManager::occupyRack() to reserve the rack.
|
||||
* 3. Calls WarehouseManager::occupyBin() to reserve the bin.
|
||||
* etl_stock_summary is updated later by approveStock().
|
||||
*
|
||||
* Update flow (id > 0):
|
||||
* - Updates contact_id, description, and log only.
|
||||
* - Quantity, rack, lot, and serial are immutable after creation.
|
||||
* - Quantity, bin, lot, and serial are immutable after creation.
|
||||
*
|
||||
* Must be called inside dbTransaction() by the caller.
|
||||
*
|
||||
* @param array $data Keys: id, warehouse, product_sku, quantity, zone, aisle, rack,
|
||||
* @param array $data Keys: id, warehouse, product_sku, quantity, zone, aisle, bin,
|
||||
* contact_id, description, lot_number, expiry_date, serial_number.
|
||||
* @param array $logging Audit entry to append to the log column.
|
||||
* @param string $uuid UUID for this transaction (shared across transfer pairs).
|
||||
@@ -303,10 +303,10 @@ class StockManager {
|
||||
|
||||
$this->pdo->prepare(
|
||||
"INSERT INTO `$table`
|
||||
(uuid, company_id, `date`, product_sku, `in`, price, zone, aisle, rack,
|
||||
(uuid, company_id, `date`, product_sku, `in`, price, zone, aisle, bin,
|
||||
contact_id, `description`, `log`, `type`, lot_number, serial_number, status, updated_at)
|
||||
VALUES
|
||||
(:uuid, :company_id, :date, :product_sku, :quantity, :price, :zone, :aisle, :rack,
|
||||
(:uuid, :company_id, :date, :product_sku, :quantity, :price, :zone, :aisle, :bin,
|
||||
:contact_id, :description, :log, 'in', :lot_number, :serial_number, 0, NOW())"
|
||||
)->execute([
|
||||
':uuid' => $uuid,
|
||||
@@ -317,7 +317,7 @@ class StockManager {
|
||||
':price' => (float)($data['price'] ?? 0),
|
||||
':zone' => $data['zone'],
|
||||
':aisle' => $data['aisle'],
|
||||
':rack' => $data['rack'],
|
||||
':bin' => $data['bin'],
|
||||
':contact_id' => (int)($data['contact_id'] ?? 0),
|
||||
':description' => $data['description'] ?? '',
|
||||
':log' => json_encode($table_log),
|
||||
@@ -328,13 +328,13 @@ class StockManager {
|
||||
$td_stock_id = (int)$this->pdo->lastInsertId();
|
||||
|
||||
// Reserve the location immediately so draft stock-in rows cannot
|
||||
// leave the same rack available for another receipt. Balance still
|
||||
// leave the same bin available for another receipt. Balance still
|
||||
// changes only when approveStock() runs.
|
||||
$whMgmt->occupyRack(
|
||||
$whMgmt->occupyBin(
|
||||
$warehouse_id,
|
||||
$data['zone'],
|
||||
$data['aisle'],
|
||||
$data['rack'],
|
||||
$data['bin'],
|
||||
$data['product_sku'],
|
||||
$td_stock_id
|
||||
);
|
||||
@@ -347,9 +347,9 @@ class StockManager {
|
||||
* Insert a new stock_out record or update metadata on an existing one.
|
||||
*
|
||||
* Insert flow (id = 0):
|
||||
* 1. Validates the rack is occupied with the correct SKU / lot / serial.
|
||||
* 2. Inserts the td_stock_<warehouse_id> row, copying quantity and lot info from the rack.
|
||||
* 3. Calls WarehouseManager::releaseRack() to free the rack slot.
|
||||
* 1. Validates the bin is occupied with the correct SKU / lot / serial.
|
||||
* 2. Inserts the td_stock_<warehouse_id> row, copying quantity and lot info from the bin.
|
||||
* 3. Calls WarehouseManager::releaseBin() to free the bin slot.
|
||||
* 4. Calls WarehouseManager::adjustBalance() to update etl_stock_summary.
|
||||
*
|
||||
* Update flow (id > 0):
|
||||
@@ -357,11 +357,11 @@ class StockManager {
|
||||
*
|
||||
* Must be called inside dbTransaction() by the caller.
|
||||
*
|
||||
* @param array $data Keys: id, warehouse, product_sku, zone, aisle, rack,
|
||||
* @param array $data Keys: id, warehouse, product_sku, zone, aisle, bin,
|
||||
* contact_id, description, lot_number, serial_number.
|
||||
* @param array $logging Audit entry to append to the log column.
|
||||
* @param string $uuid UUID for this transaction.
|
||||
* @throws Exception If the rack is empty, holds a different SKU/lot/serial.
|
||||
* @throws Exception If the bin is empty, holds a different SKU/lot/serial.
|
||||
*/
|
||||
public function saveStockOut(array $data, array $logging, string $uuid): int
|
||||
{
|
||||
@@ -400,33 +400,33 @@ class StockManager {
|
||||
|
||||
} else {
|
||||
|
||||
// Validate rack holds the expected product / lot / serial
|
||||
$source_stock = $whMgmt->getRackStock(
|
||||
// Validate bin holds the expected product / lot / serial
|
||||
$source_stock = $whMgmt->getBinStock(
|
||||
$warehouse_id,
|
||||
$data['zone'], $data['aisle'], $data['rack']
|
||||
$data['zone'], $data['aisle'], $data['bin']
|
||||
);
|
||||
|
||||
if (!$source_stock) {
|
||||
throw new Exception(
|
||||
"Rack {$data['zone']}-{$data['aisle']}-{$data['rack']} is empty — nothing to take out."
|
||||
"Bin {$data['zone']}-{$data['aisle']}-{$data['bin']} is empty — nothing to take out."
|
||||
);
|
||||
}
|
||||
|
||||
if ($source_stock['product_sku'] !== $data['product_sku']) {
|
||||
throw new Exception(
|
||||
"Rack holds {$source_stock['product_sku']}, not {$data['product_sku']}."
|
||||
"Bin holds {$source_stock['product_sku']}, not {$data['product_sku']}."
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($data['lot_number']) && $source_stock['lot_number'] !== $data['lot_number']) {
|
||||
throw new Exception(
|
||||
"Rack holds lot '{$source_stock['lot_number']}', not '{$data['lot_number']}'."
|
||||
"Bin holds lot '{$source_stock['lot_number']}', not '{$data['lot_number']}'."
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($data['serial_number']) && $source_stock['serial_number'] !== $data['serial_number']) {
|
||||
throw new Exception(
|
||||
"Rack holds serial '{$source_stock['serial_number']}', not '{$data['serial_number']}'."
|
||||
"Bin holds serial '{$source_stock['serial_number']}', not '{$data['serial_number']}'."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -438,10 +438,10 @@ class StockManager {
|
||||
|
||||
$this->pdo->prepare(
|
||||
"INSERT INTO `$table`
|
||||
(uuid, company_id, `date`, product_sku, `out`, zone, aisle, rack,
|
||||
(uuid, company_id, `date`, product_sku, `out`, zone, aisle, bin,
|
||||
contact_id, `description`, `log`, `type`, ref_id, lot_number, serial_number, status, updated_at)
|
||||
VALUES
|
||||
(:uuid, :company_id, :date, :product_sku, :quantity, :zone, :aisle, :rack,
|
||||
(:uuid, :company_id, :date, :product_sku, :quantity, :zone, :aisle, :bin,
|
||||
:contact_id, :description, :log, 'out', :ref_id, :lot_number, :serial_number, 0, NOW())"
|
||||
)->execute([
|
||||
':uuid' => $uuid,
|
||||
@@ -451,7 +451,7 @@ class StockManager {
|
||||
':quantity' => $quantity,
|
||||
':zone' => $data['zone'],
|
||||
':aisle' => $data['aisle'],
|
||||
':rack' => $data['rack'],
|
||||
':bin' => $data['bin'],
|
||||
':contact_id' => (int)($data['contact_id'] ?? 0),
|
||||
':description' => $data['description'] ?? '',
|
||||
':log' => json_encode($table_log),
|
||||
@@ -462,7 +462,7 @@ class StockManager {
|
||||
|
||||
$out_stock_id = (int)$this->pdo->lastInsertId();
|
||||
|
||||
// releaseRack and adjustBalance deferred — called from approveStock() only.
|
||||
// releaseBin and adjustBalance deferred — called from approveStock() only.
|
||||
return $out_stock_id;
|
||||
}
|
||||
}
|
||||
@@ -475,11 +475,11 @@ class StockManager {
|
||||
* sharing the same UUID and cross-referencing each other via ref_id.
|
||||
*
|
||||
* Insert flow (id = 0):
|
||||
* 1. Validates the source rack holds the correct SKU / lot / serial.
|
||||
* 1. Validates the source bin holds the correct SKU / lot / serial.
|
||||
* 2. Inserts the outbound row in td_stock_<from>.
|
||||
* 3. Inserts the inbound row in td_stock_<to> with ref_id pointing to from.
|
||||
* 4. Back-fills ref_id on the from row so both point at each other.
|
||||
* 5. Releases the source rack, occupies the destination rack.
|
||||
* 5. Releases the source bin, occupies the destination bin.
|
||||
* 6. Adjusts balance on both warehouses (out from source, in to dest).
|
||||
*
|
||||
* Update flow (id > 0):
|
||||
@@ -488,11 +488,11 @@ class StockManager {
|
||||
* Must be called inside dbTransaction() by the caller.
|
||||
*
|
||||
* @param array $data Keys: id, warehouse_from, warehouse_to, product_sku,
|
||||
* zone_from, aisle_from, rack_from, zone_to, aisle_to, rack_to,
|
||||
* zone_from, aisle_from, bin_from, zone_to, aisle_to, bin_to,
|
||||
* contact_id, description, lot_number, serial_number.
|
||||
* @param array $logging Audit entry to append to the log column on both rows.
|
||||
* @param string $uuid UUID shared by both the from and to rows.
|
||||
* @throws Exception If source rack validation fails or paired record is missing on update.
|
||||
* @throws Exception If source bin validation fails or paired record is missing on update.
|
||||
*/
|
||||
public function saveStockTransfer(array $data, array $logging, string $uuid): int
|
||||
{
|
||||
@@ -566,39 +566,39 @@ class StockManager {
|
||||
return 0; // update — no new row
|
||||
}
|
||||
|
||||
// Insert: validate source rack, then create paired rows
|
||||
// Insert: validate source bin, then create paired rows
|
||||
$from_warehouse = (int)$data['warehouse_from'];
|
||||
$from_zone = $data['zone_from'];
|
||||
$from_aisle = $data['aisle_from'];
|
||||
$from_rack = $data['rack_from'];
|
||||
$from_bin = $data['bin_from'];
|
||||
$to_warehouse = (int)$data['warehouse_to'];
|
||||
$to_zone = $data['zone_to'];
|
||||
$to_aisle = $data['aisle_to'];
|
||||
$to_rack = $data['rack_to'];
|
||||
$to_bin = $data['bin_to'];
|
||||
|
||||
$source_stock = $whMgmt->getRackStock(
|
||||
$from_warehouse, $from_zone, $from_aisle, $from_rack
|
||||
$source_stock = $whMgmt->getBinStock(
|
||||
$from_warehouse, $from_zone, $from_aisle, $from_bin
|
||||
);
|
||||
|
||||
if (!$source_stock) {
|
||||
throw new Exception("Source rack {$from_zone}-{$from_aisle}-{$from_rack} is empty.");
|
||||
throw new Exception("Source bin {$from_zone}-{$from_aisle}-{$from_bin} is empty.");
|
||||
}
|
||||
|
||||
if ($source_stock['product_sku'] !== $data['product_sku']) {
|
||||
throw new Exception(
|
||||
"Source rack holds {$source_stock['product_sku']}, not {$data['product_sku']}."
|
||||
"Source bin holds {$source_stock['product_sku']}, not {$data['product_sku']}."
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($data['lot_number']) && $source_stock['lot_number'] !== $data['lot_number']) {
|
||||
throw new Exception(
|
||||
"Source rack holds lot '{$source_stock['lot_number']}', not '{$data['lot_number']}'."
|
||||
"Source bin holds lot '{$source_stock['lot_number']}', not '{$data['lot_number']}'."
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($data['serial_number']) && $source_stock['serial_number'] !== $data['serial_number']) {
|
||||
throw new Exception(
|
||||
"Source rack holds serial '{$source_stock['serial_number']}', not '{$data['serial_number']}'."
|
||||
"Source bin holds serial '{$source_stock['serial_number']}', not '{$data['serial_number']}'."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -615,11 +615,11 @@ class StockManager {
|
||||
$this->pdo->prepare(
|
||||
"INSERT INTO `$from_table`
|
||||
(uuid, company_id, `date`, product_sku, `out`,
|
||||
ref_warehouse, zone, aisle, rack,
|
||||
ref_warehouse, zone, aisle, bin,
|
||||
contact_id, `description`, `log`, `type`, lot_number, serial_number, status, updated_at)
|
||||
VALUES
|
||||
(:uuid, :company_id, :date, :product_sku, :quantity,
|
||||
:ref_warehouse, :zone, :aisle, :rack,
|
||||
:ref_warehouse, :zone, :aisle, :bin,
|
||||
:contact_id, :description, :log, 'transfer', :lot_number, :serial_number, 0, NOW())"
|
||||
)->execute([
|
||||
':uuid' => $uuid,
|
||||
@@ -630,7 +630,7 @@ class StockManager {
|
||||
':ref_warehouse' => $to_warehouse,
|
||||
':zone' => $from_zone,
|
||||
':aisle' => $from_aisle,
|
||||
':rack' => $from_rack,
|
||||
':bin' => $from_bin,
|
||||
':contact_id' => $contact_id,
|
||||
':description' => $description,
|
||||
':log' => json_encode($table_log),
|
||||
@@ -643,11 +643,11 @@ class StockManager {
|
||||
$this->pdo->prepare(
|
||||
"INSERT INTO `$to_table`
|
||||
(uuid, company_id, `date`, product_sku, `in`,
|
||||
ref_warehouse, ref_id, zone, aisle, rack,
|
||||
ref_warehouse, ref_id, zone, aisle, bin,
|
||||
contact_id, `description`, `log`, `type`, lot_number, serial_number, status, updated_at)
|
||||
VALUES
|
||||
(:uuid, :company_id, :date, :product_sku, :quantity,
|
||||
:ref_warehouse, :ref_id, :zone, :aisle, :rack,
|
||||
:ref_warehouse, :ref_id, :zone, :aisle, :bin,
|
||||
:contact_id, :description, :log, 'transfer', :lot_number, :serial_number, 0, NOW())"
|
||||
)->execute([
|
||||
':uuid' => $uuid,
|
||||
@@ -659,7 +659,7 @@ class StockManager {
|
||||
':ref_id' => $from_stock_id,
|
||||
':zone' => $to_zone,
|
||||
':aisle' => $to_aisle,
|
||||
':rack' => $to_rack,
|
||||
':bin' => $to_bin,
|
||||
':contact_id' => $contact_id,
|
||||
':description' => $description,
|
||||
':log' => json_encode($table_log),
|
||||
@@ -678,7 +678,7 @@ class StockManager {
|
||||
':company_id' => $this->company_id,
|
||||
]);
|
||||
|
||||
// releaseRack, occupyRack and adjustBalance deferred — called from approveStock() only.
|
||||
// releaseBin, occupyBin and adjustBalance deferred — called from approveStock() only.
|
||||
return $from_stock_id;
|
||||
}
|
||||
|
||||
@@ -730,13 +730,13 @@ class StockManager {
|
||||
"UPDATE `{$table}` SET status = 1, updated_at = NOW() WHERE id = :id AND company_id = :company_id"
|
||||
)->execute([':id' => $id, ':company_id' => $this->company_id]);
|
||||
|
||||
// ── Rack state + balance ──────────────────────────────────────────
|
||||
// ── Bin state + balance ──────────────────────────────────────────
|
||||
if ($type === 'in') {
|
||||
|
||||
// Occupy rack now that stock is approved
|
||||
$whMgmt->occupyRack(
|
||||
// Occupy bin now that stock is approved
|
||||
$whMgmt->occupyBin(
|
||||
$warehouse_id,
|
||||
$row['zone'], $row['aisle'], $row['rack'],
|
||||
$row['zone'], $row['aisle'], $row['bin'],
|
||||
$row['product_sku'],
|
||||
$id
|
||||
);
|
||||
@@ -766,9 +766,9 @@ class StockManager {
|
||||
$remaining_qty = (float)$remaining_sth->fetchColumn();
|
||||
}
|
||||
|
||||
// Release the rack only when the source batch is fully consumed.
|
||||
// Release the bin only when the source batch is fully consumed.
|
||||
if ($remaining_qty <= 0.000001) {
|
||||
$whMgmt->releaseRack($warehouse_id, $row['zone'], $row['aisle'], $row['rack']);
|
||||
$whMgmt->releaseBin($warehouse_id, $row['zone'], $row['aisle'], $row['bin']);
|
||||
}
|
||||
$whMgmt->adjustBalance('out', $warehouse_id, $row['product_sku'], 0, (float)$row['out'],
|
||||
$id, $row['source'] ?? '', (int)($row['source_id'] ?? 0), $row['date'] ?? '');
|
||||
@@ -848,19 +848,19 @@ class StockManager {
|
||||
)->execute([':id' => $inbound_row['id'], ':company_id' => $this->company_id]);
|
||||
}
|
||||
|
||||
// Rack state: release source, occupy destination
|
||||
// Bin state: release source, occupy destination
|
||||
if ($from_row && $from_wh_id) {
|
||||
$whMgmt->releaseRack(
|
||||
$whMgmt->releaseBin(
|
||||
$from_wh_id,
|
||||
$from_row['zone'], $from_row['aisle'], $from_row['rack']
|
||||
$from_row['zone'], $from_row['aisle'], $from_row['bin']
|
||||
);
|
||||
$whMgmt->adjustBalance('out', $from_wh_id, $from_row['product_sku'], 0, (float)$from_row['out'],
|
||||
(int)$from_row['id'], $from_row['source'] ?? '', (int)($from_row['source_id'] ?? 0), $from_row['date'] ?? '');
|
||||
}
|
||||
if ($inbound_row && $paired_wh_id) {
|
||||
$whMgmt->occupyRack(
|
||||
$whMgmt->occupyBin(
|
||||
$paired_wh_id,
|
||||
$inbound_row['zone'], $inbound_row['aisle'], $inbound_row['rack'],
|
||||
$inbound_row['zone'], $inbound_row['aisle'], $inbound_row['bin'],
|
||||
$inbound_row['product_sku'],
|
||||
$inbound_row['id']
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user