Wiki
Cykl życia oferty i state machine
Deep dive w przejścia statusów oferty, reguły walidacji i ślad audytowy w QuoteNode.
Cykl życia oferty i state machine
Każda oferta w QuoteNode przechodzi przez ściśle walidowany cykl życia. System egzekwuje dozwolone przejścia i odrzuca każdą próbę zmiany statusu na stan nieprawidłowy.
Definicje statusów
| Status | Opis | Edytowalna | Terminalny |
|---|---|---|---|
| DRAFT | Oferta jest przygotowywana. Klient jeszcze jej nie widzi. | Tak | Nie |
| SENT | Oferta została dostarczona klientowi e-mailem albo linkiem publicznym. | Nie | Nie |
| OPENED | Klient co najmniej raz otworzył link publiczny. | Nie | Nie |
| NEGOTIATION | Klient odpowiedział pytaniem albo kontrofertą. | Nie | Nie |
| ACCEPTED | Klient potwierdził zamówienie. | Nie | Tak |
| REJECTED | Klient odrzucił ofertę. | Nie | Tak |
| EXPIRED | Termin ważności minął bez decyzji. | Nie | Tak |
| ARCHIVED | Oferta została ręcznie zarchiwizowana przez handlowca. | Nie | Tak |
Reguły przejść
DRAFT ──────────► SENT
│ │
│ ▼
│ OPENED
│ │
│ ├──► NEGOTIATION
│ │ │
│ ├──► ACCEPTED (terminalny)
│ ├──► REJECTED (terminalny)
│ └──► EXPIRED (terminalny)
│
└──► ARCHIVED (terminalny)
Dozwolone przejścia
- DRAFT → SENT — gdy handlowiec wysyła ofertę e-mailem albo generuje link publiczny
- SENT → OPENED — gdy klient pierwszy raz otworzy link publiczny
- OPENED → NEGOTIATION — gdy klient wyśle odpowiedź
- OPENED → ACCEPTED — gdy klient kliknie “Accept” na stronie publicznej
- OPENED → REJECTED — gdy klient kliknie “Decline” na stronie publicznej
- SENT/OPENED/NEGOTIATION → EXPIRED — gdy minie
valid_until - DRAFT → ARCHIVED — gdy handlowiec ręcznie archiwizuje niewysłaną ofertę
- NEGOTIATION → ACCEPTED — gdy klient zaakceptuje po negocjacji
- NEGOTIATION → REJECTED — gdy klient odrzuci po negocjacji
Niedozwolone przejścia
- SENT → DRAFT
- ACCEPTED → dowolny inny status
- REJECTED → dowolny inny status
- EXPIRED → dowolny inny status
- ARCHIVED → dowolny inny status
Co dzieje się przy poszczególnych przejściach
DRAFT → SENT
- System sprawdza, czy oferta ma co najmniej jedną pozycję.
- Tworzony jest niemutowalny snapshot przechwytujący cały stan oferty.
- Przy wysyłce e-mail system kolejkuje wiadomość z PDF-em i lub linkiem publicznym.
- Przy generowaniu linku publicznego tworzony jest losowy token 256-bit; w bazie zapisywany jest tylko jego hash SHA-256.
- Status zmienia się na SENT.
- Na osi czasu klienta zapisywane jest zdarzenie.
- Powstaje wpis w logu audytowym.
SENT → OPENED
- Klient otwiera URL linku publicznego.
- Hash tokenu zostaje dopasowany w bazie.
- Status zmienia się na OPENED tylko przy pierwszym otwarciu.
- Zapisywane jest zdarzenie webowe: czas, IP, user agent i kraj.
- Handlowiec może otrzymać powiadomienie.
OPENED → ACCEPTED
- Klient klika “Accept” na stronie publicznej.
- System sprawdza rate limiting oraz warstwę bot-detection.
- Opcjonalnie przechwytuje dane intentu zamówienia.
- Status zmienia się na ACCEPTED.
- Handlowiec dostaje natychmiastowe powiadomienie.
- Na osi czasu klienta ląduje nowe zdarzenie.
Snapshoty
Snapshot jest kompletnym, niemutowalnym zapisem oferty w momencie wysłania. Obejmuje między innymi:
- dane klienta,
- wszystkie pozycje,
- wyliczone sumy, rabaty, VAT i dostawę,
- użyty kurs FX,
- branding,
- ustawienia szablonu i widocznych kolumn.
Snapshoty nigdy nie są modyfikowane. Jeśli oferta zostaje wysłana ponownie po zmianach, powstaje nowa wersja snapshotu z podniesionym numerem wersji.
PDF-y są zawsze generowane ze snapshotów, nigdy z live data. Dzięki temu dokument zawsze dokładnie odpowiada temu, co zostało pokazane klientowi.
Ślad audytowy
Każde przejście statusu zapisuje niemutowalny wpis audytowy zawierający:
- identyfikator użytkownika i jego rolę albo aktora systemowego,
- adres IP źródła,
- poprzedni i nowy status,
- timestamp serwerowy,
- opcjonalny komentarz, na przykład powód odrzucenia.
Log audytowy ma charakter append-only. Wpisów nie da się edytować, usuwać ani przycinać nawet z poziomu administratora.
Operacje po stanach terminalnych
Ofert w stanach terminalnych nie można już modyfikować, ale mogą służyć jako baza dla nowych ofert:
- Clone — tworzy nowy DRAFT z tymi samymi pozycjami i cenami
- Extend Validity — klonuje ofertę z nową datą ważności
- Update Prices — klonuje ofertę i przelicza ceny na podstawie aktualnego katalogu i kursów FX
- Use as Template — klonuje bez klienta i bez cen
Każda z tych operacji tworzy nową ofertę z nowym ID i statusem DRAFT. Oryginał pozostaje niezmieniony.