From 0c73cc872588fc5d9f1facd15e54f1038d3677b7 Mon Sep 17 00:00:00 2001 From: Alexandr Date: Mon, 12 Oct 2026 09:51:31 +0300 Subject: [PATCH 1/3] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=B4=D0=BB=D0=B8=D0=BD=D1=8B=20=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=BA=D0=BE=D0=B2=D1=8B=D1=85=20=D0=BF=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=D1=81=D0=BA?= =?UTF-8?q?=D0=B8=D1=85=20=D0=BF=D0=BE=D0=BB=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 + .../classes/general/RCrmActions.php | 5 +- .../general/history/RetailCrmHistory_v5.php | 78 +++++++-- .../general/order/RetailCrmOrder_v5.php | 19 ++- intaro.retailcrm/description.ru | 2 +- intaro.retailcrm/install/version.php | 4 +- intaro.retailcrm/lib/component/constants.php | 2 +- .../lib/service/usercustomfieldsservice.php | 77 ++++++++- .../history/RetailCrmHistory_v5Test.php | 67 ++++++++ .../general/order/RetailCrmOrder_v5Test.php | 103 ++++++++++++ .../service/UserCustomFieldsServiceTest.php | 156 ++++++++++++++++++ 11 files changed, 488 insertions(+), 28 deletions(-) create mode 100644 tests/lib/service/UserCustomFieldsServiceTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cd8ffb3..46f1b710 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 2026-10-12 v6.9.6 +- Исправлена проверка длины строковых пользовательских полей. Длина считается в символах, слишком длинное значение больше не очищает поле. + ## 2026-09-22 v6.9.5 - Исправлена кодировка конфигурационных XML-файлов в пакете модуля. diff --git a/intaro.retailcrm/classes/general/RCrmActions.php b/intaro.retailcrm/classes/general/RCrmActions.php index 46661e8a..d7afcf7f 100644 --- a/intaro.retailcrm/classes/general/RCrmActions.php +++ b/intaro.retailcrm/classes/general/RCrmActions.php @@ -622,7 +622,10 @@ public static function convertCrmValueToCmsField($crmValue, $type) case 'STRING': case 'string': case 'text': - $result = strlen($crmValue) <= 500 ? $crmValue : ''; + if (ServiceLocator::get(UserCustomFieldsService::class)->isStringLengthExceeded($crmValue, $type)) { + $result = null; + } + break; } diff --git a/intaro.retailcrm/classes/general/history/RetailCrmHistory_v5.php b/intaro.retailcrm/classes/general/history/RetailCrmHistory_v5.php index 9f1ca07b..c4ad5f8e 100644 --- a/intaro.retailcrm/classes/general/history/RetailCrmHistory_v5.php +++ b/intaro.retailcrm/classes/general/history/RetailCrmHistory_v5.php @@ -35,6 +35,7 @@ use Intaro\RetailCrm\Component\Builder\Bitrix\LoyaltyDataBuilder; use Intaro\RetailCrm\Service\ManagerService; use Intaro\RetailCrm\Service\OrderLoyaltyDataService; +use Intaro\RetailCrm\Service\UserCustomFieldsService; use Intaro\RetailCrm\Component\ConfigProvider; use Intaro\RetailCrm\Component\Constants; use Intaro\RetailCrm\Component\Handlers\EventsHandlers; @@ -1344,22 +1345,7 @@ public static function orderHistory(): bool $newOrder->setField('PRICE', $orderSumm); if (self::$CUSTOM_FIELDS_IS_ACTIVE === 'Y' && !empty($matchedCustomOrderFields)) { - foreach ($order['customFields'] as $code => $value) { - if (isset($matchedCustomOrderFields[$code])) { - $arrayIdentifier = explode('#', $matchedCustomOrderFields[$code], 2); - $property = $propertyCollection->getItemByOrderPropertyId($arrayIdentifier[0]); - $value = RCrmActions::convertCrmValueToCmsField($value, $property->getType()); - $queryResult = $property->setField('VALUE', $value); - - if (!$queryResult->isSuccess()) { - RCrmActions::eventLog( - 'RetailCrmHistory::orderHistory', - 'CustomOrderPropSave', - 'Error when saving a property: ' . $arrayIdentifier[1] - ); - } - } - } + self::setCustomOrderProps($propertyCollection, $order, $matchedCustomOrderFields); } self::orderSave($newOrder); @@ -2349,6 +2335,50 @@ private static function getDataUser($customerBuilder, $customFields) return self::convertBooleanFields($customerArray); } + /** + * Sets the matched retailCRM custom fields to the order properties. + * A string value that exceeds the length limit is skipped, the property keeps its current value. + * + * @param \Bitrix\Sale\PropertyValueCollectionBase $propertyCollection + * @param array $order + * @param array $matchedCustomOrderFields + */ + public static function setCustomOrderProps($propertyCollection, array $order, array $matchedCustomOrderFields): void + { + $customFieldsService = ServiceLocator::get(UserCustomFieldsService::class); + + foreach ($order['customFields'] as $code => $value) { + if (isset($matchedCustomOrderFields[$code])) { + $arrayIdentifier = explode('#', $matchedCustomOrderFields[$code], 2); + $property = $propertyCollection->getItemByOrderPropertyId($arrayIdentifier[0]); + + if ($customFieldsService->isStringLengthExceeded($value, $property->getType())) { + $customFieldsService->logStringLengthExceeded( + 'RetailCrmHistory::orderHistory', + UserCustomFieldsService::DIRECTION_TO_CMS, + 'order (CRM id ' . ($order['id'] ?? '') . ')', + (string) ($arrayIdentifier[1] ?? $arrayIdentifier[0]), + (string) $code, + $value + ); + + continue; + } + + $value = RCrmActions::convertCrmValueToCmsField($value, $property->getType()); + $queryResult = $property->setField('VALUE', $value); + + if (!$queryResult->isSuccess()) { + RCrmActions::eventLog( + 'RetailCrmHistory::orderHistory', + 'CustomOrderPropSave', + 'Error when saving a property: ' . $arrayIdentifier[1] + ); + } + } + } + } + private static function getCustomUserFields($customer, $matchedCustomFields, $customUserFieldTypes) { $customFields = []; @@ -2357,9 +2387,25 @@ private static function getCustomUserFields($customer, $matchedCustomFields, $cu && isset($customer['customFields']) && is_array($customer['customFields']) ) { + $customFieldsService = ServiceLocator::get(UserCustomFieldsService::class); + foreach ($customer['customFields'] as $code => $value) { if (isset($matchedCustomFields[$code]) && !empty($customUserFieldTypes[$matchedCustomFields[$code]])) { $type = $customUserFieldTypes[$matchedCustomFields[$code]]; + + if ($customFieldsService->isStringLengthExceeded($value, $type)) { + $customFieldsService->logStringLengthExceeded( + 'RetailCrmHistory::getCustomUserFields', + UserCustomFieldsService::DIRECTION_TO_CMS, + 'customer (CRM id ' . ($customer['id'] ?? '') . ')', + (string) $matchedCustomFields[$code], + (string) $code, + $value + ); + + continue; + } + $customFields[$matchedCustomFields[$code]] = RCrmActions::convertCrmValueToCmsField($value, $type); } } diff --git a/intaro.retailcrm/classes/general/order/RetailCrmOrder_v5.php b/intaro.retailcrm/classes/general/order/RetailCrmOrder_v5.php index 8dad5fff..3339b185 100644 --- a/intaro.retailcrm/classes/general/order/RetailCrmOrder_v5.php +++ b/intaro.retailcrm/classes/general/order/RetailCrmOrder_v5.php @@ -27,6 +27,7 @@ use RetailCrm\ApiClient; use Intaro\RetailCrm\Service\ManagerService; use Intaro\RetailCrm\Service\LoyaltyAccountService; +use Intaro\RetailCrm\Service\UserCustomFieldsService; use RetailCrm\Response\ApiResponse; use Intaro\RetailCrm\Component\ConfigProvider; use Intaro\RetailCrm\Model\Api\Response\OrdersCreateResponse; @@ -159,7 +160,23 @@ public static function orderSend( continue; } - $order['customFields'][$arParams['customOrderProps'][$prop['ID'] . '#' . $prop['CODE']]] = RCrmActions::convertCmsFieldToCrmValue($prop['VALUE'][0], $prop['TYPE']); + $crmFieldCode = $arParams['customOrderProps'][$prop['ID'] . '#' . $prop['CODE']]; + $customFieldsService = ServiceLocator::get(UserCustomFieldsService::class); + + if ($customFieldsService->isStringLengthExceeded($prop['VALUE'][0], $prop['TYPE'] ?? '')) { + $customFieldsService->logStringLengthExceeded( + 'RetailCrmOrder::orderSend', + UserCustomFieldsService::DIRECTION_TO_CRM, + 'order ' . $arOrder['ID'], + (string) $prop['CODE'], + (string) $crmFieldCode, + $prop['VALUE'][0] + ); + + continue; + } + + $order['customFields'][$crmFieldCode] = RCrmActions::convertCmsFieldToCrmValue($prop['VALUE'][0], $prop['TYPE']); } elseif (is_array($arParams['optionsOrderProps'][$arOrder['PERSON_TYPE_ID']]) && $search = array_search($prop['CODE'], $arParams['optionsOrderProps'][$arOrder['PERSON_TYPE_ID']])) {//other if (in_array($search, ['fio', 'phone', 'email'])) {//fio, phone, email diff --git a/intaro.retailcrm/description.ru b/intaro.retailcrm/description.ru index f0824911..1388f9a2 100644 --- a/intaro.retailcrm/description.ru +++ b/intaro.retailcrm/description.ru @@ -1 +1 @@ -- Исправлена кодировка конфигурационных XML-файлов в пакете модуля. +- Исправлена проверка длины строковых пользовательских полей: длина считается в символах, слишком длинное значение больше не очищает поле. diff --git a/intaro.retailcrm/install/version.php b/intaro.retailcrm/install/version.php index 273f7e55..4813c77b 100644 --- a/intaro.retailcrm/install/version.php +++ b/intaro.retailcrm/install/version.php @@ -1,6 +1,6 @@ '6.9.5', - 'VERSION_DATE' => '2026-09-22 12:00:00' + 'VERSION' => '6.9.6', + 'VERSION_DATE' => '2026-10-12 12:00:00' ]; diff --git a/intaro.retailcrm/lib/component/constants.php b/intaro.retailcrm/lib/component/constants.php index 6cee79b8..9a837f58 100644 --- a/intaro.retailcrm/lib/component/constants.php +++ b/intaro.retailcrm/lib/component/constants.php @@ -18,7 +18,7 @@ */ class Constants { - public const MODULE_VERSION = '6.9.5'; + public const MODULE_VERSION = '6.9.6'; public const CRM_PURCHASE_PRICE_NULL = 'purchasePrice_null'; public const BITRIX_USER_ID_PREFIX = 'bitrixUserId-'; public const CRM_USERS_MAP = 'crm_users_map'; diff --git a/intaro.retailcrm/lib/service/usercustomfieldsservice.php b/intaro.retailcrm/lib/service/usercustomfieldsservice.php index 62756cd1..8da40046 100644 --- a/intaro.retailcrm/lib/service/usercustomfieldsservice.php +++ b/intaro.retailcrm/lib/service/usercustomfieldsservice.php @@ -13,12 +13,20 @@ use Bitrix\Main\UserFieldTable; use Intaro\RetailCrm\Component\ConfigProvider; +use RCrmActions; /** * Builds retailCRM custom fields from mapped Bitrix user fields. */ class UserCustomFieldsService { + public const STRING_MAX_LENGTH = 500; + + public const DIRECTION_TO_CRM = 'Bitrix -> RetailCRM'; + public const DIRECTION_TO_CMS = 'RetailCRM -> Bitrix'; + + private const STRING_TYPES = ['STRING', 'string', 'text']; + /** * @param array $userFields * @@ -35,10 +43,22 @@ public function getCustomFields(array $userFields): array continue; } - $result[$crmCode] = $this->convertValue( - $userFields[$bitrixCode], - $fieldTypes[$bitrixCode] ?? '' - ); + $type = $fieldTypes[$bitrixCode] ?? ''; + + if ($this->isStringLengthExceeded($userFields[$bitrixCode], $type)) { + $this->logStringLengthExceeded( + __METHOD__, + self::DIRECTION_TO_CRM, + 'customer' . (isset($userFields['ID']) ? ' ' . $userFields['ID'] : ''), + (string) $bitrixCode, + (string) $crmCode, + $userFields[$bitrixCode] + ); + + continue; + } + + $result[$crmCode] = $this->convertValue($userFields[$bitrixCode], $type); } return $result; @@ -78,11 +98,56 @@ public function getUserFieldTypes(array $fieldNames = []): array return $result; } + /** + * @param mixed $value + * @param mixed $type + * + * @return bool + */ + public function isStringLengthExceeded($value, $type): bool + { + return in_array($type, self::STRING_TYPES, true) + && is_scalar($value) + && mb_strlen((string) $value) > self::STRING_MAX_LENGTH; + } + + /** + * @param string $method + * @param string $direction + * @param string $entity + * @param string $bitrixCode + * @param string $crmCode + * @param mixed $value + */ + public function logStringLengthExceeded( + string $method, + string $direction, + string $entity, + string $bitrixCode, + string $crmCode, + $value + ): void { + RCrmActions::eventLog( + $method, + 'CustomFieldLengthExceeded', + sprintf( + 'Custom field value is too long and was not transferred. Direction: %s; entity: %s;' + . ' Bitrix field: %s; RetailCRM field: %s; length: %d; limit: %d', + $direction, + $entity, + $bitrixCode, + $crmCode, + mb_strlen((string) $value), + self::STRING_MAX_LENGTH + ) + ); + } + /** * @param mixed $value * @param mixed $type * - * @return mixed + * @return mixed null if the string value exceeds the limit, see isStringLengthExceeded() */ public function convertValue($value, $type) { @@ -93,7 +158,7 @@ public function convertValue($value, $type) return $value === 'Y' ? 1 : 0; case 'STRING': case 'string': - return strlen($value) <= 500 ? $value : ''; + return $this->isStringLengthExceeded($value, $type) ? null : $value; case 'datetime': return date('Y-m-d', strtotime($value)); default: diff --git a/tests/classes/general/history/RetailCrmHistory_v5Test.php b/tests/classes/general/history/RetailCrmHistory_v5Test.php index 68023b0c..fd77838a 100644 --- a/tests/classes/general/history/RetailCrmHistory_v5Test.php +++ b/tests/classes/general/history/RetailCrmHistory_v5Test.php @@ -198,6 +198,73 @@ public function testSetManager() $this->assertEquals(1515, $cmsOrder->getField('RESPONSIBLE_ID')); } + /** + * @dataProvider stringCustomFieldProvider + */ + public function testConvertCrmValueToCmsFieldStringLength(string $value, bool $isTransferred): void + { + self::assertSame($isTransferred ? $value : null, RCrmActions::convertCrmValueToCmsField($value, 'STRING')); + } + + /** + * @dataProvider stringCustomFieldProvider + */ + public function testSetCustomOrderPropsStringLength(string $value, bool $isTransferred): void + { + $property = $this->createMock(\Bitrix\Sale\PropertyValue::class); + $property->method('getType') + ->willReturn('STRING'); + $property->expects($isTransferred ? $this->once() : $this->never()) + ->method('setField') + ->with('VALUE', $value) + ->willReturn(new \Bitrix\Main\Result()); + + $propertyCollection = $this->createMock(\Bitrix\Sale\PropertyValueCollection::class); + $propertyCollection->method('getItemByOrderPropertyId') + ->with('7') + ->willReturn($property); + + RetailCrmHistory::setCustomOrderProps( + $propertyCollection, + ['id' => 100, 'customFields' => ['measurements' => $value]], + ['measurements' => '7#MEASUREMENTS'] + ); + } + + /** + * @dataProvider stringCustomFieldProvider + */ + public function testGetCustomUserFieldsStringLength(string $value, bool $isTransferred): void + { + $method = new ReflectionMethod(RetailCrmHistory::class, 'getCustomUserFields'); + $method->setAccessible(true); + $oldCustomFieldsStatus = RetailCrmHistory::$CUSTOM_FIELDS_IS_ACTIVE; + RetailCrmHistory::$CUSTOM_FIELDS_IS_ACTIVE = 'Y'; + + try { + $customFields = $method->invoke( + null, + ['id' => 100, 'customFields' => ['note' => $value]], + ['note' => 'UF_FIELD_USER_1'], + ['UF_FIELD_USER_1' => 'string'] + ); + } finally { + RetailCrmHistory::$CUSTOM_FIELDS_IS_ACTIVE = $oldCustomFieldsStatus; + } + + self::assertSame($isTransferred ? ['UF_FIELD_USER_1' => $value] : [], $customFields); + } + + public function stringCustomFieldProvider(): array + { + return [ + 'cyrillic string shorter than the limit' => [str_repeat('я', 454), true], + 'exactly 500 characters' => [str_repeat('я', 500), true], + '501 characters does not change the current value' => [str_repeat('я', 501), false], + 'empty string clears the field' => ['', true], + ]; + } + private function deleteTestingUser(): void { $dbUser = CUser::GetList(($by = 'ID'), ($sort = 'DESC'), ['=EMAIL' => 'testbitrixreg@gmail.com']); diff --git a/tests/classes/general/order/RetailCrmOrder_v5Test.php b/tests/classes/general/order/RetailCrmOrder_v5Test.php index c2cd1ca5..e4bf911d 100644 --- a/tests/classes/general/order/RetailCrmOrder_v5Test.php +++ b/tests/classes/general/order/RetailCrmOrder_v5Test.php @@ -158,6 +158,109 @@ public function emptyCustomFieldValueProvider(): array ]; } + /** + * @dataProvider stringCustomFieldProvider + */ + public function testOrderSendStringCustomFieldLength(string $value, bool $isSent): void + { + $order = $this->sendOrderWithStringCustomField($value); + + if ($isSent) { + self::assertSame($value, $order['customFields']['measurements']); + } else { + self::assertArrayNotHasKey('measurements', $order['customFields'] ?? []); + } + } + + public function stringCustomFieldProvider(): array + { + return [ + 'cyrillic string shorter than the limit' => [str_repeat('я', 454), true], + 'exactly 500 characters' => [str_repeat('я', 500), true], + '501 characters is not sent' => [str_repeat('я', 501), false], + ]; + } + + public function testOrderSendDoesNotLogEmptyStringCustomField(): void + { + $lastLogId = $this->getLastEventLogId(); + + $this->sendOrderWithStringCustomField(''); + + self::assertSame('', $this->getLengthExceededLogDescription($lastLogId)); + } + + public function testOrderSendLogsTooLongCustomField(): void + { + $lastLogId = $this->getLastEventLogId(); + + $this->sendOrderWithStringCustomField(str_repeat('я', 501)); + + $description = $this->getLengthExceededLogDescription($lastLogId); + + self::assertStringContainsString('Bitrix -> RetailCRM', $description); + self::assertStringContainsString('order 1', $description); + self::assertStringContainsString('MEASUREMENTS', $description); + self::assertStringContainsString('measurements', $description); + self::assertStringContainsString('length: 501', $description); + self::assertStringContainsString('limit: 500', $description); + self::assertStringNotContainsString('яя', $description); + } + + private function getLastEventLogId(): int + { + global $DB; + + $row = $DB->Query('SELECT MAX(ID) AS ID FROM b_event_log')->Fetch(); + + return (int) ($row['ID'] ?? 0); + } + + private function getLengthExceededLogDescription(int $afterId): string + { + global $DB; + + $row = $DB->Query( + 'SELECT DESCRIPTION FROM b_event_log WHERE ID > ' . $afterId + . " AND ITEM_ID = 'CustomFieldLengthExceeded' ORDER BY ID DESC LIMIT 1" + )->Fetch(); + + return (string) ($row['DESCRIPTION'] ?? ''); + } + + private function sendOrderWithStringCustomField(string $value): array + { + $arFields = $this->getArFields(); + $arFields['PROPS']['properties'][] = [ + 'ID' => 3, + 'CODE' => 'MEASUREMENTS', + 'VALUE' => [$value], + 'TYPE' => 'STRING', + ]; + + $this->initSystemData(); + RetailcrmConfigProvider::setCustomFieldsStatus('Y'); + RetailcrmConfigProvider::setSyncIntegrationPayment('N'); + + return RetailCrmOrder::orderSend( + $arFields, + new stdClass(), + [ + 'optionsOrderTypes' => RetailcrmConfigProvider::getOrderTypes(), + 'optionsPayStatuses' => RetailcrmConfigProvider::getPaymentStatuses(), + 'optionsContragentType' => RetailcrmConfigProvider::getContragentTypesBySite(), + 'optionsDelivTypes' => RetailcrmConfigProvider::getDeliveryTypes(), + 'optionsPayTypes' => RetailcrmConfigProvider::getPaymentTypes(), + 'optionsOrderProps' => ['bitrixType' => ['fio' => 'FIO']], + 'optionsPayment' => ['Y' => 'paid'], + 'customOrderProps' => ['3#MEASUREMENTS' => 'measurements'], + ], + false, + null, + 'ordersCreate' + ); + } + public function initSystemData(): void { RetailcrmConfigProvider::setOrderTypes(['bitrixType' => 'crmType']); diff --git a/tests/lib/service/UserCustomFieldsServiceTest.php b/tests/lib/service/UserCustomFieldsServiceTest.php new file mode 100644 index 00000000..d4cde1be --- /dev/null +++ b/tests/lib/service/UserCustomFieldsServiceTest.php @@ -0,0 +1,156 @@ +isStringLengthExceeded($value, $type)); + } + + public function stringLengthProvider(): array + { + return [ + 'cyrillic string shorter than the limit' => [str_repeat('я', 454), 'STRING', false], + 'exactly 500 characters' => [str_repeat('я', 500), 'string', false], + '501 characters' => [str_repeat('я', 501), 'STRING', true], + '501 single-byte characters' => [str_repeat('a', 501), 'text', true], + 'length is counted in characters: 300 characters take 600 bytes' => [str_repeat('я', 300), 'STRING', false], + 'empty string' => ['', 'STRING', false], + 'null' => [null, 'STRING', false], + 'array' => [[], 'STRING', false], + 'type without the length limit' => [str_repeat('я', 501), 'datetime', false], + ]; + } + + /** + * @dataProvider convertValueProvider + * + * @param mixed $expected + */ + public function testConvertValue(string $value, $expected): void + { + self::assertSame($expected, (new UserCustomFieldsService())->convertValue($value, 'string')); + } + + public function convertValueProvider(): array + { + return [ + 'cyrillic string shorter than the limit' => [str_repeat('я', 454), str_repeat('я', 454)], + 'exactly 500 characters' => [str_repeat('я', 500), str_repeat('я', 500)], + '501 characters is not an empty string' => [str_repeat('я', 501), null], + 'empty string clears the field' => ['', ''], + ]; + } + + /** + * @dataProvider customFieldsProvider + */ + public function testGetCustomFields(string $value, array $expected): void + { + $oldMatchedUserFields = ConfigProvider::getMatchedUserFields(); + $oldMatchedUserFields = is_array($oldMatchedUserFields) ? $oldMatchedUserFields : null; + + try { + $this->setMatchedUserFields([self::BITRIX_FIELD => self::CRM_FIELD]); + + self::assertSame($expected, $this->createService()->getCustomFields([self::BITRIX_FIELD => $value])); + } finally { + $this->setMatchedUserFields($oldMatchedUserFields); + } + } + + public function customFieldsProvider(): array + { + return [ + 'cyrillic string shorter than the limit' => [str_repeat('я', 454), [self::CRM_FIELD => str_repeat('я', 454)]], + 'exactly 500 characters' => [str_repeat('я', 500), [self::CRM_FIELD => str_repeat('я', 500)]], + '501 characters is not sent' => [str_repeat('я', 501), []], + 'empty string clears the field' => ['', [self::CRM_FIELD => '']], + ]; + } + + public function testGetCustomFieldsLogsTooLongValue(): void + { + $value = str_repeat('я', 501); + $lastLogId = $this->getLastLogId(); + $oldMatchedUserFields = ConfigProvider::getMatchedUserFields(); + $oldMatchedUserFields = is_array($oldMatchedUserFields) ? $oldMatchedUserFields : null; + + try { + $this->setMatchedUserFields([self::BITRIX_FIELD => self::CRM_FIELD]); + $this->createService()->getCustomFields(['ID' => 4242, self::BITRIX_FIELD => $value]); + } finally { + $this->setMatchedUserFields($oldMatchedUserFields); + } + + $description = $this->getLogDescription($lastLogId); + + self::assertStringContainsString(UserCustomFieldsService::DIRECTION_TO_CRM, $description); + self::assertStringContainsString('customer 4242', $description); + self::assertStringContainsString(self::BITRIX_FIELD, $description); + self::assertStringContainsString(self::CRM_FIELD, $description); + self::assertStringContainsString('length: 501', $description); + self::assertStringContainsString('limit: 500', $description); + self::assertStringNotContainsString('яя', $description); + } + + private function createService(): UserCustomFieldsService + { + return new class extends UserCustomFieldsService { + public function getUserFieldTypes(array $fieldNames = []): array + { + return array_fill_keys($fieldNames, 'string'); + } + }; + } + + private function setMatchedUserFields(?array $matchedUserFields): void + { + ConfigProvider::setMatchedUserFields($matchedUserFields); + Helpers::setConfigProperty('mathedCustomFields', $matchedUserFields); + } + + private function getLastLogId(): int + { + global $DB; + + $row = $DB->Query('SELECT MAX(ID) AS ID FROM b_event_log')->Fetch(); + + return (int) ($row['ID'] ?? 0); + } + + private function getLogDescription(int $afterId): string + { + global $DB; + + $row = $DB->Query( + "SELECT DESCRIPTION FROM b_event_log WHERE ID > " . $afterId + . " AND ITEM_ID = 'CustomFieldLengthExceeded' ORDER BY ID DESC LIMIT 1" + )->Fetch(); + + return (string) ($row['DESCRIPTION'] ?? ''); + } +} From f4190801cbf923bf17a3a36bedc53c6568e02e2c Mon Sep 17 00:00:00 2001 From: Alexandr Date: Mon, 12 Oct 2026 10:03:03 +0300 Subject: [PATCH 2/3] =?UTF-8?q?=D0=95=D0=B4=D0=B8=D0=BD=D1=8B=D0=B9=20?= =?UTF-8?q?=D1=82=D0=B5=D0=BA=D1=81=D1=82=20=D0=BE=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- intaro.retailcrm/description.ru | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intaro.retailcrm/description.ru b/intaro.retailcrm/description.ru index 1388f9a2..522b68ec 100644 --- a/intaro.retailcrm/description.ru +++ b/intaro.retailcrm/description.ru @@ -1 +1 @@ -- Исправлена проверка длины строковых пользовательских полей: длина считается в символах, слишком длинное значение больше не очищает поле. +- Исправлена проверка длины строковых пользовательских полей. Длина считается в символах, слишком длинное значение больше не очищает поле. From e5013b144bf106a21cd2f3e7660224ea2e29d834 Mon Sep 17 00:00:00 2001 From: Alexandr Date: Thu, 29 Oct 2026 15:51:27 +0300 Subject: [PATCH 3/3] =?UTF-8?q?=D0=9E=D1=82=D0=BA=D0=B0=D1=82=20=D0=B2?= =?UTF-8?q?=D0=B5=D1=80=D1=81=D0=B8=D0=B8=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 --- intaro.retailcrm/description.ru | 2 +- intaro.retailcrm/install/version.php | 4 ++-- intaro.retailcrm/lib/component/constants.php | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46f1b710..4cd8ffb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,3 @@ -## 2026-10-12 v6.9.6 -- Исправлена проверка длины строковых пользовательских полей. Длина считается в символах, слишком длинное значение больше не очищает поле. - ## 2026-09-22 v6.9.5 - Исправлена кодировка конфигурационных XML-файлов в пакете модуля. diff --git a/intaro.retailcrm/description.ru b/intaro.retailcrm/description.ru index 522b68ec..f0824911 100644 --- a/intaro.retailcrm/description.ru +++ b/intaro.retailcrm/description.ru @@ -1 +1 @@ -- Исправлена проверка длины строковых пользовательских полей. Длина считается в символах, слишком длинное значение больше не очищает поле. +- Исправлена кодировка конфигурационных XML-файлов в пакете модуля. diff --git a/intaro.retailcrm/install/version.php b/intaro.retailcrm/install/version.php index 4813c77b..273f7e55 100644 --- a/intaro.retailcrm/install/version.php +++ b/intaro.retailcrm/install/version.php @@ -1,6 +1,6 @@ '6.9.6', - 'VERSION_DATE' => '2026-10-12 12:00:00' + 'VERSION' => '6.9.5', + 'VERSION_DATE' => '2026-09-22 12:00:00' ]; diff --git a/intaro.retailcrm/lib/component/constants.php b/intaro.retailcrm/lib/component/constants.php index 9a837f58..6cee79b8 100644 --- a/intaro.retailcrm/lib/component/constants.php +++ b/intaro.retailcrm/lib/component/constants.php @@ -18,7 +18,7 @@ */ class Constants { - public const MODULE_VERSION = '6.9.6'; + public const MODULE_VERSION = '6.9.5'; public const CRM_PURCHASE_PRICE_NULL = 'purchasePrice_null'; public const BITRIX_USER_ID_PREFIX = 'bitrixUserId-'; public const CRM_USERS_MAP = 'crm_users_map';