getStockContext($warehouse_id, $id); $row = $warehouse['row'] ?? []; $table = $warehouse['table']; // Append this edit/create event to td_stock_.log (stock record history) $table_log = json_decode($row['log'] ?? '[]', true); $table_log[] = $logging; // Look up the source stock record via the rack. // Under 1:1, the rack points to exactly one td_stock_ row. $source_stock = $whMgmt->getRackStock( $warehouse_id, $data["zone"], $data["aisle"], $data["rack"] ); if (!$source_stock) { throw new Exception( "Rack {$data['zone']}-{$data['aisle']}-{$data['rack']} is empty — nothing to take out." ); } // Defensive check — UI cascade should already prevent mismatches if ($source_stock['product_sku'] !== $data["product_sku"]) { throw new Exception( "Rack holds {$source_stock['product_sku']}, not {$data['product_sku']}." ); } // Quantity comes from the source stock_in row, NOT user input. // Under 1:1, taking from this rack = taking the full batch. $quantity = (int) $source_stock['in']; $ref_id = (int) $source_stock['id']; if ($id > 0) { // UPDATE existing stock_out record $sql = "UPDATE `$table` SET `product_sku` = :product_sku, `out` = :quantity, `zone` = :zone, `aisle` = :aisle, `rack` = :rack, `contact_id` = :contact_id, `description` = :description, `log` = :log WHERE id = :id AND company_id = :company_id"; $sth = $pdo->prepare($sql); $sth->execute([ ":id" => $id, ":company_id" => $company_id, ":product_sku" => $data["product_sku"], ":quantity" => $quantity, ":zone" => $data["zone"], ":aisle" => $data["aisle"], ":rack" => $data["rack"], ":contact_id" => (int)$data["contact_id"], ":description" => $data["description"], ":log" => json_encode($table_log), ]); // Only touch md_rack if the physical location actually changed. $rack_changed = ( ($row['zone'] ?? '') != $data['zone'] || ($row['aisle'] ?? '') != $data['aisle'] || ($row['rack'] ?? '') != $data['rack'] ); if ($rack_changed) { // Re-occupy the old rack with the batch it originally held // (logically: the stock goes back to where it came from) $whMgmt->occupyRack( $warehouse_id, $row['zone'], $row['aisle'], $row['rack'], $row['product_sku'], $row['ref_id'] ?? null ); // Release the new rack (the real stock-out action) $whMgmt->releaseRack( $warehouse_id, $data['zone'], $data['aisle'], $data['rack'] ); } } else { // INSERT new stock_out record. // ref_id links this stock_out row back to the stock_in batch it consumed. $sql = "INSERT INTO `$table` (uuid, company_id, `date`, product_sku, `out`, zone, aisle, rack, contact_id, `description`, `log`, `type`, ref_id) VALUES (:uuid, :company_id, :date, :product_sku, :quantity, :zone, :aisle, :rack, :contact_id, :description, :log, 'out', :ref_id)"; $sth = $pdo->prepare($sql); $sth->execute([ ":uuid" => $uuid, ":company_id" => $company_id, ":date" => date('Y-m-d H:i:s'), ":product_sku" => $data["product_sku"], ":quantity" => $quantity, ":zone" => $data["zone"], ":aisle" => $data["aisle"], ":rack" => $data["rack"], ":contact_id" => (int)$data["contact_id"], ":description" => $data["description"], ":log" => json_encode($table_log), ":ref_id" => $ref_id, ]); // Release the rack — stock is now gone from this location. $whMgmt->releaseRack( $warehouse_id, $data["zone"], $data["aisle"], $data["rack"] ); } // Balance: total_out increases by delta. // INSERT: $row['out'] = 0 → delta = full quantity // UPDATE: $row['out'] = old → delta = new - old $whMgmt->adjustBalance( 'out', $warehouse_id, $data["product_sku"], $row['out'] ?? 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)); ?>