Orbito - Dokumentacja modułówOrbito - Dokumentacja modułów
Wprowadzenie
Pierwsze kroki
API
Komunikacja
Widoki
Wdrożenie
Społeczność
For AI agents
Wprowadzenie
Pierwsze kroki
API
Komunikacja
Widoki
Wdrożenie
Społeczność
For AI agents
  • API biblioteki

    • API biblioteki @orbito/api-lib
    • Core
    • Log
    • Utils
    • Pages
    • Automation
    • DB
    • Config
    • Files
    • Problems
    • History
    • OAuth
    • Time
    • Mail
    • Templates
    • Lock
    • Uprawnienia

History

Moduł History to wspólna historia zdarzeń firmy (od SDK 3.10.0). Historia to tylko dopisywany rejestr tego, co się wydarzyło z obiektami biznesowymi firmy: zamówienie zostało utworzone, status się zmienił, przesyłka została nadana. Wszystkie moduły piszą do tego samego rejestru, a użytkownik czyta go w Ustawienia → Historia, filtrując po module źródłowym i po module, którego zdarzenie dotyczy.

Nie jest to ani Log, ani Problems. Logi służą diagnostyce technicznej dla programisty. Problems to bieżąca lista rzeczy, które użytkownik musi naprawić — wpisy deduplikują się po problemKey i znikają po resolve. Historia jest niezmiennym śladem audytowym: wpisu nie da się zmienić ani skasować, a każde add dopisuje kolejny.

const { History } = require('@orbito/api-lib');

History.add({ code, desc, relations, userId? })   // -> { _id } albo null
History.list(filters?)                            // -> { items, pagination }

Parametry History.add:

ParametrTypWymaganyOpis
codestringTakKod zdarzenia nadawany przez moduł, np. ORDER_STATUS_CHANGED (1–100 znaków). Służy do filtrowania i diagnostyki — użytkownik czyta desc, nie ten kod
descstring | objectTakGotowy opis zdarzenia dla użytkownika. Zwykły string (1–2000 znaków) albo mapa językowa { pl: '...', en: '...' } — klucze w formacie kodu języka (^[a-z]{2}(-[A-Z]{2})?$), każda wartość 1–2000 znaków, min. 1 klucz
relationsarrayTakEncje, których dotyczy zdarzenie — od 1 do 10 elementów
relations[].modulestringTakKod modułu będącego właścicielem encji, w formacie <vendor>/<nazwa>, np. core/orders (max 100 znaków)
relations[].objectstringTakTyp encji w tym module, np. order, shipment (max 50 znaków, wzorzec ^[a-z][a-z0-9_-]*$)
relations[].idstringTakIdentyfikator encji (1–100 znaków). String, a nie ObjectId — dzięki temu działają też identyfikatory z systemów zewnętrznych
relations[].instanceIdstringNieInstalacja modułu będąca właścicielem encji (24 znaki hex), gdy samo id nie jest jednoznaczne w obrębie firmy (kilka instalacji tego samego modułu, identyfikatory zewnętrzne)
userIdstringNieKto wywołał zdarzenie (24 znaki hex). Bierzesz go z kontekstu wywołania (this.userId w handlerze metody); brak oznacza zdarzenie systemowe

Historia nie ma odpowiednika update ani delete — poprawkę zapisujesz jako kolejne zdarzenie.

Zalecana forma desc — mapa językowa

Wpis niesie gotowy opis, bo moduł działa w osobnym kontenerze i może być zainstalowany w kilku wersjach naraz — ani rdzeń, ani panel nie mają jak wykonać jego deskryptora w czasie odczytu. Skoro opis zapisujesz raz i na zawsze, przekazuj go jako mapę { pl, en, ... }: zespół firmy bywa wielojęzyczny, a wszyscy widzą tę samą historię — podawaj oba języki. Rdzeń przechowuje mapę 1:1 — nie tłumaczy ani nie normalizuje jej zawartości — a panel pokazuje wersję w języku zalogowanego użytkownika, z fallbackiem locale → en → pl → pierwsza dostępna wartość (funkcja i18nText we froncie). Zwykły string wciąż działa i jest pokazywany każdemu tak samo.

Relacje i źródło wpisu

relations mówi, czego dotyczy zdarzenie, i może wskazywać kilka encji naraz: utworzenie przesyłki dotyczy i przesyłki, i zamówienia, dla którego powstała. Zapytanie „historia zamówienia X” odpowiada po trójce module + object + id, czyli po danych, które pytający już zna — dlatego każda relacja musi podać wszystkie trzy pola.

Nie przekazujesz informacji o module, instalacji ani o czasie zdarzenia: kod modułu, instancję (z nazwą do wyświetlenia) i znacznik at dopisuje rdzeń na podstawie tożsamości połączenia kontenera. Moduł nie może więc antydatować wpisu ani zapisać go w cudzym imieniu. userId przekaż z kontekstu wywołania — bez niego wpis jest zdarzeniem systemowym (cron, integracja przychodząca) i panel pokazuje przy nim „system”. Wyjątkiem jest akcja automatyzacji: jej pochodzenie dopisuje samo SDK — patrz Pochodzenie wpisu.

Metoda nigdy nie odrzuca Promise

Zapis historii jest efektem ubocznym operacji biznesowej i nie może jej wywrócić, dlatego add — tak samo jak Log i Problems — nie rzuca wyjątku. Błąd transportu lub walidacji jest logowany na console.error przy ORBITO_DEBUG=1, a metoda zwraca null. Nie opieraj więc logiki modułu na tym, że wpis powstał.

History.add

Dopisuje wpis do historii firmy. Zwraca { _id } nowego wpisu. Wołaj ją po tym, jak opisywana zmiana faktycznie się wydarzyła.

const { History } = require('@orbito/api-lib');

await History.add({
    code: 'ORDER_STATUS_CHANGED',
    desc: {
        pl: `Status zmieniony z „${from.name}” na „${to.name}”`,
        en: `Status changed from "${from.name}" to "${to.name}"`
    },
    relations: [{ module: 'core/orders', object: 'order', id: String(order._id) }],
    userId: this.userId
});

Zdarzenie dotyczące kilku encji — przesyłka utworzona w core.shipping dla zamówienia z core.orders — podaje obie relacje, dzięki czemu pojawi się zarówno w historii przesyłki, jak i w historii zamówienia:

await History.add({
    code: 'SHIPMENT_CREATED',
    desc: {
        pl: `Utworzono przesyłkę ${shipment.number} (${carrier.name})`,
        en: `Shipment ${shipment.number} created (${carrier.name})`
    },
    relations: [
        { module: 'core/shipping', object: 'shipment', id: String(shipment._id) },
        { module: 'core/orders', object: 'order', id: String(shipment.orderId) }
    ],
    userId: this.userId
});

Zdarzenie wywołane bez udziału użytkownika (cykliczne pobranie statusów, hook, automatyzacja) po prostu pomija userId — jeśli wywołanie wyszło z biegu automatyzacji, SDK samo dopisze do wpisu jego pochodzenie:

await History.add({
    code: 'SHIPMENT_STATUS_CHANGED',
    desc: {
        pl: `Status przesyłki ${shipment.number} zmieniony na „${status}”`,
        en: `Shipment ${shipment.number} status changed to "${status}"`
    },
    relations: [{ module: 'core/shipping', object: 'shipment', id: String(shipment._id) }]
});

Pochodzenie wpisu (automatyzacja)

(od SDK 4.3.0)

Wpis zapisany w wyniku akcji automatyzacji niesie pole origin — panel pokazuje wtedy w kolumnie użytkownika nazwę automatyzacji z linkiem do konkretnego biegu (oś czasu w Ustawienia → Automatyzacje → Biegi), a nie „system”. Ten sam link pojawia się w historii zamówienia w module zamówień.

origin: {
    type: 'automation',
    automationId: '66f0...',          // _id automatyzacji
    automationName: 'Wysyłka po opłaceniu',   // nazwa w chwili biegu (snapshot)
    runId: '66f1...:cron:2026-10-06T08:00:00.000Z'   // klucz biegu (string, nie ObjectId)
}

origin jest null dla wpisów użytkownika i zdarzeń systemowych. automationName to migawka z chwili biegu — późniejsza zmiana nazwy albo usunięcie automatyzacji nie zmienia wpisu (historia jest niezmienna).

Moduł niczego nie przekazuje. History.add nie przyjmuje origin w parametrach (rdzeń wycina takie pole z payloadu). Pochodzenie dokleja samo SDK z kontekstu wywołania:

  • w execute akcji automatyzacji (Automation),
  • w handlerach metod i hooków osiągniętych z takiej akcji przez Core.callModule / Core.callInstanceMethod / Core.execHook — także w innych modułach i dalej w łańcuchu, np. akcja core/orders zmienia status → hook → core/shipping zapisuje wpis o przesyłce z tym samym biegiem.

Dzięki temu wystarczy podbić @orbito/api-lib do ^4.3.0 — kod modułu się nie zmienia. Wpis z biegu automatyzacji nie ma userId, nawet gdy bieg wyzwolił użytkownik: liczy się to, że akcję wykonała automatyzacja.

Starsze SDK

Moduł na SDK sprzed 4.3.0 zapisuje wpisy bez pochodzenia, także te z akcji automatyzacji — panel pokaże przy nich „system”. To samo dotyczy łańcucha: jeśli moduł pośredni (np. ten, który woła execHook) ma starsze SDK, pochodzenie nie przejdzie dalej.

History.list

(od SDK 3.12.0)

Czyta historię zdarzeń, stosując te same filtry co strona Ustawienia → Historia (GET /history). Wynik jest stronicowany i posortowany od najnowszych wpisów.

Parametry History.list — wszystkie opcjonalne, łączone przez AND; bez żadnego z nich wywołanie przechodzi po całej historii firmy:

ParametrTypOpis
module, object, idstringFiltr relacji — cała trójka albo żadna z nich. Dopasowuje wpisy, w których którakolwiek relacja odpowiada podanej trójce module + object + id
instanceIdstringZawęża filtr relacji do konkretnej instalacji (24 znaki hex). Sam, bez pełnej trójki module/object/id, jest odrzucany
sourceModulestringTylko wpisy zapisane przez ten moduł
relatedModulestringTylko wpisy z dowolną relacją do tego modułu
codestringTylko wpisy o tym kodzie zdarzenia
from, tostring | DateZakres dat at (włącznie)
pagenumberNumer strony, liczony od 1 (domyślnie 1)
limitnumberLiczba wpisów na stronę — domyślnie 20, rdzeń przycina do 200

Zwraca { items, pagination }:

  • items[] — wpisy w kształcie { _id, source: { moduleCode, instanceId, instanceName }, code, desc, relations, userId, user: { _id, firstname, lastname } | null, origin, at }. desc i relations są dokładnie takie, jak przekazane do add; user to dane autora dociągnięte przez rdzeń (null dla zdarzeń systemowych albo usuniętego użytkownika); origin to pochodzenie wpisu albo null. Identyfikatory i daty przychodzą jako stringi (at to ISO string), bo wynik podróżuje jako JSON.
  • pagination — { page, limit, total }. Gdy total jest większe niż liczba elementów w items, zwrócona strona nie obejmuje całej historii.

Zakres odczytu — cała historia firmy

list nie ogranicza się do encji własnego modułu ani do wpisów zapisanych przez własny moduł. Moduł może odczytać całą historię swojej firmy, np. zdarzenia core/shipping i core/invoicing dotyczące jednego z jego zamówień — wystarczy, że tamte moduły dodały do swojego wpisu relację do Twojej encji (tak jak w przykładzie z SHIPMENT_CREATED wyżej). Firmę wyznacza tożsamość połączenia, więc modułu nigdy nie da się użyć do odczytania historii cudzej firmy.

list odrzuca Promise przy błędzie

W odróżnieniu od add, list rzuca wyjątek, gdy rdzeń odrzuci zapytanie (np. niepełną trójkę relacji) albo gdy wywołanie się nie powiedzie. To odczyt danych biznesowych — wywołujący (np. widok modala) ma zobaczyć błąd, a nie pustą listę udającą brak historii.

Przykład — historia własnej encji razem ze zdarzeniami dopisanymi przez inne moduły:

// historia zamówienia, razem ze zdarzeniami core/shipping i core/invoicing,
// jeśli te dodały do swoich wpisów relację do tego zamówienia
const { items, pagination } = await History.list({
    module: 'core/orders', object: 'order', id: String(order._id),
    limit: 200
});

if (pagination.total > items.length) {
    // pobrana strona nie obejmuje całej historii — widoczne są tylko najnowsze wpisy
}

Dobre praktyki

Opis pisz dla użytkownika i od razu po ludzku – z historii ma dać się przeczytać, co się stało, bez zaglądania do kodu modułu. Wartości, które się zmieniły, wpleć w zdanie; surowych danych zdarzenie nie przenosi.

// ❌ Nigdy
desc: 'ORDER_STATUS_CHANGED'
desc: JSON.stringify({ from, to })

// ✅ Zamiast tego
desc: { pl: `Status zmieniony z „${from.name}” na „${to.name}”`, en: `Status changed from "${from.name}" to "${to.name}"` }

Każdy code powinien znaczyć jedno – to po nim filtrujesz listę i rozpoznajesz zdarzenia swojego modułu. Używaj stałego zestawu kodów w UPPER_SNAKE_CASE (ORDER_STATUS_CHANGED, SHIPMENT_CREATED), a nie treści opisu przepisanej na kod.

Wskaż wszystkie encje, których zdarzenie dotyczy – jeśli operacja łączy dwa obiekty, podaj obie relacje. Wpis z jedną relacją nie pojawi się w historii tej drugiej encji, a historii nie da się później poprawić.

// ❌ Nigdy
relations: [{ module: 'core/shipping', object: 'shipment', id }]   // utworzenie przesyłki dla zamówienia

// ✅ Zamiast tego
relations: [
    { module: 'core/shipping', object: 'shipment', id },
    { module: 'core/orders', object: 'order', id: orderId }
]

Zapisuj zdarzenia biznesowe, a nie kroki techniczne – historia jest dla użytkownika. Ponowienie zapytania do API, odświeżenie tokenu czy szczegóły odpowiedzi zewnętrznego serwisu zostaw Log, a sytuacje, które użytkownik musi naprawić — Problems.

Nie loguj zdarzenia, które się nie wydarzyło – add wołaj po udanym zapisie zmiany, nie przed. Historia jest niezmienna, więc wpisu o nieudanej operacji nie da się wycofać.

Ostatnio zaktualizowano:: 6.10.2026, 12:11
Contributors: Wiktor Koźmiński, Claude Fable 5.1, Claude Opus 5.5 (1M context)
Prev
Problems
Next
OAuth