Fide-Soft
Architektura i wdrożenia

Aplikacja mobilna bez zasięgu, spięta z ERP: kolejka, konflikty, podpis

Jak zbudować aplikację terenową, która działa bez internetu i nie psuje danych w ERP: lokalny zapis, kolejka zmian, rozwiązywanie konfliktów, zdjęcia i podpis, idempotencja po stronie ERP, monitoring i lista kontrolna.

· 8 min czytania

Serwisant kończy przegląd w kotłowni w piwnicy. Zasięgu nie ma. Klient podpisuje protokół na telefonie, serwisant robi zdjęcie tabliczki znamionowej i jedzie dalej. Godzinę później, gdzieś na trasie, telefon łapie sieć i wszystko ma trafić do systemu: wizyta jako zamknięta, protokół jako PDF, pozycje do faktury, a w firmie z ERP także zużyte części jako rozchód z magazynu.

Pracę bez zasięgu, podpis i protokół obsługuje Aplikacja Serwisanta, którą budujemy dla instalatorów. Ten tekst opisuje, co musi zrobić aplikacja terenowa, żeby praca bez zasięgu nie kończyła się zdublowanymi dokumentami w ERP ani nadpisanymi zmianami z biura.

Za co odpowiada aplikacja, a za co backend

flowchart LR
  subgraph TEL["Telefon"]
    ui["Ekran wizyty"] --> db[("Lokalna baza")]
    db --> q["Kolejka zmian"]
    files[("Zdjęcia, podpis, PDF")]
  end
  q -->|"gdy jest sieć"| api["Backend / API"]
  files -->|"upload z ponawianiem"| store[("Magazyn plików")]
  api --> out["Outbox do ERP"]
  out -->|"dokumenty, rozchody"| erp[("ERP")]
  erp -->|"klienci, urządzenia, cennik"| api
  api -->|"zlecenia na dziś"| db
Telefon nigdy nie rozmawia z ERP bezpośrednio. Między nimi jest backend, który kolejkuje operacje i pilnuje, żeby żadna nie wykonała się dwa razy.

Podział, który trzeba ustalić przed pierwszym ekranem:

ZadanieGdzie
Zapis wizyty, zdjęć i podpisu bez siecitelefon, lokalna baza
Kolejność i ponawianie wysyłektelefon (do backendu), backend (do ERP)
Reguły: kto może zamknąć wizytę, co trafia na fakturębackend
Numeracja dokumentówERP albo backend, nigdy telefon
Dane referencyjne (klienci, urządzenia, cennik)ERP, kopia w telefonie tylko do odczytu

Ostatni wiersz oszczędza najwięcej problemów. Telefon może mieć kopię kartoteki, żeby szukać klienta bez zasięgu, ale nie jest miejscem, w którym kartoteka się zmienia. Nowy klient założony w terenie idzie do backendu jako propozycja i dostaje identyfikator ERP dopiero po synchronizacji.

Lokalny zapis i kolejka zmian

Każda czynność użytkownika zapisuje się najpierw lokalnie i od razu jest widoczna na ekranie. Równolegle trafia do kolejki jako zmiana z:

  • identyfikatorem nadanym w telefonie (UUID), a nie przez serwer, bo serwera w tej chwili nie ma,
  • czasem utworzenia i identyfikatorem urządzenia,
  • wersją rekordu, którą telefon widział przed zmianą.

Kolejka wysyła zmiany w kolejności, po jednej albo w paczkach. Odpowiedź „przyjęto” usuwa zmianę z kolejki. Timeout nie usuwa niczego: zmiana idzie ponownie, a backend rozpoznaje ją po identyfikatorze i nie zapisuje drugi raz.

W Aplikacji Serwisanta lokalny zapis i kolejkę zapewnia baza Firestore z trwałą pamięcią podręczną na urządzeniu. Przy własnym backendzie (np. Go z PostgreSQL, jak w Gustio) tę samą rolę pełni lokalna baza w telefonie i tabela przyjętych zmian po stronie serwera.

Co widzi użytkownik

Użytkownik musi wiedzieć, co jeszcze nie poszło. Wystarczy prosty wskaźnik: „3 zmiany czekają na wysłanie”. Bez niego serwisant zamyka aplikację, wylogowuje się albo czyści dane telefonu i traci pracę z całego dnia. Wylogowanie z niewysłanymi zmianami musi być zablokowane albo wyraźnie ostrzeżone.

Konflikty: biuro i teren zmieniają to samo

sequenceDiagram
  participant T as Telefon (offline)
  participant B as Backend
  participant O as Biuro
  O->>B: zmiana terminu wizyty (wersja 4 → 5)
  T->>T: zamknięcie wizyty na wersji 4
  T->>B: zamknij wizytę, widziałem wersję 4
  B->>B: wersja na serwerze to 5: konflikt
  B-->>T: przyjęte, zmiana terminu nadpisana statusem „zamknięta”
  B->>O: powiadomienie: wizyta zamknięta po zmianie terminu
Konflikt nie jest błędem. Trzeba z góry ustalić, które pole wygrywa i kto dostaje informację.

Reguła „ostatni zapis wygrywa” na całym rekordzie jest najprostsza i najgorsza: zmiana z telefonu, który był offline od rana, nadpisze wszystko, co biuro poprawiło w tym czasie. Lepiej rozstrzygać na poziomie pól:

  • Fakty z terenu wygrywają z planem z biura. Wizyta, która się odbyła, jest zamknięta, nawet jeśli biuro w międzyczasie przesunęło jej termin.
  • Dane referencyjne wygrywają z telefonem. Cena i dane klienta pochodzą z ERP. Telefon może zaproponować zmianę, ale jej nie nadpisuje.
  • Listy łączy się, nie podmienia. Zdjęcia i zużyte części z telefonu dochodzą do tych z serwera, zamiast je zastępować.
  • Każdy konflikt zostawia ślad. Wpis w historii rekordu i powiadomienie dla osoby, której zmiana przegrała.

Zdjęcia, skany i podpis

Pliki to osobna kolejka, bo są duże, a mobilny internet na trasie bywa słaby.

  • Plik zapisuje się lokalnie z identyfikatorem, a rekord wizyty odwołuje się do tego identyfikatora, nie do adresu w chmurze, którego jeszcze nie ma.
  • Upload idzie w tle z ponawianiem. Zdjęcie zmniejszone w telefonie do rozmiaru, który wystarcza w dokumentacji, ładuje się kilka razy szybciej niż oryginał z aparatu.
  • Podpis zamyka dane. PDF protokołu z podpisem generuje się w telefonie i zapisuje razem z sumą kontrolną treści. Jeśli ktoś później zmieni pozycje wizyty, protokół musi powstać od nowa, z nowym podpisem. Podpis pod innymi danymi niż te, które klient widział, to problem przy reklamacji.

Idempotencja po stronie ERP

Backend przyjmuje zmianę z telefonu raz, ale do ERP i tak może wysłać ją dwa razy: ERP odpowiedział po czasie, worker padł w połowie, ktoś ręcznie ponowił zadanie. Dlatego do ERP nic nie idzie bezpośrednio z obsługi żądania telefonu.

  1. Zmiana z telefonu zapisuje się w bazie backendu razem z wpisem w outbox w jednej transakcji.
  2. Osobny proces bierze wpisy z outbox i wysyła je do ERP z kluczem idempotencji (np. identyfikator wizyty i typ dokumentu).
  3. Przed utworzeniem dokumentu proces sprawdza, czy dokument z tym kluczem już jest w ERP. Jeśli jest, zapisuje jego numer i kończy.
  4. ERP niedostępny: wpis zostaje w outbox i wraca z rosnącym odstępem. Telefon o tym nie wie i nie musi, bo jego część pracy jest skończona.

Ten sam wzorzec opisujemy dokładniej przy integracji z KSeF, gdzie podwójna faktura ma skutki podatkowe.

Bezpieczeństwo

  • Telefon to urządzenie, które się gubi. Lokalna baza zawiera dane klientów, więc dostęp do aplikacji wymaga logowania, a sesja wygasa. Po zgłoszeniu zgubienia telefonu konto urządzenia blokuje się na serwerze i kolejne synchronizacje są odrzucane.
  • Żadnych kluczy do ERP w aplikacji. Klucze API do ERP, KSeF i bramki SMS są tylko w backendzie. Aplikację da się rozpakować, więc wszystko, co jest w niej zapisane, trzeba traktować jak publiczne.
  • Weryfikacja aplikacji. Backend sprawdza, czy żądanie pochodzi z prawdziwej aplikacji (w Firebase: App Check), a nie ze skryptu, który podszywa się pod nią.
  • Role. Technik widzi swoje zlecenia, właściciel całą firmę, klient tylko link do swojej oferty. Uprawnienia sprawdza serwer, nie ekran aplikacji.

Co monitorować

SygnałPróg
Urządzenie z niewysłanymi zmianamidłużej niż 24 godziny
Wpisy w outbox do ERPstarsze niż 30 minut
Konflikty na rekordachkażdy, z informacją, kto przegrał
Pliki wskazane w rekordach, których nie ma w magazyniekażdy
Awarie aplikacji po nowej wersjiwzrost względem poprzedniej wersji

Pierwszy wiersz wymaga, żeby aplikacja raportowała stan kolejki przy każdym połączeniu. Bez tego telefon, który od tygodnia nie wysłał niczego, wygląda w biurze dokładnie tak samo jak telefon, którego nikt nie używa.

Lista kontrolna przed wydaniem

  1. Tryb samolotowy: pełna wizyta ze zdjęciami i podpisem, potem włączenie sieci. Wszystko dociera, nic się nie dubluje.
  2. Ta sama wizyta zmieniona w biurze i w telefonie offline: wynik zgodny z regułami, wpis w historii, powiadomienie.
  3. Zerwanie połączenia w połowie uploadu zdjęcia: plik dochodzi po wznowieniu.
  4. ERP wyłączony na godzinę: zmiany czekają w outbox i dochodzą po przywróceniu, każdy dokument raz.
  5. Próba wylogowania z niewysłanymi zmianami: blokada albo ostrzeżenie.
  6. Zmiana pozycji wizyty po podpisie wymusza nowy protokół.
  7. Zablokowane urządzenie nie może zsynchronizować danych.
  8. W kodzie aplikacji nie ma żadnego klucza do systemów zewnętrznych.

Jeżeli punkt 1 albo 4 wymaga ręcznego poprawiania danych w ERP, aplikacja nie jest gotowa do pracy w terenie.

Powiązane

Opisz, co Twoi ludzie robią w terenie i do jakiego systemu ma to trafić - odpowiem, jak wyglądałaby pierwsza wersja aplikacji i ile by kosztowała.

Co dalej

Rozpoznajesz problem? Sprawdźmy Wasz przepływ danych.

Audyt konta BaseLinker i wymiany danych z partnerami: 2 500 - 6 000 zł. Wynik to konkretny dokument z rekomendacją architektury i orientacyjnym kosztem kolejnego etapu. Bez zobowiązania kontynuacji u nas.

Umów diagnozę przepływu danych