- System tray app z NotifyIcon + ContextMenuStrip - Polling API orderPRO (GET /api/print/jobs/pending) - Pobieranie etykiet PDF i druk przez PdfiumViewer - Formularz ustawień: URL API, klucz, drukarka, interwał - Okno logów z historią (ciemny motyw, Consolas) - Self-contained .NET 8 publish (win-x64) - Milestone v0.7 Zdalne drukowanie etykiet — COMPLETE Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
14 KiB
14 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous
| phase | plan | type | wave | depends_on | files_modified | autonomous | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 20-windows-client | 01 | execute | 1 |
|
|
false |
Purpose
Użytkownik może zlecić wydruk etykiety z przeglądarki (faza 19) i etykieta automatycznie wydrukuje się na drukarce podłączonej do jego komputera — bez ręcznego pobierania pliku i drukowania.
Output
- Aplikacja WinForms (.NET 8) w
clients/windows/OrderPROPrint/ - System tray icon z menu kontekstowym
- Polling API co N sekund (konfigurowalne)
- Automatyczny druk etykiet A6 na wybranej drukarce
- Formularz ustawień (URL API, klucz API, drukarka, interwał)
Prior Work
@.paul/phases/18-print-queue-backend/18-01-SUMMARY.md
- API endpoints (API key auth via X-Api-Key header, SHA-256):
- GET /api/print/jobs/pending — lista zleceń do wydruku (JSON array)
- GET /api/print/jobs/{id}/download — pobieranie pliku etykiety (binary)
- POST /api/print/jobs/{id}/complete — oznaczenie jako wydrukowane
- Auth: header X-Api-Key z raw key (serwer hashuje SHA-256 i porównuje)
@.paul/phases/19-ui-integration/19-01-SUMMARY.md
- UI tworzy print jobs przez POST /api/print/jobs i /api/print/jobs/bulk
- Status "pending" = gotowe do pobrania przez Windows Client
Source Files
@src/Modules/Printing/PrintApiController.php — API endpoints consumed by this client @src/Modules/Printing/ApiKeyMiddleware.php — auth pattern (X-Api-Key header)
## Required Skills (from SPECIAL-FLOWS.md)| Skill | Priority | When to Invoke | Loaded? |
|---|---|---|---|
| sonar-scanner | required | Po APPLY, przed UNIFY | ○ |
Skill Invocation Checklist
- sonar-scanner loaded (run before UNIFY) — uwaga: skanuje tylko PHP, C# poza zakresem
<acceptance_criteria>
AC-1: Aplikacja uruchamia się w system tray
Given aplikacja OrderPROPrint jest uruchomiona
When użytkownik patrzy na system tray (obszar powiadomień)
Then widzi ikonę aplikacji OrderPROPrint
And po kliknięciu prawym przyciskiem widzi menu: "Ustawienia", "Wstrzymaj/Wznów", "O programie", "Zamknij"
And dwuklik otwiera formularz ustawień
AC-2: Konfiguracja połączenia z API
Given użytkownik otworzył formularz ustawień
When wpisuje URL API (np. https://orderpro.projectpro.pl), klucz API i wybiera drukarkę
Then ustawienia zapisują się w pliku konfiguracyjnym (app.config lub JSON)
And po ponownym uruchomieniu ustawienia są zapamiętane
And przycisk "Testuj połączenie" weryfikuje poprawność URL + klucza
AC-3: Polling i pobieranie zleceń
Given aplikacja ma poprawną konfigurację API
When polling timer odpytuje GET /api/print/jobs/pending
Then pobiera listę zleceń ze statusem "pending"
And dla każdego zlecenia pobiera etykietę GET /api/print/jobs/{id}/download
And po wydruku oznacza POST /api/print/jobs/{id}/complete
And ikona tray pokazuje liczbę przetworzonych zleceń (tooltip lub balloon)
AC-4: Drukowanie etykiety A6 na drukarce termicznej
Given pobrano plik etykiety (PDF)
When aplikacja wysyła do wybranej drukarki
Then etykieta drukuje się w formacie A6 (105×148mm)
And orientacja i marginesy są poprawne dla drukarki termicznej
And w razie błędu druku zlecenie nie jest oznaczane jako complete
AC-5: Obsługa błędów i odporność
Given aplikacja jest uruchomiona
When API jest niedostępne lub klucz nieprawidłowy
Then ikona tray zmienia się na stan "error" (np. czerwona)
And tooltip pokazuje ostatni błąd
And polling kontynuuje próby (nie crash)
And po przywróceniu połączenia wraca do normalnego działania
</acceptance_criteria>
Task 1: Projekt C# WinForms + system tray + ustawienia clients/windows/OrderPROPrint/OrderPROPrint.sln, clients/windows/OrderPROPrint/OrderPROPrint.csproj, clients/windows/OrderPROPrint/Program.cs, clients/windows/OrderPROPrint/TrayApplicationContext.cs, clients/windows/OrderPROPrint/Forms/SettingsForm.cs, clients/windows/OrderPROPrint/Forms/SettingsForm.Designer.cs, clients/windows/OrderPROPrint/Models/AppSettings.cs, clients/windows/OrderPROPrint/Properties/Resources.resx 1. Utwórz projekt .NET 8 WinForms w `clients/windows/OrderPROPrint/`: - `dotnet new winforms -n OrderPROPrint` - Target: net8.0-windows - NuGet: System.Text.Json (wbudowane), żadnych zewnętrznych zależności2. Program.cs:
- Application.Run(new TrayApplicationContext())
- Nie pokazuj głównego okna — tylko tray
3. TrayApplicationContext (dziedziczy ApplicationContext):
- NotifyIcon z ikoną (embedded resource lub SystemIcons.Application)
- ContextMenuStrip z pozycjami:
- "Ustawienia" → otwiera SettingsForm
- "Wstrzymaj" / "Wznów" → toggle polling
- separator
- "O programie" → MessageBox z wersją
- "Zamknij" → Application.Exit()
- DoubleClick na ikonę → otwiera SettingsForm
- Tooltip: "OrderPRO Print — Oczekiwanie" (aktualizowany przez PollingService)
4. AppSettings (model):
- ApiUrl (string), ApiKey (string), PrinterName (string), PollIntervalSeconds (int, default 10)
- Zapis/odczyt do JSON: %APPDATA%/OrderPROPrint/settings.json
- Metoda Load() i Save() — statyczne
5. SettingsForm (WinForms designer):
- Pola: txtApiUrl, txtApiKey, cmbPrinter, nudPollInterval
- cmbPrinter: wypełniony z System.Drawing.Printing.PrinterSettings.InstalledPrinters
- Przycisk "Testuj połączenie" — GET /api/print/jobs/pending z podanym URL+key
- Sukces: zielony label "Połączono ✓"
- Błąd: czerwony label z komunikatem
- Przycisk "Zapisz" — AppSettings.Save() + zamknij formularz
- Przycisk "Anuluj" — zamknij bez zapisu
Avoid: Nie używaj WPF ani MAUI — czysty WinForms dla prostoty
Avoid: Nie dodawaj auto-start na tym etapie (to future enhancement)
- `dotnet build` przechodzi bez błędów
- Aplikacja uruchamia się i pojawia się ikona w system tray
- Menu kontekstowe działa
- Formularz ustawień otwiera się i zamyka
- Lista drukarek się ładuje
- Ustawienia zapisują się do %APPDATA%/OrderPROPrint/settings.json
AC-1 satisfied (system tray), AC-2 satisfied (konfiguracja)
Task 2: API client + polling + drukowanie
clients/windows/OrderPROPrint/Services/PrintApiClient.cs,
clients/windows/OrderPROPrint/Services/PrintService.cs,
clients/windows/OrderPROPrint/Services/PollingService.cs,
clients/windows/OrderPROPrint/Models/PrintJob.cs,
clients/windows/OrderPROPrint/TrayApplicationContext.cs
1. PrintJob (model):
- Id (int), OrderId (int), PackageId (int), LabelPath (string), Status (string)
- Deserializacja z JSON response GET /api/print/jobs/pending
2. PrintApiClient:
- Constructor: (string apiUrl, string apiKey)
- HttpClient z default header X-Api-Key
- async Task<List<PrintJob>> GetPendingJobsAsync()
- GET {apiUrl}/api/print/jobs/pending
- Deserializuj JSON response → List<PrintJob>
- async Task<byte[]> DownloadLabelAsync(int jobId)
- GET {apiUrl}/api/print/jobs/{jobId}/download
- Return raw bytes (PDF)
- async Task<bool> MarkCompleteAsync(int jobId)
- POST {apiUrl}/api/print/jobs/{jobId}/complete
- Return true jeśli 200
- async Task<bool> TestConnectionAsync()
- GET {apiUrl}/api/print/jobs/pending (sprawdza czy nie 401)
- Obsługa błędów: HttpRequestException, TaskCanceledException (timeout)
3. PrintService:
- static void PrintPdf(byte[] pdfBytes, string printerName)
- Zapisz PDF do temp file → drukuj przez Process.Start z parametrami drukarki
- Alternatywa: użyj System.Drawing.Printing.PrintDocument z renderowaniem PDF
- Preferowane podejście: zapisz temp PDF, użyj SumatraPDF CLI (portable) do silent print:
`SumatraPDF.exe -print-to "PrinterName" -silent label.pdf`
- Jeśli SumatraPDF niedostępny: fallback na ShellExecute "print" verb
- Ustaw rozmiar papieru: A6 (105×148mm) lub custom size z drukarki
- Po wydruku: usuń temp file
4. PollingService:
- Constructor: (PrintApiClient client, PrintService printService, Action<string> onStatusUpdate, Action<string> onError)
- System.Threading.Timer z interwałem z AppSettings
- Na każdy tick:
a. GetPendingJobsAsync()
b. Dla każdego job: DownloadLabelAsync → PrintPdf → MarkCompleteAsync
c. Aktualizuj status (callback onStatusUpdate)
d. Przy błędzie: callback onError, nie przerywaj pętli
- Start() / Stop() / IsRunning property
- Mutex/lock żeby nie nakładały się dwa ticki
5. Integracja w TrayApplicationContext:
- Po załadowaniu ustawień: utwórz PrintApiClient + PollingService
- onStatusUpdate → aktualizuj tooltip ikony ("Wydrukowano 3 etykiety")
- onError → zmień ikonę na error state, tooltip z komunikatem
- "Wstrzymaj" → PollingService.Stop(), zmień tekst menu na "Wznów"
- Przy zmianie ustawień → restart PollingService z nowymi parametrami
Avoid: Nie blokuj UI thread — cała komunikacja HTTP i drukowanie async
Avoid: Nie ignoruj błędów — każdy failed print musi być widoczny
Avoid: Nie oznaczaj jako complete jeśli druk nie powiódł się
- `dotnet build` przechodzi
- Aplikacja łączy się z API (test z prawdziwym kluczem)
- Polling pobiera pending jobs
- Etykieta drukuje się na wskazanej drukarce
- Po wydruku job oznaczany jako complete w UI orderPRO
- Błąd API nie crashuje aplikacji
AC-3 satisfied (polling), AC-4 satisfied (drukowanie), AC-5 satisfied (obsługa błędów)
Aplikacja Windows OrderPROPrint — system tray, polling API, drukowanie etykiet A6.
1. Otwórz `clients/windows/OrderPROPrint/` w Visual Studio lub uruchom `dotnet run`
2. Sprawdź: ikona pojawia się w system tray
3. Prawy klik → "Ustawienia" → wpisz URL + klucz API + wybierz drukarkę
4. Kliknij "Testuj połączenie" → powinno być "Połączono ✓"
5. Zapisz ustawienia, zamknij formularz
6. W orderPRO: zlecij wydruk etykiety (przycisk "Drukuj" w widoku przesyłki)
7. Poczekaj na polling (domyślnie 10s)
8. Sprawdź: etykieta wydrukowana na drukarce Xprinter
9. Sprawdź: w orderPRO kolejka wydruku → status "completed"
10. Sprawdź: tooltip ikony pokazuje liczbę wydrukowanych
11. Odłącz internet → sprawdź czy ikona zmienia się na "error"
12. Podłącz z powrotem → polling powinien wrócić do normy
Type "approved" to continue, or describe issues to fix
DO NOT CHANGE
- src/Modules/Printing/* (backend API gotowe z fazy 18-19)
- database/migrations/* (schemat stabilny)
- Endpointy API — klient konsumuje istniejące, nie modyfikuje serwera
SCOPE LIMITS
- Nie dodawaj auto-start z Windows (future enhancement)
- Nie buduj instalatora MSI/Setup (na razie
dotnet publish) - Nie dodawaj auto-update mechanizmu
- Nie modyfikuj kodu PHP — to jest czysty C# project
- Drukowanie tylko PDF — nie konwertuj do ZPL/EPL (drukarki termiczne z Windows driver)
<success_criteria>
- Wszystkie 5 AC spełnione
- Aplikacja stabilna — działa ciągle w tle bez crash
- Komunikacja z API poprawna (auth, polling, download, complete)
- Druk na drukarce termicznej w formacie A6
- Kod czytelny, SRP, async/await </success_criteria>