0) { // Find the "from" row that's being edited. // We look it up across all td_stock_ via warehouse_from // since $id alone isn't enough (it's per-table). $from_warehouse = (int)($data['warehouse_from'] ?? 0); $to_warehouse = (int)($data['warehouse_to'] ?? 0); $from_ctx = $whMgmt->getStockContext($from_warehouse, $id); $from_table = $from_ctx['table']; $from_row = $from_ctx['row']; if (!$from_row) { throw new Exception("Transfer record not found."); } // Find the paired "to" row via ref_id stored on the "from" row $to_id = (int) $from_row['ref_id']; $to_ctx = $whMgmt->getStockContext($to_warehouse, $to_id); $to_table = $to_ctx['table']; $to_row = $to_ctx['row']; if (!$to_row) { throw new Exception("Paired destination record missing — data integrity issue."); } // Append this edit to both rows' logs $from_log = json_decode($from_row['log'] ?? '[]', true) ?: []; $from_log[] = $logging; $to_log = json_decode($to_row['log'] ?? '[]', true) ?: []; $to_log[] = $logging; // UPDATE "from" row — only metadata fields $sql = "UPDATE `$from_table` SET contact_id = :contact_id, description = :description, `log` = :log WHERE id = :id AND company_id = :company_id"; $pdo->prepare($sql)->execute([ ":contact_id" => $contact_id, ":description" => $description, ":log" => json_encode($from_log), ":id" => $id, ":company_id" => $company_id, ]); // UPDATE "to" row — same metadata $sql = "UPDATE `$to_table` SET contact_id = :contact_id, description = :description, `log` = :log WHERE id = :id AND company_id = :company_id"; $pdo->prepare($sql)->execute([ ":contact_id" => $contact_id, ":description" => $description, ":log" => json_encode($to_log), ":id" => $to_id, ":company_id" => $company_id, ]); // Done — no rack ops, no balance changes on edit. return; } // ══════════════════════════════════════════════════════════ // CREATE FLOW (unchanged from before) // ══════════════════════════════════════════════════════════ // Gather source + destination parameters $from_warehouse = (int)$data['warehouse_from']; $from_zone = $data['zone_from']; $from_aisle = $data['aisle_from']; $from_rack = $data['rack_from']; $to_warehouse = (int)$data['warehouse_to']; $to_zone = $data['zone_to']; $to_aisle = $data['aisle_to']; $to_rack = $data['rack_to']; // Validate source rack has stock of the expected SKU. // Quantity comes from here — NOT user input. $source_stock = $whMgmt->getRackStock( $from_warehouse, $from_zone, $from_aisle, $from_rack ); if (!$source_stock) { throw new Exception( "Source rack {$from_zone}-{$from_aisle}-{$from_rack} is empty." ); } if ($source_stock['product_sku'] !== $data["product_sku"]) { throw new Exception( "Source rack holds {$source_stock['product_sku']}, not {$data['product_sku']}." ); } $quantity = (int) $source_stock['in']; // Resolve both td_stock_ tables $from_ctx = $whMgmt->getStockContext($from_warehouse, 0); $to_ctx = $whMgmt->getStockContext($to_warehouse, 0); $from_table = $from_ctx['table']; $to_table = $to_ctx['table']; // Share UUID across both paired rows for easy grouping $transfer_uuid = $uuid; // INSERT "from" row (outbound from source warehouse) $table_log = [$logging]; $sql = "INSERT INTO `$from_table` (uuid, company_id, `date`, product_sku, `out`, ref_warehouse, zone, aisle, rack, contact_id, `description`, `log`, `type`) VALUES (:uuid, :company_id, :date, :product_sku, :quantity, :ref_warehouse, :zone, :aisle, :rack, :contact_id, :description, :log, 'transfer')"; $pdo->prepare($sql)->execute([ ":uuid" => $transfer_uuid, ":company_id" => $company_id, ":date" => date('Y-m-d H:i:s'), ":product_sku" => $data["product_sku"], ":quantity" => $quantity, ":ref_warehouse" => $to_warehouse, ":zone" => $from_zone, ":aisle" => $from_aisle, ":rack" => $from_rack, ":contact_id" => $contact_id, ":description" => $description, ":log" => json_encode($table_log), ]); $from_stock_id = (int) $pdo->lastInsertId(); // INSERT "to" row (inbound at destination warehouse) $sql = "INSERT INTO `$to_table` (uuid, company_id, `date`, product_sku, `in`, ref_warehouse, ref_id, zone, aisle, rack, contact_id, `description`, `log`, `type`) VALUES (:uuid, :company_id, :date, :product_sku, :quantity, :ref_warehouse, :ref_id, :zone, :aisle, :rack, :contact_id, :description, :log, 'transfer')"; $pdo->prepare($sql)->execute([ ":uuid" => $transfer_uuid, ":company_id" => $company_id, ":date" => date('Y-m-d H:i:s'), ":product_sku" => $data["product_sku"], ":quantity" => $quantity, ":ref_warehouse" => $from_warehouse, ":ref_id" => $from_stock_id, ":zone" => $to_zone, ":aisle" => $to_aisle, ":rack" => $to_rack, ":contact_id" => $contact_id, ":description" => $description, ":log" => json_encode($table_log), ]); $to_stock_id = (int) $pdo->lastInsertId(); // Back-fill the "from" row's ref_id now that we have to_stock_id // so both rows point at each other $sql = "UPDATE `$from_table` SET ref_id = :ref_id WHERE id = :id AND company_id = :company_id"; $pdo->prepare($sql)->execute([ ":ref_id" => $to_stock_id, ":id" => $from_stock_id, ":company_id" => $company_id, ]); // Move rack state: release source, occupy destination // md_rack.log gets 'release' + 'occupy' entries automatically $whMgmt->releaseRack($from_warehouse, $from_zone, $from_aisle, $from_rack); $whMgmt->occupyRack( $to_warehouse, $to_zone, $to_aisle, $to_rack, $data["product_sku"], $to_stock_id ); // Balance adjustments on both warehouses $whMgmt->adjustBalance('out', $from_warehouse, $data["product_sku"], 0, $quantity); $whMgmt->adjustBalance('in', $to_warehouse, $data["product_sku"], 0, $quantity); }); $answer["success"] = 1; } catch (PDOException $e) { $answer["success"] = 0; $answer["message"] = "Database error, please try again"; http_response_code(500); } catch (Exception $e) { $answer["success"] = 0; $answer["message"] = $e->getMessage(); http_response_code(400); } exit(json_encode($answer)); ?>