Вопрос: Как сделать добавление товаров в корзину только из одной родительской категории?

Лучший ответ:
Давай поговорим немножко о другом.
Предположим, что у тебя следующая структура:
Москва (id: 1)
    Приборы (id: 2)
    Инструменты (id: 5)
Санкт-Петербург (id: 3)
    Инструменты (id: 4)
Если товар принадлежит к категории 5, то он точно из Москвы и соответственно при добавлении товара из id:2 он должен добавиться.
А если товар у тебя есть одновременно и там и там? Ну скажем, товар был добавлен и в id:4 и в id:5 - ситуация может быть и такой. В твоем коде, если товар так был занесен, то при добавлении родительский раздел может быть как СПБ, так и Москва, тогда в одном из городов твоя логика работать не будет, а это неправильно.
Я тут набросал пару примеров и сделал пару допущений:
1) Какой-то товар уже в корзине
2) Ты уже знаешь ID города, товар из которого добавлен в корзину
Весь код (без магии), отвечающий за проверку возможности добавлять товар:/**
 * @var integer Идентификатор товара из CITY_PRODUCTS_IBLOCK_ID, добавляемого в корзину
 */
$iProductId = 650;

/**
 * @var integer Идентификатор города, товар которого уже добавлен в корзину
 */
$iCityId = 91;

if ( canAddByCity($iProductId, $iCityId) )
{
	echo "Можно добавить";
}
else
{
	echo "Нельзя добавить";
}
Как видишь, в этом коде нет ничего интересного. Вся магия начинается в функции canAddByCity (да-да, написано не оптимально, нужно оптимизировать и т.п. и т.д. но для пояснения сойдет):/**
 * @var int Уровень вложенности, где находятся города (у меня 2)
 */
define('CITY_DEPTH_LEVEL', 2);

/**
 * @var integer Идентификатор инфоблока, где ищем товары
 */
define('CITY_PRODUCTS_IBLOCK_ID', 5);

/**
 * Возвращает true, если товар $product можно добавить в корзину
 * @param integer $product 
 * @param integer $city 
 * @return boolean
 */
function canAddByCity( $product, $city = 0 )
{
	if ( empty($city) )
	{
		return true;
	}

	if ( !\Bitrix\Main\Loader::IncludeModule('iblock') )
	{
		return false;
	}

	/**
	 * @var array Список категорий верхнего уровня для сравнения
	 */
	$arRootGroups = [];

	/**
	 * @var CDBResult
	 * @see https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/getelementgroups.php
	 */
	$resGroups = \CIBlockElement::GetElementGroups($product, true);

	while( $arGroup = $resGroups->fetch() )
	{
		/**
		 * А вдруг товар без категории и сразу добавлен в город
		 * т.е. без привязки?
		 */
		if ( $arGroup['DEPTH_LEVEL'] == \CITY_DEPTH_LEVEL )
		{
			$arRootGroups[] = $arGroup['ID'];
			continue;
		}

		/**
		 * @todo Запросы в цикле это очень плохо. 
		 * Лучше запомнить где-то разделы и лишние уровни и дополнительным
		 * циклом пройтись после - запросов будет меньше и будет быстрее работать
		 */
		$iRootSectionId = getSectionIdByDepthLevel($arGroup['ID']);

		if ( !empty($iRootSectionId) )
		{
			$arRootGroups[] = $iRootSectionId;
			continue;
		}
	}

	// Какие корневые группы имеет этот товар
	// можно посмотреть через код
	//var_dump($arRootGroups);

	return in_array($city, $arRootGroups);

}

/**
 * Возвращает код родительского раздела $section из инфоблока $iblockId
 * на уровне CITY_DEPTH_LEVEL. В случае отсутствия возвращает 0
 * @param integer $section 
 * @param integer $iblockId 
 * @return integer
 */
function getSectionIdByDepthLevel( $section, $iblockId = \CITY_PRODUCTS_IBLOCK_ID )
{
	$section = intval($section);

	if ( empty($section) )
	{
		return 0;
	}

	if ( empty($iblockId) )
	{
		return 0;
	}

	if ( !\Bitrix\Main\Loader::IncludeModule('iblock') )
	{
		return 0;
	}

	$resChain = \CIBlockSection::GetNavChain($iblockId, $section, ['ID','DEPTH_LEVEL','IBLOCK_SECTION_ID']); 

	while( $arSection = $resChain->fetch() )
	{
		if ( $arSection['DEPTH_LEVEL'] == \CITY_DEPTH_LEVEL)
		{
			return $arSection['ID'];
		}
	}

	return 0;
}

Худший ответ:
У товара есть основной раздел, к которому он привязан, его можно менять через админку (https://dev.1c-bitrix.ru/learning/course/?COURSE_I... Поэтому получая родительский раздел по товару, вы будете всегда получать один и тот же, основной раздел. Как выше сказали, смотреть нужно на раздел, в котором сейчас пользователь находится, если он не совпадает с основным разделом товара, то такой товар в корзину добавлять не следует