# 10. Etapy wdrożenia

Dziesięć etapów. Każdy kończy się działającą aplikacją, numerem wersji i paczką ZIP. Etap jest skończony, kiedy wszystkie kryteria odbioru są odhaczone, nie wcześniej.

## Podsumowanie

| Etap | Wersja | Zakres | Nowe tabele |
|---|---|---|---|
| 0 | 0.1.0 | Dokumentacja, schemat bazy, plan | wszystkie zaprojektowane |
| 1 | 1.0.0 | Fundament: instalator, router, logowanie, migracje, `/version`, `/changelog`, `/admin/health` | system, salony, użytkownicy |
| 2 | 1.1.0 | Pracownicy, uprawnienia, grafiki, usługi, klientki | oferta, klientki |
| 3 | 1.2.0 | Kalendarz i wizyty | wizyty |
| 4 | 1.3.0 | Dashboard i statystyki | brak |
| 5 | 1.4.0 | SMS: kolejka, szablony, przypomnienia, pakiety | SMS |
| 6 | 1.5.0 | Abonament, plany, Stripe | rozliczenia |
| 7 | 1.6.0 | Rezerwacje online | rezerwacje |
| 8 | 1.7.0 | Automatyzacje: odzyskiwanie klientek, urodziny, po wizycie, odpowiedzi SMS | skrzynka SMS |
| 9 | 1.8.0 | Panel operatora, rozliczenia SMS, wsparcie | brak |
| 10 | 2.0.0 | RODO, eksporty, kopie zapasowe, wydajność, dopracowanie | brak |

Kolejność nie jest przypadkowa. Do etapu 3 włącznie powstaje coś, czego pierwszy salon może zacząć używać zamiast zeszytu. Od etapu 5 produkt ma to, za co ktoś zapłaci. Od etapu 6 da się na nim zarabiać.

---

## Etap 0. Dokumentacja i fundament (0.1.0)

**Cel:** wiedzieć dokładnie, co budujemy, zanim powstanie pierwsza linia kodu.

**Zakres:**
- Komplet dokumentacji w `docs/`
- Schemat bazy 1.0.0 i migracja inicjalna
- Plan etapów z kryteriami odbioru
- Decyzje techniczne: stack, izolacja salonów, operator SMS, płatności

**Kryteria odbioru:**
- [ ] Każdy moduł ze specyfikacji ma swoje miejsce w którymś etapie
- [ ] Każda tabela ma opisane kolumny, indeksy i powód istnienia
- [ ] Schemat SQL przechodzi walidację składni
- [ ] Zakres MVP jest zamknięty, a pomysły poza nim są w poczekalni

**Poza kodem, do zrobienia przed etapem 1:**
- [ ] Sprawdzenie i rejestracja domeny
- [ ] Konto Stripe, weryfikacja firmy
- [ ] Konto sms.pl, klucze testowe, wniosek o nazwę nadawcy
- [ ] Hosting z PHP 8.2, MySQL 8, certyfikatem SSL i cronem co minutę
- [ ] Ustalenie z księgowością kwestii VAT i formy sprzedaży

---

## Etap 1. Fundament aplikacji (1.0.0)

**Cel:** działający szkielet, do którego dokłada się moduły. Bez tego etapu każdy kolejny byłby dwa razy dłuższy.

**Zakres:**
- Instalator: sprawdzenie wymagań, dane bazy, utworzenie schematu, konto operatora, pierwszy salon
- Router z czystymi adresami, front controller, `.htaccess`
- Warstwa bazy: PDO, repozytorium bazowe z izolacją salonu
- Migrator z `/admin/migracje` i wiersza poleceń
- Logowanie, wylogowanie, reset hasła, sesje w bazie, ograniczanie prób
- Rejestracja salonu z okresem próbnym
- Layout panelu: nagłówek, nawigacja, wersja mobilna
- `/version`, `/changelog`, `/admin/health`
- Panel operatora w wersji minimalnej: lista salonów, podgląd stanu
- Logowanie błędów, strony 404 i 500
- Zadanie cron i runner kolejki, na razie bez zadań

**Migracje:** `0001_init.sql`

**Kryteria odbioru:** wszystkie odhaczone 17.09.2026, wersja wydana.

- [x] Instalacja na czystym hostingu przechodzi od zera do ekranu logowania bez ręcznego dotykania bazy
- [x] Rejestracja salonu tworzy konto właścicielki i loguje ją do pustego panelu
- [x] Reset hasła działa, token wygasa po godzinie i jest jednorazowy
- [x] Po pięciu nieudanych próbach logowania konto jest blokowane na 15 minut
- [x] `/version` zwraca poprawny numer w tekście i w JSON
- [x] `/changelog` pokazuje wpisy z pliku
- [x] `/admin/health` pokazuje wszystkie sprawdzenia, a brak crona daje status błędu
- [x] Migrator wykrywa podmienioną migrację i odmawia uruchomienia
- [x] Żaden adres poza `public_html` nie jest dostępny z przeglądarki
- [x] Panel wygląda poprawnie na telefonie, bez poziomego przewijania
- [x] Test izolacji salonów przechodzi: cudzy rekord jest niewidoczny, a podmiana `salon_id` przy zapisie nie działa
- [x] POST bez tokenu CSRF kończy się kodem 403

**Czego nie ma w tej wersji, świadomie:** dwuskładnikowego logowania operatora (etap 9), wysyłki SMTP przez własne gniazdo (na razie `mail()`, a poza produkcją wiadomości trafiają do logu).

---

## Etap 2. Pracownicy, usługi, klientki (1.1.0)

**Cel:** wypełnić system danymi, na których będzie działał kalendarz.

**Zakres:**
- Pracownicy: lista, dodawanie, edycja, dezaktywacja, zaproszenie mailem
- Role i uprawnienia: katalog, wartości domyślne, wyjątki na użytkownika
- Grafiki pracy i urlopy
- Kategorie usług i usługi z czasem, ceną, buforem i przypisaniem pracowników
- Klientki: lista, wyszukiwarka, filtry, karta klientki, zgody
- Import klientek z CSV
- Ustawienia salonu i godziny otwarcia
- Dane startowe: przykładowe kategorie i usługi dla nowego salonu

**Migracje:** `0002` do `0004`, oferta i klientki

**Kryteria odbioru:**
- [ ] Test izolacji: użytkownik salonu A dostaje 404 na każdym rekordzie salonu B
- [ ] Pracownik z rolą `staff` nie widzi finansów ani ustawień, także przy ręcznym wpisaniu adresu
- [ ] Zmiana uprawnienia działa natychmiast, bez ponownego logowania
- [ ] Dodanie klientki z istniejącym numerem pokazuje istniejącą kartę zamiast tworzyć duplikat
- [ ] Import 500 klientek z CSV kończy się w mniej niż 30 sekund i raportuje pominięte wiersze
- [ ] Usługa użyta w wizycie nie da się usunąć, a komunikat wyjaśnia dlaczego
- [ ] Lista 2000 klientek wczytuje się poniżej 500 ms
- [ ] Nowy salon po rejestracji ma przykładowe usługi i nie startuje z pustym ekranem

---

## Etap 3. Kalendarz i wizyty (1.2.0)

**Cel:** serce systemu. Po tym etapie salon może przestać używać zeszytu.

**Zakres:**
- Kalendarz w widokach dnia, tygodnia i miesiąca
- Filtr pracowników, kolory, godziny pracy, urlopy na siatce
- Tworzenie wizyty: klientka, usługi, pracownik, termin
- Kilka usług w jednej wizycie, ceny i czasy kopiowane
- Edycja, przesuwanie przeciąganiem, zmiana czasu trwania
- Statusy wizyt i szybka zmiana statusu
- Wykrywanie kolizji terminów
- Szybki podgląd wizyty z panelem bocznym
- Odwoływanie z powodem
- Historia zmian wizyty
- "Umów ponownie" z karty klientki
- Wersja mobilna kalendarza

**Migracje:** `0005` do `0007`, wizyty

**Kryteria odbioru:**
- [ ] Utworzenie wizyty od kliknięcia do zapisu zajmuje poniżej 20 sekund
- [ ] Dwie wizyty tego samego pracownika nie mogą się nałożyć, także przy przeciąganiu i przy zmianie czasu trwania
- [ ] Przeciągnięcie wizyty zapisuje się bez przeładowania strony i trafia do historii
- [ ] Bufor po usłudze blokuje termin, ale nie jest pokazywany klientce
- [ ] Widok tygodnia z 200 wizytami wczytuje się poniżej 800 ms i wykonuje mniej niż 15 zapytań
- [ ] Wizyta z trzema usługami ma poprawny czas trwania i sumę cen
- [ ] Zmiana ceny usługi nie zmienia kwot na wizytach już utworzonych
- [ ] Kalendarz działa na telefonie: widok dnia, dodawanie, zmiana statusu
- [ ] Test przejścia czasu letniego i zimowego daje poprawne godziny

---

## Etap 4. Dashboard i statystyki (1.3.0)

**Cel:** pokazać właścicielce, co się dzieje w salonie, bez wchodzenia w kalendarz.

**Zakres:**
- Dashboard: kafelki, lista dzisiaj i jutro, szybkie akcje
- Pasek wizyt z przeszłości bez ustawionego statusu
- Statystyki: sześć liczb z porównaniem do poprzedniego okresu
- Najpopularniejsze usługi
- Wyniki pracowników
- Wykres przychodu w zakresie dat
- Zakresy dat i własny zakres
- Ograniczenia widoczności według uprawnień

**Migracje:** brak, ewentualnie indeksy pod zapytania agregujące

**Kryteria odbioru:**
- [ ] Dashboard wczytuje się poniżej 400 ms przy 5000 wizyt w bazie
- [ ] Liczby na dashboardzie zgadzają się z tym, co widać w kalendarzu
- [ ] Przychód liczy wyłącznie wizyty odbyte
- [ ] Pracownica bez `finance.view` nie widzi kwot nigdzie, także w podglądzie wizyty
- [ ] Pracownica z `stats.view_own` widzi wyłącznie swój wiersz w wynikach
- [ ] Zakres dat działa na czasie lokalnym salonu, także przy zakresie obejmującym zmianę czasu
- [ ] Statystyki za miesiąc z 1000 wizyt liczą się poniżej 600 ms

---

## Etap 5. SMS (1.4.0)

**Cel:** funkcja, która sprzedaje produkt.

**Zakres:**
- Adapter operatora sms.pl, tryb testowy bez realnej wysyłki
- Kolejka zadań i runner
- Szablony wiadomości ze zmiennymi i licznikiem znaków
- Automatyczne przypomnienia z ustawieniami godziny i wyprzedzenia
- Wysyłka ręczna z karty klientki i z podglądu wizyty
- Saldo kredytów, księga transakcji, pakiety
- Historia wysyłek z filtrami i ponawianiem
- Raporty doręczeń przez webhook
- Limity zabezpieczające
- Kontrola zgód przed każdą wysyłką

**Migracje:** `0008` do `0012`, tabele SMS

**Kryteria odbioru:**
- [ ] Wizyta utworzona na jutro powoduje zaplanowanie jednego SMS, nie dwóch
- [ ] Odwołanie wizyty anuluje zaplanowany SMS
- [ ] Przesunięcie wizyty aktualizuje termin przypomnienia
- [ ] Dwa równoległe przebiegi crona nie wysyłają tej samej wiadomości dwa razy
- [ ] Klientka bez zgody na SMS nie dostaje żadnej wiadomości
- [ ] Wiadomość z polskimi znakami jest poprawnie liczona jako UCS-2 i pobiera właściwą liczbę kredytów
- [ ] Zerowe saldo wstrzymuje wysyłkę i pokazuje ostrzeżenie zamiast cicho gubić wiadomości
- [ ] Awaria API operatora nie blokuje pracy w panelu
- [ ] Raport doręczenia aktualizuje status wiadomości
- [ ] Suma `sms_transactions` zgadza się z `salons.sms_balance` co do jednego kredytu
- [ ] Wysyłka do 100 klientek pokazuje koszt przed potwierdzeniem

---

## Etap 6. Abonament i płatności (1.5.0)

**Cel:** pobierać pieniądze.

**Zakres:**
- Plany w bazie, ograniczenia funkcji według planu
- Okres próbny i jego zakończenie
- Stripe Checkout dla abonamentu i dla pakietów SMS
- Webhook z weryfikacją podpisu i jednokrotnym przetworzeniem
- Ekran `/abonament`: plan, historia płatności, faktury
- Zmiana i anulowanie planu
- Blokady po nieudanej płatności, według harmonogramu z dokumentu 04
- Zadanie porównujące stan ze Stripe
- Wiadomości e-mail: koniec okresu próbnego, nieudana płatność, potwierdzenie

**Migracje:** `0013` do `0016`, rozliczenia

**Kryteria odbioru:**
- [ ] Pełna ścieżka zakupu na kluczach testowych kończy się aktywnym kontem
- [ ] Ręczne otwarcie adresu powrotu z bramki nie daje dostępu
- [ ] To samo zdarzenie webhooka wysłane dwa razy przetwarza się raz
- [ ] Zdarzenia przychodzące w odwrotnej kolejności nie psują statusu subskrypcji
- [ ] Nieudana płatność uruchamia harmonogram blokad i pierwszy e-mail
- [ ] Obniżenie planu przy dwóch pracownikach wymaga dezaktywacji konta i mówi to wprost
- [ ] Zakup pakietu SMS dopisuje kredyty dopiero po potwierdzeniu płatności
- [ ] Zawieszone konto pozwala się zalogować i wyeksportować dane
- [ ] Lista kontrolna przejścia na klucze produkcyjne odhaczona w całości

---

## Etap 7. Rezerwacje online (1.6.0)

**Cel:** system zaczyna sam przynosić wizyty.

**Zakres:**
- Publiczna strona `/{slug}`
- Silnik wolnych terminów
- Ścieżka rezerwacji w sześciu krokach
- Blokady terminów
- Tworzenie albo dopasowanie klientki
- Potwierdzenia SMS i e-mail
- Odwoływanie przez klientkę linkiem
- Konfiguracja w panelu i podgląd
- Zabezpieczenia strony publicznej
- Oznaczenie rezerwacji online w kalendarzu i na dashboardzie

**Migracje:** `0017` do `0019`, rezerwacje

**Kryteria odbioru:** pełna lista w `08-rezerwacje-online.md`

---

## Etap 8. Automatyzacje (1.7.0)

**Cel:** system pracuje, kiedy salon śpi.

**Zakres:**
- Klientki do odzyskania: wykrywanie na podstawie średniego odstępu wizyt
- Lista z wyjaśnieniem, dlaczego klientka jest na liście, i przyciskiem wysyłki
- SMS urodzinowy
- SMS po wizycie
- Prośba o opinię
- Odpowiedzi klientek: potwierdzanie i odwoływanie wizyty SMS-em
- Skrzynka wiadomości przychodzących w panelu
- Powiadomienia dla salonu

**Migracje:** `0020` do `0022`, skrzynka i pola pomocnicze

**Kryteria odbioru:**
- [ ] Klientka z wizytą za 74 dni przy średniej 35 dni trafia na listę, a klientka z wizytą za 20 dni nie
- [ ] Klientka z umówioną przyszłą wizytą nie trafia na listę do odzyskania
- [ ] SMS urodzinowy idzie raz w roku, o ustalonej godzinie, tylko przy zgodzie marketingowej
- [ ] Odpowiedź TAK zmienia status na potwierdzoną i klientka dostaje potwierdzenie
- [ ] Odpowiedź NIE odwołuje wizytę, zwalnia termin i powiadamia salon
- [ ] Nierozpoznana odpowiedź trafia do skrzynki i nie robi nic automatycznie
- [ ] Odpowiedź na numer bez pasującej wizyty nie powoduje błędu

---

## Etap 9. Panel operatora (1.8.0)

**Cel:** móc obsługiwać klientów systemu bez grzebania w bazie.

**Zakres:**
- Lista salonów: status, plan, liczba wizyt, ostatnie logowanie, saldo SMS
- Karta salonu: dane, subskrypcja, płatności, zużycie SMS, historia
- Ręczne działania: przedłużenie dostępu, dodanie kredytów, zmiana planu, zawieszenie
- Rozliczenie SMS: kupione, zużyte, koszt u operatora, marża
- Podgląd logów i błędów
- Statystyki systemu: nowe rejestracje, konwersja z okresu próbnego, rezygnacje, przychód miesięczny
- Wejście na konto salonu w trybie wsparcia, z wpisem w dzienniku i widoczną informacją dla właścicielki

**Migracje:** brak, ewentualnie indeksy

**Kryteria odbioru:**
- [ ] Każda akcja operatora trafia do dziennika z informacją kto, co i kiedy
- [ ] Tryb wsparcia jest widoczny dla właścicielki salonu i wygasa po godzinie
- [ ] Ręczne dodanie kredytów przechodzi przez księgę transakcji, nie przez zmianę salda
- [ ] Rozliczenie SMS zgadza się z fakturą od operatora za ten sam okres

---

## Etap 10. Dopracowanie (2.0.0)

**Cel:** produkt, który da się sprzedawać bez asysty.

**Zakres:**
- RODO: eksport danych salonu, eksport danych klientki, usunięcie konta z danymi, retencja
- Regulamin, polityka prywatności, umowa powierzenia przetwarzania danych
- Kopie zapasowe: automatyczne, sprawdzane, z przetestowanym odtworzeniem
- Wydajność: przegląd zapytań, brakujące indeksy, cache
- Scalanie duplikatów klientek
- Dostępność: kontrast, nawigacja klawiaturą, etykiety pól
- Ekrany pustych stanów i pierwszego uruchomienia
- Pomoc: krótkie podpowiedzi przy pierwszym wejściu na każdy ekran
- Przegląd wszystkich komunikatów błędów pod kątem zrozumiałości

**Kryteria odbioru:**
- [ ] Eksport danych salonu daje plik, który da się otworzyć i który zawiera komplet
- [ ] Usunięcie konta faktycznie kasuje dane po zadeklarowanym okresie
- [ ] Odtworzenie bazy z kopii zapasowej przetestowane na osobnym serwerze
- [ ] Żadne zapytanie na typowej stronie nie przekracza 100 ms
- [ ] Każdy ekran ma sensowny stan pusty, zamiast pustej tabeli
- [ ] Osoba, która nigdy nie widziała systemu, umawia pierwszą wizytę bez pytania o pomoc

---

## Co po wersji 2.0.0

Kolejność z poczekalni, według tego, co najczęściej będzie wracać od użytkowniczek. Nie planujemy tego teraz, bo po sześciu miesiącach z prawdziwymi salonami lista i tak będzie wyglądać inaczej niż dzisiaj.
