EventManager::getInstance()->addEventHandlerCompatible(
'sale',
'OnSaleComponentOrderCreated',
[SaleOrderEvents::class, 'fillLocation']
);
class SaleOrderEvents
{
public static function fillLocation(
Order $order,
array &$arUserResult,
HttpRequest $request,
array &$arParams,
array &$arResult,
array &$arDeliveryServiceAll,
array &$arPaySystemServiceAll
): void {
if (!Loader::includeModule('sale')) {
return;
}
$propertyCollection = $order->getPropertyCollection();
$locationProperty = null;
$locationPropertyId = 0;
foreach ($propertyCollection as $property) {
if ($property->isUtil()) {
continue;
}
$propertyData = $property->getProperty();
if (($propertyData['TYPE'] ?? '') === 'LOCATION') {
$locationProperty = $property;
$locationPropertyId = (int) $propertyData['ID'];
break;
}
}
if (!$locationProperty || $locationPropertyId <= 0) {
return;
}
if (self::hasPostedLocation($request, $locationPropertyId)) {
return;
}
$locationCode = self::resolveUserLocationCode();
if (!$locationCode) {
return;
}
$locationRow = self::getLocationByCode($locationCode);
if (!$locationRow) {
return;
}
$locationProperty->setValue($locationCode);
$locationId = (int) $locationRow['ID'];
if ($locationId > 0) {
$arUserResult['ORDER_PROP'][$locationPropertyId] = $locationId;
$arUserResult['DELIVERY_LOCATION'] = $locationId;
}
$arUserResult['RECREATE_ORDER'] = 'Y';
$arUserResult['CALCULATE_PAYMENT'] = 'Y';
}
private static function hasPostedLocation(HttpRequest $request, int $propertyId): bool
{
$flatValue = trim((string) $request->getPost('ORDER_PROP_' . $propertyId));
if ($flatValue !== '') {
return true;
}
$orderPost = $request->getPost('order');
if (is_array($orderPost)) {
$nestedValue = trim((string) ($orderPost['ORDER_PROP_' . $propertyId] ?? ''));
if ($nestedValue !== '') {
return true;
}
}
return false;
}
private static function resolveUserLocationCode(): ?string
{
$codeFromProfile = self::getLocationCodeFromUserProfile();
if ($codeFromProfile) {
return $codeFromProfile;
}
$ip = self::getClientIp();
if (!$ip) {
return null;
}
$geoData = self::getCityByIp($ip);
if (!$geoData || empty($geoData['city'])) {
return null;
}
$location = self::findCityLocationByName((string) $geoData['city']);
if (!$location) {
return null;
}
return (string) $location['CODE'];
}
private static function getLocationCodeFromUserProfile(): ?string
{
global $USER;
if (!is_object($USER) || !$USER->IsAuthorized()) {
return null;
}
$userId = (int) $USER->GetID();
if ($userId <= 0) {
return null;
}
$userData = UserTable::getList([
'select' => ['ID', 'PERSONAL_CITY'],
'filter' => ['=ID' => $userId],
'limit' => 1,
])->fetch();
if (!$userData || empty($userData['PERSONAL_CITY'])) {
return null;
}
$personalCity = trim((string) $userData['PERSONAL_CITY']);
if ($personalCity === '') {
return null;
}
if (ctype_digit($personalCity)) {
$location = LocationTable::getList([
'filter' => [
'=ID' => (int) $personalCity,
'=NAME.LANGUAGE_ID' => LANGUAGE_ID,
],
'select' => ['ID', 'CODE', 'NAME_RU' => 'NAME.NAME'],
'limit' => 1,
])->fetch();
if ($location && !empty($location['CODE'])) {
return (string) $location['CODE'];
}
}
$location = self::findCityLocationByName($personalCity);
if ($location && !empty($location['CODE'])) {
return (string) $location['CODE'];
}
return null;
}
private static function findCityLocationByName(string $cityName): ?array
{
$cityName = self::normalizeCityName($cityName);
if ($cityName === '') {
return null;
}
$location = LocationTable::getList([
'filter' => [
'=NAME.LANGUAGE_ID' => LANGUAGE_ID,
'=TYPE.CODE' => ['CITY', 'VILLAGE'],
'=NAME.NAME' => $cityName,
],
'select' => [
'ID',
'CODE',
'NAME_RU' => 'NAME.NAME',
'TYPE_CODE' => 'TYPE.CODE',
],
'limit' => 1,
])->fetch();
if ($location) {
return $location;
}
$location = LocationTable::getList([
'filter' => [
'=NAME.LANGUAGE_ID' => LANGUAGE_ID,
'=TYPE.CODE' => ['CITY', 'VILLAGE'],
'%NAME.NAME' => $cityName,
],
'select' => [
'ID',
'CODE',
'NAME_RU' => 'NAME.NAME',
'TYPE_CODE' => 'TYPE.CODE',
],
'limit' => 1,
])->fetch();
return $location ?: null;
}
private static function getLocationByCode(string $code): ?array
{
$row = LocationTable::getByCode($code, [
'filter' => [
'=NAME.LANGUAGE_ID' => LANGUAGE_ID,
],
'select' => [
'ID',
'CODE',
'NAME_RU' => 'NAME.NAME',
],
])->fetch();
return $row ?: null;
}
private static function getClientIp(): ?string
{
$request = Application::getInstance()->getContext()->getRequest();
$ip = trim((string) $request->getRemoteAddress());
return $ip !== '' ? $ip : null;
}
private static function getCityByIp(string $ip): ?array
{
$httpClient = new HttpClient([
'socketTimeout' => 3,
'streamTimeout' => 3,
]);
$response = $httpClient->get('http://ip-api.com/json/' . urlencode($ip) . '?lang=ru');
if (!$response) {
return null;
}
$data = json_decode($response, true);
if (!is_array($data)) {
return null;
}
if (($data['status'] ?? '') !== 'success') {
return null;
}
return $data;
}
private static function normalizeCityName(string $cityName): string
{
$cityName = trim($cityName);
$cityName = preg_replace('/\s+/u', ' ', $cityName);
return trim((string) $cityName);
}
} |