Przejdź do głównej zawartości

Zamówienia do księgowości

Dla: Programista

System księgowy albo ERP potrzebuje zamówień z cenami, kosztami dodatkowymi i płatnościami, a potem ich zmian. Ten przepis pobiera przy pierwszym uruchomieniu wszystkie zamówienia, a przy kolejnych, np. co 5–15 minut, tylko te, które zmieniły się od poprzedniego razu.

  • Klucz z zakresami orders:read i prices:read. Bez prices:read pola z kwotami (ceny produktów i operacji, narzuty, koszty dodatkowe, kwoty płatności, cena transportu i kursy walut) są niedostępne, a zapytanie, które ich używa, API odrzuca błędem API_SCOPE_MISSING.
  • Klienta API z przepisu Obsługa błędów i ponowień. Kod niżej wysyła żądania przez niego.
  • Miejsce na znacznik synchronizacji, np. plik albo wiersz w bazie: jeden ciąg znaków z datą.

Order.updatedAt zmienia się przy każdej zmianie zamówienia albo czegokolwiek w nim: produktów, ich elementów i operacji, płatności, kosztów dodatkowych, plików, osoby kontaktowej, dostaw i danych do faktury. Filtr updatedFrom zwraca zamówienia zmienione w tej chwili lub później, a sortowanie UPDATED_AT rosnąco ustawia je w kolejności zmian.

  1. Pierwsza synchronizacja pobiera wszystkie zamówienia, także archiwalne: updatedFrom jest wtedy pusty.

  2. Zapisuj zamówienie po id: wstaw je albo zastąp w całości, razem z listą produktów, kosztów i płatności. Produkt albo płatność usunięte w Prynt po prostu znikają z listy zamówienia.

  3. Kolejną porcję zaczynaj od updatedAt ostatniego pobranego zamówienia, nie od następnego numeru strony. Zamówienie zmienione w trakcie przeskakuje na koniec listy, a przy numerach stron przesunęłoby pozostałe i jedno z nich by umknęło. Kolejną stronę z tym samym updatedFrom bierz tylko wtedy, gdy cała strona ma ten sam updatedAt.

  4. Po udanym końcu zapisz znacznik: najpóźniejszy updatedAt, jaki widziałeś. Następna synchronizacja zaczyna od znacznika minus 5 minut. Zapas łapie zmiany zapisane tuż przed poprzednią synchronizacją, a widoczne w API dopiero po niej.

Zamówienie pobrane dwa razy niczego nie psuje, bo zapisujesz je po id. Przerwana synchronizacja nie zapisuje znacznika, więc następna po prostu powtórzy się od poprzedniego.

updatedAt przesuwa tylko prawdziwa zmiana. Jeśli Twój system odsyła do Prynt te same wartości, które pobrał (np. przy synchronizacji w obie strony), zamówienie nie wróci w następnej porcji. Zmiana zapisana przez Twoją integrację wróci tak samo jak zmiana z Prynt.

  • Produkt: price to cena netto całej pozycji (wszystkich sztuk razem), priceMarkup to narzut kwotowy, więc wartość netto produktu to price + priceMarkup. taxRate jest ułamkiem, np. 0.23 dla 23%.
  • Koszty dodatkowe: amount netto i taxRate jako ułamek. Koszt z productId dotyczy produktu, a bez niego całego zamówienia.
  • Waluta: kwoty zamówienia są w jego walucie (currencyId), a exchangeRate przelicza je na walutę drukarni. Na starszych zamówieniach oba pola są puste: to waluta drukarni.
  • Płatności: amount w walucie płatności (currency), z datą paidAt.
  • Pozycje bez faktury: produkt z noInvoice: true drukarnia oznaczyła jako niefakturowany.

Zapytanie pobiera 25 zamówień na stronę: z produktami, kosztami i płatnościami mieści się w limicie kosztu zapytania. Jeśli dodasz pola z listami, zmniejsz stronę. Zobacz Limity.

Przykłady pokazują adres zastępczy. Wpisz adres swojej drukarni, żeby go podmienić.

Wymaga curl, jq i skryptu prynt-call.sh z przepisu Obsługa błędów i ponowień w tym samym katalogu. Każde zamówienie wypisuje jako jedną linię JSON, a znacznik trzyma w pliku ostatnia-synchronizacja.txt.

sync-orders.sh
#!/usr/bin/env bash
# sync-orders.sh — pobiera zamówienia zmienione od poprzedniej synchronizacji (curl, jq; prynt-call.sh
# z przepisu „Obsługa błędów i ponowień”). Każde zamówienie wypisuje jako jedną linię JSON, którą
# zapisujesz u siebie po `id`: ./sync-orders.sh >>zamowienia.ndjson
# Znacznik synchronizacji trzyma w pliku ostatnia-synchronizacja.txt i zapisuje go dopiero na końcu.
set -euo pipefail
cursor_file=ostatnia-synchronizacja.txt
# 25 zamówień na stronę: z produktami, kosztami i płatnościami mieści się w limicie kosztu zapytania.
query='query ChangedOrders($from: DateTime, $page: Int!) {
orders(
filter: { updatedFrom: $from }
sort: { field: UPDATED_AT, direction: ASC }
pagination: { page: $page, pageSize: 25 }
) {
items {
id displayId status updatedAt clientId billingEntityId
currencyId exchangeRate transportPrice paymentMethod paymentStatus paymentDueDate
invoice { companyName nip street zip city country }
products { id number name quantity unit price priceMarkup taxRate noInvoice }
additionalCosts { id productId name amount taxRate }
payments { id amount currency paidAt method reference }
}
pageInfo { hasNextPage }
}
}'
since=$(cat "$cursor_file" 2>/dev/null || true)
from=null # wartość JSON zmiennej $from: null przy pierwszej synchronizacji (wszystkie zamówienia)
if [[ -n $since ]]; then
# 5 minut zapasu na zmiany zapisane tuż przed poprzednią synchronizacją, a widoczne po niej.
from=$(jq -n --arg since "$since" '$since | sub("\\.[0-9]+Z$"; "Z") | fromdateiso8601 - 300 | todateiso8601')
fi
body=$(mktemp) result=$(mktemp)
trap 'rm -f "$body" "$result"' EXIT
latest=$since page=1
while :; do
jq -n --arg query "$query" --argjson from "$from" --argjson page "$page" \
'{ query: $query, variables: { from: $from, page: $page } }' >"$body"
./prynt-call.sh "$body" >"$result"
jq -c '.data.orders.items[]' "$result"
# Czasy z API mają jeden format (np. 2026-10-05T10:41:02.000Z), więc porównujemy je jako tekst.
latest=$(jq -r --arg latest "$latest" \
'[$latest, .data.orders.items[].updatedAt] | map(select(. != "")) | max // ""' "$result")
[[ $(jq -r '.data.orders.pageInfo.hasNextPage' "$result") == true ]] || break
last=$(jq -c '.data.orders.items[-1].updatedAt' "$result")
# Dalej czytamy od czasu ostatniego zamówienia, nie od kolejnego numeru strony; kolejną stronę
# bierzemy tylko wtedy, gdy cała strona ma ten sam czas zmiany.
if [[ $last == "$from" ]]; then
page=$((page + 1))
else
from=$last page=1
fi
done
if [[ -n $latest ]]; then echo "$latest" >"$cursor_file"; fi
Okno terminala
./sync-orders.sh >>zamowienia.ndjson # wczytaj linie do księgowości, zapisując po id
  • Zamówienia tylko w wybranych statusach odfiltruj u siebie. Filtr statusIn w zapytaniu pominąłby zamówienie, które z takiego statusu wyszło, i Twoja kopia zostałaby nieaktualna.
  • Wspólne zasady list i synchronizacji: Paginacja, filtry i synchronizacja.