LPD-Direct (Logiczny Przepływ Danych) to deklaratywny język pseudokodu służący do projektowania, dokumentowania i automatyzacji procesów biznesowych oraz technologicznych.
Jego głównym celem jest eliminacja bariery komunikacyjnej między ekspertami biznesowymi (wiedzącymi dlaczego coś robimy) a zespołami technicznymi (wiedzącymi jak to zrobić). Składnia wymusza łączenie instrukcji technicznych z ich uzasadnieniem biznesowym.
Fundamenty LPD-Direct:
- Czytelność: Kod musi być zrozumiały dla osoby nietechnicznej.
- Kontekst: Każda akcja musi mieć jawnie zdefiniowany cel biznesowy.
- Pełna Kontrola: Język obsługuje nie tylko sukces ("happy path"), ale też błędy, decyzje ludzkie i sztuczną inteligencję.
- Jednoznaczność: Każda konstrukcja ma dokładnie jedną interpretację.
Każdy proces opisany w LPD-Direct składa się z trzech głównych sekcji:
- Wyzwalacz (TRIGGER): Definicja, co uruchamia proces.
- Ciało Procesu: Logiczna sekwencja operacji podzielona na fazy (np. Gromadzenie, Przetwarzanie, Decyzje).
- Zakończenie: Jawny sygnał końca przepływu.
Każde polecenie operacyjne musi być poprzedzone dwoma komentarzami. To nie jest opcjonalne.
// Co robimy:(Opis techniczny czynności)// Po co:(Uzasadnienie biznesowe)
Wyjątki: TRIGGER i SYSTEMS nie wymagają podwójnego komentarza (mają wbudowany opis).
// Co robimy: Filtrujemy zamówienia ze statusem 'Anulowane'.
// Po co: Aby nie naliczać prowizji od sprzedaży, która nie doszła do skutku.
FILTER ZAMOWIENIA WHERE Status != 'Anulowane' AS ZAMOWIENIA_SKUTECZNE
Opcjonalna sekcja definiująca zewnętrzne systemy i oprogramowanie używane w procesie. Umieszczana bezpośrednio po TRIGGER, przed pierwszym SET/LOAD.
Składnia:
// SYSTEMS:
// - <Nazwa Systemu>: <Typ> - <Opis, co zawiera/robi>
// - <Nazwa Systemu>: <Typ> - <Opis>
Przykład:
// TRIGGER: SCHEDULE EVERY DAY AT 09:00 CET
// SYSTEMS:
// - ERP Firma: REST API - System fakturowania i zarządzania płatnościami klientów
// - BazaHR: PostgreSQL - Baza danych pracowników i struktury organizacyjnej
// - Salesforce: REST API - CRM do zarządzania leadami i kontaktami
// - Mail Service: SMTP - Serwer pocztowy do wysyłki automatycznych powiadomień
// - Slack: Webhook - Komunikator zespołowy do alertów operacyjnych
// Co robimy: Ustalamy dzisiejszą datę.
// Po co: Do obliczeń terminów.
SET DZISIAJ = CURRENT_DATE()
Cel: Dokumentacja zależności procesu od zewnętrznych systemów - ułatwia onboarding nowych osób i identyfikację punktów awarii.
Format wpisu:
- Nazwa Systemu: Nazwa używana w aliasach LOAD/SEND
- Typ: REST API, GraphQL, SQL (PostgreSQL/MySQL), SMTP, Webhook, File System, itp.
- Opis: Zwięzły opis roli systemu w procesie (1 zdanie)
| Typ | Przykład | Opis |
|---|---|---|
STRING |
'Tekst' |
Tekst w pojedynczych apostrofach |
NUMBER |
123, 45.67 |
Liczby całkowite i dziesiętne |
BOOLEAN |
TRUE, FALSE |
Wartości logiczne |
NULL |
NULL |
Brak wartości |
DATE |
2024-01-15 |
Data w formacie ISO 8601 |
DATETIME |
2024-01-15T09:00:00Z |
Data i czas w formacie ISO 8601 |
| Typ | Przykład | Opis |
|---|---|---|
DATASET |
Rezultat LOAD/FILTER/CALCULATE | Tabela danych z nazwanymi kolumnami |
LIST |
[1, 2, 3] |
Lista wartości |
Jednostki są częścią literału i muszą być oddzielone spacją:
SET PROG_VIP = 5000 EUR
SET TIMEOUT = 30 SECONDS
SET DELAY = 7 DAYS
Dostępne jednostki: EUR, USD, PLN, SECONDS, MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS, PERCENT
Format: UPPER_SNAKE_CASE
SET DZISIAJ = CURRENT_DATE()
LOAD API FROM '...' AS WSZYSTKIE_ZALEGLOSCI
FILTER DATA WHERE x > 10 AS WYNIK_FILTROWANIA
Format: Bez cudzysłowów, PascalCase z podkreślnikami dla złożonych nazw
FILTER SPRZEDAZ WHERE Status = 'Aktywny' AS RESULT
CALCULATE on DATA:
Marza_PLN = Cena_Sprzedazy - Koszt_Zakupu
AS RESULT_Z_MARZA
Wyjątek: Jeśli nazwa kolumny zawiera spacje lub znaki specjalne, użyj pojedynczych apostrofów:
FILTER DATA WHERE 'Full Name' = 'Jan Kowalski' AS RESULT
Format: Zawsze w pojedynczych apostrofach '...'
SET NAZWA = 'Raport Miesięczny'
SEND ALERT 'Uwaga: wykryto anomalię!' TO 'Slack / #alerts'
| Operator | Znaczenie | Przykład |
|---|---|---|
= |
Równe | Status = 'Aktywny' |
!= |
Różne | Status != 'Anulowane' |
> |
Większe | Kwota > 1000 |
>= |
Większe lub równe | Kwota >= 1000 |
< |
Mniejsze | Kwota < 1000 |
<= |
Mniejsze lub równe | Termin_Platnosci <= DZISIAJ |
| Operator | Znaczenie | Przykład | Precedencja |
|---|---|---|---|
NOT |
Negacja | NOT (x > 10) |
1 (najwyższa) |
AND |
Koniunkcja | x > 10 AND y < 20 |
2 |
OR |
Alternatywa | Status = 'A' OR Status = 'B' |
3 (najniższa) |
Nawiasy: Używaj () do wymuszenia kolejności wykonywania.
FILTER DATA WHERE (Status = 'Aktywny' OR Status = 'Testowy') AND Kwota > 1000 AS RESULT
| Operator | Znaczenie | Przykład |
|---|---|---|
IS NULL |
Sprawdza czy wartość jest NULL | Email IS NULL |
IS NOT NULL |
Sprawdza czy wartość nie jest NULL | Email IS NOT NULL |
IS EMPTY |
Sprawdza czy dataset jest pusty | DATASET IS EMPTY |
IS NOT EMPTY |
Sprawdza czy dataset zawiera dane | DATASET IS NOT EMPTY |
| Operator | Znaczenie | Precedencja |
|---|---|---|
+ |
Dodawanie | 2 |
- |
Odejmowanie | 2 |
* |
Mnożenie | 1 (najwyższa) |
/ |
Dzielenie | 1 |
% |
Modulo (reszta z dzielenia) | 1 |
| Funkcja | Zwraca | Przykład |
|---|---|---|
CURRENT_DATE() |
Dzisiejsza data | 2024-01-15 |
CURRENT_DATETIME() |
Obecna data i czas | 2024-01-15T09:30:00Z |
ADD_DAYS(date, n) |
Data + n dni | ADD_DAYS(DZISIAJ, 7) |
SUBTRACT_DAYS(date, n) |
Data - n dni | SUBTRACT_DAYS(DZISIAJ, 30) |
FORMAT_DATE(date, format) |
Formatowanie daty | FORMAT_DATE(DZISIAJ, 'DD-MM-YYYY') |
DAYS_BETWEEN(date1, date2) |
Różnica w dniach | DAYS_BETWEEN(Termin, DZISIAJ) |
| Funkcja | Zwraca | Przykład |
|---|---|---|
UPPER(string) |
Tekst wielkimi literami | UPPER('test') → 'TEST' |
LOWER(string) |
Tekst małymi literami | LOWER('TEST') → 'test' |
TRIM(string) |
Tekst bez spacji na krańcach | TRIM(' test ') → 'test' |
LENGTH(string) |
Długość tekstu | LENGTH('test') → 4 |
CONCAT(str1, str2, ...) |
Łączenie tekstów | CONCAT('A', 'B') → 'AB' |
SUBSTRING(string, start, len) |
Wycinek tekstu | SUBSTRING('test', 1, 2) → 'te' |
| Funkcja | Zwraca | Przykład |
|---|---|---|
ROUND(number, decimals) |
Zaokrąglenie | ROUND(3.14159, 2) → 3.14 |
CEIL(number) |
Zaokrąglenie w górę | CEIL(3.1) → 4 |
FLOOR(number) |
Zaokrąglenie w dół | FLOOR(3.9) → 3 |
ABS(number) |
Wartość bezwzględna | ABS(-5) → 5 |
MIN(a, b, ...) |
Minimum | MIN(1, 5, 3) → 1 |
MAX(a, b, ...) |
Maksimum | MAX(1, 5, 3) → 5 |
| Funkcja | Zwraca | Przykład |
|---|---|---|
SUM(column) |
Suma wartości | SUM(Kwota) |
AVG(column) |
Średnia | AVG(Ocena) |
COUNT(column) |
Liczba rekordów | COUNT(ID) |
MIN(column) |
Minimum | MIN(Cena) |
MAX(column) |
Maksimum | MAX(Cena) |
Składnia: // TRIGGER: <typ> <parametry>
Typy wyzwalaczy:
// TRIGGER: SCHEDULE EVERY DAY AT 08:00 CET
// TRIGGER: SCHEDULE EVERY MONDAY AT 09:00 CET
// TRIGGER: SCHEDULE EVERY 1st DAY OF MONTH AT 00:00 UTC
// TRIGGER: ON WEBHOOK RECEIVE (endpoint: '/api/nowy-klient')
// TRIGGER: MANUAL BY USER (Rola: 'Analityk Danych')
// TRIGGER: ON FILE UPLOAD (path: '/uploads/*.csv')
Wyzwalacz jest zawsze jednolinijkowy i znajduje się na początku procesu.
Składnia: SET <ZMIENNA> = <wartość>
Deklaruje nową zmienną globalną. Zmienna nie może być nadpisana przez kolejny SET.
// Co robimy: Zapisujemy dzisiejszą datę w zmiennej.
// Po co: Aby użyć jej później do filtrowania przeterminowanych zadań.
SET DZISIAJ = CURRENT_DATE()
// Co robimy: Definiujemy próg kwotowy dla zamówień VIP.
// Po co: Aby łatwo zmienić tę wartość w przyszłości bez przeszukiwania całego kodu.
SET PROG_VIP = 5000 EUR
Błąd - próba nadpisania przez SET:
SET DZISIAJ = CURRENT_DATE()
SET DZISIAJ = ADD_DAYS(DZISIAJ, 1) // ❌ BŁĄD: Nie można nadpisać zmiennej przez SET
Składnia: OVERRIDE <ZMIENNA> = <wartość>
Nadpisuje wartość istniejącej zmiennej. Zmienna musi być wcześniej zadeklarowana przez SET.
// Co robimy: Zapisujemy dzisiejszą datę.
// Po co: Punkt startowy dla obliczeń.
SET DZISIAJ = CURRENT_DATE()
// Co robimy: Przesuwamy datę o 1 dzień do przodu.
// Po co: Aby obliczyć termin "jutro" dla procesu planowania.
OVERRIDE DZISIAJ = ADD_DAYS(DZISIAJ, 1)
Błąd - OVERRIDE bez wcześniejszego SET:
OVERRIDE NOWA_ZMIENNA = 100 // ❌ BŁĄD: Zmienna nie została wcześniej zadeklarowana
Uwaga: OVERRIDE należy używać ostrożnie - może prowadzić do trudniejszego śledzenia przepływu danych. W większości przypadków lepiej utworzyć nową zmienną:
SET DZISIAJ = CURRENT_DATE()
SET JUTRO = ADD_DAYS(DZISIAJ, 1) // ✅ Preferowane - jasne co jest co
Składnia: LOAD <źródło> FROM <lokalizacja> [parametry] AS <DATASET>
Typy źródeł:
// Co robimy: Pobieramy listę faktur niezapłaconych z API ERP.
// Po co: Aby zidentyfikować klientów zalegających z płatnościami.
LOAD API FROM 'https://erp.firma.pl/api/invoices?status=unpaid' AS FAKTURY
ON ERROR: STOP AND ALERT 'Admin ERP'
// Co robimy: Pobieramy listę aktywnych pracowników z bazy HR.
// Po co: Aby wiedzieć, komu naliczyć wynagrodzenie.
LOAD SQL FROM 'BazaHR' QUERY 'SELECT * FROM employees WHERE status = active' AS PRACOWNICY
ON ERROR: RETRY 3 TIMES THEN STOP AND ALERT 'Zespół Kadr'
// Co robimy: Wczytujemy dane sprzedażowe z pliku Excel.
// Po co: Aby przeanalizować wyniki sprzedaży w ostatnim kwartale.
LOAD FILE FROM '/data/sprzedaz_Q4.xlsx' SHEET 'Sprzedaż' AS DANE_SPRZEDAZY
ON ERROR: STOP AND ALERT 'Data Analyst'
Uwagi:
'BazaHR'to alias zdefiniowany w konfiguracji środowiska (poza zakresem języka)- Credentials są zarządzane przez środowisko wykonawcze
Składnia: FILTER <DATASET> WHERE <warunek> AS <NOWY_DATASET>
// Co robimy: Filtrujemy faktury przeterminowane o ponad 7 dni.
// Po co: Dajemy klientom tydzień "okresu karencji" zanim zaczniemy windykację.
FILTER WSZYSTKIE_ZALEGLOSCI WHERE Termin_Platnosci <= SUBTRACT_DAYS(DZISIAJ, 7) AS ZALEGLOSCI_DO_WINDYKACJI
Złożone warunki:
// Co robimy: Filtrujemy zamówienia aktywne lub testowe z kwotą powyżej 1000 EUR.
// Po co: Aby wykluczyć anulowane zamówienia i małe transakcje testowe.
FILTER ZAMOWIENIA WHERE (Status = 'Aktywny' OR Status = 'Testowy') AND Kwota > 1000 EUR AS ZAMOWIENIA_WAZNE
Składnia: SORT <DATASET> BY <kolumna> <ASC|DESC> [, <kolumna> <ASC|DESC> ...] AS <NOWY_DATASET>
Kierunki sortowania:
ASC- rosnąco (od najmniejszej do największej)DESC- malejąco (od największej do najmniejszej)
Sortowanie po jednej kolumnie:
// Co robimy: Sortujemy produkty według ceny rosnąco.
// Po co: Aby pokazać najtańsze produkty na początku listy.
SORT PRODUKTY BY Cena ASC AS PRODUKTY_POSORTOWANE
Sortowanie po wielu kolumnach:
// Co robimy: Sortujemy zamówienia według daty malejąco, a potem według kwoty rosnąco.
// Po co: Aby zobaczyć najnowsze zamówienia, a wśród nich najpierw te najmniejsze.
SORT ZAMOWIENIA BY Data_Zamowienia DESC, Kwota ASC AS ZAMOWIENIA_POSORTOWANE
Uwagi:
- Domyślny kierunek to
ASCjeśli nie podano - Przy sortowaniu po wielu kolumnach, pierwsza kolumna ma najwyższy priorytet
- Wartości NULL są traktowane jako najmniejsze (pojawiają się na początku przy ASC)
Składnia:
GROUP <DATASET> BY <kolumna1> [, <kolumna2> ...]:
<Agregat1> = <funkcja_agregująca>(kolumna)
<Agregat2> = <funkcja_agregująca>(kolumna)
AS <NOWY_DATASET>
Wcięcia: Obowiązkowe 4 spacje dla każdego wiersza wewnątrz bloku.
Funkcje agregujące: SUM(), AVG(), COUNT(), MIN(), MAX()
Grupowanie po jednej kolumnie:
// Co robimy: Grupujemy sprzedaż według produktu i sumujemy sprzedane jednostki.
// Po co: Aby zobaczyć, ile sztuk każdego produktu sprzedaliśmy w sumie.
GROUP SPRZEDAZ BY Produkt:
Suma_Jednostek = SUM(Ilosc)
Suma_Przychodu = SUM(Cena * Ilosc)
AS SPRZEDAZ_PER_PRODUKT
Grupowanie po wielu kolumnach:
// Co robimy: Grupujemy zamówienia według kraju i miasta klienta.
// Po co: Aby zobaczyć średnią wartość zamówienia w każdym mieście.
GROUP ZAMOWIENIA BY Kraj, Miasto:
Liczba_Zamowien = COUNT(ID_Zamowienia)
Srednia_Wartosc = AVG(Kwota)
Max_Zamowienie = MAX(Kwota)
AS STATYSTYKI_LOKALIZACJI
Semantyka:
- Resultat zawiera jedną linię na unikalną kombinację wartości w kolumnach GROUP BY
- Kolumny użyte w GROUP BY są automatycznie dostępne w wyniku
- Wszystkie inne kolumny muszą być zagregowane
Przykład z rzeczywistymi danymi:
Wejście (SPRZEDAZ):
| Produkt | Kraj | Kwota |
|---------|------|-------|
| Laptop | PL | 5000 |
| Laptop | PL | 4500 |
| Laptop | DE | 6000 |
| Mysz | PL | 100 |
GROUP SPRZEDAZ BY Produkt, Kraj:
Suma_Sprzedazy = SUM(Kwota)
Liczba = COUNT(Kwota)
AS WYNIK
Wyjście (WYNIK):
| Produkt | Kraj | Suma_Sprzedazy | Liczba |
|---------|------|----------------|--------|
| Laptop | PL | 9500 | 2 |
| Laptop | DE | 6000 | 1 |
| Mysz | PL | 100 | 1 |
Składnia:
CALCULATE on <DATASET>:
<Nowa_Kolumna_1> = <wyrażenie>
<Nowa_Kolumna_2> = <wyrażenie>
AS <NOWY_DATASET>
Wcięcia: Obowiązkowe 4 spacje dla każdego wiersza wewnątrz bloku.
// Co robimy: Wyliczamy marżę dla każdego produktu.
// Po co: Aby zidentyfikować najmniej rentowne towary.
CALCULATE on SPRZEDAZ:
Marza_PLN = Cena_Sprzedazy - Koszt_Zakupu
Marza_Procent = (Marza_PLN / Cena_Sprzedazy) * 100
AS SPRZEDAZ_Z_MARZA
Funkcje agregujące:
// Co robimy: Wyliczamy całkowitą sprzedaż i średnią marżę.
// Po co: Aby uzyskać podsumowanie wyników sprzedażowych.
CALCULATE on SPRZEDAZ:
Suma_Sprzedazy = SUM(Kwota)
Srednia_Marza = AVG(Marza_Procent)
AS PODSUMOWANIE_SPRZEDAZY
Składnia: JOIN <typ> <DATASET1> WITH <DATASET2> ON <klucz> AS <NOWY_DATASET>
Typy JOIN:
INNER- tylko dopasowane rekordy z obu stronLEFT- wszystkie z lewej + dopasowane z prawejRIGHT- wszystkie z prawej + dopasowane z lewejFULL- wszystkie rekordy z obu stron
Domyślny typ: Jeśli nie podano, używany jest INNER.
// Co robimy: Łączymy tabelę sprzedaży z bazą klientów po ID_Klienta.
// Po co: Aby wzbogacić dane transakcyjne o informacje demograficzne (miasto, wiek).
JOIN INNER SPRZEDAZ WITH KLIENCI ON ID_Klienta AS DANE_PELNE
// Co robimy: Łączymy zamówienia z płatnościami, zachowując wszystkie zamówienia.
// Po co: Aby zobaczyć, które zamówienia nie mają jeszcze płatności.
JOIN LEFT ZAMOWIENIA WITH PLATNOSCI ON ID_Zamowienia AS ZAMOWIENIA_Z_PLATNOSCI
Składnia:
IF <warunek> THEN
// instrukcje (z podwójnymi komentarzami)
ELSE IF <warunek> THEN
// instrukcje
ELSE
// instrukcje
END IF
Wcięcia: Obowiązkowe 4 spacje dla treści wewnątrz bloków.
// Co robimy: Sprawdzamy, czy po filtracji zostały jakiekolwiek dane do wysyłki.
// Po co: Aby nie wysyłać pustych raportów do zarządu.
IF DANE_PELNE IS EMPTY THEN
// Co robimy: Kończymy proces w tym miejscu.
// Po co: Brak danych oznacza brak potrzeby generowania raportu.
STOP PROCESS (Log: 'Brak danych z dzisiaj, pomijam wysyłkę')
ELSE IF COUNT(DANE_PELNE) < 10 THEN
// Co robimy: Wysyłamy uproszczony raport.
// Po co: Dla małej liczby danych pełny raport jest przesadą.
SEND DANE_PELNE TO API 'https://raport.pl/simple'
ELSE
// Co robimy: Generujemy i wysyłamy pełny raport.
// Po co: Duża liczba danych wymaga szczegółowej analizy.
SEND DANE_PELNE TO API 'https://raport.pl/full'
END IF
Warunki na zmiennych:
IF PROG_VIP > 10000 EUR THEN
// instrukcje
END IF
Semantyka: AI wykonuje zadanie dla każdego rekordu osobno (iteracja po wierszach).
ASK_AI on <DATASET>:
ZADANIE: '<opis zadania z [Nazwa_Kolumny] jako placeholder>'
ZWROC: <Nazwa_Nowej_Kolumny>
AS <NOWY_DATASET>
Wcięcia: Obowiązkowe 4 spacje dla ZADANIE i ZWROC.
// Co robimy: Prosimy AI o wygenerowanie spersonalizowanej treści maila.
// Po co: Aby komunikacja była dopasowana do klienta, a nie brzmiała jak robot.
ASK_AI on ZALEGLOSCI_DO_WINDYKACJI:
ZADANIE: 'Napisz krótkie przypomnienie o fakturze [Nr_Faktury] na kwotę [Kwota]. Użyj uprzejmego tonu.'
ZWROC: Tresc_Maila
AS PRZYGOTOWANE_WYSYLKI
ASK_AI: <opis zadania w naturalnym języku> AS <NOWY_DATASET>
Używaj gdy szczegóły (które kolumny, jak nazywać output) są oczywiste z kontekstu.
// Co robimy: AI generuje uprzejme przypomnienia o płatnościach.
// Po co: Aby komunikacja była dopasowana do klienta.
ASK_AI: napisz uprzejme przypomnienia o fakturach AS PRZYGOTOWANE_WYSYLKI
// Co robimy: AI ocenia sentyment komentarzy klientów.
// Po co: Aby wyłapać niezadowolonych klientów.
ASK_AI: oceń sentyment komentarzy na skali 1-5 AS ANKIETY_OCENIONE
Obsługa błędów (dla obu składni):
ASK_AI on DANE:
ZADANIE: 'Wyciągnij NIP z tekstu [Body].'
ZWROC: NIP
AS DANE_Z_NIP
ON ERROR: RETRY 2 TIMES THEN SKIP
ASK_AI: wyciągnij NIP z treści maili AS DANE_Z_NIP
ON ERROR: RETRY 2 TIMES THEN SKIP
Kiedy użyć której składni:
- Pełna: Gdy precyzyjnie określasz źródłowe kolumny
[Nazwa_Kolumny]i nazwę nowej kolumny - Uproszczona: Gdy szczegóły są jasne z kontekstu lub dla szybkiego prototypowania
Składnia:
AWAIT APPROVAL FROM '<rola>' ON <DATASET>
POKAZ: '<kolumna1>', '<kolumna2>', ...
PYTANIE: '<treść pytania>'
AS <NOWY_DATASET>
Semantyka:
- Proces zatrzymuje się i czeka na decyzję użytkownika
<NOWY_DATASET>zawiera tylko zatwierdzone rekordy- Odrzucone rekordy są trwale usuwane z przepływu
- Użytkownik może edytować wartości w kolumnach przed zatwierdzeniem
Wcięcia: Obowiązkowe 4 spacje dla POKAZ i PYTANIE.
// Co robimy: Wstrzymujemy proces do momentu akceptacji faktury przez menedżera.
// Po co: Zapobieganie automatycznemu opłaceniu błędnych lub zbyt wysokich faktur.
AWAIT APPROVAL FROM 'Menedżer Działu' ON FAKTURY_DO_OPLACENIA
POKAZ: 'Kontrahent', 'Kwota_Brutto', 'Opis_Uslugi'
PYTANIE: 'Czy akceptujesz tę fakturę do płatności?'
AS FAKTURY_ZATWIERDZONE
Timeout (opcjonalnie):
AWAIT APPROVAL FROM 'Kierownik Finansów' ON PRZYGOTOWANE_WYSYLKI
POKAZ: 'Klient', 'Kwota', 'Tresc_Maila'
PYTANIE: 'Czy wysłać to przypomnienie VIP?'
TIMEOUT: 2 HOURS THEN SKIP
AS ZATWIERDZONE_VIP
Składnia:
SEND <DATASET> TO API '<url>' [parametry]
// Co robimy: Wysyłamy przetworzone leady do systemu Salesforce przez API.
// Po co: Aby dział sprzedaży mógł natychmiast rozpocząć kontakt z nowymi klientami.
SEND GOTOWE_LEADY TO API 'https://salesforce.com/api/leads/bulk' (Method: POST, Headers: {'Authorization': 'Bearer TOKEN_VAR'})
ON ERROR: RETRY 5 TIMES THEN ALERT 'Admin CRM'
Uwaga: TOKEN_VAR to referencja do zmiennej środowiskowej (poza zakresem języka).
SEND EMAIL TO '<recipient>' SUBJECT '<temat>' BODY '<treść>'
// Co robimy: Wysyłamy email z linkiem do raportu.
// Po co: Aby zarząd miał dostęp do wyników z rana.
SEND EMAIL TO 'prezes@firma.pl' SUBJECT 'Raport Dzienny' BODY 'Dostępny pod: https://raport.pl/daily'
SEND ALERT '<wiadomość>' TO '<kanał>'
// Co robimy: Wysyłamy powiadomienie na komunikator Slack.
// Po co: Aby poinformować zespół operacyjny o wykryciu anomalii w danych.
SEND ALERT 'Uwaga: Wykryto 5 transakcji powyżej 1mln PLN!' TO 'Slack / #finanse-alerty'
SEND <DATASET> TO FILE '<ścieżka>' FORMAT <CSV|XLSX|JSON>
// Co robimy: Eksportujemy dane do pliku CSV.
// Po co: Aby użytkownik mógł pobrać wyniki do Excela.
SEND RAPORT_KONCOWY TO FILE '/exports/raport_sprzedaz_Q4.csv' FORMAT CSV
Składnia: ON ERROR: <strategia>
Strategie:
| Strategia | Znaczenie | Kontynuacja procesu |
|---|---|---|
STOP |
Natychmiastowe zakończenie całego procesu | Nie |
STOP AND ALERT '<rola>' |
Zakończenie + powiadomienie | Nie |
SKIP |
Pomiń ten krok, kontynuuj dalej | Tak |
RETRY <n> TIMES THEN SKIP |
Ponów n razy, potem pomiń | Tak |
RETRY <n> TIMES THEN STOP |
Ponów n razy, potem zakończ | Warunkowe |
RETRY <n> TIMES THEN ALERT '<rola>' |
Ponów n razy, potem powiadom | Tak |
Zakres działania: ON ERROR odnosi się do bezpośrednio poprzedzającego polecenia.
// Co robimy: Pobieramy dane z API, które może być niestabilne.
// Po co: Aby uzyskać aktualne kursy walut do przeliczeń.
LOAD API FROM 'https://api.nbp.pl/exchangerates/tables/A/' AS KURSY_WALUT
ON ERROR: RETRY 3 TIMES THEN SKIP
Semantyka SKIP: Proces kontynuuje, ale zmienna KURSY_WALUT będzie NULL. Użytkownik musi obsłużyć ten przypadek:
IF KURSY_WALUT IS NULL THEN
// Co robimy: Używamy wczorajszych kursów.
// Po co: Aby proces mógł kontynuować mimo awarii NBP.
SET KURSY_WALUT = WCZORAJSZE_KURSY
END IF
Semantyka STOP: Cały proces kończy się, żadne kolejne instrukcje nie są wykonywane.
Składnia:
STOP PROCESS [(Log: '<wiadomość>')]
// Co robimy: Kończymy proces przedwcześnie.
// Po co: Brak danych oznacza brak pracy do wykonania.
STOP PROCESS (Log: 'Brak nowych faktur, proces zakończony')
Uwaga: Koniec pliku oznacza naturalny koniec procesu - nie trzeba pisać STOP PROCESS na końcu.
- SET: Deklaruje nową zmienną - niemutowalną przez SET (nie można ponownie użyć SET na tej samej nazwie)
- OVERRIDE: Nadpisuje istniejącą zmienną - mutowalną (można wielokrotnie nadpisywać)
- Zakres: Globalny (widoczne w całym procesie)
SET COUNTER = 0
OVERRIDE COUNTER = 1 // ✅ OK
OVERRIDE COUNTER = 2 // ✅ OK - można wielokrotnie nadpisywać
SET COUNTER = 3 // ❌ BŁĄD - nie można użyć SET na istniejącej zmiennej
- Niemutowalne - każda transformacja tworzy nowy dataset
- Zakres: Lokalny (widoczne od momentu utworzenia do końca procesu)
- Nadpisywanie: Dozwolone, ale stary dataset przestaje istnieć
FILTER DATA WHERE x > 10 AS RESULT
FILTER RESULT WHERE y < 5 AS RESULT // ✅ OK: RESULT zostaje zastąpiony
FILTER DATA WHERE z > 0 AS RESULT // ✅ OK: RESULT znowu zastąpiony
Dobra praktyka: Używaj unikalnych nazw dla jasności:
FILTER DATA WHERE x > 10 AS DATA_STEP1
FILTER DATA_STEP1 WHERE y < 5 AS DATA_STEP2
Wszystkie operacje wykonują się sekwencyjnie (jedna po drugiej) w kolejności zapisania.
Brak równoległości w samym języku - jeśli środowisko wykonawcze obsługuje równoległość (np. przetwarzanie wsadowe), jest to transparentne dla użytkownika.
Każde polecenie jest atomowe - albo wykonuje się w całości, albo w ogóle (i wywołuje błąd).
Brak częściowych rezultatów: Jeśli LOAD pobierze 500 z 1000 rekordów i padnie, zwrócony zostanie błąd (nie dataset z 500 rekordami).
Język nie obsługuje transakcji ani rollbacku. Jeśli proces wykonał 10 kroków i padł na 11., pierwsze 10 zostaje (np. wysłane emaile, zapisane pliki).
Odpowiedzialność użytkownika: Projektuj procesy idempotentne lub używaj zewnętrznych mechanizmów transakcyjnych.
// TRIGGER: SCHEDULE EVERY DAY AT 09:00 CET
// --- Faza 1: Inicjalizacja i Dane ---
// Co robimy: Ustalamy dzisiejszą datę do obliczeń opóźnień.
// Po co: Aby precyzyjnie wyliczyć liczbę dni po terminie.
SET DZISIAJ = CURRENT_DATE()
// Co robimy: Pobieramy z ERP faktury niezapłacone, z terminem płatności < DZISIAJ.
// Po co: Aby zidentyfikować klientów zalegających z płatnościami.
LOAD API FROM 'https://erp.firma.pl/api/invoices?status=unpaid' AS WSZYSTKIE_ZALEGLOSCI
ON ERROR: STOP AND ALERT 'Admin ERP'
// Co robimy: Filtrujemy tylko te przeterminowane o ponad 7 dni.
// Po co: Dajemy klientom tydzień "okresu karencji" zanim zaczniemy windykację.
FILTER WSZYSTKIE_ZALEGLOSCI WHERE Termin_Platnosci <= SUBTRACT_DAYS(DZISIAJ, 7) AS ZALEGLOSCI_DO_WINDYKACJI
// --- Faza 2: AI i Decyzje ---
// Co robimy: Prosimy AI o wygenerowanie spersonalizowanej, grzecznej treści maila dla każdego klienta.
// Po co: Aby komunikacja była dopasowana do klienta, a nie brzmiała jak robot.
ASK_AI on ZALEGLOSCI_DO_WINDYKACJI:
ZADANIE: 'Napisz krótkie przypomnienie o fakturze [Nr_Faktury] na kwotę [Kwota]. Użyj uprzejmego tonu.'
ZWROC: Tresc_Maila
AS PRZYGOTOWANE_WYSYLKI
// Co robimy: Dzielimy proces w zależności od kwoty zaległości.
// Po co: Duże kwoty są ryzykowne relacyjnie, chcemy by człowiek rzucił okiem przed wysyłką.
IF PRZYGOTOWANE_WYSYLKI.Kwota > 10000 EUR THEN
// --- Ścieżka VIP (wymaga akceptacji) ---
// Co robimy: Wstrzymujemy proces dla tych rekordów i wysyłamy prośbę do Kierownika Finansów.
// Po co: Uniknięcie pomyłki przy kluczowych klientach.
AWAIT APPROVAL FROM 'Kierownik Finansów' ON PRZYGOTOWANE_WYSYLKI
POKAZ: 'Klient', 'Kwota', 'Tresc_Maila'
PYTANIE: 'Czy wysłać to przypomnienie VIP?'
AS ZATWIERDZONE_VIP
// Co robimy: Wysyłamy tylko zatwierdzone maile VIP.
// Po co: Aby dotrzeć do kluczowych klientów z potwierdzoną komunikacją.
SEND ZATWIERDZONE_VIP TO API 'https://mail.firma.pl/send'
ELSE
// --- Ścieżka Standard (automatyczna) ---
// Co robimy: Wysyłamy maile automatycznie dla mniejszych kwot.
// Po co: Oszczędność czasu menedżera przy rutynowych przypomnieniach.
SEND PRZYGOTOWANE_WYSYLKI TO API 'https://mail.firma.pl/send'
END IF
// TRIGGER: SCHEDULE EVERY 1st DAY OF MONTH AT 00:00 UTC
// Co robimy: Ustawiamy zmienne czasowe dla poprzedniego miesiąca.
// Po co: Aby raport dotyczył kompletnego miesiąca.
SET DATA_OD = SUBTRACT_DAYS(CURRENT_DATE(), 30)
SET DATA_DO = CURRENT_DATE()
// Co robimy: Pobieramy dane sprzedażowe z bazy produkcyjnej.
// Po co: Aby przeanalizować wyniki sprzedaży za miniony miesiąc.
LOAD SQL FROM 'BazaProdukcyjna' QUERY 'SELECT * FROM sales WHERE sale_date BETWEEN @DATA_OD AND @DATA_DO' AS SPRZEDAZ
ON ERROR: RETRY 3 TIMES THEN STOP AND ALERT 'DBA Team'
// Co robimy: Wyliczamy marżę dla każdej transakcji.
// Po co: Aby zidentyfikować najbardziej i najmniej rentowne produkty.
CALCULATE on SPRZEDAZ:
Marza_PLN = Cena_Sprzedazy - Koszt_Zakupu
Marza_Procent = (Marza_PLN / Cena_Sprzedazy) * 100
AS SPRZEDAZ_Z_MARZA
// Co robimy: Filtrujemy transakcje z ujemną marżą.
// Po co: Aby zidentyfikować produkty sprzedawane ze stratą.
FILTER SPRZEDAZ_Z_MARZA WHERE Marza_PLN < 0 AS PRODUKTY_STRATNE
// Co robimy: Sprawdzamy, czy są produkty ze stratą.
// Po co: Aby zdecydować, czy trzeba wysłać alert do zarządu.
IF PRODUKTY_STRATNE IS NOT EMPTY THEN
// Co robimy: Wysyłamy alert o produktach ze stratą.
// Po co: Aby zarząd mógł szybko zareagować na problem.
SEND ALERT 'Uwaga: Wykryto produkty sprzedawane ze stratą!' TO 'Slack / #management-alerts'
// Co robimy: Eksportujemy szczegóły do pliku.
// Po co: Aby zarząd miał pełne dane do analizy.
SEND PRODUKTY_STRATNE TO FILE '/reports/produkty_stratne_miesiac.xlsx' FORMAT XLSX
END IF
// Co robimy: Generujemy podsumowanie całościowe.
// Po co: Aby uzyskać kluczowe metryki sprzedażowe miesiąca.
CALCULATE on SPRZEDAZ_Z_MARZA:
Suma_Sprzedazy = SUM(Cena_Sprzedazy)
Suma_Marzy = SUM(Marza_PLN)
Srednia_Marza_Procent = AVG(Marza_Procent)
AS RAPORT_MIESIAC
// Co robimy: Wysyłamy raport miesięczny emailem.
// Po co: Aby dyrektor sprzedaży otrzymał automatyczny raport.
SEND EMAIL TO 'dyrektor.sprzedazy@firma.pl' SUBJECT 'Raport Miesięczny Sprzedaży' BODY 'Suma sprzedaży: [Suma_Sprzedazy], Marża: [Suma_Marzy]'
Przed wdrożeniem procesu, sprawdź:
- Każde polecenie operacyjne ma
// Co robimy:i// Po co: - Wszystkie zmienne i datasety używają
UPPER_SNAKE_CASE - Kolumny są w PascalCase lub w apostrofach (jeśli zawierają spacje)
- Stringi literalne są w pojedynczych apostrofach
'...' - Wszystkie operacje ryzykowne (LOAD, SEND, ASK_AI) mają
ON ERROR - Warunki IF używają
AND/OR/NOT(nie&&/||/!) - Bloki CALCULATE, GROUP i ASK_AI mają wcięcia 4 spacje
- Każdy JOIN ma określony typ (INNER/LEFT/RIGHT/FULL)
- Każdy SORT ma określony kierunek (ASC/DESC) dla każdej kolumny
- AWAIT APPROVAL ma TIMEOUT jeśli proces nie może czekać w nieskończoność
- Używasz SET do deklaracji, OVERRIDE do nadpisania (nie używaj SET dwukrotnie na tej samej zmiennej)
- Proces ma jasny TRIGGER na początku
- Jeśli proces używa zewnętrznych systemów, rozważ dodanie sekcji SYSTEMS
- Jeśli używasz SKIP w ON ERROR, obsługujesz przypadek NULL