Skip to content
← Back to Functions
Code

Inventory Reservation Planner

Plans stock reservations for checkout without immediately reducing inventory incorrectly.

Function signature

ogPlanInventoryReservation(cart_lines = array(), stock_records = array())

Categories

  • Ecommerce Workflows

Parameters

cart_linesCart line rows needing inventory reservation review.stock_recordsAvailable/reserved stock records keyed by SKU or product identity.

Return value

Public-safe status string returned by the function for explicit controller branching or logging.

  • success
  • message
  • data

Compatibility

Existing function name, slug, path, and call order preserved; advertised metadata corrected to the actual source behavior.

Minimum PHP version: 7.4

Security notes

Use caller-owned allowlists and context-specific escaping; validate admin actions, export fields, privacy plans, cache keys, templates, settings, routes, and ecommerce policies before production use.

Code

<?php

/*
 * Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
 * Free for personal and internal use. Paid project use requires visible credit
 * to Jeffery L. Paris. Corporate use requires a paid license fee unless a
 * separate written license states otherwise.
 */

/**
 * Plans stock reservations for checkout without directly reducing inventory.
 *
 * Stock is checked against requested quantities and already-reserved quantities.
 * Execution must still happen inside a database transaction at order-write time.
 *
 * @param array $cart_lines Cart lines with product_id and quantity.
 * @param array $stock_records Stock rows keyed by product ID or containing product_id fields.
 * @return array Reservation plan with shortages and reserve-ready rows.
 */
function ogPlanInventoryReservation($cart_lines = array(), $stock_records = array()) {
	$result = array(
		'success' => false,
		'message' => '',
		'data' => array()
	);

	if (!is_array($cart_lines)) {
		$result['message'] = 'Cart lines must be an array.';
		return $result;
	}

	if (!is_array($stock_records)) {
		$result['message'] = 'Stock records must be an array.';
		return $result;
	}

	$stock_by_product = array();
	foreach ($stock_records as $key => $stock) {
		if (!is_array($stock)) {
			continue;
		}

		$product_id = 0;
		if (!empty($stock['product_id'])) {
			$product_id = (int)$stock['product_id'];
		} elseif (is_numeric($key)) {
			$product_id = (int)$key;
		}

		if ($product_id <= 0) {
			continue;
		}

		$available = 0;
		if (!empty($stock['available_quantity'])) {
			$available = (int)$stock['available_quantity'];
		}

		$reserved = 0;
		if (!empty($stock['reserved_quantity'])) {
			$reserved = (int)$stock['reserved_quantity'];
		}

		$stock_by_product[$product_id] = array(
			'available_quantity' => $available,
			'reserved_quantity' => $reserved,
			'net_available' => $available - $reserved
		);
	}

	$reservations = array();
	$shortages = array();

	foreach ($cart_lines as $line) {
		if (!is_array($line)) {
			continue;
		}

		$product_id = 0;
		if (!empty($line['product_id'])) {
			$product_id = (int)$line['product_id'];
		}

		$quantity = 1;
		if (!empty($line['quantity'])) {
			$quantity = (int)$line['quantity'];
		}

		if ($product_id <= 0 || $quantity < 1) {
			$shortages[] = array(
				'product_id' => $product_id,
				'requested_quantity' => $quantity,
				'available_quantity' => 0,
				'reason' => 'Invalid product or quantity.'
			);
			continue;
		}

		$net_available = 0;
		if (!empty($stock_by_product[$product_id])) {
			$net_available = (int)$stock_by_product[$product_id]['net_available'];
		}

		if ($net_available >= $quantity) {
			$reservations[] = array(
				'product_id' => $product_id,
				'quantity' => $quantity,
				'reservation_key' => hash('sha256', $product_id . ':' . $quantity . ':' . count($reservations))
			);
		} else {
			$shortages[] = array(
				'product_id' => $product_id,
				'requested_quantity' => $quantity,
				'available_quantity' => $net_available,
				'reason' => 'Insufficient inventory.'
			);
		}
	}

	$result['success'] = empty($shortages);
	if (empty($shortages)) {
		$result['message'] = 'Inventory reservation plan is ready.';
	} else {
		$result['message'] = 'Inventory shortages were found.';
	}
	$result['data'] = array(
		'reservations' => $reservations,
		'shortages' => $shortages,
		'requires_transaction' => true
	);

	return $result;
}