Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion intaro.retailcrm/classes/general/RCrmActions.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
78 changes: 62 additions & 16 deletions intaro.retailcrm/classes/general/history/RetailCrmHistory_v5.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 = [];
Expand All @@ -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);
}
}
Expand Down
19 changes: 18 additions & 1 deletion intaro.retailcrm/classes/general/order/RetailCrmOrder_v5.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
77 changes: 71 additions & 6 deletions intaro.retailcrm/lib/service/usercustomfieldsservice.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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;
Expand Down Expand Up @@ -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)
{
Expand All @@ -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:
Expand Down
67 changes: 67 additions & 0 deletions tests/classes/general/history/RetailCrmHistory_v5Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -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']);
Expand Down
Loading