При интеграции сторонних систем с облачной версией 1С (сервис 1C:Fresh) разработчики часто используют стандартный интерфейс OData. Это удобный REST API, позволяющий получать и записывать данные без написания сложного кода интеграции на стороне 1С. Однако при попытке отправить данные (выполнить POST, PATCH или DELETE-запрос) программисты часто сталкиваются с ошибкой 403 Forbidden, в то время как GET-запросы на чтение работают абсолютно корректно. Рассмотрим подробно, почему возникает эта ошибка и как правильно настроить систему для успешной записи данных.
Если вы изучаете старые ветки форумов (до конца 2022 года), вы можете встретить категоричные утверждения о том, что запись через OData в облаке 1С:Фреш в принципе невозможна и заблокирована администраторами сервиса из соображений безопасности. Действительно, долгое время интерфейс OData во «Фреше» работал исключительно в режиме «только чтение». Любые попытки модификации данных извне блокировались на уровне веб-сервера облака с возвратом статуса 403 Forbidden.
Ситуация изменилась: на сегодняшний день фирма «1С» официально разрешила и поддерживает полнофункциональный интерфейс OData (включая запись) в облачном сервисе 1С:Фреш. Тем не менее, настроить его работу без ошибок получается не сразу. Разберем основные технические причины появления ошибки 403 и способы их устранения.
Проанализируем интересное поведение системы, которое часто сбивает разработчиков с толку. При замене протокола в URL-адресе с https:// на http:// ошибка 403 Forbidden пропадает, но документ все равно не создается, а сервер в ответ присылает список существующих документов (как при обычном GET-запросе).
Выясним причину этого феномена. Сервис 1С:Фреш работает строго по защищенному протоколу HTTPS. Когда вы отправляете POST-запрос на небезопасный URL (начинающийся с http://), веб-сервер облака не отвергает его сразу, а возвращает HTTP-перенаправление (редирект со статусом 301 или 302) на защищенный адрес https://....
Большинство современных HTTP-клиентов (включая PHP Guzzle-клиент, используемый в примере) по умолчанию обрабатывают этот редирект автоматически. Но в целях безопасности при обработке редиректа для метода POST многие клиенты принудительно преобразуют его в GET-запрос. В результате до 1С доходит не ваша попытка создать документ, а обычный запрос на получение списка документов. Именно поэтому вы получаете успешный ответ со списком данных, но физически в базе ничего не создается.
Решение: всегда используйте исключительно протокол HTTPS во всех запросах к API.
Перейдем к настройкам прав в самой информационной базе 1С. Даже если вы включили необходимые объекты метаданных в настройках стандартного интерфейса OData (через меню Администрирование -> Синхронизация данных -> Настройки стандартного интерфейса OData), этого недостаточно для выполнения записи.
Запросы к OData выполняются в контексте определенного пользователя 1С, учетные данные которого передаются в заголовке авторизации (Basic Auth). Проверим следующие настройки:
Document_РеализацияТоваровУслуг). Одного лишь включения документа в состав OData недостаточно — права роли 1С имеют наивысший приоритет.При работе с OData в локальных (on-premise) базах разработчики часто используют заголовок 1C_OData-DataLoadMode: true. Этот режим аналогичен свойству ОбменДанными.Загрузка = Истина в коде 1С. Он позволяет записывать объекты в обход стандартных бизнес-проверок конфигурации, подписок на события и контроля остатков.
В облаке 1С:Фреш использование заголовка 1C_OData-DataLoadMode строго запрещено! Любой запрос, содержащий этот заголовок, автоматически блокируется веб-сервером сервиса и возвращает ошибку 403 Forbidden. Запись данных в облаке должна выполняться исключительно по стандартным бизнес-правилам прикладного решения.
Поскольку 1С:Фреш является мультиарендной (облачной) платформой, в ней критически важно правильно адресовать запросы к конкретной области данных (конкретному клиенту). Идентификатор области данных должен обязательно присутствовать в URL.
Посмотрим на пример правильного URL для отправки POST-запроса:
https://1cfresh.com/a/ea/1020368/odata/standard.odata/Document_РеализацияТоваровУслуг?$format=json
Здесь число 1020368 — это уникальный номер вашей области данных (конкретного приложения в личном кабинете Фреша). Убедитесь, что этот номер указан верно.
Напишем правильный класс для отправки данных, учитывающий все вышеперечисленные нюансы безопасности и синтаксиса OData в 1С:Фреш.
class OneCData {
private $client;
private $server;
public function __construct($login, $password, $tenantId) {
// Инициализируем Guzzle-клиент с базовой авторизацией
// Важно: работаем только по HTTPS протоколу!
$this->client = new \GuzzleHttp\Client([
'auth' => [$login, $password],
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json; charset=utf-8'
]
]);
// Формируем URL с указанием области данных (разделителя)
$this->server = "https://1cfresh.com/a/ea/{$tenantId}/odata/standard.odata";
}
public function createObject($entityName, $data) {
$url = "{$this->server}/{$entityName}?\$format=json";
try {
// Отправляем POST-запрос
$response = $this->client->request('POST', $url, [
'json' => $data,
'http_errors' => true // Guzzle выбросит исключение при ошибках 4xx/5xx
]);
$content = $response->getBody()->getContents();
return json_decode($content, true);
} catch (\GuzzleHttp\Exception\ClientException $e) {
// Перехватываем ошибки клиента (включая 403 Forbidden)
$response = $e->getResponse();
$errorDetails = $response ? $response->getBody()->getContents() : $e->getMessage();
throw new \Exception("Ошибка OData (Client Error): " . $errorDetails);
} catch (\Exception $e) {
throw new \Exception("Системная ошибка запроса: " . $e->getMessage());
}
}
}
При заполнении JSON для создания сложного документа (например, Реализации товаров и услуг) необходимо передавать GUID-идентификаторы существующих в базе справочников (Организация, Контрагент, Склад, Номенклатура). Обратите внимание на структуру табличной части Товары, которая передается в виде массива объектов.
$newInvoice = [
'Posted' => false, // Документ будет создан не проведенным (черновик)
'Number' => 'РН-100',
'Date' => date('Y-m-d') . 'T' . date('H:i:s'),
'СпособЗачетаАвансов' => 'Автоматически',
'ВидОперации' => 'Товары',
'Организация_Key' => '3742dc60-41b2-11ea-d689-fa163e51b5f9', // GUID организации
'Контрагент_Key' => '478e193c-4332-11ea-c09e-fa163e51b5f9', // GUID контрагента
'Склад_Key' => '37d7fcf8-f79f-11e9-829c-0242c0a8c005', // GUID склада
'ВалютаДокумента_Key' => 'ee8de508-f79e-11e9-a889-0242c0a8c005', // GUID валюты
'Товары' => [
[
'LineNumber' => 1,
'Номенклатура_Key' => '48ddeef2-4297-11ea-c09e-fa163e51b5f9', // Указываем GUID номенклатуры
'Количество' => 2,
'Цена' => 600,
'Сумма' => 1200,
'СтавкаНДС' => 'БезНДС',
'СуммаНДС' => 0
]
],
'СуммаДокумента' => 1200
];
try {
$oneC = new OneCData('api_user', 'strong_password', '1020368');
$result = $oneC->createObject('Document_РеализацияТоваровУслуг', $newInvoice);
echo "Документ успешно создан! Идентификатор в 1С: " . $result['Ref_Key'];
} catch (\Exception $e) {
echo "Не удалось создать документ: " . $e->getMessage();
}
Если вы продолжаете получать ошибку доступа, пройдите последовательно по следующим пунктам:
1C_OData-DataLoadMode.Document_РеализацияТоваровУслуг, а не document_реализациятоваровуслуг).