1
0
mirror of synced 2024-12-11 22:56:05 +03:00

Динамическое добавление свойств в ICML (#375)

This commit is contained in:
Kocmonavtik 2024-12-10 14:33:00 +03:00 committed by GitHub
parent 1ec3bccd0c
commit 3162031b65
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 817 additions and 118 deletions

View File

@ -1,3 +1,6 @@
## 2024-12-09 v6.6.0
- Добавлено динамическое изменение свойств товаров при настройке экспорта
## 2024-12-08 v6.5.39
- Исправлена поломка заказов с промокодом Maxma при включенной передаче корзины в CRM

View File

@ -1 +1 @@
- Исправлена поломка заказов с промокодом Maxma при включенной передаче корзины в CRM
- Добавлено динамическое изменение свойств товаров при настройке экспорта

View File

@ -38,7 +38,8 @@ if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/bitrix/php_interface/retailcrm/exp
}
}
$settingService = SettingsService::getInstance([], '');
global $PROFILE_ID;
$settingService = SettingsService::getInstance([], '', $PROFILE_ID);
$iblockPropertySku = [];
$iblockPropertySkuHl = [];
$iblockPropertyUnitSku = [];
@ -46,7 +47,7 @@ if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/bitrix/php_interface/retailcrm/exp
$iblockPropertyProductHl = [];
$iblockPropertyUnitProduct = [];
foreach (array_keys($settingService->actrualPropList) as $prop) {
foreach (array_keys($settingService->actualPropList) as $prop) {
$skuUnitProps = ('iblockPropertyUnitSku_' . $prop);
$skuUnitProps = $$skuUnitProps;

View File

@ -26,7 +26,8 @@ CModule::IncludeModule('intaro.retailcrm');
//TODO заменить вызов на сервис-локатор, когда он приедет
$settingsService = SettingsService::getInstance(
$arOldSetupVars ?? [],
$ACTION
$ACTION,
$PROFILE_ID
);
$isSetupModulePage = $settingsService->isSetupModulePage();
@ -110,7 +111,7 @@ if ($STEP === 1) {
}
</style>
<form method="post" action="<?=$APPLICATION->GetCurPage()?>">
<form method="post" id="submit-form" action="<?=$APPLICATION->GetCurPage()?>">
<?php
if ($ACTION === 'EXPORT_EDIT' || $ACTION === 'EXPORT_COPY') {
?>
@ -190,8 +191,7 @@ if ($STEP === 1) {
<tbody>
<?php
foreach ($settingsService->actrualPropList as $propertyKey => $property) {
$productSelected = false; ?>
foreach ($settingsService->defaultPropList as $propertyKey => $property) { ?>
<tr class="adm-list-table-row">
<td class="adm-list-table-cell">
@ -215,14 +215,14 @@ if ($STEP === 1) {
if ($keyField === $propertyKey) { ?>
<option value="<?=$field['CODE']?>"
<?php
$productSelected = $settingsService->isOptionSelected(
$isSelected = $settingsService->isOptionSelected(
$field,
$arIBlock['OLD_PROPERTY_PRODUCT_SELECT'],
$propertyKey
);
?>
<?= $productSelected ? ' selected' : ''?>
<?= $isSelected ? ' selected' : ''?>
>
<?=$field['name']?>
</option>
@ -241,7 +241,7 @@ if ($STEP === 1) {
<?php
echo $settingsService->getOptionClass($prop, true);
$productSelected = $settingsService->isOptionSelected(
$isSelected = $settingsService->isOptionSelected(
$prop,
$arIBlock['OLD_PROPERTY_PRODUCT_SELECT'],
$propertyKey
@ -251,7 +251,7 @@ if ($STEP === 1) {
= $settingsService->getHlTableName($prop)
?? $productHlTableName;
echo $productSelected ? ' selected' : '';
echo $isSelected ? ' selected' : '';
?>
>
<?=$prop['NAME']?>
@ -376,19 +376,18 @@ if ($STEP === 1) {
<option value="<?=$prop['CODE']?>"
<?php
echo $settingsService->getOptionClass($prop, false);
if (!$productSelected) {
$isSelected = $settingsService->isOptionSelected(
$prop,
$arIBlock['OLD_PROPERTY_SKU_SELECT'],
$propertyKey
);
$skuHlTableName
= $settingsService->getHlTableName($prop)
?? $skuHlTableName;
$isSelected = $settingsService->isOptionSelected(
$prop,
$arIBlock['OLD_PROPERTY_SKU_SELECT'],
$propertyKey
);
echo $isSelected ? ' selected' : '';
}
$skuHlTableName
= $settingsService->getHlTableName($prop)
?? $skuHlTableName;
echo $isSelected ? ' selected' : '';
?>
>
<?=$prop['NAME']?>
@ -473,9 +472,97 @@ if ($STEP === 1) {
</tr>
<?php
} ?>
<?php
$catalogId = $arIBlock['ID'];
$catalogCustomProps = $settingsService->customPropList[$catalogId];
if (!empty($catalogCustomProps)) {
foreach ($catalogCustomProps as $catalogCustomPropCode => $catalogCustomPropValue) { ?>
<tr class="adm-list-table-row custom-property-row">
<td class="adm-list-table-cell custom-property-title">
<?=htmlspecialcharsex($catalogCustomPropValue)?>
</td>
<td class="adm-list-table-cell">
<select
name="iblockPropertyProduct_<?=$catalogCustomPropCode?>[<?= $catalogId ?>]"
id="iblockPropertyProduct_<?=$catalogCustomPropCode . $catalogId ?>"
class="property-export"
data-type="<?= $catalogCustomPropCode ?>"
onchange="propertyChange(this)"
style="width: 200px">
<option value=""></option>
<?php foreach ($arIBlock['PROPERTIES_PRODUCT'] as $prop) {
$productHlTableName = ''; ?>
<option value="<?=$prop['CODE']?>"
<?php
echo $settingsService->getOptionClass($prop, true);
$isSelected = $settingsService->isOptionSelected(
$prop,
$arIBlock['OLD_PROPERTY_PRODUCT_SELECT'],
$catalogCustomPropCode
);
$productHlTableName
= $settingsService->getHlTableName($prop)
?? $productHlTableName;
echo $isSelected ? ' selected' : '';
?>
>
<?=$prop['NAME']?>
</option>
<?php } ?>
</select>
</td>
<?php if ($arIBlock['PROPERTIES_SKU'] !== null) { ?>
<td class="adm-list-table-cell">
<select
name="iblockPropertySku_<?=$catalogCustomPropCode?>[<?= $catalogId ?>]"
id="iblockPropertySku_<?=$catalogCustomPropCode . $catalogId ?>"
class="property-export"
data-type="<?= $catalogCustomPropCode ?>"
onchange="propertyChange(this)"
style="width: 200px">
<option value=""></option>
<?php foreach ($arIBlock['PROPERTIES_SKU'] as $prop) {
$skuHlTableName = ''; ?>
<option value="<?=$prop['CODE']?>"
<?php
echo $settingsService->getOptionClass($prop, false);
$isSelected = $settingsService->isOptionSelected(
$prop,
$arIBlock['OLD_PROPERTY_SKU_SELECT'],
$catalogCustomPropCode
);
$skuHlTableName
= $settingsService->getHlTableName($prop)
?? $skuHlTableName;
echo $isSelected ? ' selected' : '';
?>
>
<?=$prop['NAME']?>
</option>
<?php } ?>
</select>
<button id="delete-custom-row" class="adm-btn-save" type="button" style="margin-left: 10px"><?= GetMessage('DELETE_PROPERTY');?></button>
</td>
<?php } ?>
</tr>
<?php }
}
?>
</tbody>
</table>
<br>
<button class="adm-btn-save add-custom-row" type="button" style="margin-top: 20px;"><?= GetMessage('ADD_PROPERTY');?></button>
<br>
</div>
</div>
@ -484,6 +571,20 @@ if ($STEP === 1) {
</div>
<input type="hidden" name="count_checked" id="count_checked" value="<?=$intCountChecked?>">
<br>
<template id="custom-property-template-row">
<tr class="adm-list-table-row custom-property-row">
<td class="adm-list-table-cell">
<input type="text" title="Название нового свойства" name="custom-property-title" style="width: 200px">
</td>
<td class="adm-list-table-cell">
<select name="iblockPropertyProduct_" id="iblockPropertyProduct_" class="property-export" onchange="propertyChange(this)" style="width: 200px"></select>
</td>
<td class="adm-list-table-cell">
<select name="iblockPropertySku_" id="iiblockPropertySku_" class="property-export" onchange="propertyChange(this)" style="width: 200px"></select>
<button id="delete-new-custom-row" class="adm-btn-save" type="button" style="margin-left: 10px"><?= GetMessage('DELETE_PROPERTY');?></button>
</td>
</tr>
</template>
<h3><?=GetMessage('SETTINGS_EXPORT')?></h3>
<span class="text"><?=GetMessage('FILENAME')?><br><br></span>
<input type="text" name="SETUP_FILE_NAME" value="<?=htmlspecialcharsbx(strlen($SETUP_FILE_NAME) > 0 ?
@ -566,7 +667,7 @@ if ($STEP === 1) {
<input type="hidden" name="STEP" value="<?=$STEP + 1?>">
<input type="hidden" name="SETUP_FIELDS_LIST" value="<?=
$settingsService->getSetupFieldsString(
array_keys($settingsService->actrualPropList) ?? [],
array_keys($settingsService->actualPropList) ?? [],
$hlblockModule === true,
$hlBlockList ?? []
)
@ -577,8 +678,109 @@ if ($STEP === 1) {
</form>
<?php CJSCore::Init(['jquery']);?>
<?php CUtil::InitJSCore(['intaro_custom_props']); ?>
<script type="text/javascript">
BX.ready(function() {
if (typeof createCustomPropsRaw !== 'function') {
$('.add-custom-row').hide();
}
});
$('.add-custom-row').click(function () {
if (typeof createCustomPropsRaw === 'function') {
createCustomPropsRaw($(this));
}
});
$(document).on('click', '#delete-new-custom-row', function () {
if (typeof deleteCustomPropRow === 'function') {
deleteCustomPropRow($(this));
}
});
$(document).on('click', '#delete-custom-row', function () {
let buttonElem = $(this);
if (typeof addCustomPropToDelete === 'function' && typeof deleteCustomPropRow === 'function') {
addCustomPropToDelete(buttonElem);
deleteCustomPropRow(buttonElem);
}
});
$(document).on('blur', 'input[name="custom-property-title"]', function () {
if (typeof getUniquePropertyCode === 'function' && typeof addCustomPropCodeToSelectAttributes === 'function') {
let inputElem = $(this);
let newPropertyTitle = inputElem.val();
if (!newPropertyTitle) {
return;
}
let newPropertyCode = getUniquePropertyCode(newPropertyTitle);
addCustomPropCodeToSelectAttributes(newPropertyCode, inputElem);
}
});
$('#submit-form').submit(function (formEvent) {
if (typeof setCustomProperties !== "function") {
return;
}
formEvent.preventDefault();
let savePromise = null;
let deletePromise = null;
let formElem = formEvent.currentTarget;
let profileId = $($('input[name="PROFILE_ID"]')).val();
setCustomProperties();
if (Object.keys(customProps).length > 0) {
savePromise = BX.ajax.runAction('intaro:retailcrm.api.customexportprops.save', {
json: {
properties: customProps,
profileId: profileId
},
}).then(addParamsToSetupFieldsList());
}
if (Object.keys(customPropsToDelete).length > 0) {
deletePromise = BX.ajax.runAction('intaro:retailcrm.api.customexportprops.delete', {
json: {
properties: customPropsToDelete,
profileId: profileId
},
}).then(deleteParamsFromSetupFieldsList());
}
const promises = [savePromise, deletePromise].filter(Boolean);
if (promises.length > 0) {
Promise.all(promises)
.finally(() => {
formElem.submit();
});
} else {
formElem.submit();
}
});
const setupFieldsListElement = $('input[name="SETUP_FIELDS_LIST"]');
let customProps = {};
let customPropsToDelete = {};
const setupFieldsParamsToFill = [
'iblockPropertySku_',
'iblockPropertyUnitSku_',
'iblockPropertyProduct_',
'iblockPropertyUnitProduct_',
'highloadblockb_hlsys_marking_code_group_',
'highloadblock_productb_hlsys_marking_code_group_',
'highloadblockeshop_color_reference_',
'highloadblock_producteshop_color_reference_',
'highloadblockeshop_brand_reference_',
'highloadblock_producteshop_brand_reference_'
];
function checkLoadStatus(object)
{
if (object.checked) {

View File

@ -50,6 +50,10 @@ $arJsConfig = [
'js' => '/bitrix/js/intaro/sms.js',
'rel' => [],
],
'intaro_custom_props' => [
'js' => '/bitrix/js/intaro/export/custom-props-export.js',
'rel' => [],
],
];
foreach ($arJsConfig as $ext => $arExt) {

View File

@ -0,0 +1,228 @@
function deleteCustomPropRow(deleteButton)
{
deleteButton.closest('tr').remove();
}
function addCustomPropToDelete(deleteButton)
{
let deletedPropTitle = deleteButton.closest('td').siblings().filter('.custom-property-title').text().trim();
let deletedPropCode = deleteButton.siblings().filter('select').first().data('type');
let customPropCatalogId = deleteButton.closest('.iblockExportTable').data('type');
let values = {
'code': deletedPropCode,
'title': deletedPropTitle,
};
if (customPropsToDelete.hasOwnProperty(customPropCatalogId)) {
customPropsToDelete[customPropCatalogId].push(values);
} else {
customPropsToDelete[customPropCatalogId] = [values];
}
}
function addCustomPropCodeToSelectAttributes(customPropCode, customPropTitleElem)
{
let selectElements = customPropTitleElem.closest('.custom-property-row').find('td select');
let catalogId = customPropTitleElem.closest('.iblockExportTable').data('type');
selectElements.each(function (index, element) {
let selectElem = $(element);
let newSelectIdValue = selectElem.attr('id').match(/^[^_]*_/)[0] + customPropCode + catalogId;
let newSelectNameValue = selectElem.attr('name').match(/^[^_]*_/)[0] + customPropCode + `[${catalogId}]`;
selectElem.attr('id', newSelectIdValue);
selectElem.attr('name', newSelectNameValue);
selectElem.data('type', customPropCode);
triggerSelectChange(selectElem);
});
}
function triggerSelectChange(selectElem)
{
if (selectElem.val().length > 0) {
selectElem.trigger('change', [self]);
}
}
function setCustomProperties()
{
let customPropertiesRows = $('.custom-property-row');
if (customPropertiesRows.length === 0) {
return;
}
let customPropertyCatalogId;
let customPropertyTitle = '';
let customPropertyCode = '';
let productPropertyMatch = '';
let offerPropertyMatch = '';
let catalogIds = [];
customPropertiesRows.each(function (index, propertyRow) {
let propertyRowObj = $(propertyRow);
customPropertyCatalogId = propertyRowObj.closest('.iblockExportTable').data('type');
customPropertyTitle = propertyRowObj.find('input[name="custom-property-title"]').val();
if (!customPropertyTitle) {
return true;
}
customPropertyCode = getUniquePropertyCode(customPropertyTitle);
productPropertyMatch = propertyRowObj.find('select[name=custom-product-property-select]').val();
offerPropertyMatch = propertyRowObj.find('select[name=custom-offer-property-select]').val();
let values = {
'title': customPropertyTitle,
'code': customPropertyCode,
'productProperty': productPropertyMatch,
'offerProperty': offerPropertyMatch
};
if (catalogIds.indexOf(customPropertyCatalogId) === -1) {
customProps[customPropertyCatalogId] = [values];
} else {
customProps[customPropertyCatalogId].push(values);
}
catalogIds.push(customPropertyCatalogId);
});
}
function getUniquePropertyCode(customPropertyTitle)
{
let uniqueValue = transliterate(customPropertyTitle).replace(/ /g, '_');
let counter = 0;
const setupFieldsListValues = setupFieldsListElement.val().split(',');
while (setupFieldsListValues.includes(uniqueValue)) {
uniqueValue = `${customPropertyTitle}${++counter}`;
}
return uniqueValue;
}
function addParamsToSetupFieldsList()
{
let newParams = '';
if (Object.keys(customProps).length === 0) {
return;
}
for (let propertiesByCatalogId of Object.values(customProps)) {
propertiesByCatalogId.forEach(function (values) {
setupFieldsParamsToFill.forEach(function (param) {
newParams += ',' + param + values.code;
});
});
}
let newValue = setupFieldsListElement.val() + newParams;
setupFieldsListElement.val(newValue);
return true;
}
function deleteParamsFromSetupFieldsList()
{
let setupFields = setupFieldsListElement.val();
if (Object.keys(customPropsToDelete).length === 0) {
return;
}
for (let propsByCatalogId of Object.values(customPropsToDelete)) {
propsByCatalogId.forEach(function (propValues) {
setupFieldsParamsToFill.forEach(function (param) {
let paramToDelete = ',' + param + propValues.code;
setupFields = setupFields.replace(paramToDelete, '');
});
});
}
setupFieldsListElement.val(setupFields);
return true;
}
function createCustomPropsRaw(addRowButton)
{
let templateRow = $($('#custom-property-template-row').html());
let templateSelectElements = templateRow.find('select');
let prevTableRow = $(addRowButton).prev('table').find('tbody tr:last-child');
let lastRawSelectElements = prevTableRow.find('td select');
lastRawSelectElements.each(function (index, element) {
let selectElement = $(element);
let templateSelectElement = templateSelectElements[index];
fillTemplateSelect(selectElement, templateSelectElement);
prevTableRow.after(templateRow);
});
}
function fillTemplateSelect(sourceSelectElement, templateSelectElement)
{
let selectOptions = sourceSelectElement.find('option');
selectOptions.each(function (index, element) {
let optionElem = $(element);
let value = $(optionElem).val();
let text = $(optionElem).text();
$('<option>', { value: value, text: text }).appendTo(templateSelectElement);
});
}
function transliterate(titleToTransliterate)
{
const hasCyrillicChars = /[\u0400-\u04FF]/.test(titleToTransliterate);
if (!hasCyrillicChars) {
return titleToTransliterate;
}
translitedText = '';
for (var i = 0; i < titleToTransliterate.length; i++) {
switch (titleToTransliterate[i]) {
case 'а': case 'А': translitedText += 'a'; break;
case 'б': case 'Б': translitedText += 'b'; break;
case 'в': case 'В': translitedText += 'v'; break;
case 'г': case 'Г': translitedText += 'g'; break;
case 'д': case 'Д': translitedText += 'd'; break;
case 'е': case 'Е': translitedText += 'e'; break;
case 'ё': case 'Ё': translitedText += 'yo'; break;
case 'ж': case 'Ж': translitedText += 'zh'; break;
case 'з': case 'З': translitedText += 'z'; break;
case 'и': case 'И': translitedText += 'i'; break;
case 'й': case 'Й': translitedText += 'y'; break;
case 'к': case 'К': translitedText += 'k'; break;
case 'л': case 'Л': translitedText += 'l'; break;
case 'м': case 'М': translitedText += 'm'; break;
case 'н': case 'Н': translitedText += 'n'; break;
case 'о': case 'О': translitedText += 'o'; break;
case 'п': case 'П': translitedText += 'p'; break;
case 'р': case 'Р': translitedText += 'r'; break;
case 'с': case 'С': translitedText += 's'; break;
case 'т': case 'Т': translitedText += 't'; break;
case 'у': case 'У': translitedText += 'u'; break;
case 'ф': case 'Ф': translitedText += 'f'; break;
case 'х': case 'Х': translitedText += 'h'; break;
case 'ц': case 'Ц': translitedText += 'c'; break;
case 'ч': case 'Ч': translitedText += 'ch'; break;
case 'ш': case 'Ш': translitedText += 'sh'; break;
case 'щ': case 'Щ': translitedText += 'sch'; break;
case 'ъ': case 'Ъ': translitedText += ''; break;
case 'ы': case 'Ы': translitedText += 'y'; break;
case 'ь': case 'Ь': translitedText += ''; break;
case 'э': case 'Э': translitedText += 'e'; break;
case 'ю': case 'Ю': translitedText += 'yu'; break;
case 'я': case 'Я': translitedText += 'ya'; break;
default: translitedText += titleToTransliterate[i]; break;
}
}
return translitedText;
}

View File

@ -1,6 +1,6 @@
<?php
$arModuleVersion = [
'VERSION' => '6.5.39',
'VERSION_DATE' => '2024-12-08 18:30:00'
'VERSION' => '6.6.0',
'VERSION_DATE' => '2024-12-09 14:00:00'
];

View File

@ -47,3 +47,5 @@ $MESS['BASE_PRICE'] = 'Base price';
$MESS['WAIT'] = 'Loading...';
$MESS["OFFERS_VALUE"] = "Maximum number of trade offers for a product";
$MESS["LOAD_NON_ACTIVITY"] = "Unload inactive products, services and trade offers";
$MESS["ADD_PROPERTY"] = "Add property";
$MESS["DELETE_PROPERTY"] = "Delete";

View File

@ -47,3 +47,5 @@ $MESS["UNIT_MEASUREMENT_KG"] = "кг.";
$MESS['BASE_PRICE'] = 'Базовая цена';
$MESS['WAIT'] = 'Загрузка...';
$MESS["OFFERS_VALUE"] = "Максимальное количество торговых предложений у товара";
$MESS["ADD_PROPERTY"] = "Добавить свойство";
$MESS["DELETE_PROPERTY"] = "Удалить";

View File

@ -24,15 +24,19 @@ trait InstallerTrait
false
);
$path = $_SERVER['DOCUMENT_ROOT'] . '/local/';
$pathFrom = $_SERVER['DOCUMENT_ROOT'] .
'/bitrix/modules/' .
Constants::MODULE_ID .
'/install/export/bitrix/js/intaro/export'
;
CheckDirPath($path);
$file = new \Bitrix\Main\IO\File($path . 'icml_property_retailcrm.txt', $siteId = null);
if (!$file->isExists()) {
$file->putContents("");
}
CopyDirFiles(
$pathFrom,
$_SERVER['DOCUMENT_ROOT'] . '/bitrix/js/intaro/export/',
true,
true,
false
);
}
public function subscriptionSetup()

View File

@ -0,0 +1,95 @@
<?php
namespace Intaro\RetailCrm\Controller;
use Bitrix\Main\Engine\Controller;
use Bitrix\Main\Result;
use Bitrix\Main\Error;
use Bitrix\Main\Application;
use Intaro\RetailCrm\Icml\SettingsService;
/**
* @category Integration
* @package Intaro\RetailCrm\Controller
* @author RetailCRM <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see http://retailcrm.ru/docs
*/
class CustomExportProps extends Controller
{
private function getRequestData(): array
{
$data = $this->getRequest()->getInput();
if ($data === null) {
}
return json_decode($data, true);
}
public function saveAction()
{
$requestData = $this->getRequestData();
$props = $requestData['properties'];
$profileId = $requestData['profileId'];
$settingsService = SettingsService::getInstance(
[],
null,
$profileId
);
$idCategories = array_keys($props);
if ($idCategories !== []) {
$settingsService->setProfileCatalogs($idCategories);//сразу заменяет и create и update
}
foreach ($props as $catalogId => $propsArray) {
$catalogCustomProps = [];
foreach ($propsArray as $property) {
$catalogCustomProps[] = [
'code' => $property['code'],
'title' => $property['title']
];
}
$settingsService
->setCatalogCustomPropsOptionName($catalogId)
->saveCustomProps($catalogCustomProps)
;
}
}
public function deleteAction()
{
$requestData = $this->getRequestData();
$props = $requestData['properties'];
$profileId = $requestData['profileId'];
$settingsService = SettingsService::getInstance(
[],
null,
$profileId
);
foreach ($props as $catalogId => $propsArray) {
$catalogCustomProps = [];
foreach ($propsArray as $property) {
$catalogCustomProps[] = [
'code' => $property['code'],
'title' => $property['title']
];
}
$settingsService
->setCatalogCustomPropsOptionName($catalogId)
->removeCustomProps($catalogCustomProps, $catalogId)
;
}
}
}

View File

@ -8,14 +8,19 @@ use CCatalogGroup;
use CCatalogSku;
use CCatalogVat;
use CIBlock;
use COption;
use Bitrix\Main\Config\Option;
use Intaro\RetailCrm\Service\Hl;
use RetailcrmConfigProvider;
use Bitrix\Main\Application;
use Bitrix\Main\Entity\Query;
/**
* Отвечает за управление настройками выгрузки icml каталога
*
* @var $PROFILE_ID - зачем если получаем в в конструкторе инстансе
* Class SettingsService
*
*
* @package Intaro\RetailCrm\Icml
*/
class SettingsService
@ -32,6 +37,14 @@ class SettingsService
*/
public const INFOBLOCK_WITH_SKU = 'P';
private const MODULE_ID = 'intaro.retailcrm';
private string $catalogCustomPropsOptionName;
private string $profileCatalogsOptionName;
private string $exportProfileId;
/**
* @var array
*/
@ -98,7 +111,13 @@ class SettingsService
public $loadNonActivity;
/** @var array */
public $actrualPropList = [];
public $actualPropList = [];
/** @var array */
public $customPropList = [];
/** @var array */
public $defaultPropList = [];
/**
* @var \Intaro\RetailCrm\Icml\SettingsService|null
@ -112,7 +131,7 @@ class SettingsService
* @param array $arOldSetupVars
* @param string|null $action
*/
private function __construct(array $arOldSetupVars, ?string $action)
private function __construct(array $arOldSetupVars, ?string $action, ?string $profileId)
{
$this->arOldSetupVars = $arOldSetupVars;
$this->action = $action;
@ -128,7 +147,15 @@ class SettingsService
$this->getPriceTypes();
$this->getVatRates();
$this->actrualPropList = array_merge($this->getIblockPropsPreset(), $this->parseNewProps());
$this->exportProfileId = $profileId ?? '0';
$this->profileCatalogsOptionName = sprintf('exportProfileId_%s_catalogs', $this->exportProfileId);
$this->linkNewProfile();
$this->deleteEmptyProfileCatalogs();
$this->customPropList = $this->getNewProps();
$this->defaultPropList = $this->getIblockPropsPreset();
$this->actualPropList = $this->getActualPropList();
}
/**
@ -137,16 +164,30 @@ class SettingsService
*
* @return \Intaro\RetailCrm\Icml\SettingsService|null
*/
public static function getInstance(array $arOldSetupVars, ?string $action): ?SettingsService
public static function getInstance(array $arOldSetupVars, ?string $action, ?string $profileId): ?SettingsService
{
if (is_null(self::$instance)) {
self::$instance = new self($arOldSetupVars, $action);
self::$instance = new self($arOldSetupVars, $action, $profileId);
}
return self::$instance;
}
public function getPriceTypes()
private function getActualPropList(): array
{
$customProps = [];
foreach ($this->customPropList as $propsByCatalog) {
$customProps = array_merge($customProps, $propsByCatalog);
}
return [
...$this->defaultPropList,
...$customProps
];
}
private function getPriceTypes()
{
$dbPriceType = CCatalogGroup::GetList(['SORT' => 'ASC'], [], [], [], ['ID', 'NAME', 'BASE']);
@ -155,7 +196,7 @@ class SettingsService
}
}
public function getVatRates()
private function getVatRates()
{
$dbVatRate = CCatalogVat::GetListEx(['SORT' => 'ASC'], ['ACTIVE' => 'Y'], false, false, ['ID', 'NAME', 'RATE']);
@ -230,7 +271,9 @@ class SettingsService
private function setProperties(array &$properties, string $propName): void
{
foreach ($this->arOldSetupVars[$propName] as $iblock => $val) {
$properties[$iblock][$propName] = $val;
if (!empty($val)) {
$properties[$iblock][$propName] = $val;
}
}
}
@ -260,27 +303,22 @@ class SettingsService
];
}
private function parseNewProps(): array
private function getNewProps(): array
{
global $APPLICATION;
$result = [];
$text = $APPLICATION->GetFileContent($_SERVER["DOCUMENT_ROOT"] . "/local/icml_property_retailcrm.txt");
$currentProfileCatalogIds = $this->getProfileCatalogs();
if ($text === false) {
return $result;
}
if (!is_null($currentProfileCatalogIds)) {
foreach ($currentProfileCatalogIds as $catalogId) {
$catalogCustomProps = $this
->setCatalogCustomPropsOptionName($catalogId)
->getCustomProps()
;
preg_match_all('/\w+\s*=\s*\w+[ *\w+]*/mu', $text, $matches);
foreach ($matches[0] as $newProp) {
$elements = explode("=", $newProp);
if (empty($elements[0]) || empty($elements[1])) {
continue;
foreach ($catalogCustomProps as $prop) {
$result[$catalogId][$prop['code']] = $prop['title'];
}
}
$result[trim($elements[0])] = trim($elements[1]);
}
return $result;
@ -289,7 +327,7 @@ class SettingsService
/**
* @return array
*/
public function getHintProps(): array
private function getHintProps(): array
{
return [
'article' => ['ARTICLE', 'ART', 'ARTNUMBER', 'ARTICUL', 'ARTIKUL'],
@ -365,7 +403,7 @@ class SettingsService
public function setProps(): void
{
foreach (array_keys($this->actrualPropList) as $prop) {
foreach (array_keys($this->actualPropList) as $prop) {
$this->setProperties($this->iblockPropertySku, 'iblockPropertySku_' . $prop);
$this->setProperties($this->iblockPropertyUnitSku, 'iblockPropertyUnitSku_' . $prop);
$this->setProperties($this->iblockPropertyProduct, 'iblockPropertyProduct_' . $prop);
@ -548,7 +586,7 @@ class SettingsService
*
* @return array
*/
public function getSiteList(int $iblockId): array
private function getSiteList(int $iblockId): array
{
$siteList = [];
@ -576,7 +614,9 @@ class SettingsService
$dbSkuProperties = CIBlock::GetProperties($iblockOffer['IBLOCK_ID'], [], ['MULTIPLE' => 'N']);
while ($prop = $dbSkuProperties->Fetch()) {
$propertiesSKU[] = $prop;
if ($prop['CODE'] !== '') {
$propertiesSKU[] = $prop;
}
}
}
@ -597,7 +637,7 @@ class SettingsService
$props = [];
if (isset($oldValues[$iblockId])) {
foreach (array_keys($this->actrualPropList) as $prop) {
foreach (array_keys($this->actualPropList) as $prop) {
$fullKey = $keyGroup . '_' . $prop;
$props[$prop] = $oldValues[$iblockId][$fullKey];
}
@ -623,14 +663,16 @@ class SettingsService
*
* @return array|null
*/
public function getProductProps(int $iblockId): ?array
private function getProductProps(int $iblockId): ?array
{
$propertiesProduct = null;
$iblockResult = CIBlock::GetProperties($iblockId, [], ['MULTIPLE' => 'N']);
while ($prop = $iblockResult->Fetch()) {
$propertiesProduct[] = $prop;
if ($prop['CODE'] !== '') {
$propertiesProduct[] = $prop;
}
}
return $propertiesProduct;
@ -642,7 +684,7 @@ class SettingsService
*
* @return bool
*/
public function isExport($iblockId, $iblockExport): bool
private function isExport($iblockId, $iblockExport): bool
{
if (is_array($iblockExport) && count($iblockExport) !== 0) {
return (in_array($iblockId, $iblockExport));
@ -747,4 +789,152 @@ class SettingsService
return [$arIBlockList, $intCountChecked, $intCountAvailIBlock, $arIBlockList['iblockExport'] ?? false];
}
public function setCatalogCustomPropsOptionName(string $catalogId): self
{
$this->catalogCustomPropsOptionName = sprintf(
'exportCustomProps_ProfileId_%s_catalogId_%s',
$this->exportProfileId,
$catalogId
);
return $this;
}
private function getCustomProps(): ?array
{
$props = unserialize(COption::GetOptionString(self::MODULE_ID, $this->catalogCustomPropsOptionName));
if (!$props) {
return null;
}
return $props;
}
public function removeCustomProps(array $propsToDelete, string $catalogId): void
{
$currentCatalogProps = $this->getCustomProps();
$updatedCatalogProps = array_values(
array_filter(
$currentCatalogProps,
fn ($currentProp) => !in_array($currentProp, $propsToDelete)
));
if (empty($updatedCatalogProps)) {
$this->deleteOptionEntry($this->catalogCustomPropsOptionName);
$this->deleteProfileCatalog($catalogId);
} else {
$this->updateCustomProps($updatedCatalogProps);
}
}
private function updateCustomProps(array $updatedProps)
{
$serializedProps = serialize($updatedProps);
$this->updateOptionEntry($this->catalogCustomPropsOptionName, $serializedProps);
}
private function setCustomProps(array $props)
{
$propsString = serialize($props);
$this->setOptionEntry($this->catalogCustomPropsOptionName, $propsString);
}
public function saveCustomProps(array $newProps): void
{
$currentProps = $this->getCustomProps();
if (is_null($currentProps)) {
$this->setCustomProps($newProps);
} else {
$updatedProps = array_merge($currentProps, $newProps);
$this->updateCustomProps($updatedProps);
}
}
private function getProfileCatalogs(): ?array
{
$catalogs = unserialize(COption::GetOptionString(self::MODULE_ID, $this->profileCatalogsOptionName));
if (!$catalogs) {
return null;
}
return $catalogs;
}
public function setProfileCatalogs(array $catalogsId): void
{
$catalogs = serialize($catalogsId);
$this->setOptionEntry($this->profileCatalogsOptionName, $catalogs);
}
private function deleteProfileCatalog(string $catalogId): void
{
$currentCatalogs = $this->getProfileCatalogs();
$catalogIdIndex = array_search($catalogId, $currentCatalogs);
if ($catalogIdIndex !== false) {
unset($currentCatalogs[$catalogIdIndex]);
}
$updatedCatalogs = serialize($currentCatalogs);
$this->updateOptionEntry($this->profileCatalogsOptionName, $updatedCatalogs);
}
private function deleteEmptyProfileCatalogs(): void
{
$currentCatalogs = $this->getProfileCatalogs();
if (is_null($currentCatalogs)) {
$this->deleteOptionEntry($this->profileCatalogsOptionName);
}
}
private function setOptionEntry(string $name, string $value)
{
COption::SetOptionString(self::MODULE_ID, $name, $value);
}
private function updateOptionEntry(string $name, string $value)
{
COption::SetOptionString(self::MODULE_ID, $name, '');
COption::SetOptionString(self::MODULE_ID, $name, $value);
}
private function deleteOptionEntry(string $name)
{
COption::RemoveOption(self::MODULE_ID, $name);
}
private function linkNewProfile(): void
{
$currentProfileCatalogs = unserialize(COption::GetOptionString(self::MODULE_ID, $this->profileCatalogsOptionName));
if (!$currentProfileCatalogs) {
$tmpProfileName = 'exportProfileId_0_catalogs';
$currentProfileCatalogs = unserialize(COption::GetOptionString(self::MODULE_ID, $tmpProfileName));
if ($currentProfileCatalogs) {
$this->setOptionEntry($this->profileCatalogsOptionName, serialize($currentProfileCatalogs));
$this->deleteOptionEntry($tmpProfileName);
}
}
foreach ($currentProfileCatalogs as $catalogId) {
$optionName = sprintf('exportCustomProps_ProfileId_%s_catalogId_%s', $this->exportProfileId, $catalogId);
$propsCatalog = unserialize(COption::GetOptionString(self::MODULE_ID, $optionName));
if (!$propsCatalog) {
$tmpOptionName = sprintf('exportCustomProps_ProfileId_%s_catalogId_%s', '0', $catalogId);
$propsCatalog = unserialize(COption::GetOptionString(self::MODULE_ID, $tmpOptionName));
if ($propsCatalog) {
$this->setOptionEntry($optionName, serialize($propsCatalog));
$this->deleteOptionEntry($tmpOptionName);
}
}
}
}
}

View File

@ -115,8 +115,8 @@ class XmlOfferBuilder
public function __construct(XmlSetup $setup, array $measure, ?string $serverName)
{
$this->setup = $setup;
$this->setSettingsService();
$this->purchasePriceNull = RetailcrmConfigProvider::getCrmPurchasePrice();
$this->settingsService = SettingsService::getInstance([], '');
$this->vatRates = $this->settingsService->vatRates;
$this->measures = $this->prepareMeasures($measure);
$this->serverName = $serverName;
@ -141,6 +141,12 @@ class XmlOfferBuilder
return $this->xmlOffer;
}
private function setSettingsService(): void
{
global $PROFILE_ID;
$this->settingsService = SettingsService::getInstance([], '', $PROFILE_ID);
}
/**
* @param array $categories
*/
@ -479,7 +485,7 @@ class XmlOfferBuilder
private function createParamObject(array $params): array
{
$offerParams = [];
$names = $this->settingsService->actrualPropList;
$names = $this->settingsService->actualPropList;
foreach ($params as $code => $value) {

View File

@ -1187,41 +1187,22 @@ function update()
Loader::includeModule('highloadblock');
COption::SetOptionString('intaro.retailcrm', 'api_version', 'v5');
$orderDischarge = Option::get('intaro.retailcrm', 'order_discharge');
if ($orderDischarge === '0') {
$dateAgent = new DateTime();
$dateAgent->add('PT60S');
CAgent::AddAgent(
'RCrmActions::uploadOrdersAgent();',
'intaro.retailcrm',
'N',
180,
$dateAgent->format('d.m.Y H:i:s'),
'Y',
$dateAgent->format('d.m.Y H:i:s'),
30
);
COption::SetOptionString('intaro.retailcrm', 'order_discharge', '2');
}
loadJsExport();
}
function createCustomPropertyFile()
function loadJsExport()
{
$path = $_SERVER['DOCUMENT_ROOT'] . '/local/';
$pathFrom = $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/intaro.retailcrm/install/export/bitrix/js/intaro/export';
CheckDirPath($path);
$file = new \Bitrix\Main\IO\File($path . 'icml_property_retailcrm.txt', $siteId = null);
if (!$file->isExists()) {
$file->putContents("");
}
CopyDirFiles(
$pathFrom,
$_SERVER['DOCUMENT_ROOT'] . '/bitrix/js/intaro/export/',
true,
true,
false
);
}
try {
update();
} catch (Main\ObjectPropertyException | Main\ArgumentException | Main\SystemException $exception) {

View File

@ -17,25 +17,6 @@ class SettingServiceTest extends \BitrixTestCase
;
}
public function testConstruct(): SettingsService
{
$path = $_SERVER['DOCUMENT_ROOT'] . '/local/';
CheckDirPath($path);
$file = new \Bitrix\Main\IO\File($path . '/icml_property_retailcrm.txt', $siteId = null);
$file->putContents("property1 = test prop \n property2 = test prop 2");
$settingService = SettingsService::getInstance($this->getSetupVars(), "");
$this->assertInstanceOf(SettingsService::class, $settingService);
$this->assertArrayHasKey('property1', $settingService->actrualPropList);
$this->assertArrayHasKey('property2', $settingService->actrualPropList);
return $settingService;
}
private function getSetupVars()
{
return [