Numer RMA jako identyfikator sprawy w systemie

Numer RMA wskazuje jedną sprawę reklamacyjną albo zwrotną i nic więcej. Nie opisuje klienta i nie zastępuje numeru zamówienia. Nie tworzy też roszczenia, bo podstawą żądania klienta jest rękojmia albo gwarancja. Mimo to ten sam numer pojawia się w wiadomości do klienta i na etykiecie paczki, a potem w bazie danych i na dokumentach rozliczeniowych. Z tego powodu jego format warto zaprojektować raz, zanim trafi do pierwszej sprawy.

Sprawa ma w systemie kilka numerów naraz, więc trzeba rozdzielić ich role. Tabela porządkuje je według tego, kto numer nadaje i do czego służy.

NumerKto nadajeCo identyfikujeGdzie się pojawia
Numer RMAsprzedawca lub producentsprawę reklamacyjną albo zwrotnąe-mail do klienta, etykieta paczki
Numer zamówieniasklep lub ERPtransakcję sprzedażypotwierdzenie zamówienia, faktura
Numer listu przewozowegoprzewoźnikprzesyłkę w sieci kurieraetykieta kuriera, śledzenie przesyłki
Numer seryjnyproducentegzemplarz urządzeniatabliczka znamionowa, karta gwarancyjna
SSCCnadawca jednostki logistycznej według standardu GS1jednostkę logistyczną, na przykład paletęetykieta logistyczna GS1

Reguła: numer RMA identyfikuje sprawę, a pozostałe numery są jej atrybutami, więc system przechowuje je w osobnych polach i nie wyprowadza jednego z drugiego.

Zapisane zgłoszenie z internetowego formularza w Studio RMA.net ma przypisany automatycznie unikalny numer zgłoszenia, zwany też numerem referencyjnym albo numerem RMA. Opis aplikacji znajduje się na stronie Studio RMA.net, a moment nadania numeru i jego rolę w autoryzacji zwrotu wyjaśnia tekst o return merchandise authorization. Ogólną architekturę systemu opisuje artykuł RMA system - architektura i integracje.

Format numeru RMA i maska numeracji

Numer składa się z segmentów o stałej długości, które da się zweryfikować prostym wzorcem. Poniższa tabela pokazuje przykładową maskę RMA-RRRR-NNNNNN-C i rolę każdego segmentu. Format jest przykładem projektowym, a nie opisem numeracji konkretnego produktu.

SegmentPrzykładRolaUwaga
PrefiksRMAodróżnia numer sprawy od innych numerów w korespondencjistały, wielkie litery
Rok2026pozwala rozpocząć licznik od jedynki i skraca numer licznikacztery cyfry
Licznik000123kolejność nadania w danym rokusześć cyfr z zerami wiodącymi
Cyfra kontrolna3wykrywa pomyłkę przy ręcznym przepisaniuliczona z roku i licznika

Pełny numer z przykładu ma postać RMA-2026-000123-3. Separatorem jest myślnik, a nie ukośnik, ponieważ ukośnik wymaga kodowania, gdy numer trafia do adresu URL albo do nazwy pliku. Myślnik przechodzi bez zmian przez wiadomość e-mail i adres strony.

Reguła: numer RMA nie zawiera danych osobowych ani kwoty zamówienia, bo widzi go kurier i każdy, kto ma dostęp do wiadomości e-mail.

Długość i dozwolone znaki

Pole na numer w bazie i w interfejsach przewoźników ma ograniczoną długość, a klient przepisuje numer ręcznie z wiadomości do formularza. Krótki numer z cyframi i wielkimi literami ogranicza pomyłki. Litery O oraz I łatwo pomylić z cyframi 0 oraz 1, więc jeśli licznik zawiera litery, wyklucza się je z alfabetu. Numer z przykładu ma 17 znaków i mieści się w polu VARCHAR(20) z zapasem na ewentualny dodatek.

Cyfra kontrolna numeru

Cyfra kontrolna wykrywa pomyłkę przy przepisywaniu, zanim zapytanie trafi do bazy. Algorytm Luhna wyłapuje pojedynczą błędną cyfrę i większość zamian sąsiednich cyfr. Nie chroni natomiast przed celowym zgadywaniem numerów, więc nie zastępuje kontroli dostępu.

public static int CyfraKontrolna(string cyfry)
{
    int suma = 0;
    bool podwoj = true;
    for (int i = cyfry.Length - 1; i >= 0; i--)
    {
        int d = cyfry[i] - '0';
        if (podwoj)
        {
            d *= 2;
            if (d > 9) d -= 9;
        }
        suma += d;
        podwoj = !podwoj;
    }
    return (10 - suma % 10) % 10;
}

Metoda dostaje ciąg cyfr roku i licznika, czyli 2026000123, i zwraca cyfrę dopisywaną na końcu numeru. Przy odczycie formularza program liczy sumę ponownie i odrzuca numer z błędną cyfrą, zanim wykona zapytanie do bazy.

Nadawanie numeru RMA w transakcji bazy danych

Numer nadaje baza danych, ponieważ tylko ona widzi wszystkie równoległe zapisy. Dwie osoby rejestrujące zgłoszenia w tej samej sekundzie nie mogą dostać tej samej liczby. Aplikacja, która odczytuje ostatni numer i dodaje jedynkę, prędzej czy później wyda duplikat. Do wyboru są dwa mechanizmy, a różnią się zachowaniem przy wycofaniu transakcji.

MechanizmZachowanie przy ROLLBACKNowy rokObciążenie zapisu
SEQUENCEnumer przepada, w numeracji powstaje lukaręczny restart sekwencjibrak blokady na tabeli spraw
Tabela liczników z UPDATEnumer wraca do puli razem z transakcjąosobny wiersz na każdy rokzapisy kolejkują się na jednym wierszu

Sekwencję opisuje dokumentacja CREATE SEQUENCE. Wariant z tabelą liczników nie tworzy luk, ponieważ zapis licznika i sprawy odbywa się w jednej transakcji, a blokada wiersza licznika trwa do jej zakończenia. Wiersz na nowy rok zakłada zadanie SQL Agent w grudniu, więc procedura nie wstawia go sama.

CREATE TABLE dbo.LicznikRma (
    Rok     SMALLINT NOT NULL PRIMARY KEY,
    Ostatni INT      NOT NULL
);
GO
CREATE PROCEDURE dbo.NadajNumerRma
    @Rok SMALLINT OUTPUT,
    @Nr  INT      OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON;
    SET @Rok = YEAR(SYSUTCDATETIME());

    UPDATE dbo.LicznikRma
       SET @Nr = Ostatni = Ostatni + 1
     WHERE Rok = @Rok;

    IF @@ROWCOUNT = 0
        THROW 50020, N'Brak licznika RMA na bieżący rok.', 1;
END;

Reguła: procedurę wywołuje się w tej samej transakcji, która wstawia sprawę, bo dopiero wtedy wycofanie zapisu zwalnia też numer.

Procedura zwraca rok i licznik, a aplikacja składa z nich numer razem z cyfrą kontrolną z poprzedniej sekcji.

string baza = rok.ToString() + nr.ToString("D6");
string numer = $"RMA-{rok}-{nr:D6}-{CyfraKontrolna(baza)}";

Unikalność wymuszona przez bazę

Poprawny algorytm nadawania nie wystarcza jako jedyne zabezpieczenie, bo numer można wstawić ręcznie albo zaimportować ze starego arkusza. Gwarancję daje indeks unikalny, który odrzuca duplikat na poziomie silnika, opisany w dokumentacji indeksów unikalnych. Ograniczenie CHECK pilnuje dodatkowo kształtu numeru.

CREATE TABLE dbo.SprawaRma (
    SprawaId   INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
    NumerRma   VARCHAR(20)  NOT NULL,
    DataWplywu DATETIME2(0) NOT NULL DEFAULT SYSUTCDATETIME(),
    CONSTRAINT UQ_SprawaRma_Numer UNIQUE (NumerRma),
    CONSTRAINT CK_SprawaRma_Numer CHECK (NumerRma LIKE
        'RMA-[0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9]-[0-9]')
);

Klucz SprawaId jest liczbą wewnętrzną, a NumerRma służy światu zewnętrznemu. Relacje z innymi tabelami opierają się na kluczu wewnętrznym, więc zmiana formatu numeru nie wymaga przepisywania kluczy obcych. Nazwy tabel i kolumn są przykładowe.

Podwójne zgłoszenie z formularza

Klient klika przycisk dwa razy albo odświeża stronę po wysłaniu, a formularz wysyła ten sam ładunek ponownie. Bez zabezpieczenia powstają dwie sprawy z dwoma numerami. Formularz dołącza wtedy jednorazowy token, a tabela ma na nim indeks unikalny, więc drugi zapis zwraca numer pierwszej sprawy.

Powiązanie numeru RMA z dokumentami sprawy

Numer RMA łączy dokumenty, które powstają w różnych systemach i w różnych dniach. Potwierdzenie zgłoszenia wystawia system reklamacyjny, a korektę faktury księguje ERP. Przyjęcie zwrotu rejestruje magazyn. Bez wspólnego numeru ktoś musi te dokumenty łączyć ręcznie, po nazwisku klienta albo numerze zamówienia.

DokumentWystawiaGdzie stoi numer RMAUwaga
Potwierdzenie zgłoszeniasystem RMAtemat i treść wiadomościnumer w temacie pozwala przypisać odpowiedź klienta do sprawy
Protokół przyjęcia towarumagazyn lub serwispole sprawy w protokolezapisuje stan towaru i datę odbioru
Decyzja o reklamacjidział obsługitreść pisma do klientarozstrzygnięcie i sposób załatwienia sprawy
Dokument magazynowy zwrotuWMSopis lub pole dodatkowe dokumentunumer dokumentu nadaje WMS
Korekta fakturyERPuwagi lub pole dodatkowenumer korekty nadaje ERP

Reguła: numer RMA trafia do dokumentu jako odnośnik w polu opisu, a numer samego dokumentu nadaje system, w którym ten dokument powstaje.

Dokumenty zapisuje się w tabeli powiązanej ze sprawą kluczem wewnętrznym, a zapytanie po numerze RMA zwraca całą historię w jednym wyniku.

SELECT s.NumerRma, d.TypDokumentu, d.NumerDokumentu, d.DataWystawienia
  FROM dbo.SprawaRma AS s
  JOIN dbo.DokumentRma AS d ON d.SprawaId = s.SprawaId
 WHERE s.NumerRma = @NumerRma
 ORDER BY d.DataWystawienia;

Wynik pokazuje, na którym etapie jest sprawa i co jeszcze musi się wydarzyć. Statusy sprawy i dozwolone przejścia opisuje artykuł o programie do zarządzania reklamacjami, a terminy odpowiedzi - tekst o terminach i raportach reklamacji.

Ilustracja przedstawiająca pracowniczkę przy biurku z dokumentem w ręku, użyta jako obraz dokumentów powiązanych ze sprawą reklamacyjną
Dokumenty sprawy łączy numer RMA: od potwierdzenia zgłoszenia po decyzję i rozliczenie.

Numer jako odnośnik w ERP i WMS

Przy integracji system RMA przekazuje numer razem z poleceniem, na przykład zlecenia przyjęcia zwrotu do WMS albo korekty do ERP. System docelowy zapisuje go w polu referencyjnym i zwraca własny numer dokumentu, który system RMA dopisuje do sprawy. Oba numery wskazują wtedy na siebie nawzajem. Zasady wymiany danych z magazynem opisuje tekst o integracji systemów magazynowych.

Numer RMA na przesyłce i etykiecie zwrotnej

Magazyn rozpoznaje paczkę zwrotną po etykiecie, a nie po nazwisku nadawcy, więc numer musi być na niej czytelny dla skanera. W praktyce paczka niesie dwa numery: list przewozowy nadany przez kuriera i numer RMA nadany przez sprzedawcę. Skaner odczytuje oba, a system łączy je w jednej sprawie.

Element etykietyNadajeZawartośćRola przy odbiorze
Numer listu przewozowegoprzewoźniknumer śledzenia przesyłkipotwierdza, że paczka dotarła do magazynu
Numer RMAsprzedawcaRMA-2026-000123-3wskazuje sprawę w systemie
Adres zwrotnysprzedawcatekst adresukieruje paczkę do właściwego magazynu

Reguła: kod kreskowy zawiera wyłącznie numer RMA, a dane sprawy system pobiera z bazy, bo etykieta nie może się zdezaktualizować po zmianie danych w sprawie.

Ilustracja przedstawiająca karton z dużym kodem QR i kodem kreskowym na etykiecie, użyta jako obraz etykiety zwrotnej z numerem RMA
Etykieta z kodem na kartonie: skan przy przyjęciu wskazuje sprawę bez szukania po danych klienta.

Symbolika kodu i szablon etykiety

Code 128 koduje pełny zestaw znaków ASCII, w tym myślnik, więc numer z przykładu mieści się w jednym kodzie bez konwersji. Kod QR wymaga skanera obrazowego, natomiast Code 128 odczyta także prosty skaner laserowy. Dobór symboliki dla magazynu opisuje artykuł o kodach EAN i GS1-128. Etykieta na drukarce Zebra powstaje z szablonu ZPL.

^XA
^PW812
^FO40,40^A0N,40,40^FDZWROT - RMA^FS
^FO40,100^BY3^BCN,120,Y,N,N^FDRMA-2026-000123-3^FS
^XZ

Szablon wstawia numer jako kod kreskowy z linią czytelną dla człowieka. Odczyt wydruku skanerem jest częścią testu wdrożeniowego etykiety, bo zbyt mały moduł kodu albo słaby kontrast obniżają skuteczność skanowania.

Numer RMA a numer SSCC

Numer RMA nie należy do systemu identyfikatorów GS1, więc nie zastępuje SSCC, czyli numeru jednostki logistycznej, na przykład palety zwrotnej. Zwrot całej palety może nosić obie etykiety: SSCC identyfikuje jednostkę w łańcuchu dostaw, a numer RMA wskazuje sprawę u sprzedawcy. Budowę SSCC omawia strona etykieta logistyczna GS1, a wysyłkę z magazynu do kurierów - artykuł firmy kurierskie i wysyłka z magazynu.

Dostęp klienta do statusu po numerze RMA

Numery nadawane kolejno da się zgadywać: klient, który ma RMA-2026-000123-3, może wpisać sąsiedni numer i zobaczyć cudzą sprawę. Cyfra kontrolna tego nie zatrzyma, bo atakujący też ją policzy. Portal statusu wymaga więc drugiego elementu znanego tylko właścicielowi sprawy, na przykład adresu e-mail podanego w zgłoszeniu.

async function sprawdzStatus(numerRma, email) {
  const odpowiedz = await fetch('/api/rma/status', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ numerRma, email })
  });
  if (!odpowiedz.ok) {
    return null;
  }
  return odpowiedz.json();
}

Serwer zwraca ten sam kod błędu i ten sam komunikat dla nieistniejącego numeru oraz dla numeru z niepasującym adresem, więc odpowiedź nie zdradza, które numery istnieją. Liczbę prób z jednego adresu IP ogranicza się dodatkowo. Dane osobowe przesyła się w treści żądania POST, a nie w adresie URL, bo adresy trafiają do logów serwera i historii przeglądarki.

Zakres portalu dla odbiorców firmowych i indywidualnych opisuje artykuł o dostępie klientów B2B i B2C do systemu RMA, a kanały zgłoszeń - tekst o reklamacjach online.

Przypadki brzegowe numeracji RMA

Numeracja działa bez zarzutu, dopóki sprawa ma jeden numer i jedną przesyłkę. Kłopoty zaczynają się przy wyjątkach, które trzeba zapisać w regułach, zanim się zdarzą.

  • Anulowanie sprawy - numer nie wraca do puli, a sprawa dostaje status anulowanej, żeby wcześniejsza korespondencja nadal wskazywała właściwy rekord.
  • Kilka przesyłek w jednej sprawie - sprawa ma jeden numer RMA i wiele numerów listów przewozowych zapisanych w osobnej tabeli.
  • Kilka spraw w jednej paczce - klient odsyła razem towary z dwóch zamówień, a magazyn skanuje dwie etykiety RMA i przypisuje je do jednego przyjęcia.
  • Zmiana formatu - stare numery zostają w bazie bez zmian, a nowy format rozpoznaje się po wersji maski zapisanej w osobnej kolumnie.

Migracja numeracji z arkusza kalkulacyjnego

Firmy zaczynające od arkusza mają już numery nadane ręcznie, często w kilku formatach. Przy migracji importuje się je bez zmian jako numery historyczne, a licznik nowej numeracji ustawia się powyżej największej dotychczasowej liczby z danego roku. Indeks unikalny wychwyci duplikaty w danych źródłowych podczas importu, więc można je wyjaśnić przed uruchomieniem produkcyjnym.

Ilustracja przedstawiająca mężczyznę przy monitorze z tabelą danych, użyta jako obraz rejestru spraw i ich numerów w systemie reklamacyjnym
Rejestr spraw z numerami: po migracji z arkusza jedna baza zastępuje kilka plików.

W sklepie internetowym numer łączy się dodatkowo z zamówieniem i korespondencją, co omawia tekst o reklamacjach w sklepie internetowym.