onec

n8n community node for 1C:Enterprise AI-powered MSP integration. By KodYaz (www.kodyaz.az)

Package Information

Released: 4 mo ago
Downloads: 18 weekly / 2,400 monthly
Latest Version: 0.3.3
Author: KodYaz

Documentation

n8n-nodes-onec — 1C Kodyaz İnteqrasiya Node-u

n8n üçün xüsusi 1C (1C:Enterprise) inteqrasiya node paketi — n8n-nodes-onec. 1C bazası ilə işləmək üçün bütün alətləri təmin edir: kəşf, oxu, izləmə və toplu emal.


📋 Mündəricat

  1. Ümumi Baxış
  2. Quraşdırma
  3. Credential Konfiqurasiyası
  4. Node Əməliyyatları
  5. Parametrlər Cədvəli
  6. 1C-də Nələr Edilməlidir
  7. 1C API Spesifikasiya
  8. İnkişaf
  9. Əlaqə
  10. Lisenziya

🔭 Ümumi Baxış

Bu paket n8n ilə 1C:Enterprise serveri arasında körpü qurur. 1C Kodyaz vahid node-u bütün trigger və action əməliyyatlarını özündə birləşdirir — 9 əməliyyat təmin edir:

  • Metadata Kəşfi — 1C bazasındakı bütün obyektləri avtomatik aşkarlayır
  • Məlumat Oxu — Səhifələmə və tarix filtri ilə istənilən obyekti oxuyur
  • Sayma — Sürətli element sayı (data yüklənmir)
  • JSON Strukturunu Öyrən — Obyektin sahə adlarını və nümunə dəyərlərini göstərir
  • Yarat / Yenilə — POST ilə 1C-də soraqça elementi yaradır/yeniləyir, sənəd yaradır
  • Xüsusi Sorğu — İstənilən endpoint-ə fərdi GET sorğusu
  • Sənəd İzləmə — Yeni sənədləri polling ilə aşkarlayır (Schedule Trigger ilə)
  • Metadata İzləmə — Obyekt tiplərində dəyişiklikləri aşkarlayır (Schedule Trigger ilə)
  • Toplu Oxu — Birdən çox obyekti ardıcıl oxuyub vahid nəticədə birləşdirir

Server: http://SIZIN_SERVER/SIZIN_BAZA/hs/api
Auth: HTTP Basic Authentication (istifadəçi adı mütləq, şifrə boş qala bilər)


📦 Quraşdırma

n8n Community Nodes (tövsiyə olunur)

n8n UI-da Settings → Community Nodes → Install → n8n-nodes-onec yazıb yükləyin.

Lokal Development

npm install n8n-nodes-onec
# və ya development üçün:
npx @n8n/node-cli new --source n8n-nodes-onec

Docker

docker run -d \
  -v ~/.n8n:/home/node/.n8n \
  -e N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true \
  n8nio/n8n

Sonra n8n UI-da Settings → Community Nodes → n8n-nodes-onec quraşdırın.


🔑 Credential Konfiqurasiyası

1C API Credential

n8n UI-da Credentials → 1C API yaradın:

Sahə Təsvir Nümunə
Base URL 1C HTTP Servisin tam URL ünvanı http://SERVER/BAZA/hs/api
User 1C istifadəçi adı (mütləq) Administrator
Password 1C şifrəsi şifrəniz

⚠️ Təhlükəsizlik: 1C serverində mütləq şifrə təyin edin. Boş şifrə yalnız lokal/test mühitləri üçündür. Production mühitdə güclü şifrə istifadəsi tövsiyə olunur.

Test düyməsi /GetMetadata endpointini çağıraraq credential-i yoxlayır.


🔐 Təhlükəsizlik Tövsiyəsi: Minimum İmkanlı 1C İstifadəçiləri

Bu node-un istifadə etdiyi 1C credential-ı, 1C tərəfdə həmin istifadəçiyə təyin olunmuş bütün rol və icazələrlə işləyir — node özü əlavə məhdudiyyət tətbiq etmir.

Tövsiyə olunan praktika:

  • Hər workflow və ya operator üçün ayrıca, məqsədə uyğun 1C istifadəçisi yaradın. Məsələn, kontragent borclarını izləyən workflow üçün yalnız müvafiq reyestrə oxuma icazəsi olan istifadəçi yetərlidir.
  • Administrator və ya geniş hüquqlu hesablardan inteqrasiya üçün istifadə etməyin. Bu cür hesab sızarsa və ya workflow səhvən başqa obyektə sorğu göndərsə, məhdudiyyət yalnız 1C-nin rol sistemi tərəfindən qoyulur.
  • 1C-də rol yaratarkən, yalnız getEntities/getCount/getEntitySchema üçün oxuma, createOrUpdate üçün isə əlavə olaraq yazma icazəsi verin — heç vaxt defolt olaraq tam icazə verməyin.
  • İcazəsi olmayan obyektə sorğu zamanı 1C 500 status kodu ilə cavab verir (səbəb 1C-nin daxili icazə xətasıdır). n8n-də bu, ümumi "Request failed with status code 500" mesajı kimi görünür — bu, node-un xətası deyil, gözlənilən rədd cavabıdır.

Production-da boş şifrə istifadə etməyin. Şifrəni boş qoymaq yalnız lokal sınaq mühitləri üçün nəzərdə tutulub.


🔧 Node Əməliyyatları

Node "1C Kodyaz" vahid node kimi bütün əməliyyatları özündə birləşdirir — həm action (oxu, yaz, kəşf), həm də trigger/watch (izləmə) funksiyaları. Data & Storage / Transform / Development kateqoriyalarında yerləşir. Watch əməliyyatları n8n-in Schedule Trigger node-u ilə birlikdə işləyir.

1. Metadata Əldə Et (getMetadata)

Bazadakı bütün aktiv obyektləri kəşf edir. Hər hansı digər əməliyyatdan əvvəl çağırın.

Endpoint: GET /GetMetadata
Cavab: [{tip, ad, sinonim}] massivi

2. Məlumatları Oxu (getEntities)

Seçilmiş obyektin məlumatlarını səhifələmə və tarix filtrləri ilə oxuyur.

Endpoint: GET /{tip}/{ad}?page=N&pageSize=M
Parametrlər: entityPath, returnAll, page, pageSize, start, end

3. Sayını Öyrən (getCount)

Seçilmiş obyektin yalnız ümumi element sayını qaytarır.

Endpoint: GET /{tip}/{ad}?count=TRUE
Cavab: {total: N}

4. JSON Strukturunu Öyrən (getEntitySchema)

Seçilmiş 1C obyektindən ilk qeydi oxuyaraq onun JSON sahə adlarını, tiplərini və nümunə dəyərlərini qaytarır. POST body hazırlamazdan əvvəl obyektin strukturunu öyrənmək üçün istifadə olunur.

Endpoint: GET /{tip}/{ad}?pageSize=N (N=1-5)
Parametrlər: entityPath, schemaSampleSize
Cavab: { _entity, _schema: { fieldName: { type, example } }, _sampleRecords }

5. Yarat / Yenilə (createOrUpdate)

POST sorğusu ilə 1C-də yeni soraqça elementi yaradır və ya mövcud elementi yeniləyir, yeni sənəd yaradır. JSON body strukturu üçün əvvəlcə JSON Strukturunu Öyrən əməliyyatını istifadə edin.

Endpoint: POST /{tip}/{ad} (1C-də PostDataPOST handler-i)
Parametrlər: entityPath, jsonBody
Cavab: { _entity, _operation, _result }

POST body nümunəsi — Soraqça yaratma:

{"Наименование": "Yeni Müştəri", "Код": "001"}

POST body nümunəsi — Sənəd yaratma:

{
  "Дата": "2026-06-17T15:30:00",
  "Контрагент": "Əhməd MMC",
  "Mallar": [
    { "Номенклатура": "Alma", "Количество": 10, "Цена": 2.5 }
  ]
}

6. Xüsusi Sorğu — Raw Request (rawRequest)

İstənilən 1C API endpointinə fərdi GET sorğusu göndərir.

Endpoint: İstənilən URL (manual daxil edilir)
Parametrlər: rawUrl, rawQueryParams (key=value cütləri)

7. Yeni Sənədləri İzlə (watchDocuments)

Schedule Trigger ilə birlikdə istifadə olunur. Hər yoxlamada son yoxlama tarixindən sonra yaranan yeni qeydləri aşkarlayır.

Parametrlər: entityPath, watchPageSize, lookbackMinutes, silentOnError

8. Metadata Dəyişikliklərini İzlə (watchMetadata)

Schedule Trigger ilə birlikdə istifadə olunur. Metadata-da yeni və ya silinmiş obyekt tiplərini aşkarlayır.

Parametrlər: silentOnError

9. Çoxlu Obyekt Oxu — Batch (batchRead)

Birdən çox 1C obyektini ardıcıl oxuyur, hər obyektə etiket əlavə edir və nəticələri vahid çıxışda birləşdirir.

Parametrlər: entities[], continueOnError

📊 Parametrlər Cədvəli

Əməliyyat (operation)

Dəyər Ad Təsvir
getMetadata Metadata Əldə Et Bütün obyekt tiplərini kəşf et
getEntities Məlumatları Oxu Obyekt məlumatlarını səhifəli oxu
getCount Sayını Öyrən Yalnız ümumi element sayı
getEntitySchema JSON Strukturunu Öyrən Obyektin sahə strukturunu göstər
createOrUpdate Yarat / Yenilə POST ilə yeni qeyd yarat və ya yenilə
rawRequest Xüsusi Sorğu (Raw Request) Fərdi GET sorğusu
watchDocuments Yeni Sənədləri İzlə Polling ilə yeni sənəd aşkarla
watchMetadata Metadata Dəyişikliklərini İzlə Yeni/silinmiş obyektləri izlə
batchRead Çoxlu Obyekt Oxu (Batch) Çoxlu obyekti toplu oxu

Obyekt (entityPath) — Resource Locator

Rejim Təsvir
Siyahıdan Seç GetMetadata nəticələrindən axtarışlı dropdown (5 dəq TTL keş)
Manual Daxil Et /Tip/Ad formatında birbaşa daxil etmə

getEntities Parametrləri

Parametr Tip Default Təsvir
returnAll boolean false Bütün səhifələri avtomatik gəz (max 10,000 səhifə)
page number (≥1) 1 Manual səhifə seçimi
pageSize number (1-200) 20 Səhifə başına element sayı
start dateTime (boş) Başlanğıc tarix filteri (ISO format)
end dateTime (boş) Bitmə tarixi filteri (ISO format)

🖥 1C-də Nələr Edilməlidir

Bu bölmə 1C:Enterprise serverində Universal REST API HTTP Servisinin necə qurulacağını addım-addım izah edir.

Addım 1: HTTP Servisin Yaradılması

  1. 1C Konfiqurator-u açın
  2. Ümumi → HTTP Servisləri bölməsinə keçin
  3. Yeni HTTP Servis əlavə edin:
    • Ad: UniversalApi
    • Sinonim: Universal 1C API
    • Kök URL: hs/api

Addım 2: Şablonların (URL Templates) Əlavə Olunması

HTTP Servisin içində üç şablon yaradın:

Şablon Metod Handler Funksiyası
/GetMetadata GET GetMetadataGET
/{tip}/{ad} GET GetDataGET
/PostData POST PostDataPOST

Addım 3: Handler Funksiyalarının Tam Kodu

Aşağıdakı 3 funksiyanı HTTP Servisin moduluna əlavə edin:

3.1 GetMetadataGET — Kəşf endpointi

Функция GetMetadataGET(Запрос)
    Ответ = Новый HTTPСервисОтвет(200);
    МассивМетаданных = Новый Массив;
    
    // 1. Soraqçalar (Справочники)
    Для Каждого Справочник Из Метаданные.Справочники Цикл
        СтруктураО = Новый Структура;
        СтруктураО.Вставить("tip", "Справочник");
        СтруктураО.Вставить("ad", Справочник.Имя);
        СтруктураО.Вставить("sinonim", Справочник.Синоним);
        МассивМетаданных.Добавить(СтруктураО);
    КонецЦикла;
    
    // 2. Sənədlər (Документы)
    Для Каждого Документ Из Метаданные.Документы Цикл
        СтруктураО = Новый Структура;
        СтруктураО.Вставить("tip", "Документ");
        СтруктураО.Вставить("ad", Документ.Имя);
        СтруктураО.Вставить("sinonim", Документ.Синоним);
        МассивМетаданных.Добавить(СтруктураО);
    КонецЦикла;
    
    // 3. Məlumat Reyestrləri (РегистрыСведений)
    Для Каждого РегистрСведения Из Метаданные.РегистрыСведений Цикл
        СтруктураО = Новый Структура;
        СтруктураО.Вставить("tip", "РегистрСведений");
        СтруктураО.Вставить("ad", РегистрСведения.Имя);
        СтруктураО.Вставить("sinonim", РегистрСведения.Синоним);
        МассивМетаданных.Добавить(СтруктураО);
    КонецЦикла;
    
    // 4. Yığım Reyestrləri (РегистрыНакопления)
    Для Каждого РегистрНакопления Из Метаданные.РегистрыНакопления Цикл
        СтруктураО = Новый Структура;
        СтруктураО.Вставить("tip", "РегистрНакопления");
        СтруктураО.Вставить("ad", РегистрНакопления.Имя);
        СтруктураО.Вставить("sinonim", РегистрНакопления.Синоним);
        МассивМетаданных.Добавить(СтруктураО);
    КонецЦикла;
    
    // 5. JSON formatına çevirmə
    Попытка
        ЗаписьJSON = Новый ЗаписьJSON;
        ЗаписьJSON.УстановитьСтроку();
        ЗаписатьJSON(ЗаписьJSON, МассивМетаданных);
        СтрокаJSON = ЗаписьJSON.Закрыть();
        
        Ответ.Заголовки.Вставить("Content-type", "application/json; charset=utf-8");
        Ответ.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8);
    Исключение
        Ответ.КодСостояния = 400;
        Ответ.УстановитьТелоИзСтроки("Xəta: " + ОписаниеОшибки(), КодировкаТекста.UTF8);
    КонецПопытки;
    
    Возврат Ответ;
КонецФункции

3.2 GetDataGET — Data oxuma endpointi (TAM KOD)

Функция GetDataGET(Запрос)
    
    // ============================================================
    // UNIVERSAL 1C HTTP SERVICE — GET Handler (Tam Düzgün Versiya)
    // Soraqça + Sənəd + Məlumat Reyestri + Yığım Reyestri
    // ============================================================

    // 1. URL-dən obyektin növünü və adını oxuyuruq
    ТипОбъекта = Запрос.ПараметрыURL.Получить("tip"); 
    ИмяОбъекта = Запрос.ПараметрыURL.Получить("ad");    
    
    // Parametrlərin böyük/kiçik hərflərindən asılı olmaması üçün default dəyərlər
    ParamPage      = 1;
    РазмерСтраницы = 20;
    ParamStart     = Неопределено;
    ParamEnd       = Неопределено;
    ParamTop       = Неопределено;
    ParamCount     = "FALSE";

    Для Каждого КлючИЗначение Из Запрос.ПараметрыЗапроса Цикл
        КлючВрег = Врег(КлючИЗначение.Ключ);
        Если КлючВрег = "PAGE" Тогда
            ParamPage = Макс(1, Число(КлючИЗначение.Значение));
        ИначеЕсли КлючВрег = "PAGESIZE" Тогда
            РазмерСтраницы = Мин(Макс(1, Число(КлючИЗначение.Значение)), 200);
        ИначеЕсли КлючВрег = "START" Тогда
            ParamStart = КлючИЗначение.Значение;
        ИначеЕсли КлючВрег = "END" Тогда
            ParamEnd = КлючИЗначение.Значение;
        ИначеЕсли КлючВрег = "TOP" Тогда
            ParamTop = Число(КлючИЗначение.Значение);
        ИначеЕсли КлючВрег = "COUNT" Тогда
            ParamCount = Врег(Строка(КлючИЗначение.Значение));
        КонецЕсли;
    КонецЦикла;

    Если ЗначениеЗаполнено(ParamTop) Тогда
        ParamPage = 1;
        РазмерСтраницы = Мин(ParamTop, 200);
    КонецЕсли;
    
    Ответ = Новый HTTPСервисОтвет(200); 
    Ответ.Заголовки.Вставить("Content-type", "application/json; charset=utf-8");
                    
    // Giriş sahələrinin validasiyası
    Если Не ЗначениеЗаполнено(ТипОбъекта) Или Не ЗначениеЗаполнено(ИмяОбъекта) Тогда
        Ответ.КодСостояния = 400;
        Ответ.УстановитьТелоИзСтроки("{""error"": ""'tip' ve 'ad' parametrleri mutleqdir""}", КодировкаТекста.UTF8);
        Возврат Ответ;
    КонецЕсли;

    ВрегТип = Врег(ТипОбъекта);
    МДОбъект = Неопределено;
    Попытка
        Если ВрегТип = "СПРАВОЧНИК" Тогда             МДОбъект = Метаданные.Справочники[ИмяОбъекта];
        ИначеЕсли ВрегТип = "ДОКУМЕНТ" Тогда         МДОбъект = Метаданные.Документы[ИмяОбъекта];
        ИначеЕсли ВрегТип = "РЕГИСТРСВЕДЕНИЙ" Тогда   МДОбъект = Метаданные.РегистрыСведений[ИмяОбъекта];
        ИначеЕсли ВрегТип = "РЕГИСТРНАКОПЛЕНИЯ" Тогда МДОбъект = Метаданные.РегистрыНакопления[ИмяОбъекта];
        КонецЕсли;
    Исключение
        МДОбъект = Неопределено;
    КонецПопытки;

    Если МДОбъект = Неопределено Тогда
        Ответ.КодСостояния = 400;
        Ответ.УстановитьТелоИзСтроки("{""error"": ""Obyekt tapilmadi: " + ТипОбъекта + "." + ИмяОбъекта + """}", КодировкаТекста.UTF8);
        Возврат Ответ;
    КонецЕсли;
                    
    Попытка
        // 2. Dinamik olaraq sahələrin metaməlumatdan yığılması
        МассивПолей = Новый Массив;
        СтрокаСортировки = "";
        
        Если ВрегТип = "СПРАВОЧНИК" Тогда
            МассивПолей.Добавить("Т.Ссылка КАК Ссылка");
            МассивПолей.Добавить("Т.Код КАК Код");
            МассивПолей.Добавить("Т.Наименование КАК Наименование");
            Попытка
                Если МДОбъект.Иерархический Тогда
                    МассивПолей.Добавить("Т.Родитель КАК Родитель");
                    МассивПолей.Добавить("Т.ЭтоГруппа КАК ЭтоГруппа");
                КонецЕсли;
            Исключение
            КонецПопытки;
            МассивПолей.Добавить("Т.ПометкаУдаления КАК ПометкаУдаления");
            МассивПолей.Добавить("Т.Предопределенный КАК Предопределенный");
            СтрокаСортировки = " УПОРЯДОЧИТЬ ПО Т.Ссылка";
            
        ИначеЕсли ВрегТип = "ДОКУМЕНТ" Тогда
            МассивПолей.Добавить("Т.Ссылка КАК Ссылка");
            МассивПолей.Добавить("Т.Дата КАК Дата");
            МассивПолей.Добавить("Т.Номер КАК Номер");
            МассивПолей.Добавить("Т.ПометкаУдаления КАК ПометкаУдаления");
            МассивПолей.Добавить("Т.Проведен КАК Проведен");
            СтрокаСортировки = " УПОРЯДОЧИТЬ ПО Т.Дата УБЫВ, Т.Ссылка УБЫВ";
            
        ИначеЕсли ВрегТип = "РЕГИСТРСВЕДЕНИЙ" Тогда
            Попытка
                Если МДОбъект.Периодический <> Метаданные.Свойства.ПериодичностьРегистраСведений.Непериодический Тогда
                    МассивПолей.Добавить("Т.Период КАК Период");
                    СтрокаСортировки = " УПОРЯДОЧИТЬ ПО Т.Период УБЫВ";
                КонецЕсли;
            Исключение
            КонецПопытки;
            
            Для Каждого Измерение Из МДОбъект.Измерения Цикл МассивПолей.Добавить("Т." + Измерение.Имя + " КАК " + Измерение.Имя); КонецЦикла;
            Для Каждого Ресурс Из МДОбъект.Ресурсы Цикл МассивПолей.Добавить("Т." + Ресурс.Имя + " КАК " + Ресурс.Имя); КонецЦикла;
            
            Если ПустаяСтрока(СтрокаСортировки) И МДОбъект.Измерения.Количество() > 0 Тогда
                СтрокаСортировки = " УПОРЯДОЧИТЬ ПО Т." + МДОбъект.Измерения[0].Имя;
            КонецЕсли;
            
        ИначеЕсли ВрегТип = "РЕГИСТРНАКОПЛЕНИЯ" Тогда
            МассивПолей.Добавить("Т.Период КАК Период");
            МассивПолей.Добавить("Т.Регистратор КАК Регистратор");
            МассивПолей.Добавить("Т.НомерСтроки КАК НомерСтроки");
            МассивПолей.Добавить("Т.ВидДвижения КАК ВидДвижения");
            
            Для Каждого Измерение Из МДОбъект.Измерения Цикл МассивПолей.Добавить("Т." + Измерение.Имя + " КАК " + Измерение.Имя); КонецЦикла;
            Для Каждого Ресурс Из МДОбъект.Ресурсы Цикл МассивПолей.Добавить("Т." + Ресурс.Имя + " КАК " + Ресурс.Имя); КонецЦикла;
            СтрокаСортировки = " УПОРЯДОЧИТЬ ПО Т.Период УБЫВ, Т.Регистратор, Т.НомерСтроки";
        КонецЕсли;

        // Ümumi dinamik rekvizitlərin əlavə olunması
        Для Каждого Реквизит Из МДОбъект.Реквизиты Цикл
            ИмяРек = Реквизит.Имя;
            Если ВрегТип = "СПРАВОЧНИК" И (Врег(ИмяРек) = "КОД" Или Врег(ИмяРек) = "НАИМЕНОВАНИЕ" Или Врег(ИмяРек) = "РОДИТЕЛЬ" Или Врег(ИмяРек) = "ЭТОГРУППА") Тогда Продолжить; КонецЕсли;
            Если ВрегТип = "ДОКУМЕНТ" И (Врег(ИмяРек) = "ДАТА" Или Врег(ИмяРек) = "НОМЕР") Тогда Продолжить; КонецЕсли;
            МассивПолей.Добавить("Т." + ИмяРек + " КАК " + ИмяРек);
        КонецЦикла;

        СтрокаПолей = СтрСоединить(МассивПолей, ", ");
        
        // Şərtlər bloku (ГДЕ)
        Условия = Новый Массив;
        ПараметрыДат = Новый Структура;
        
        Если ВрегТип = "ДОКУМЕНТ" Или ВрегТип = "РЕГИСТРНАКОПЛЕНИЯ" Или (ВрегТип = "РЕГИСТРСВЕДЕНИЙ" И МассивПолей.Найти("Т.Период КАК Период") <> Неопределено) Тогда
            ИмяПоляВремени = ?(ВрегТип = "ДОКУМЕНТ", "Дата", "Период");
            Если ЗначениеЗаполнено(ParamStart) Тогда
                Условия.Добавить("Т." + ИмяПоляВремени + " >= &StartDate");
                ПараметрыДат.Вставить("StartDate", XMLЗначение(Тип("Дата"), ParamStart));
            КонецЕсли;
            Если ЗначениеЗаполнено(ParamEnd) Тогда
                Условия.Добавить("Т." + ИмяПоляВремени + " <= &EndDate");
                ПараметрыДат.Вставить("EndDate", XMLЗначение(Тип("Дата"), ParamEnd));
            КонецЕсли;
        КонецЕсли;
        
        СтрокаУсловий = ?(Условия.Количество() > 0, " ГДЕ " + СтрСоединить(Условия, " И "), "");
        
        // 3. ÜMUMİ SAYIN TAPILMASI (COUNT)
        ЗапросСчет = Новый Запрос;
        ЗапросСчет.Текст = "ВЫБРАТЬ КОЛИЧЕСТВО(*) КАК Всего ИЗ " + ТипОбъекта + "." + ИмяОбъекта + " КАК Т" + СтрокаУсловий;
        Для Каждого Пара Из ПараметрыДат Цикл 
            ЗапросСчет.УстановитьПараметр(Пара.Ключ, Пара.Значение);
        КонецЦикла;
        
        ВсегоЗаписей = 0;
        ВыборкаСчет = ЗапросСчет.Выполнить().Выбрать();
        Если ВыборкаСчет.Следующий() Тогда ВсегоЗаписей = ВыборкаСчет.Всего; КонецЕсли;

        Если ParamCount = "TRUE" Тогда
            ЗаписьJSON = Новый ЗаписьJSON; ЗаписьJSON.УстановитьСтроку();
            ЗаписатьJSON(ЗаписьJSON, Новый Структура("total", ВсегоЗаписей));
            Ответ.УстановитьТелоИзСтроки(ЗаписьJSON.Закрыть(), КодировкаТекста.UTF8);
            Возврат Ответ;
        КонецЕсли;
        
        // 4. MULTI-VERSION PAGINATION ENGINE
        Смещение = (ParamPage - 1) * РазмерСтраницы;
        ЗапросБД = Новый Запрос;
        Для Каждого Пара Из ПараметрыДат Цикл ЗапросБД.УстановитьПараметр(Пара.Ключ, Пара.Значение); КонецЦикла;

        Если (ВрегТип = "СПРАВОЧНИК" Или ВрегТип = "ДОКУМЕНТ") И Смещение > 0 Тогда
            
            ЗапросОфсет = Новый Запрос;
            ЗапросОфсет.Текст = "ВЫБРАТЬ ПЕРВЫЕ " + Формат(Смещение, "ЧГ=0") + " Т.Ссылка КАК Ссылка ИЗ " + ТипОбъекта + "." + ИмяОбъекта + " КАК Т" + СтрокаУсловий + СтрокаСортировки;
            Для Каждого Пара Из ПараметрыДат Цикл ЗапросОфсет.УстановитьПараметр(Пара.Ключ, Пара.Значение); КонецЦикла;
            
            МассивПредыдущих = ЗапросОфсет.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
            СтрокаУсловийОснова = ?(СтрокаУсловий = "", " ГДЕ Т.Ссылка НЕ В (&МассивПредыдущих)", СтрокаУсловий + " И Т.Ссылка НЕ В (&МассивПредыдущих)");
            
            ТекстЗапроса = "ВЫБРАТЬ ПЕРВЫЕ " + Формат(РазмерСтраницы, "ЧГ=0") + " " + СтрокаПолей + " ИЗ " + ТипОбъекта + "." + ИмяОбъекта + " КАК Т" + СтрокаУсловийОснова + СтрокаСортировки;
            ЗапросБД.Текст = ТекстЗапроса;
            ЗапросБД.УстановитьПараметр("МассивПредыдущих", МассивПредыдущих);
            
        ИначеЕсли (ВрегТип = "РЕГИСТРСВЕДЕНИЙ" Или ВрегТип = "РЕГИСТРНАКОПЛЕНИЯ") Тогда
            MaksLimitРек = Смещение + РазмерСтраницы;
            ТекстЗапроса = "ВЫБРАТЬ ПЕРВЫЕ " + Формат(MaksLimitРек, "ЧГ=0") + " " + СтрокаПолей + " ИЗ " + ТипОбъекта + "." + ИмяОбъекта + " КАК Т" + СтрокаУсловий + СтрокаСортировки;
            ЗапросБД.Текст = ТекстЗапроса;
            
        Иначе
            ТекстЗапроса = "ВЫБРАТЬ ПЕРВЫЕ " + Формат(РазмерСтраницы, "ЧГ=0") + " " + СтрокаПолей + " ИЗ " + ТипОбъекта + "." + ИмяОбъекта + " КАК Т" + СтрокаУсловий + СтрокаСортировки;
            ЗапросБД.Текст = ТекстЗапроса;
        КонецЕсли;
        
        РезультатЗапроса = ЗапросБД.Выполнить();
        Выборка = РезультатЗапроса.Выбрать();
        Колонки = РезультатЗапроса.Колонки;
        СсылкаСуществует = (Колонки.Найти("Ссылка") <> Неопределено);
        
        МассивРезультата = Новый Массив;
        ТекущийИндекс = 0;
        
        // 5. Məlumatın struktura yığılması
        Пока Выборка.Следующий() Цикл
            
            Если (ВрегТип = "РЕГИСТРСВЕДЕНИЙ" Или ВрегТип = "РЕГИСТРНАКОПЛЕНИЯ") И ТекущийИндекс < Смещение Тогда
                ТекущийИндекс = ТекущийИндекс + 1;
                Продолжить;
            КонецЕсли;
            
            СтруктураЭлемента = Новый Структура;
            
            Для Каждого Колонка Из Колонки Цикл
                ИмяПоля = Колонка.Имя;
                ЗначениеПоля = Выборка[ИмяПоля];
                ТипЗначения = ТипЗнч(ЗначениеПоля);
                
                Если ЗначениеПоля = Null Или ЗначениеПоля = Неопределено Тогда
                    СтруктураЭлемента.Вставить(ИмяПоля, Неопределено);
                    Продолжить;
                КонецЕсли;
                
                Если ТипЗначения = Тип("Строка") Или ТипЗначения = Тип("Число") Или ТипЗначения = Тип("Булево") Или ТипЗначения = Тип("Дата") Тогда
                    СтруктураЭлемента.Вставить(ИмяПоля, ЗначениеПоля);
                    Продолжить;
                КонецЕсли;
                
                Попытка
                    МетаИнформация = Метаданные.НайтиПоТипу(ТипЗначения);
                    Если МетаИнформация <> Неопределено Или Метаданные.Перечисления.Содержит(МетаИнформация) Тогда
                        СтруктураЭлемента.Вставить(ИмяПоля, Строка(ЗначениеПоля));
                    Иначе
                        СтруктураЭлемента.Вставить(ИмяПоля, ?(ЗначениеЗаполнено(ЗначениеПоля), Строка(ЗначениеПоля), "")); 
                    КонецЕсли;
                Исключение
                    СтруктураЭлемента.Вставить(ИмяПоля, Строка(ЗначениеПоля));
                КонецПопытки;
            КонецЦикла;
            
            // Cədvəl hissələri (ТЧ)
            Если СсылкаСуществует Тогда
                Попытка
                    МетаОбъект = Метаданные.НайтиПоТипу(ТипЗнч(Выборка.Ссылка));
                    Если МетаОбъект <> Неопределено Тогда
                        Для Каждого ТЧ Из МетаОбъект.ТабличныеЧасти Цикл
                            ИмяТЧ = ТЧ.Имя;
                            МассивТЧ = Новый Массив;
                            ТаблицаТЧ = Выборка.Ссылка[ИмяТЧ].Выгрузить();
                            
                            Для Каждого СтрокаТЧ Из ТаблицаТЧ Цикл
                                СтруктураСтрокиТЧ = Новый Структура;
                                Для Каждого КолонкаТЧ Из ТаблицаТЧ.Колонки Цикл
                                    ИмяКолонкиТЧ = КолонкаТЧ.Имя;
                                    ЗначениеТЧ = СтрокаТЧ[ИмяКолонкиТЧ];
                                    
                                    Если ЗначениеТЧ = Null Или ЗначениеТЧ = Неопределено Тогда
                                        СтруктураСтрокиТЧ.Вставить(ИмяКолонкиТЧ, Неопределено);
                                    ИначеЕсли ТипЗнч(ЗначениеТЧ) = Тип("Строка") Или ТипЗнч(ЗначениеТЧ) = Тип("Число") Или ТипЗнч(ЗначениеТЧ) = Тип("Булево") Или ТипЗнч(ЗначениеТЧ) = Тип("Дата") Тогда
                                        СтруктураСтрокиТЧ.Вставить(ИмяКолонкиТЧ, ЗначениеТЧ);
                                    Иначе
                                        СтруктураСтрокиТЧ.Вставить(ИмяКолонкиТЧ, Строка(ЗначениеТЧ));
                                    КонецЕсли;
                                КонецЦикла;
                                МассивТЧ.Добавить(СтруктураСтрокиТЧ);
                            КонецЦикла;
                            
                            СтруктураЭлемента.Вставить(ИмяТЧ, МассивТЧ);
                        КонецЦикла;
                    КонецЕсли;
                Исключение
                КонецПопытки;
            КонецЕсли;
            
            МассивРезультата.Добавить(СтруктураЭлемента);
            ТекущийИндекс = ТекущийИндекс + 1;
        КонецЦикла;
        
        // 6. CAVABIN FORMATLAŞDIRILMASI (META + DATA)
        ВсегоСтраниц = Макс(1, Цел((ВсегоЗаписей + РазмерСтраницы - 1) / РазмерСтраницы));

        Мета = Новый Структура;
        Мета.Вставить("total",      ВсегоЗаписей);
        Мета.Вставить("page",       ParamPage);
        Мета.Вставить("pageSize",   РазмерСтраницы);
        Мета.Вставить("totalPages", ВсегоСтраниц);
        Мета.Вставить("count",      МассивРезультата.Количество());

        ОтветСтруктура = Новый Структура;
        ОтветСтруктура.Вставить("meta", Мета);
        ОтветСтруктура.Вставить("data", МассивРезультата);
        
        ЗаписьJSON = Новый ЗаписьJSON;
        ЗаписьJSON.УстановитьСтроку();
        ЗаписатьJSON(ЗаписьJSON, ОтветСтруктура);
        СтрокаJSON = ЗаписьJSON.Закрыть();
        
        Ответ.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8);
        
    Исключение
        Ответ.КодСостояния = 500;
        Ответ.УстановитьТелоИзСтроки("{""error"": """ + ОписаниеОшибки() + """}", КодировкаТекста.UTF8);
    КонецПопытки;
    
    Возврат Ответ;
КонецФункции

3.3 PostDataPOST — Məlumat yaratma/yeniləmə endpointi (POST)

Bu funksiya n8n-dən gələn POST sorğusunu emal edir. Soraq kitabçalarında (Справочник) adla axtarış edib tapar, tapmasa yaradar, tapsa yeniləyər. Sənədlər (Документ) üçün yeni sənəd yaradıb təsdiqləyər, əlaqəli soraq kitabçalarını avtomatik axtarar/yaradar, cədvəl hissələrini (ТЧ) emal edər.

POST body nümunəsi (Справочник):

{
  "Наименование": "Yeni Kontragent",
  "Код": "00001"
}

POST body nümunəsi (Документ — Alış Sənədi):

{
  "Дата": "2026-06-17T15:30:00",
  "Контрагент": "Əhməd MMC",
  "Mallar": [
    { "Номенклатура": "Alma", "Количество": 10, "Цена": 2.5 },
    { "Номенклатура": "Armud", "Количество": 5, "Цена": 3.0 }
  ]
}

Tam BSL kodu:

Функция PostDataPOST(Запрос)

    // 1. URL-dən obyektin növünü və adını oxuyuruq
    ТипОбъекта = Запрос.ПараметрыURL.Получить("tip"); 
    ИмяОбъекта = Запрос.ПараметрыURL.Получить("ad");    
    
    Ответ = Новый HTTPСервисОтвет(200); 
    Ответ.Заголовки.Вставить("Content-type", "application/json; charset=utf-8");
    
    Попытка
        // 2. n8n-dən gələn POST bədənini (JSON) oxuyuruq
        СтрокаJSON = Запрос.ПолучитьТелоКакСтроку("UTF-8");
        ЧтениеJSON = Новый ЧтениеJSON;
        ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
        Данные = ПрочитатьJSON(ЧтениеJSON);
        ЧтениеJSON.Закрыть();
        
        МассивРезультатов = Новый Массив;
        
        // Əgər n8n-dən tək obyekt gəlibsə, onu massivə salırıq
        Если ТипЗнч(Данные) = Тип("Структура") Тогда
            МассивДанных = Новый Массив;
            МассивДанных.Добавить(Данные);
        Иначе
            МассивДанных = Данные;
        КонецЕсли;
        
        // =========================================================
        // MƏNTİQ 1: SORAQ KİTABÇALARI (СПРАВОЧНИК) ÜÇÜN
        // =========================================================
        Если Врег(ТипОбъекта) = "СПРАВОЧНИК" Тогда
            Менеджер = Справочники[ИмяОбъекта];
            
            Для Каждого ЭлементДанных Из МассивДанных Цикл
                Если ЭлементДанных.Свойство("Наименование") Тогда
                    ИмяЭлемента = ЭлементДанных.Наименование;
                    
                    СсылкаНаОбъект = Менеджер.НайтиПоНаименованию(ИмяЭлемента, Истина);
                    
                    Если СсылкаНаОбъект.Пустая() Тогда
                        НовыйОбъект = Менеджер.СоздатьЭлемент();
                        НовыйОбъект.Наименование = ИмяЭлемента;
                        ЗаполнитьЗначенияСвойств(НовыйОбъект, ЭлементДанных);
                        НовыйОбъект.Записать();
                        МассивРезультатов.Добавить(ИмяЭлемента + " - Uğurla yaradıldı");
                    Иначе
                        Объект = СсылкаНаОбъект.ПолучитьОбъект();
                        ЗаполнитьЗначенияСвойств(Объект, ЭлементДанных);
                        Объект.Записать();
                        МассивРезультатов.Добавить(ИмяЭлемента + " - Uğurla yeniləndi");
                    КонецЕсли;
                Иначе
                    МассивРезультатов.Добавить("Xəta: JSON daxilində 'Наименование' xassəsi tapılmadı");
                КонецЕсли;
            КонецЦикла;
            
        // =========================================================
        // MƏNTİQ 2: SƏNƏDLƏR (ДОКУМЕНТ) ÜÇÜN
        // =========================================================
        ИначеЕсли Врег(ТипОбъекта) = "ДОКУМЕНТ" Тогда
            Менеджер = Документы[ИмяОбъекта];
            
            Для Каждого ЭлементДанных Из МассивДанных Цикл
                НовыйДок = Менеджер.СоздатьДокумент();
                
                // Tarix məntiqi
                Если ЭлементДанных.Свойство("Дата") И ЗначениеЗаполнено(ЭлементДанных.Дата) Тогда
                    Попытка
                        Если ТипЗнч(ЭлементДанных.Дата) = Тип("Дата") Тогда
                            НовыйДок.Дата = ЭлементДанных.Дата;
                        Иначе
                            СтрДата = СтрЗаменить(ЭлементДанных.Дата, "T", "");
                            СтрДата = СтрЗаменить(СтрДата, "-", "");
                            СтрДата = СтрЗаменить(СтрДата, ":", "");
                            НовыйДок.Дата = Дата(СтрДата);
                        КонецЕсли;
                    Исключение
                        НовыйДок.Дата = ТекущаяДата();
                    КонецПопытки;
                    ЭлементДанных.Удалить("Дата"); 
                Иначе
                    НовыйДок.Дата = ТекущаяДата();
                КонецЕсли;
                
                ЗаполнитьЗначенияСвойств(НовыйДок, ЭлементДанных);
                
                // Avtomatik soraqça axtar/yarat
                Для Каждого Реквизит Из НовыйДок.Метаданные().Реквизиты Цикл
                    ИмяРеквизита = Реквизит.Имя;
                    Если ЭлементДанных.Свойство(ИмяРеквизита) И ТипЗнч(ЭлементДанных[ИмяРеквизита]) = Тип("Строка") И ЗначениеЗаполнено(ЭлементДанных[ИмяРеквизита]) Тогда
                        Для Каждого ТекущийТип Из Реквизит.Тип.Типы() Цикл
                            МетаИнформация = Метаданные.НайтиПоТипу(ТекущийТип);
                            Если МетаИнформация <> Неопределено И Метаданные.Справочники.Содержит(МетаИнформация) Тогда
                                ИмяСправочника = МетаИнформация.Имя;
                                СсылкаНаЭлемент = Справочники[ИмяСправочника].НайтиПоНаименованию(ЭлементДанных[ИмяРеквизита], Истина);
                                Если СсылкаНаЭлемент.Пустая() Тогда
                                    НовСправ = Справочники[ИмяСправочника].СоздатьЭлемент();
                                    НовСправ.Наименование = ЭлементДанных[ИмяРеквизита];
                                    НовСправ.Записать();
                                    НовыйДок[ИмяРеквизита] = НовСправ.Ссылка;
                                Иначе
                                    НовыйДок[ИмяРеквизита] = СсылкаНаЭлемент;
                                КонецЕсли;
                                Прервать;
                            КонецЕсли;
                        КонецЦикла;
                    КонецЕсли;
                КонецЦикла;
                
                // Cədvəl hissələri (ТЧ)
                Для Каждого ТЧ Из НовыйДок.Метаданные().ТабличныеЧасти Цикл
                    ИмяТЧ = ТЧ.Имя;
                    Если ЭлементДанных.Свойство(ИмяТЧ) И ТипЗнч(ЭлементДанных[ИмяТЧ]) = Тип("Массив") Тогда
                        Для Каждого СтрокаJSON Из ЭлементДанных[ИмяТЧ] Цикл
                            НоваяСтрока = НовыйДок[ИмяТЧ].Добавить();
                            ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаJSON);
                            
                            Для Каждого Колонка Из ТЧ.Реквизиты Цикл
                                ИмяКолонки = Колонка.Имя;
                                Если СтрокаJSON.Свойство(ИмяКолонки) И ТипЗнч(СтрокаJSON[ИмяКолонки]) = Тип("Строка") И ЗначениеЗаполнено(СтрокаJSON[ИмяКолонки]) Тогда
                                    Для Каждого ТекущийТип Из Колонка.Тип.Типы() Цикл
                                        МетаИнформация = Метаданные.НайтиПоТипу(ТекущийТип);
                                        Если МетаИнформация <> Неопределено И Метаданные.Справочники.Содержит(МетаИнформация) Тогда
                                            ИмяСправТЧ = МетаИнформация.Имя;
                                            СсылкаТЧ = Справочники[ИмяСправТЧ].НайтиПоНаименованию(СтрокаJSON[ИмяКолонки], Истина);
                                            Если СсылкаТЧ.Пустая() Тогда
                                                НовЭлТЧ = Справочники[ИмяСправТЧ].СоздатьЭлемент();
                                                НовЭлТЧ.Наименование = СтрокаJSON[ИмяКолонки];
                                                НовЭлТЧ.Записать();
                                                НоваяСтрока[ИмяКолонки] = НовЭлТЧ.Ссылка;
                                            Иначе
                                                НоваяСтрока[ИмяКолонки] = СсылкаТЧ;
                                            КонецЕсли;
                                            Прервать;
                                        КонецЕсли;
                                    КонецЦикла;
                                КонецЕсли;
                            КонецЦикла;
                        КонецЦикла;
                    КонецЕсли;
                КонецЦикла;
                
                НовыйДок.Записать(РежимЗаписиДокумента.Проведение);
                МассивРезультатов.Добавить("Sənəd nömrə " + НовыйДок.Номер + " uğurla yaradıldı və təsdiqləndi.");
            КонецЦикла;
            
        Иначе
            Ответ.КодСостояния = 400;
            МассивРезультатов.Добавить("Bu POST metodu hələlik yalnız 'Справочник' və 'Документ' növü üçün aktivdir.");
        КонецЕсли;
        
        // Nəticələri JSON olaraq qaytarırıq
        ЗаписьJSON = Новый ЗаписьJSON;
        ЗаписьJSON.УстановитьСтроку();
        ЗаписатьJSON(ЗаписьJSON, МассивРезультатов);
        Ответ.УстановитьТелоИзСтроки(ЗаписьJSON.Закрыть(), КодировкаТекста.UTF8);
        
    Исключение
        Ответ.КодСостояния = 400;
        Ответ.УстановитьТелоИзСтроки("{""error"": """ + СтрЗаменить(ОписаниеОшибки(), """", "\""") + """}", КодировкаТекста.UTF8);
    КонецПопытки;
    
    Возврат Ответ;
КонецФункции

Addım 4: Veb Serverdə Paylaşma

  1. 1C-də Administration → Veb serverdə paylaş bölməsinə keçin
  2. HTTP Servisi seçin və Publish düyməsini basın
  3. Ünvanı yoxlayın: http://<server_ip>/<baza_adı>/hs/api

Test:

curl -u <user>:<password> "http://<server>/<base>/hs/api/GetMetadata"

Əhəmiyyətli Qeydlər

Məsələ Təsvir
Case-insensitive Query parametrləri Врег() ilə yoxlanır — PAGE, Page, page eynidir
TOP parametri Köhnə OData üsulu ilə uyğunluq üçün dəstəklənir — avtomatik page=1, pageSize=TOP
Cədvəl hissələri Sənədlərin cədvəl hissələri (ТЧ) avtomatik $expand edilir
Dinamik sahələr Bütün rekvizitlər metaməlumatdan avtomatik oxunur
Xətalar 400 = obyekt tapılmadı, 500 = daxili 1C xətası
4 tip dəstəyi Справочник, Документ, РегистрСведений, РегистрНакопления
Pagination Soraqça/Sənəd üçün offset (Ссылка НЕ В), Reyestrlər üçün ПЕРВЫЕ N
Periodik Periodik reyestrlər avtomatik Период sahəsi ilə gəlir


📐 1C API Spesifikasiya

Universal Dinamik Kəşf və Data API (v1.2.0)

API iki mərhələli dinamik yanaşma təklif edir:

Mərhələ 1: Kəşf

GET /GetMetadata

Bütün mövcud 1C obyektlərini siyahı şəklində qaytarır:

Obyekt Tipi Təsvir
Справочник Statik məlumatlar: Kontragent, Nomenklatura, Anbarlar...
Документ Hərəkət sənədləri: Alış, Satış, Ödəniş...
РегистрСведений Dövri məlumatlar: Qiymətlər, xassələr...
РегистрНакопления Anbar qalıqları, dövriyyələr...

Cavab formatı:

[
  { "tip": "Справочник", "ad": "Kontragent", "sinonim": "Kontragent" },
  { "tip": "Документ", "ad": "AlışSənədi", "sinonim": "Alış sənədi" }
]

Mərhələ 2: Data İstehlakı

GET /{tip}/{ad}?page=1&pageSize=20&start=2024-01-01&end=2024-12-31
Parametr Tip Default Max Təsvir
page Integer 1 — Cari səhifə
pageSize Integer 20 200 Səhifə ölçüsü
count Boolean FALSE — TRUE = yalnız say
start ISO Date — — Başlanğıc tarix
end ISO Date — — Bitmə tarixi

Cavab formatı:

{
  "meta": { "total": 7, "page": 1, "pageSize": 2, "totalPages": 4, "count": 2 },
  "data": [
    { "Ссылка": "Əhməd", "Код": "000000002", "Наименование": "Əhməd" }
  ]
}

🛠 İnkişaf

Tələblər

  • Node.js ≥ 18
  • npm ≥ 9

Qurma və Test

npm install
npm run build       # TypeScript kompilyasiya
npm run dev         # n8n + hot reload (http://localhost:5678)
npm run lint        # ESLint yoxlaması
npm run lint:fix    # Avtomatik düzəliş
npm run test        # Vitest testləri
npm run release     # Versiya bump + publish

Fayl Strukturu

n8n-nodes-starter/
├── credentials/
│   └── OneCApi.credentials.ts       # 1C Basic Auth credential
├── nodes/
│   └── OneC/
│       ├── OneCKodyaz.node.ts       # Vahid node sinfi (9 əməliyyat + trigger/watch)
│       ├── OneCKodyaz.node.json     # Codex metadata
│       ├── listSearch/
│       │   ├── getEntityTypes.ts    # Obyekt axtarış dropdown (5 dəq TTL keş)
│       │   └── getEntityFields.ts   # POST üçün sahə adları təklifi
│       └── shared/
│           ├── transport.ts         # HTTP sorğu helper (retry + URL normallaşdırma)
│           └── types.ts             # TypeScript interfeysləri
├── test/
│   ├── extractEntityPath.test.ts    # Unit testlər (6 test)
│   └── transport.test.ts           # Retry məntiqi testləri (9 test)
├── icons/
│   ├── onec.svg
│   └── onec.dark.svg
├── README.md
└── package.json

📞 Əlaqə


📄 Lisenziya

Bütün hüquqlar qorunur © 2026 KodYaz. Bu proqram təminatı KodYaz-ın əqli mülkiyyətidir.

Discussion