Moduł internetowy dla kontrahentów - zakres w trzech systemach
Moduł internetowy dla kontrahentów udostępnia przez przeglądarkę wycinek danych systemu, który dotyczy jednej zewnętrznej firmy. Kontrahent nie instaluje oprogramowania i nie widzi danych innych klientów. Ten sam model dostępu obowiązuje w WMS. Powtarza się w YMS i w RMA, choć w każdym z tych systemów kontrahent jest kimś innym i ma inne zadania.
| System | Kontrahent | Co widzi | Co może zgłosić |
|---|---|---|---|
| WMS | właściciel towaru, odbiorca, dostawca | stany własnego towaru oraz historię dokumentów z PZ i WZ w PDF | polecenie wydania, awizo dostawy |
| YMS | przewoźnik, dostawca | własne awizacje i ich statusy, wolne okna | nową awizację lub jej zmianę albo anulowanie w dozwolonym czasie |
| RMA | klient, dystrybutor | status własnych spraw, oś czasu, decyzję | nowe zgłoszenie z załącznikami |
Szczegóły dla magazynu opisuje artykuł o module internetowym w systemie WMS, a dla placu i bram tekst o awizacjach w Studio VSS.net. Zgłoszenia reklamacyjne przez portal przedstawia strona o programie RMA on-line. Ta strona porównuje te trzy przypadki pod kątem wspólnych mechanizmów: uwierzytelniania oraz uprawnień, a także izolacji danych.
W portalu magazynowym kontrahent widzi bieżący stan z podziałem na indeks i partię oraz z lokalizacją, a przy potrzebie uzgodnienia rozbieżności może odtworzyć zapas na wybrany dzień wstecz. W części wdrożeń dane eksportuje się do pliku CSV lub XLSX, dzięki czemu nie trzeba przepisywać ich z ekranu do własnego arkusza.

Reguła: kontrahent widzi rekord tylko wtedy, gdy jest jego właścicielem, a listę rekordów zawęża serwer, nie przeglądarka.
Portal odciąża zespół po stronie magazynu, ponieważ kontrahent sam sprawdza status i pobiera dokument, zamiast dzwonić. Tę samą ideę w szerszym ujęciu omawia artykuł o oprogramowaniu logistycznym, gdzie portal jest jednym z punktów styku z zewnętrznymi firmami.
Uwierzytelnianie kontrahenta i ochrona sesji
Uwierzytelnienie potwierdza, kim jest użytkownik, a autoryzacja ustala, do czego ma dostęp. W module dla kontrahentów każda osoba dostaje indywidualny login i hasło, a komunikacja między przeglądarką a serwerem jest szyfrowana protokołem TLS. W portalu reklamacyjnym logowanie odbywa się adresem e-mail i generowanym hasłem.
Wspólne konto całego działu zakupów kontrahenta jest złym rozwiązaniem. Nie da się wtedy ustalić, kto zatwierdził zamówienie, a odejście jednego pracownika wymaga zmiany hasła u wszystkich. Indywidualne konta pozwalają odebrać dostęp jednej osobie bez wpływu na resztę zespołu.
Przechowywanie haseł i blokada prób
Hasła nie zapisuje się w bazie w postaci jawnej. Zapisuje się iterowany skrót z losową solą, a sprawdzenie polega na policzeniu skrótu dla podanego hasła i porównaniu wyniku w stałym czasie. Poniższy przykład w C# ilustruje mechanizm i nie jest kodem produktu.
using System.Security.Cryptography;
static byte[] Hash(string password, byte[] salt) =>
Rfc2898DeriveBytes.Pbkdf2(password, salt, 600_000, HashAlgorithmName.SHA256, 32);
static bool Verify(string password, byte[] salt, byte[] expected) =>
CryptographicOperations.FixedTimeEquals(Hash(password, salt), expected);
Skrót chroni hasła przy wycieku bazy, ale nie chroni przed zgadywaniem na formularzu logowania. Przed tym zabezpiecza blokada konta po serii nieudanych prób albo rosnące opóźnienie odpowiedzi. Reset hasła wykonuje się jednorazowym odnośnikiem z ograniczonym czasem ważności, wysłanym na adres konta.
Sesja i ciasteczka
Po zalogowaniu serwer wystawia identyfikator sesji w ciasteczku. Atrybuty ciasteczka decydują o tym, czy skrypt na stronie albo połączenie bez szyfrowania mogą je przechwycić. Opis atrybutów zawiera dokumentacja ciasteczek HTTP w MDN, a mechanizmy uwierzytelniania po stronie serwera przedstawia dokumentacja uwierzytelniania w ASP.NET Core.
Set-Cookie: sid=9f2c41d0b7; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=1800
| Mechanizm | Przed czym chroni | Gdzie działa |
|---|---|---|
| TLS | podsłuchiwaniem transmisji | połączenie przeglądarka - serwer |
| Skrót hasła z solą | odczytem haseł po wycieku bazy | baza danych |
| Blokada po nieudanych próbach | zgadywaniem hasła | formularz logowania |
| Ciasteczko Secure i HttpOnly | kradzieżą sesji przez skrypt | przeglądarka |
| Limit czasu sesji | użyciem pozostawionego komputera | serwer aplikacji |
Role i zakres uprawnień po stronie kontrahenta
Uprawnienia mają dwa wymiary. Rola odpowiada na pytanie, co wolno użytkownikowi, a zakres na pytanie, czyich danych dotyczy dostęp. Pracownik działu zakupów może składać zamówienia, ale tylko dla własnej firmy, a przewoźnik zakłada awizacje, ale widzi wyłącznie swoje zgłoszenia.
| Rola po stronie kontrahenta | Zakres dostępu |
|---|---|
| Administrator konta kontrahenta | pełny podgląd danych, zarządzanie użytkownikami, pobieranie wszystkich dokumentów |
| Pracownik działu zakupów | składanie zamówień i awizacji, podgląd stanów magazynowych |
| Pracownik działu finansowego | historia transakcji i dokumenty rozliczeniowe |
| Użytkownik tylko do odczytu | podgląd stanów magazynowych bez składania zamówień |
Ten podział dotyczy portalu WMS. W module awizacji rolę przewoźnika ogranicza się dodatkowo do wybranych ramp albo kontrahentów, a ustawienia globalne pozostają u supervisora po stronie magazynu. W portalu reklamacyjnym osobnym wariantem jest konto dystrybutora, który zgłasza sprawy w imieniu wielu odbiorców.

Reguła: rola odpowiada na pytanie „co wolno”, a zakres na pytanie „czyje dane”, i oba warunki sprawdza serwer przy każdym żądaniu.
Administrator konta i dziennik zdarzeń
Administrator po stronie kontrahenta zarządza własnymi użytkownikami. Gdy pracownik zmienia stanowisko albo odchodzi, odbiera mu dostęp od razu, bez angażowania działu IT magazynu. Dziennik logowań pozwala ustalić, kto i kiedy wykonał daną operację w portalu.
W dzienniku zapisuje się, kto wykonał operację i kiedy, oraz którego obiektu dotyczyła. Przy sporze o zamówienie albo awizację wystarczy odtworzyć zapis, zamiast rekonstruować przebieg z wiadomości e-mail. Zasady kont i kluczy dla systemów zewnętrznych opisuje tekst o awizacji transportu i jej interfejsie.
Przegląd kont i wygaszanie dostępu
Konta kontrahentów starzeją się szybciej niż konta pracowników, bo osoby po drugiej stronie zmieniają stanowiska bez informowania magazynu. Przydatny jest okresowy przegląd: lista kont bez logowania od ustalonego czasu trafia do administratora konta kontrahenta z pytaniem o potrzebę dostępu. Nieużywane konto dezaktywuje się, a nie usuwa, żeby zachować powiązanie z historią operacji.
Dezaktywacja natychmiast unieważnia aktywne sesje użytkownika. Sprawdzenie statusu konta przy każdym żądaniu kosztuje jedno wyszukiwanie po kluczu głównym, więc nie obciąża bazy, a zamyka lukę, w której zwolniona osoba korzysta z sesji do końca jej ważności.
Izolacja danych kontrahentów w bazie SQL Server
Izolacja danych jest najważniejszym wymaganiem portalu. Wszystkie firmy pracują na jednej bazie, więc każde zapytanie musi być zawężone do jednego kontrahenta. Identyfikator kontrahenta pobiera się z sesji po zalogowaniu, nigdy z parametru żądania, bo parametr można podmienić w przeglądarce.
Szczególnie groźny jest błąd polegający na zaufaniu numerowi rekordu z adresu. Jeśli portal pokaże dokument o numerze 1042 każdemu zalogowanemu, kolejne numery da się odgadnąć. Numery spraw w portalu reklamacyjnym są kolejne, więc sam numer nigdy nie wystarcza do wglądu w dane. Podobne wymaganie dotyczy magazynów obsługujących wielu klientów, opisanych w artykule o kontraktowej logistyce magazynowej.
- Filtr w aplikacji - każde zapytanie portalu zawiera warunek na identyfikator kontrahenta z sesji.
- Filtr w bazie - zabezpieczenie na poziomie wierszy odrzuca wiersze innych kontrahentów, nawet jeśli aplikacja pominie warunek.
Drugi poziom nie zastępuje pierwszego, tylko domyka lukę wynikającą z pomyłki programisty. W SQL Server od wersji 2016 służy do tego zabezpieczenie na poziomie wierszy, opisane w dokumentacji Row-Level Security. Poniższy przykład jest ilustracją mechanizmu, a nie opisem implementacji produktów SoftwareStudio, w których filtr może być realizowany w warstwie aplikacji.
CREATE SCHEMA Security;
GO
CREATE FUNCTION Security.fn_ContractorFilter(@ContractorId int)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN SELECT 1 AS Allowed
WHERE @ContractorId = CAST(SESSION_CONTEXT(N'ContractorId') AS int);
GO
CREATE SECURITY POLICY Security.ContractorPolicy
ADD FILTER PREDICATE Security.fn_ContractorFilter(ContractorId) ON dbo.StockBalance
WITH (STATE = ON);
GO
EXEC sp_set_session_context @key = N'ContractorId', @value = 42, @read_only = 1;
Kontekst sesji ustawia aplikacja po pobraniu połączenia z puli i przed pierwszym zapytaniem. Pominięcie tego kroku daje puste wyniki, a błędnie zapisany identyfikator ujawniłby dane innego kontrahenta, dlatego ustawienie wykonuje się w jednym miejscu kodu. Kolumna ContractorId powinna otwierać indeksy używane przez portal, żeby filtr nie zamieniał zapytań na skany całych tabel.
Statusy i powiadomienia widoczne dla kontrahenta
Kontrahent nie potrzebuje wszystkich pól rekordu. Portal reklamacyjny pokazuje uproszczone statusy zamiast wewnętrznych, a przejścia czysto wewnętrzne, na przykład przekazanie sprawy między serwisantami, w ogóle nie trafiają do wyniku zapytania. Mapowanie zapisuje się w słowniku, więc zmiana nazwy statusu wewnętrznego nie wymaga zmian w kodzie portalu.
| Status wewnętrzny | Status widoczny dla kontrahenta |
|---|---|
| Nowa, zarejestrowana | Zgłoszenie przyjęte |
| W ocenie | W trakcie oceny |
| Decyzja wydana | Rozstrzygnięcie |
| Rozliczona, zamknięta | Zakończone |

Technicznie taki filtr realizuje widok przeznaczony wyłącznie dla portalu. Widok wystawia tylko dozwolone kolumny, a portal nie ma prawa czytać tabel bazowych. Dodanie nowego pola wewnętrznego do tabeli nie ujawni go kontrahentowi, dopóki nikt nie doda go świadomie do widoku.
CREATE VIEW portal.vw_ClaimForCustomer AS
SELECT c.ClaimNo, c.CustomerId, m.NameForCustomer AS Status, c.RegisteredAt
FROM dbo.Claim AS c
JOIN dbo.CustomerStatusMap AS m ON m.StatusId = c.StatusId AND m.Visible = 1;
W portalu magazynowym kontrahent dostaje powiadomienie e-mail, gdy zgłoszenie trafia do realizacji, gdy towar zostaje skompletowany i gdy dokument WZ jest gotowy do pobrania. Adres pochodzi z konta użytkownika, więc wiadomość trafia do osoby, która złożyła zamówienie, a nie do wspólnej skrzynki firmy.
Powiadomienia wynikają ze zmian statusów. Zmiana statusu dopisuje wiersz do kolejki w tej samej transakcji, a osobny proces wysyła wiadomość. Awaria serwera poczty nie blokuje wtedy pracy magazynu, bo niewysłana wiadomość czeka w kolejce. Wiadomość zawiera odsyłacz do portalu, a nie dane sprawy i nie załączniki, więc błędnie zaadresowana poczta nie ujawnia informacji. Ten schemat rozwija artykuł o reklamacjach online.
API i EDI jako uzupełnienie formularza w portalu
Kontrahent obsługujący duży wolumen nie będzie wpisywał każdego zamówienia w formularzu. Dla takich firm portal uzupełnia się interfejsem API albo wymianą komunikatów EDI, a formularz w przeglądarce zostaje rozwiązaniem zapasowym i narzędziem dla mniejszych partnerów. W module awizacji administrator zarządza kluczami API do integracji.
await fetch('/api/awizacje', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': apiKey,
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify(awizacja)
});
Klucz API powinien podlegać tym samym regułom co konto użytkownika: identyfikować jednego kontrahenta i mieć zawężony zakres operacji. Nagłówek z kluczem idempotencji sprawia, że ponowna wysyłka po przerwaniu połączenia nie tworzy drugiej awizacji. Ogólne zasady wymiany komunikatów z ERP opisuje artykuł o integracji WMS z ERP, a wersję internetową programu magazynowego przedstawia tekst o internetowym magazynie.
Interfejs wymaga też limitu liczby żądań na klucz. Błąd w systemie kontrahenta, na przykład pętla ponawiająca wysyłkę bez przerwy, potrafi wygenerować tysiące wywołań na minutę i spowolnić portal pozostałym firmom. Po przekroczeniu limitu serwer zwraca kod 429 z nagłówkiem Retry-After, a nadawca wznawia wysyłkę po podanym czasie.
Komunikacja z kontrahentami zagranicznymi
Portal obsługuje użytkowników, którzy nie znają języka zespołu magazynu. Wersja językowa interfejsu i komunikaty w języku kierowcy skracają liczbę pomyłek przy zgłoszeniach, co opisuje artykuł o wielojęzycznej komunikacji w VSS. Produktowe opisy modułów znajdują się w serwisach awizacje.softwarestudio.com.pl oraz rma.softwarestudio.com.pl.
Testy izolacji przed uruchomieniem
Izolację danych sprawdza się testem, a nie deklaracją. Tworzy się dwa konta testowe należące do różnych kontrahentów i próbuje z konta pierwszego otworzyć rekord drugiego, podając jego numer w adresie. Poprawna odpowiedź to brak wyniku, a nie komunikat o braku uprawnień, bo drugi wariant potwierdza istnienie rekordu.
GET /api/awizacje/1042 HTTP/1.1
Cookie: sid=sesja-kontrahenta-A
HTTP/1.1 404 Not Found
Drugi test dotyczy ról. Konto z uprawnieniem tylko do odczytu próbuje wysłać zamówienie i powinno dostać odmowę z kodem 403. Oba testy powtarza się po każdej zmianie w portalu, bo nowy ekran albo nowy punkt końcowy najłatwiej pomija filtr kontrahenta.
Wdrożenie portalu zaczyna się od spisu kontrahentów i przypisania im ról oraz zakresów danych, a dopiero potem od konfiguracji kont. Kolejność odwrotna kończy się kontami o zbyt szerokim dostępie, których nikt później nie porządkuje.



