sailing Foudra MRP
Podręcznik chevron_rightapi/index

API

JSON przez HTTPS w subdomenie firmy, z uwierzytelnieniem kluczem API w postaci Authorization: Bearer fdr_…. Klucz jest ograniczony swoim zakresem: klucze read mogą tylko GET, klucze write mogą wszystko poza zatwierdzeniem lub wysłaniem zamówienia zakupu, a klucze approve mogą także to. Liczby dziesiętne (ilości, kwoty) są napisami — liczby w JSON są zmiennoprzecinkowe, a te takie nie są. Błędy mają postać {"error": "…"} z odpowiednim kodem statusu.

Aplikacja używana przez wiele firm (Syncomatic, aplikacja na telefon) zamiast klucza korzysta z OAuth 2.0, aby każdy użytkownik sam na nią pozwalał i mógł ją odłączyć w Connected apps (połączone aplikacje). Zob. OAuth na końcu tej strony; jej tokeny dostępu (fdo_…) wysyła się tak samo jak klucze i obowiązują je te same zakresy.

Odczyt

Punkt końcowy Zwraca
GET /api/parts · /api/parts/{id} Części, stronicowane (?page=&per=); ?category=504 zawęża do kategorii, ?q= przeszukuje numer, nazwę i opis; część zawiera on_hand
GET /api/parts/{id}/barcode.png Numer części jako kod kreskowy Code 128 w PNG, z numerem wydrukowanym pod spodem; ?scale=1–10, ?height= (piksele), ?text=0 dla samych kresek. JSON każdej części podaje tę ścieżkę jako barcode_url
GET /api/parts/{id}/bom Pozycje zestawienia materiałowego części: komponent, ilość na sztukę, oznaczenie referencyjne, operacja marszruty
GET /api/parts/{id}/routing Operacje marszruty części, z sumami
GET /api/parts/{id}/images Obrazy części: id, nazwa pliku, znacznik obrazu głównego
GET /api/suppliers Dostawcy
GET /api/parts/{id}/sourcing Kto sprzedaje część, pod jakim numerem i za ile
GET /api/demand?status=open&channel= Zapotrzebowania z kanałem, numerem referencyjnym, ilością i ilością wysłaną
GET /api/channels Kanały
GET /api/channels/{code}/listings Wszystkie części wystawione w kanale z numerem części w kanale; null oznacza, że jeszcze nie wysłano
GET /api/stock Jeden wiersz na część, lokalizację i partię (lot_id, lot_code, expires_at; zapas nieśledzony ich nie ma)
GET /api/lots?q= Partie i numery seryjne, od najnowszych: kod, część, stan dostępny, data ważności, partia dostawcy
GET /api/lots/{id}/barcode.png Kod partii lub numeru seryjnego jako PNG z Code 128, tak jak dla części; barcode_url w JSON każdej partii
GET /api/sourcing?supplier=&supplier_part_number=&part_number= Która z naszych części odpowiada numerowi dostawcy, albo odwrotnie. Wielkość liter nie ma znaczenia
GET /api/purchase-orders?status=&number=PO-00004 Zamówienia z dostawcą, sumą częściową i linkiem. number znajduje zamówienie tak, jak nazywają je ludzie
GET /api/purchase-orders/{id} Zamówienie z pozycjami
GET /api/workstations · /api/workstations/{id} Stanowiska z grupą i statusem
GET /api/workstations/{id}/next Praca, którą to stanowisko powinno teraz wykonać, z materiałami — null, chyba że stanowisko jest wolne i jakaś operacja jest gotowa. ?material=<part id> ogranicza do pracy zużywającej tę część. Odpowiedź zawiera current_material stanowiska i requires_material_change
GET /api/work-orders?status=&number=WO-00023&external_ref= · /api/work-orders/{id} Zlecenia; pojedyncze zlecenie zawiera operacje oraz materiały i gotowość każdej z nich. Każde otwarte zlecenie ma prognozę z harmonogramu w przód: expected_start, expected_finish, late (zakończenie po due_at) i delay_reasons; porównaj expected_finish z due_at, aby zobaczyć poślizg. Zlecenia zakończone i anulowane nie mają prognozy (null)
GET /api/{parts,workstations,work-orders}/{id}/custom Nieprzezroczysty JSON albo null

Zapis

Punkt końcowy Treść Działanie
POST /api/purchase-orders {supplier, currency?, exchange_rate?, expected_at?, external_ref?, notes?, lines:[{supplier_part_number | part_id, quantity, unit_price?}]} Tworzy szkic; pozycje podane numerem dostawcy są rozwiązywane przez źródła zaopatrzenia. Nieznany numer powoduje odrzucenie całego żądania ze wskazaniem pozycji. Zatwierdza człowiek
PUT /api/workstations/{id}/status {"status": "idle|busy|blocked|offline", "note"?} Odzwierciedla stan maszyny
POST /api/work-order-steps/{id}/start {"workstation_id"?} Podejmuje operację
POST /api/work-order-steps/{id}/complete {"quantity": "9", "consumed"?: [{"part_id", "quantity"}], "used"?: [{"part_id", "lots": ["R-2291"]}], "made"?: ["G0088"], "expires"?: "2027-03-31"} Rejestruje wykonane sztuki; rozchodowuje materiały automatycznie według normy z zestawienia materiałowego albo według zgłoszonego rzeczywistego zużycia dla części wymienionych w consumed; ostatnia operacja przyjmuje wyrób. Dla części śledzonych: used wskazuje zużyte partie lub numery seryjne, made numery seryjne wyrobu (jeden na sztukę, wymagane) albo jego partię (domyślnie numer zlecenia), expires datę ważności nowej partii wyrobu
POST /api/work-order-steps/{id}/scrap {"quantity": "9", "consumed"?: […], "used"?: […], "note"?} Przebieg, który niczego nie wytworzył: materiały zużyte, brak wyrobu, licznik operacji bez zmian
POST /api/work-order-steps/{id}/pause Trwająca operacja z pozostałą ilością wraca do oczekujących, z zachowaniem postępu i zwolnieniem stanowiska — następna praca jest wybierana od nowa. Menedżer druku robi tak po każdym stole
PUT /api/workstations/{id}/material {"part_id": 934} lub {"part_id": null} Czym stanowisko jest uzbrojone — filament załadowany do drukarki
PUT /api/tasks/{source} [{key, priority, message, href, actions?: [{key, label, tone?}]}] Zastępuje w całości zestaw zadań tego źródła; wszystko, czego brak, zostaje zamknięte. Nazwy źródeł to identyfikatory pisane małymi literami. Zadanie może oferować do sześciu akcji — przycisków, które człowiek może nacisnąć; naciśnięcie zamyka zadanie z kluczem akcji jako outcome, który źródło odczytuje. Zadanie z wynikiem nigdy nie jest ponownie otwierane: dla nowego wystąpienia użyj nowego klucza
GET /api/tasks/{source} Otwarte zadania tego źródła i niedawno zamknięte z outcome — w ten sposób integracja dowiaduje się, który przycisk naciśnięto
PUT /api/{kind}/{id}/custom Sam JSON Zastępuje nieprzezroczysty blok (maks. 64 KB)
POST /api/parts {category_code, name, part_type, description?, notes?, unit_of_measure?, is_stocked?, minimum_stock?, lead_time_days?, tracking?, custom?} Tworzy część; numer magazynowy jest nadawany w kategorii. Zwraca część
POST /api/parts/{id}/bom/lines {child_part_id, quantity, reference?, notes?, routing_step_id?} Dodaje pozycję zestawienia materiałowego; obowiązują reguły dotyczące cykli
PUT /api/bom/lines/{id} {quantity?, reference?, notes?} Edytuje pozycję
POST /api/parts/{id}/routing/steps {name, group_code?, seq?, setup_minutes?, run_minutes_per_unit?, batch_size?, instructions?} Dodaje operację marszruty; puste seq przyjmuje kolejną dziesiątkę
POST /api/parts/{id}/images multipart, pole file Wysyła obraz (maks. 12 MB); pierwszy staje się głównym
PUT /api/parts/{id}/sourcing/{supplier} {supplier_part_number, unit_price?, currency?, product_url?, minimum_order_qty?, pack_size?, lead_time_days?, is_preferred?, notes?} Tworzy wiersz dostawcy dla części (201) albo go aktualizuje (200); niewysłane pola zachowują wartość. Pierwsze źródło części staje się preferowanym
POST /api/demand {channel, reference, part_id | part_number | channel_part_number, quantity, due_at?, kind?, notes?} Zapotrzebowanie przyjęte. Idempotentne względem (channel, reference): powtórzenie, gdy zapotrzebowanie jest otwarte, aktualizuje ilość i datę i odpowiada 200; pierwsze nadejście odpowiada 201. Zamknięty lub anulowany numer referencyjny pozostaje bez zmian
POST /api/demand/{id}/ship {quantity?, location_id? | location_code?, lots?} Zapotrzebowanie wysłane. Wydaje tę ilość (puste: resztę) z lokalizacji lub z największej pozycji części jako ruch w rejestrze odwołujący się do zapotrzebowania; zamyka zapotrzebowanie, gdy wszystko zostało wysłane. lots wskazuje wysłane partie lub numery seryjne, gdziekolwiek są: wymagane dla wyrobu śledzonego numerami seryjnymi. 409, gdy stan dostępny jest niewystarczający
POST /api/demand/{id}/cancel Zapotrzebowanie anulowane
PUT /api/channels/{code}/listings/{part_id} {channel_part_number?, is_listed?, notes?} Wystawia część w kanale albo zapisuje dla niej numer kanału

Aby dostawać powiadomienia o zdarzeniach zamiast pytać, zob. webhooki.

Dla asystenta

Czego potrzebuje agent czatu, aby pilnować, żeby człowiek trzymał się planu: jedno wywołanie „co czeka na mnie”, wyszukiwanie po nazwach, których używają ludzie, i przyciski. Każdy element ma url, który człowiek może kliknąć.

Punkt końcowy Zwraca
GET /api/brief Wszystko, co wymaga teraz uwagi: otwarte zadania, stany stanowisk, szkice i otwarte zlecenia produkcyjne, zamówienia zakupu czekające na zatwierdzenie, zatwierdzone, ale niewysłane, i przeterminowane, zlecenia planowane do zatwierdzenia, potrzebne przesunięcia, braki oraz praca, która według kontroli zdolności produkcyjnej skończy się z opóźnieniem. headline mówi to samo w kilku zdaniach, od najpilniejszego
GET /api/tasks Wszystkie otwarte zadania, od najpilniejszego, z ich actions
GET /api/planning Strona planowania: zlecenia planowane proposed i firmed (każde podaje because, z jakiego powodu) oraz harmonogram każdej grupy stanowisk z opóźnioną pracą
GET /api/demand/{id}/progress „Gdzie jest moje zamówienie?”: zapotrzebowanie, zapas części i nadchodzące dostawy, propozycje przebiegu planowania dla tego zamówienia, zlecenia produkcyjne, które je wykonują, i które z nich są zaplanowane z opóźnieniem. Podaż należy do części, a nie do zamówienia: dwa zamówienia na jedną część widzą ten sam zapas

Działania

Punkt końcowy Treść Wymaga Działanie
POST /api/tasks/{id}/act {"action": "ready"} operator Naciska jeden z przycisków zadania
POST /api/tasks/{id}/snooze {"days": 2} operator Ukrywa zadanie; na tydzień, gdy nie podano liczby
POST /api/tasks/{id}/resolve operator Zamyka zadanie ręcznie
POST /api/work-orders {part_number | part_id, location, quantity?, external_ref?, due_at?, priority?, notes?, output_location?, release?} planista Tworzy zlecenie produkcyjne: szkic albo uruchomione, gdy release ma wartość true. location to kod miejsca, z którego pobiera części (i do którego odkłada wyrób, chyba że output_location jest inne). external_ref to twoja nazwa tego, czego dotyczy zlecenie. Wyślij Idempotency-Key, inaczej ponowienie utworzy drugie zlecenie
POST /api/work-orders/{id}/issue {part_number | part_id, quantity?, note?, lots?} operator Pobiera część z magazynu dla zlecenia, poza jego zestawieniem materiałowym; lots wskazuje partie lub numery seryjne. 409, gdy jest jej za mało
POST /api/work-orders/{id}/release planista Ze szkicu do uruchomionego. Odpowiada zleceniem i gotowością każdej operacji
POST /api/work-orders/{id}/cancel planista Anuluje niezakończone zlecenie
POST /api/planning/run planista Uruchamia planowanie teraz. Odpowiada propozycjami
POST /api/planning/firm {"ids": [12, 13], "location": "BHILL"} planista Zamienia propozycje w szkice zleceń produkcyjnych i zamówień zakupu. location (lub location_id) jest potrzebne, gdy wśród nich jest produkcja
POST /api/purchase-orders/{id}/cancel planista Anuluje zamówienie
POST /api/purchase-orders/{id}/approve administrator, klucz approve Ze szkicu do zatwierdzonego
POST /api/purchase-orders/{id}/send planista, klucz approve Rejestruje zamówienie jako wysłane. Foudra nie przekazuje go dostawcy

Do każdego z nich stosują się cztery zasady:

Każda odpowiedź zawiera summary — zdanie, które można powtórzyć człowiekowi — data i acted_as. Odmowy: 403 z powodu zakresu klucza lub roli użytkownika, 404, 409, gdy obiekt nie jest w stanie, który na to pozwala (komunikat mówi dlaczego), 422 dla błędnej akcji lub ponownie użytego klucza.

Schemat farmy drukarek

Gdy stanowisko jest wolne, odpytuj /next?material=<loaded> — czy jest coś gotowego z tego, co załadowane? Jeśli nie, /next bez filtra mówi, czego potrzebuje pierwsza praca w kolejce; przekaż zadanie change filament (zmień filament) i przejdź w stan zablokowany. Człowiek zakłada szpulę, ustawia Loaded material (załadowany materiał) na stronie stanowiska i naciska Ready. Potem start; druk; complete z ilością i rzeczywiście zużytym filamentem; ponownie status idle. Nieudany wydruk to scrap, a nie zakończenie z ilością zero. MRP nic nie wie o drukarkach.

Zapisy przez API są rejestrowane na użytkownika, który utworzył klucz, chyba że X-On-Behalf-Of wskazuje kogoś innego (zob. wyżej).

OAuth

Dla aplikacji działającej w imieniu użytkowników dowolnej firmy. Aplikacje rejestruje Foudra (poproś przez pomoc techniczną): dostajesz client_id, sekret dla aplikacji działającej po stronie serwera, a twoje dokładne adresy przekierowania (redirect URI) zostają zapisane. Przepływ to przyznanie kodu autoryzacji z PKCE (RFC 6749, RFC 7636), a PKCE z S256 jest wymagane od każdego klienta.

  1. Skieruj przeglądarkę na https://foudra.com/oauth/authorize?response_type=code&client_id=…&redirect_uri=…&scope=read%20write&state=…&code_challenge=…&code_challenge_method=S256. Użytkownik wskazuje firmę, loguje się w niej i widzi, o co prosisz.
  2. Po Allow przeglądarka wraca na redirect_uri?code=…&state=…; po Deny na ?error=access_denied. Kod jest ważny 10 minut i działa raz.
  3. POST https://foudra.com/oauth/token, kodowany jako formularz, z uwierzytelnieniem klienta przez HTTP Basic (klient publiczny zamiast tego wysyła client_id w formularzu): grant_type=authorization_code&code=…&redirect_uri=…&code_verifier=…. Odpowiedź:
{"access_token": "fdo_…", "token_type": "Bearer", "expires_in": 3600,
 "refresh_token": "fdor_…", "scope": "read write",
 "company": {"slug": "acme", "name": "Acme Widgets", "api_base_url": "https://acme.foudra.com"}}

Wywołuj API pod api_base_url z Authorization: Bearer fdo_…: token działa tylko tam. Wygasły lub unieważniony token dostaje 401 z WWW-Authenticate: Bearer error="invalid_token".

  1. Odśwież token przed upływem godziny lub po nim przez grant_type=refresh_token&refresh_token=fdor_…. Tokeny odświeżania rotują: każde użycie zwraca nowy i zużywa stary. Przedstawienie zużytego tokenu odświeżania jest traktowane jako kradzież i unieważnia wszystkie tokeny z tego logowania; użytkownik musi ponownie na ciebie pozwolić. Dlatego zapisz nowy token odświeżania, zanim użyjesz nowego tokenu dostępu, i nigdy nie ponawiaj odświeżenia starym. Token odświeżania nieużywany przez 90 dni wygasa.
  2. POST https://foudra.com/oauth/revoke z token=… (RFC 7009), gdy użytkownik odłączy się po twojej stronie. Unieważnienie tokenu odświeżania unieważnia także jego tokeny dostępu.

Zakresy to zakresy kluczy API: read, write, approve, każdy obejmujący poprzedni. To, co dostajesz, jest ograniczone twoją rejestracją i rolą użytkownika (przeglądający i operatorzy read; planiści write; administratorzy i właściciele approve), a ograniczenie jest stosowane ponownie przy każdym żądaniu, więc użytkownik zdegradowany lub usunięty zabiera ze sobą twój dostęp. scope w odpowiedzi z tokenem mówi, co przyznano. Zapisy są przypisywane użytkownikowi, a operacje zwracają "acted_as": {"user": …, "application": …}. X-On-Behalf-Of działa tak samo jak przy kluczach.

Błędy punktu końcowego tokenu są zgodne z RFC 6749: {"error": "invalid_grant", "error_description": "…"} ze statusem 400 albo invalid_client z 401.