Aplikacja obsługi reklamacji w przeglądarce i na telefonie

Aplikacja obsługi reklamacji pracuje w dwóch środowiskach naraz. Pierwsze to punkt przyjęcia, gdzie pracownik ma stałe łącze oraz komputer ze skanerem. Drugie to teren, gdzie liczy się telefon albo tablet, a zasięg bywa przypadkowy. Wymagania tych miejsc różnią się bardziej niż sama wielkość ekranu.

Według opisu producenta, powtórzonego w artykule o architekturze systemu RMA, Studio RMA.net ma interfejs oparty na jQuery i standardach HTML5 oraz CSS3, który działa w przeglądarce komputera i na urządzeniach mobilnych. Moduły na Androida i iOS służą serwisantom do rejestrowania zgłoszeń, aktualizacji statusów napraw i przeglądania historii serwisowej urządzeń. Poniższa tabela porządkuje trzy sytuacje, w których zgłoszenie trafia do rejestru.

Miejsce przyjęciaKto zgłaszaUrządzenieGłówne ograniczenie
Punkt obsługiPracownik przy kliencieKomputer i skaner kodówSzybkość wpisu, bo klient czeka przy ladzie
TerenSerwisant u odbiorcyTelefon lub tabletZasięg sieci i rozmiar zdjęć
Formularz klientaKlient samodzielnieTelefon klientaBrak przeszkolenia, więc formularz musi prowadzić

Ten tekst zajmuje się dwoma pierwszymi wierszami: przyjęciem w punkcie i pracą poza siedzibą. Zgłoszenie składane przez klienta w przeglądarce opisuje artykuł o aplikacji do reklamacji z dostępem z przeglądarki i telefonu.

Przyjęcie reklamacji w punkcie obsługi

Przy ladzie każda sekunda wpisywania jest widoczna dla klienta, więc aplikacja obsługi reklamacji ogranicza pisanie do minimum. Pracownik identyfikuje zakup po numerze dokumentu, a resztę danych pobiera program. Typowy przebieg przyjęcia składa się z czterech kroków:

  1. Identyfikacja dokumentu sprzedaży - zeskanowanie kodu z paragonu lub faktury albo wpisanie numeru ręcznie.
  2. Wybór reklamowanych pozycji - zaznaczenie towarów z dokumentu wraz z ilością, której dotyczy wada.
  3. Opis wady i zdjęcia - pole opisu z listą kontrolną oraz fotografie towaru zrobione aparatem urządzenia.
  4. Numer i etykieta - nadanie numeru RMA, potwierdzenie dla klienta i etykieta naklejana na towar.

Studio RMA.net pozwala, według opisu producenta, projektować własne pola formularza oraz dodawać checklisty i zdjęcia, więc przebieg można dopasować do kategorii towaru. Zasady numeracji i formatu numeru omawia tekst o numerze RMA, a sam moment rejestracji w bazie przedstawia artykuł o programie do rejestracji reklamacji.

Ilustracja: dwoje pracowników obsługi klienta w słuchawkach przy komputerach
Stanowisko obsługi klienta, przy którym zgłoszenie przyjmuje się z dokumentu sprzedaży.

Skaner kodów kreskowych podłączony do komputera zachowuje się jak klawiatura: wpisuje odczytany ciąg znaków w pole, w którym stoi kursor, i kończy go klawiszem Enter. Aplikacja musi więc obsłużyć zdarzenie Enter w polu identyfikacji i zaraz potem oddać kursor temu samemu polu.

<input id="kod" type="text" autocomplete="off" autofocus>
<script>
const pole = document.getElementById('kod');
pole.addEventListener('keydown', async (e) => {
  if (e.key !== 'Enter') return;
  const kod = pole.value.trim();
  pole.value = '';
  const odp = await fetch('/api/reklamacje/dokument?numer=' + encodeURIComponent(kod));
  if (odp.ok) { wypelnijFormularz(await odp.json()); }
  else { pokazKomunikat('Nie znaleziono dokumentu ' + kod); }
  pole.focus(); // skaner wpisuje tam, gdzie jest kursor
});
</script>

Reguła: pole odbierające dane ze skanera odzyskuje fokus po każdej operacji, bo inaczej odczyt trafia w przypadkowe miejsce formularza.

Adres w przykładzie i funkcje pomocnicze są umowne. Kod pomija identyfikację użytkownika i obsługę błędów sieci, które w wdrożeniu trzeba dopisać.

Dane z dokumentu sprzedaży w aplikacji obsługi reklamacji

Formularz wypełnia się szybko tylko wtedy, gdy aplikacja zna dokument sprzedaży. Dane o zakupie mieszkają w systemie ERP lub sklepowym, więc aplikacja reklamacyjna pobiera je na jeden z kilku sposobów. Wybór wpływa na to, co się dzieje, gdy system sprzedaży jest chwilowo niedostępny.

Sposób pobraniaJak działaZaletaRyzyko
Zapytanie na żądaniePo zeskanowaniu numeru aplikacja wywołuje usługę systemu sprzedażyDane zawsze aktualneWolna odpowiedź systemu sprzedaży zatrzymuje przyjęcie
Kopia replikowanaDokumenty sprzedaży trafiają cyklicznie do bazy reklamacjiSzybki odczyt, praca przy niedostępnym ERPDokument z ostatnich minut może jeszcze nie istnieć
Import plikuPlik z dokumentami wczytywany według harmonogramuProsta integracja bez usługNajwolniejsza aktualizacja, ręczne wyjaśnianie różnic

Wymiana danych z ERP przez replikację albo usługi sieciowe pojawia się w opisach integracji SoftwareStudio, a jej mechanizmy omawia artykuł o integracji WMS z ERP przez REST i kolejki. Te same wzorce dotyczą aplikacji reklamacyjnej. Przy zapytaniu na żądanie warto ustawić limit czasu i przejść na wpis ręczny, gdy system sprzedaży nie odpowiada, żeby klient nie czekał przy ladzie.

const ctrl = new AbortController();
const limit = setTimeout(() => ctrl.abort(), 3000);
try {
  const odp = await fetch('/api/reklamacje/dokument?numer=' + encodeURIComponent(kod),
                          { signal: ctrl.signal });
  if (odp.ok) wypelnijFormularz(await odp.json());
} catch (e) {
  wlaczWpisReczny(kod); // brak odpowiedzi w 3 s: rejestracja bez danych z ERP
} finally {
  clearTimeout(limit);
}

Zgłoszenie zarejestrowane bez dokumentu dostaje znacznik braku danych, a po powrocie systemu sprzedaży ktoś uzupełnia powiązanie. Dzięki temu przyjęcie nie zależy od dostępności innego systemu, a rejestr nie zawiera wpisów bez śladu, dlaczego brakuje dokumentu. Powiązanie z kartoteką klientów w systemie CRM opisuje strona o Customer Relationship Management.

Zgłoszenie w terenie i praca serwisanta

Serwisant w terenie ma inne zadanie niż pracownik przy ladzie. Nie przyjmuje towaru od klienta, tylko ocenia urządzenie na miejscu. Rejestruje zgłoszenie i zmienia status naprawy. Dlatego w aplikacji mobilnej ważniejsza od bogatego formularza jest szybkość dostępu do czterech rzeczy:

  • Historia serwisowa urządzenia - dotychczasowe zgłoszenia i naprawy tego samego egzemplarza, wyszukiwane po numerze seryjnym.
  • Zgłoszenie z danymi klienta - formularz wypełniony z kartoteki, żeby serwisant nie przepisywał adresu.
  • Zmiana statusu naprawy - przejście z listy dozwolonych, na przykład z „w naprawie” do „gotowe do odbioru”.
  • Zdjęcia i notatka - dokumentacja stanu urządzenia przed naprawą i po niej.
Ilustracja: pracownica w kurtce roboczej trzyma terminal mobilny z ekranem pokazującym etykietę z kodem
Urządzenie przenośne w rękach pracownika terenowego: historia urządzenia oraz status naprawy ze zdjęciami.

Aplikacja uruchamiana w przeglądarce wymaga połączenia z serwerem. Zgłoszenie sporządzone w piwnicy albo w hali bez zasięgu trzeba wysłać po odzyskaniu sieci, a praca offline wymaga osobnej implementacji po stronie aplikacji. Porównanie aplikacji instalowanej z przeglądarkową i wersją PWA znajduje się w artykule o reklamacjach w aplikacji, więc tutaj pominięto je na rzecz mechanizmu synchronizacji, opisanego w ostatniej sekcji.

Zestaw funkcji dla pracownika terenowego ma wspólną część z aplikacją RMA online, czyli role użytkowników oraz integracje z innymi systemami. Różni się nastawieniem na krótkie sesje, w których liczy się jeden cel: dopisać zdjęcie albo zmienić status i wrócić do pracy.

Zdjęcia w aplikacji obsługi reklamacji

Zdjęcie usterki skraca ocenę zasadności, bo pracownik widzi wadę, zanim towar trafi do serwisu. Problem w tym, że zdjęcie z telefonu waży kilka megabajtów, a łącze w terenie bywa wolne. Aplikacja może zmniejszyć obraz w przeglądarce, zanim go wyśle. Wystarczy narysować go na elemencie canvas w mniejszej skali i zapisać jako JPEG.

async function zmniejszZdjecie(plik, maxBok = 1600) {
  const bmp = await createImageBitmap(plik, { imageOrientation: 'from-image' });
  const skala = Math.min(1, maxBok / Math.max(bmp.width, bmp.height));
  const plotno = document.createElement('canvas');
  plotno.width = Math.round(bmp.width * skala);
  plotno.height = Math.round(bmp.height * skala);
  plotno.getContext('2d').drawImage(bmp, 0, 0, plotno.width, plotno.height);
  bmp.close();
  return new Promise(ok => plotno.toBlob(ok, 'image/jpeg', 0.8));
}

Opcja imageOrientation: 'from-image' uwzględnia obrót zapisany w metadanych zdjęcia, dzięki czemu fotografia zrobiona telefonem trzymanym w pionie nie trafia do sprawy położona na boku. Działanie funkcji opisują dokumentacje createImageBitmap oraz toBlob.

Zmniejszenie po stronie klienta nie zastępuje kontroli po stronie serwera. Serwer sprawdza typ i rozmiar pliku, zapisuje go pod nazwą nadaną przez system i przypisuje do numeru sprawy, a nie do nazwy z urządzenia. Zdjęcie dołączone do zgłoszenia trafia potem do protokołu oceny, więc jego autor i czas dodania powinny zostać w bazie razem z plikiem.

Skan kodu w aplikacji obsługi reklamacji

Numer RMA trafia na etykietę z kodem kreskowym, więc przyjęcie zwrotu w magazynie sprowadza się do zeskanowania paczki. Ten sam kod służy w aplikacji do odnalezienia sprawy, potwierdzenia odbioru towaru i zmiany jej statusu. Rolę numeru w obiegu przesyłki omawia artykuł o numerze RMA, a współpracę z magazynem opisuje tekst o aplikacji RMA online.

Sposób odczytuSprzętZaletaOgraniczenie
Skaner jako klawiaturaSkaner USB przy komputerzeSzybki odczyt bez konfiguracji aplikacjiWymaga stałego fokusu w polu
Terminal z wbudowanym skaneremKolektor z AndroidemOdczyt w ruchu, jedną rękąOsobny sprzęt i aplikacja na terminal
Kamera telefonuSmartfon lub tabletBrak dodatkowego urządzeniaWolniejszy odczyt, zależny od światła i przeglądarki
Ilustracja: terminal mobilny ze skanerem kodów kreskowych na tle regałów magazynowych
Terminal ze skanerem odczytuje kod z etykiety na paczce zwrotnej.

Terminale z Androidem i wbudowanym skanerem omawia artykuł o aplikacji magazynowej na Androida; te same urządzenia mogą obsłużyć przyjęcie zwrotu, jeśli aplikacja reklamacyjna udostępnia odpowiedni ekran.

Odczyt kodu kamerą w przeglądarce umożliwia interfejs BarcodeDetector. Nie każda przeglądarka go udostępnia, więc kod musi sprawdzić jego obecność i zaproponować wpis ręczny albo skaner sprzętowy jako rozwiązanie zapasowe. Stan wsparcia opisuje dokumentacja Barcode Detection API.

async function odczytajKod(video) {
  if (!('BarcodeDetector' in window)) return null; // wpis ręczny lub skaner sprzętowy
  const detektor = new BarcodeDetector({ formats: ['code_128', 'qr_code'] });
  const wyniki = await detektor.detect(video);
  return wyniki.length ? wyniki[0].rawValue : null;
}

Jakość odczytu zależy w równym stopniu od etykiety i od kodu aplikacji. Kod potrzebuje jasnego marginesu z obu stron i wyraźnego kontrastu, a etykieta naklejona na krawędź paczki i zagięta bywa nieczytelna dla kamery. Przy wpisie ręcznym numeru pomaga cyfra kontrolna dopisana do numeru RMA: aplikacja odrzuca literówkę, zanim zapyta bazę, i pokazuje komunikat od razu, a nie po nieudanym wyszukiwaniu. Format numeru i sposób jego nadawania są opisane w artykule o numerze RMA, do którego prowadzi link wyżej.

Wybór między aplikacją webową a mobilną

Aplikacja webowa i aplikacja instalowana rozwiązują ten sam problem inaczej, a wybór zależy od tego, czego wymaga stanowisko. Cztery pytania rozstrzygają większość przypadków:

  • Dostęp do sprzętu - skaner z pakietem SDK producenta albo drukarka etykiet zwykle wymagają aplikacji instalowanej lub usługi pośredniczącej.
  • Sposób aktualizacji - aplikacja w przeglądarce zmienia się raz na serwerze, a instalowana wymaga wersji na każdy system i zgody użytkowników.
  • Praca bez sieci - wymaga lokalnej kolejki zgłoszeń, a w przeglądarce dodatkowej implementacji.
  • Koszt utrzymania - osobne wersje na Androida i iOS oznaczają dwa cykle testów po każdej zmianie.

Przy punktach przyjęcia z komputerem wystarcza przeglądarka, a moduł mobilny opłaca się tam, gdzie serwisant korzysta z aparatu i historii urządzeń oraz zmienia statusy poza biurem. Architekturę modułów aplikacji pokazuje tekst o aplikacjach WMS, a zestaw funkcji Studio RMA.net przedstawia strona o aplikacji do obsługi reklamacji.

Zgłoszenia z terenu a rejestr centralny

Zgłoszenie z urządzenia często przychodzi do serwera dwa razy. Telefon wysyła je, tracąc łączność przed odpowiedzią, więc aplikacja ponawia próbę, a w bazie pojawia się duplikat. Rozwiązaniem jest identyfikator nadawany na urządzeniu w chwili powstania zgłoszenia, na przykład wygenerowany identyfikator GUID. Serwer przyjmuje ten sam identyfikator tylko raz.

-- przykładowy schemat, nazwy poglądowe
CREATE UNIQUE INDEX UX_Reklamacja_IdZgloszenia
    ON dbo.Reklamacja (IdZgloszeniaKlienta)
    WHERE IdZgloszeniaKlienta IS NOT NULL;

INSERT INTO dbo.Reklamacja (IdZgloszeniaKlienta, DataWplywu, Opis)
SELECT @IdZgloszenia, @DataWplywu, @Opis
WHERE NOT EXISTS (
    SELECT 1 FROM dbo.Reklamacja WHERE IdZgloszeniaKlienta = @IdZgloszenia
);

Indeks filtrowany, opisany w dokumentacji indeksów filtrowanych, pilnuje unikalności tylko tam, gdzie identyfikator istnieje, więc zgłoszenia z punktu przyjęcia bez takiego identyfikatora nie są nim objęte. Gdy dwa równoległe żądania trafią na serwer jednocześnie, drugie zakończy się błędem naruszenia indeksu, który aplikacja traktuje jako potwierdzenie, że zgłoszenie już jest w rejestrze.

Reguła: zgłoszenie z urządzenia dostaje identyfikator w chwili powstania, a serwer przyjmuje go tylko raz.

Drugą pułapką jest data wpływu. Zegar telefonu może się mylić, więc serwer zapisuje własny czas odbioru, a czas z urządzenia trzyma w osobnej kolumnie. Termin odpowiedzi liczy się od dnia, w którym zgłoszenie dotarło do sprzedawcy, co przy wolnej synchronizacji bywa dzień wcześniej niż wpis na serwerze. Zmiany statusu wprowadzone przez dwie osoby naraz wykrywa się kolumną typu rowversion, porównywaną przy zapisie. Ogólny przebieg sprawy po zapisie w rejestrze przedstawia tekst o programie do obsługi reklamacji, a wybór programu według kryteriów - artykuł o programie do reklamacji.