OData 1С — стандартный REST-интерфейс платформы 8.3: базу публикуют на веб-сервере, включают объекты в состав интерфейса, и справочники, документы и регистры доступны по HTTP в JSON или XML без доработки конфигурации. Ниже — публикация, состав интерфейса, пользователь, параметры запросов, запись и проведение документа, клиенты на curl и на 1С, ошибки.
Сравнение OData с другими способами доступа — в обзоре API 1С. Метод с проверками и бизнес-логикой делают HTTP-сервисом.
$metadata.Конфигуратор (на Windows — от имени администратора) → «Администрирование» → «Публикация на веб-сервере». Задайте имя публикации — оно войдёт в адрес, например trade, — и отметьте «Публиковать стандартный интерфейс OData». На машине с Apache или IIS нужна платформа с компонентом «Модули расширения веб-сервера».
Флажок попадает в default.vrd каталога публикации. Там же при большом потоке запросов включают повторное использование сеансов:
<point xmlns="http://v8.1c.ru/8.2/virtual-resource-system"
base="/trade" ib="Srvr=srv1c;Ref=trade;">
<standardOdata enable="true" reuseSessions="autouse"
sessionMaxAge="20" poolSize="10" poolTimeout="5"/>
</point>
На Linux публикуют утилитой webinst, передав готовый vrd из каталога публикации ключом -descriptor: webinst -publish -apache24 -wsdir trade -dir /var/www/trade -descriptor /var/www/trade/default.vrd -confPath /etc/apache2/apache2.conf.
Публикация открывает только адрес. Какие объекты видны через OData API, хранится в информационной базе, а не в конфигурации, и по умолчанию там пусто.
Форма БСП «Настройка автоматического REST-сервиса». Есть там, где внедрена подсистема OData из БСП. В КА 2.5: «НСИ и администрирование» → «Настройка интеграции» → «Синхронизация данных» → ссылка «Настройки стандартного интерфейса OData». Нужно право «Администрирование данных».
odata.user) с единственной ролью «Удаленный доступ (OData)».Кодом. В УТ 11.4.14 формы нет, в конфигурациях без БСП — тоже. Методы ПолучитьСоставСтандартногоИнтерфейсаOData() и УстановитьСоставСтандартногоИнтерфейсаOData() есть с 8.3.5 и доступны на сервере, в толстом клиенте и во внешнем соединении. Установка заменяет состав целиком, поэтому код начинает с текущего. Ниже — модуль объекта внешней обработки: из серверной процедуры формы вызовите РеквизитФормыВЗначение("Объект").ДобавитьВСоставOData(Имена) под администратором.
// Модуль объекта внешней обработки. Запускать под администратором:
// состав OData хранится в информационной базе, а не в конфигурации.
// Добавляет объекты в состав стандартного интерфейса OData.
// ПолныеИмена - Массив строк вида "Документ.ЗаказКлиента".
// СЗависимостями - добавить и объекты, на которые ссылаются реквизиты
// (справочники, перечисления, документы), а также владельцев справочника.
Процедура ДобавитьВСоставOData(ПолныеИмена, СЗависимостями = Истина) Экспорт
// установка заменяет состав целиком, поэтому начинаем с текущего
Состав = Новый Массив;
Включены = Новый Соответствие;
Для Каждого ОбъектМД Из ПолучитьСоставСтандартногоИнтерфейсаOData() Цикл
Состав.Добавить(ОбъектМД);
Включены.Вставить(ОбъектМД.ПолноеИмя(), Истина);
КонецЦикла;
КДобавлению = Новый Массив;
Для Каждого ПолноеИмя Из ПолныеИмена Цикл
ОбъектМД = Метаданные.НайтиПоПолномуИмени(ПолноеИмя);
Если ОбъектМД = Неопределено Тогда
ВызватьИсключение СтрШаблон("В конфигурации нет объекта %1", ПолноеИмя);
КонецЕсли;
КДобавлению.Добавить(ОбъектМД);
Если СЗависимостями Тогда
Для Каждого Зависимость Из СвязанныеОбъекты(ОбъектМД) Цикл
КДобавлению.Добавить(Зависимость);
КонецЦикла;
КонецЕсли;
КонецЦикла;
Для Каждого ОбъектМД Из КДобавлению Цикл
Если Включены[ОбъектМД.ПолноеИмя()] = Неопределено Тогда
Состав.Добавить(ОбъектМД);
Включены.Вставить(ОбъектМД.ПолноеИмя(), Истина);
КонецЕсли;
КонецЦикла;
УстановитьСоставСтандартногоИнтерфейсаOData(Состав);
КонецПроцедуры
// Объекты метаданных из типов реквизитов справочника или документа
// и реквизитов его табличных частей (один уровень, без рекурсии).
Функция СвязанныеОбъекты(ОбъектМД)
Результат = Новый Массив;
ЭтоСправочник = Метаданные.Справочники.Содержит(ОбъектМД);
Если Не ЭтоСправочник И Не Метаданные.Документы.Содержит(ОбъектМД) Тогда
Возврат Результат;
КонецЕсли;
Если ЭтоСправочник Тогда
Для Каждого Владелец Из ОбъектМД.Владельцы Цикл // владельцы подчинённого справочника
Результат.Добавить(Владелец);
КонецЦикла;
КонецЕсли;
Реквизиты = Новый Массив;
Для Каждого Реквизит Из ОбъектМД.Реквизиты Цикл
Реквизиты.Добавить(Реквизит);
КонецЦикла;
Для Каждого ТабличнаяЧасть Из ОбъектМД.ТабличныеЧасти Цикл
Для Каждого Реквизит Из ТабличнаяЧасть.Реквизиты Цикл
Реквизиты.Добавить(Реквизит);
КонецЦикла;
КонецЦикла;
// классы, которые можно включить в состав по ссылке из реквизита
Допустимые = ",Справочник,Документ,Перечисление,ПланВидовХарактеристик,ПланСчетов,"
+ "ПланВидовРасчета,ПланОбмена,БизнесПроцесс,Задача,";
Для Каждого Реквизит Из Реквизиты Цикл
Для Каждого ТипРеквизита Из Реквизит.Тип.Типы() Цикл
МетаданныеТипа = Метаданные.НайтиПоТипу(ТипРеквизита);
Если МетаданныеТипа = Неопределено Тогда
Продолжить; // строка, число, дата и другие примитивные типы
КонецЕсли;
Класс = СтрРазделить(МетаданныеТипа.ПолноеИмя(), ".")[0];
Если СтрНайти(Допустимые, "," + Класс + ",") > 0 Тогда
Результат.Добавить(МетаданныеТипа);
КонецЕсли;
КонецЦикла;
КонецЦикла;
Возврат Результат;
КонецФункции
// Имена коллекций OData для текущего состава:
// "Справочник.Номенклатура" -> "Catalog_Номенклатура".
Функция КоллекцииOData() Экспорт
Префиксы = Новый Соответствие;
Префиксы.Вставить("Справочник", "Catalog");
Префиксы.Вставить("Документ", "Document");
Префиксы.Вставить("ЖурналДокументов", "DocumentJournal");
Префиксы.Вставить("Константа", "Constant");
Префиксы.Вставить("ПланОбмена", "ExchangePlan");
Префиксы.Вставить("ПланСчетов", "ChartOfAccounts");
Префиксы.Вставить("ПланВидовХарактеристик", "ChartOfCharacteristicTypes");
Префиксы.Вставить("ПланВидовРасчета", "ChartOfCalculationTypes");
Префиксы.Вставить("РегистрСведений", "InformationRegister");
Префиксы.Вставить("РегистрНакопления", "AccumulationRegister");
Префиксы.Вставить("РегистрБухгалтерии", "AccountingRegister");
Префиксы.Вставить("РегистрРасчета", "CalculationRegister");
Префиксы.Вставить("БизнесПроцесс", "BusinessProcess");
Префиксы.Вставить("Задача", "Task");
Результат = Новый Массив;
Для Каждого ОбъектМД Из ПолучитьСоставСтандартногоИнтерфейсаOData() Цикл
Части = СтрРазделить(ОбъектМД.ПолноеИмя(), ".");
Префикс = Префиксы[Части[0]];
Если Префикс <> Неопределено Тогда // перечисления в список не берём
Результат.Добавить(Префикс + "_" + Части[1]);
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Изменения состава попадают в журнал регистрации с событием «Данные. Изменение состава стандартного интерфейса OData» — проверьте его, если интеграция перестала видеть коллекцию.
Внешняя система входит как пользователь базы, логин и пароль идут в заголовке Basic. Поэтому снаружи — только HTTPS, а пользователь отдельный:
odata_user) — кириллицу в Basic часть клиентов кодирует неверно; включена «Аутентификация 1С:Предприятия»;allowedOnly=true — вернутся только разрешённые записи.Корень https://1c.example.com/trade/odata/standard.odata/ показывает коллекции из состава, $metadata — их поля и типы с точными именами. То же и первые запросы — из командной строки:
# Адрес публикации; с -u без пароля curl спросит его сам
BASE='https://1c.example.com/trade/odata/standard.odata'
# Корень: коллекции, включённые в состав
curl -u odata_user -G "$BASE/" --data-urlencode '$format=json'
# Описание коллекций, полей и типов (XML)
curl -u odata_user "$BASE/\$metadata" -o metadata.xml
# Номенклатура: только элементы, нужные поля, первые пять
curl -u odata_user -G "$BASE/Catalog_Номенклатура" \
--data-urlencode '$format=json' \
--data-urlencode '$select=Ref_Key,Code,Description,Артикул' \
--data-urlencode '$filter=IsFolder eq false and DeletionMark eq false' \
--data-urlencode '$top=5'
# Срез последних регистра сведений: курсы валют на дату
curl -u odata_user -G "$BASE/InformationRegister_КурсыВалют/SliceLast(Period=datetime'2025-03-01T00:00:00')" \
--data-urlencode '$format=json'
Имя коллекции — английский класс объекта и имя из конфигуратора, не синоним:
| Что | Адрес от корня |
|---|---|
| Элементы справочника | Catalog_Номенклатура |
| Один элемент по GUID | Catalog_Номенклатура(guid'…') |
| Документы | Document_ЗаказКлиента |
| Строки табличной части | Document_ЗаказКлиента_Товары |
| Срез последних регистра сведений | InformationRegister_КурсыВалют/SliceLast() |
| Остатки регистра накопления | AccumulationRegister_ТоварыНаСкладах/Balance() |
Стандартные реквизиты — по-английски: Ref_Key, Code, Description, DeletionMark, IsFolder; у документов Number, Date, Posted; у строк LineNumber. Свои — как в конфигураторе. Ссылка Партнер приходит GUID-строкой (уникальный идентификатор) в Партнер_Key, а Партнер без суффикса — навигационное свойство для $expand. У составного типа есть парное поле с суффиксом _Type, перечисления передаются именами значений: "Статус": "НеСогласован".
| Параметр | Пример | Зачем |
|---|---|---|
$filter | Posted eq true and Date ge datetime'2025-03-01T00:00:00' | Отбор |
$select | Ref_Key,Number,Date | Только нужные поля |
$expand | Партнер | Связанный объект в той же выборке |
$top, $skip | $top=500&$skip=1000 | Порция выборки |
$orderby | Date desc | Сортировка |
$inlinecount | allpages | Общее число записей в odata.count |
$format | json | JSON вместо Atom XML |
allowedOnly | true | Только записи, разрешённые RLS |
В $filter работают eq, ne, gt, ge, lt, le, and, or, not и функции substringof, startswith, endswith. Строки пишут в апострофах (апостроф внутри удваивается), даты — datetime'…', ссылки — guid'…'. Виртуальные таблицы регистров (Balance, Turnovers, BalanceAndTurnovers, SliceLast, SliceFirst) принимают Period или StartPeriod и EndPeriod, а также Condition и Dimensions.
Запись идёт через объектную модель: срабатывают ПередЗаписью, ПриЗаписи, подписки на события, при проведении — ОбработкаПроведения. Логика формы (заполнение по умолчанию, пересчёт сумм) не выполняется.
Ref_Key и номером. Табличная часть идёт в теле массивом строк.Коллекция(guid'…') меняет только переданные поля.…/Post проводит документ, на …/Unpost — отменяет проведение; PostingModeOperational=true — оперативное проведение."DeletionMark": true.1C_OData-DataLoadMode: true пишет в режиме загрузки (ОбменДанными.Загрузка = Истина): проверки с Если ОбменДанными.Загрузка Тогда Возврат пропускаются.# Создать заказ: тело в order.json (UTF-8), в ответе - документ с Ref_Key и номером
curl -u "odata_user:$ODATA_PASSWORD" -X POST "$BASE/Document_ЗаказКлиента?\$format=json" \
-H 'Content-Type: application/json' --data-binary @order.json
# Изменить реквизит: в теле только то, что меняется
curl -u "odata_user:$ODATA_PASSWORD" -X PATCH \
"$BASE/Document_ЗаказКлиента(guid'<Ref_Key>')?\$format=json" \
-H 'Content-Type: application/json' -d '{"Комментарий": "Оплачен на сайте"}'
# Провести документ (неоперативно)
curl -u "odata_user:$ODATA_PASSWORD" -X POST \
"$BASE/Document_ЗаказКлиента(guid'<Ref_Key>')/Post?PostingModeOperational=false"
Тело заказа клиента УТ 11 (order.json). Обязательные поля задаёт проверка заполнения документа, поэтому заказ с сайта обычно создают непроведённым, а проводит его менеджер:
{
"Date": "2025-03-14T10:00:00",
"Организация_Key": "<GUID организации>",
"Партнер_Key": "<GUID партнёра>",
"Контрагент_Key": "<GUID контрагента>",
"Соглашение_Key": "<GUID соглашения>",
"Склад_Key": "<GUID склада>",
"Валюта_Key": "<GUID валюты>",
"ХозяйственнаяОперация": "РеализацияКлиенту",
"Статус": "НеСогласован",
"ЦенаВключаетНДС": true,
"Комментарий": "Заказ с сайта",
"Товары": [
{"Номенклатура_Key": "<GUID товара>", "КоличествоУпаковок": 2,
"Количество": 2, "Цена": 1500, "Сумма": 3000}
]
}
Для обмена OData между базами 1С клиент пишут на HTTPСоединение и только на сервере: КодироватьСтроку и ВызватьHTTPМетод в тонком клиенте недоступны. Место — общий модуль с флажком «Сервер» или модуль объекта обработки. Об авторизации подробнее — в статье HTTP-запрос из 1С.
// Общий модуль с флажком «Сервер» или модуль объекта обработки.
// Пароль в код не пишите: берите его из настроек или безопасного хранилища.
// Параметры подключения к публикации OData.
// Сервер - "1c.example.com" (без протокола); Публикация - имя публикации, "trade".
Функция ПодключениеOData(Сервер, Публикация, Пользователь, Пароль, HTTPS = Истина) Экспорт
ЗащищенноеСоединение = ?(HTTPS, Новый ЗащищенноеСоединениеOpenSSL(), Неопределено);
Подключение = Новый Структура;
Подключение.Вставить("Корень", "/" + Публикация + "/odata/standard.odata/");
Подключение.Вставить("Соединение", Новый HTTPСоединение(Сервер, ?(HTTPS, 443, 80),
Пользователь, Пароль, , 60, ЗащищенноеСоединение));
Возврат Подключение;
КонецФункции
// Запрос к стандартному интерфейсу OData.
// Ресурс - "Catalog_Номенклатура", "Document_ЗаказКлиента(guid'...')/Post" и т.п.
// Параметры - Соответствие: ключ "$filter", "$select"..., значение - строка без кодирования.
// Данные - Структура или Соответствие для тела POST и PATCH.
// Возвращает Соответствие из JSON-ответа или Неопределено, если ответ пустой.
Функция ВызватьOData(Подключение, Метод, Ресурс, Параметры = Неопределено, Данные = Неопределено) Экспорт
// кириллицу и пробелы кодируем, а скобки, апострофы и "/" оставляем как есть
Адрес = Подключение.Корень
+ КодироватьСтроку(Ресурс, СпособКодированияСтроки.URLВКодировкеURL)
+ "?$format=json";
Если Параметры <> Неопределено Тогда
Для Каждого Параметр Из Параметры Цикл
Адрес = Адрес + "&" + Параметр.Ключ + "="
+ КодироватьСтроку(Параметр.Значение, СпособКодированияСтроки.КодировкаURL);
КонецЦикла;
КонецЕсли;
Запрос = Новый HTTPЗапрос(Адрес);
Если Данные <> Неопределено Тогда
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, Данные);
Запрос.Заголовки.Вставить("Content-Type", "application/json");
Запрос.УстановитьТелоИзСтроки(Запись.Закрыть(), КодировкаТекста.UTF8,
ИспользованиеByteOrderMark.НеИспользовать);
КонецЕсли;
Ответ = Подключение.Соединение.ВызватьHTTPМетод(Метод, Запрос);
Текст = Строка(Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8));
Если Ответ.КодСостояния >= 300 Тогда
ВызватьИсключение СтрШаблон("OData %1 %2: HTTP %3. %4",
Метод, Ресурс, Ответ.КодСостояния, ТекстОшибкиOData(Текст));
КонецЕсли;
Если ПустаяСтрока(Текст) Тогда
Возврат Неопределено;
КонецЕсли;
Возврат ПрочитатьОтветOData(Текст);
КонецФункции
// Только в Соответствие: ключ "odata.metadata" не годится в имя свойства Структуры.
Функция ПрочитатьОтветOData(Текст)
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(Текст);
Результат = ПрочитатьJSON(Чтение, Истина);
Чтение.Закрыть();
Возврат Результат;
КонецФункции
// Текст из {"odata.error": {"code": "...", "message": {"lang": "ru", "value": "..."}}}.
Функция ТекстОшибкиOData(Текст)
Попытка
ОтветСОшибкой = ПрочитатьОтветOData(Текст);
Возврат ОтветСОшибкой["odata.error"]["message"]["value"];
Исключение
// 401 и 404 веб-сервера приходят не в JSON, а текстом или HTML
Возврат Лев(СокрЛП(Текст), 300);
КонецПопытки;
КонецФункции
Примеры: чтение с отбором и $expand, создание заказа клиента, изменение и проведение.
// Примеры вызовов (тот же модуль). Имена реквизитов - из УТ 11;
// в другой конфигурации сверьте их с $metadata.
// Номенклатура по артикулу: только элементы, без помеченных на удаление.
Функция НоменклатураПоАртикулу(Подключение, Артикул) Экспорт
Параметры = Новый Соответствие;
Параметры.Вставить("$filter", СтрШаблон(
"Артикул eq '%1' and IsFolder eq false and DeletionMark eq false",
СтрЗаменить(Артикул, "'", "''"))); // апостроф внутри строки OData удваивается
Параметры.Вставить("$select", "Ref_Key,Code,Description,Артикул");
Параметры.Вставить("$top", "20");
Возврат ВызватьOData(Подключение, "GET", "Catalog_Номенклатура", Параметры)["value"];
КонецФункции
// Проведённые заказы клиентов за период с наименованием партнёра.
Функция ЗаказыЗаПериод(Подключение, Начало, Окончание) Экспорт
Параметры = Новый Соответствие;
Параметры.Вставить("$filter", СтрШаблон(
"Posted eq true and Date ge datetime'%1' and Date le datetime'%2'",
XMLСтрока(НачалоДня(Начало)), XMLСтрока(КонецДня(Окончание))));
Параметры.Вставить("$expand", "Партнер");
Параметры.Вставить("$select", "Ref_Key,Number,Date,СуммаДокумента,Партнер/Description");
Параметры.Вставить("$orderby", "Date");
Возврат ВызватьOData(Подключение, "GET", "Document_ЗаказКлиента", Параметры)["value"];
КонецФункции
// Заказ клиента с сайта: создаётся непроведённым, в статусе «Не согласован».
// Ключи - Структура с GUID-строками: Организация, Партнер, Контрагент,
// Соглашение, Склад, Валюта. Товары - Массив структур Номенклатура, Количество, Цена.
Функция СоздатьЗаказКлиента(Подключение, Ключи, Товары) Экспорт
Заказ = Новый Структура;
Заказ.Вставить("Date", XMLСтрока(ТекущаяДатаСеанса()));
Заказ.Вставить("Организация_Key", Ключи.Организация);
Заказ.Вставить("Партнер_Key", Ключи.Партнер);
Заказ.Вставить("Контрагент_Key", Ключи.Контрагент);
Заказ.Вставить("Соглашение_Key", Ключи.Соглашение);
Заказ.Вставить("Склад_Key", Ключи.Склад);
Заказ.Вставить("Валюта_Key", Ключи.Валюта);
Заказ.Вставить("ХозяйственнаяОперация", "РеализацияКлиенту"); // перечисление - именем значения
Заказ.Вставить("Статус", "НеСогласован");
Заказ.Вставить("ЦенаВключаетНДС", Истина);
Заказ.Вставить("Комментарий", "Заказ с сайта");
Строки = Новый Массив;
Для Каждого Товар Из Товары Цикл
НоваяСтрока = Новый Структура;
НоваяСтрока.Вставить("Номенклатура_Key", Товар.Номенклатура);
НоваяСтрока.Вставить("КоличествоУпаковок", Товар.Количество);
НоваяСтрока.Вставить("Количество", Товар.Количество);
НоваяСтрока.Вставить("Цена", Товар.Цена);
НоваяСтрока.Вставить("Сумма", Товар.Количество * Товар.Цена);
Строки.Добавить(НоваяСтрока);
КонецЦикла;
Заказ.Вставить("Товары", Строки); // табличная часть - массивом внутри документа
Создан = ВызватьOData(Подключение, "POST", "Document_ЗаказКлиента", , Заказ);
Возврат Создан["Ref_Key"];
КонецФункции
// PATCH: меняются только переданные поля, остальные остаются как были.
Процедура ИзменитьОбъектOData(Подключение, Коллекция, Идентификатор, Изменения) Экспорт
ВызватьOData(Подключение, "PATCH",
СтрШаблон("%1(guid'%2')", Коллекция, Идентификатор), , Изменения);
КонецПроцедуры
// Проведение - POST на функцию Post; отмена проведения - Unpost.
Процедура ПровестиДокументOData(Подключение, Коллекция, Идентификатор, Оперативно = Ложь) Экспорт
Параметры = Новый Соответствие;
Параметры.Вставить("PostingModeOperational", ?(Оперативно, "true", "false"));
ВызватьOData(Подключение, "POST",
СтрШаблон("%1(guid'%2')/Post", Коллекция, Идентификатор), Параметры);
КонецПроцедуры
URLВКодировкеURL кодирует кириллицу в имени коллекции, но оставляет скобки, апострофы и /: …(guid'…')/Post остаётся адресом.КодировкаURL целиком, с пробелами и апострофами.ПрочитатьJSON(Чтение, Истина) читает в соответствие: ключ odata.metadata с точкой не годится в имя свойства структуры (подробнее — JSON в 1С).value, одиночный объект и результат POST — без обёртки, отсюда Создан["Ref_Key"].odata.error.message.value.$expand=Партнер с Партнер/Description в $select — наименование партнёра без второго запроса; XMLСтрока(Дата) — формат для datetime'…'.Тексты — из ресурсов платформы 8.3.27 и кода БСП, в других версиях формулировки могут отличаться.
default.vrd нет standardOdata enable="true".Authorization.allowedOnly=true.datetime, GUID без guid.$filter=Ref_Key eq guid'…'.Document_…_Товары.ПередЗаписью заказа не даёт провести его при включённых автоматических скидках и СкидкиРассчитаны = false. Передайте "СкидкиРассчитаны": true, если скидку посчитал сайт, или проводите заказ из формы.Нет. Публикация — настройка веб-сервера, состав хранится в данных базы и задаётся формой БСП или внешней обработкой. Недостающую роль добавляют расширением.
Нет. В состав включается объект целиком, право «Чтение» тоже задаётся на объект, а $select не мешает запросить остальное. Если часть полей показывать нельзя, нужен HTTP-сервис.
$orderby=Code&$top=500&$skip=0, затем $skip=500, 1000 и далее, пока value не придёт пустым. Общее число записей вернёт $inlinecount=allpages. И задавайте $select, иначе каждый элемент придёт со всеми реквизитами.
Для простых сценариев — да. Но регистрации изменений и квитирования в OData нет: новое придётся отбирать по датам. Для регулярной синхронизации надёжнее планы обмена — см. способы обмена данными 1С.
Если интеграцию нужно довести до рабочего состояния — HTTPS, роль под конкретные объекты, клиент с обработкой ошибок, — её быстрее поручить программисту: услуги по доработке 1С, для интернет-магазина — обмен 1С с сайтом. Разместите задачу бесплатно: оценка бесплатная, ответ в течение рабочего дня.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.