feat(139): sonar critical major cleanup

Phase 139 complete:

- 139-01 refreshed Sonar baseline and cleaned delivery-status/statistics slice

- 139-02 reduced BLOCKER/CRITICAL/MAJOR issues from 605 to 495

- Added typed exceptions and migrated alert includes to component helper
This commit is contained in:
2026-05-17 23:31:23 +02:00
parent ff91a29e4f
commit 251025fffc
66 changed files with 868 additions and 221 deletions

View File

@@ -13,8 +13,8 @@ Sprzedawca moĹĽe obsĹugiwać zamĂłwienia ze wszystkich kanaĹĂłw
| Attribute | Value | | Attribute | Value |
|-----------|-------| |-----------|-------|
| Version | 3.9.0-dev | | Version | 3.9.0-dev |
| Status | v3.9 Stabilizacja i splata dlugu technicznego in progress - Phase 138 Security and Legacy Hardening complete; Phase 139 ready to plan | | Status | v3.9 Stabilizacja i splata dlugu technicznego in progress - Phase 139 Sonar Critical/Major Cleanup complete; Phase 140 ready to plan |
| Last Updated | 2026-05-17 (Phase 138 closed) | | Last Updated | 2026-05-17 (Phase 139 closed) |
## Requirements ## Requirements
@@ -138,6 +138,7 @@ Sprzedawca moĹĽe obsĹugiwać zamĂłwienia ze wszystkich kanaĹĂłw
- [x] Fakturownia Invoice Idempotency: delegowane faktury uzywaja stabilnego `oid=orders.internal_order_number`, lookup-first `GET /invoices.json?oid=...`, lokalnego stanu `pending_external`/`failed_retryable` i auto-attach po timeoutach — Phase 136 - [x] Fakturownia Invoice Idempotency: delegowane faktury uzywaja stabilnego `oid=orders.internal_order_number`, lookup-first `GET /invoices.json?oid=...`, lokalnego stanu `pending_external`/`failed_retryable` i auto-attach po timeoutach — Phase 136
- [x] Delivery Status Backlog Verification: `DELIVERY-STATUS-MGMT` zamkniete jako wdrozone; runtime korzysta z DB-driven statusow, a read-only DB check nie wykazal starych ani niepoprawnych kluczy automatyzacji — Phase 137 - [x] Delivery Status Backlog Verification: `DELIVERY-STATUS-MGMT` zamkniete jako wdrozone; runtime korzysta z DB-driven statusow, a read-only DB check nie wykazal starych ani niepoprawnych kluczy automatyzacji — Phase 137
- [x] Security and Legacy Hardening: test SMTP ma strict TLS by default z lokalnym `SMTP_ALLOW_SELF_SIGNED_DEV`, szablony e-mail/SMS blokuja nieznane placeholdery, raw `$_SESSION` jest izolowany w `Session`, a wskazane widoki uzywaja `$component()` zamiast hard `require` — Phase 138 - [x] Security and Legacy Hardening: test SMTP ma strict TLS by default z lokalnym `SMTP_ALLOW_SELF_SIGNED_DEV`, szablony e-mail/SMS blokuja nieznane placeholdery, raw `$_SESSION` jest izolowany w `Session`, a wskazane widoki uzywaja `$component()` zamiast hard `require` — Phase 138
- [x] Sonar Critical/Major Cleanup: Phase 139 odswiezyla baseline Sonar i zmniejszyla OPEN BLOCKER/CRITICAL/MAJOR z 648 do 495 przez delivery-status/statistics cleanup, typowane wyjatki oraz szeroka migracje alert include/import patterns — Phase 139
- [x] Integracja polkurier.pl (fundament): pojedyncza globalna konfiguracja w `/settings/integrations/polkurier`, szyfrowany Token API + login, karta w hubie integracji obok Apaczki i realny test polaczenia przez `apimetod=test_auth_api` zweryfikowany na zywym koncie operatora; `ShipmentProviderRegistry` netkniety — `PolkurierShipmentService/TrackingService` w kolejnych fazach — Phase 127 - [x] Integracja polkurier.pl (fundament): pojedyncza globalna konfiguracja w `/settings/integrations/polkurier`, szyfrowany Token API + login, karta w hubie integracji obok Apaczki i realny test polaczenia przez `apimetod=test_auth_api` zweryfikowany na zywym koncie operatora; `ShipmentProviderRegistry` netkniety — `PolkurierShipmentService/TrackingService` w kolejnych fazach — Phase 127
- [x] polkurier ShipmentService + TrackingService + UI prepare panel: pelen kontrakt API (createShipment/getLabel/getStatus/cancelOrder/getAvailableCarriers), `PolkurierShipmentService` implementujacy `ShipmentProviderInterface` z normalizacja shipmenttype (lowercase) i splitem ulicy na street/housenumber/flatnumber, `PolkurierTrackingService` mapujacy statusy O/P/A/WP/D/Z/W na znormalizowane, panel "polkurier" w `prepare.php` z dynamiczna lista uslug z `available_carriers`, seed migracja `delivery_status_mappings(provider='polkurier')` z 7 wpisami z PDF v1.11; live test na #114/#115 zakonczony sukcesem po 4 iteracjach (ReferenceError → uppercase shipmenttype → orderno parsing → A4/A6); rozmiar etykiety sterowany w panelu klienta polkurier.pl (Ustawienia konta → Preferencje etykiet), NIE przez API — Phase 128 - [x] polkurier ShipmentService + TrackingService + UI prepare panel: pelen kontrakt API (createShipment/getLabel/getStatus/cancelOrder/getAvailableCarriers), `PolkurierShipmentService` implementujacy `ShipmentProviderInterface` z normalizacja shipmenttype (lowercase) i splitem ulicy na street/housenumber/flatnumber, `PolkurierTrackingService` mapujacy statusy O/P/A/WP/D/Z/W na znormalizowane, panel "polkurier" w `prepare.php` z dynamiczna lista uslug z `available_carriers`, seed migracja `delivery_status_mappings(provider='polkurier')` z 7 wpisami z PDF v1.11; live test na #114/#115 zakonczony sukcesem po 4 iteracjach (ReferenceError → uppercase shipmenttype → orderno parsing → A4/A6); rozmiar etykiety sterowany w panelu klienta polkurier.pl (Ustawienia konta → Preferencje etykiet), NIE przez API — Phase 128
- [x] Order User Notes module (Phase 129): pelen CRUD notatek autorskich operatora per zamowienie. Reuse `order_notes` przez nowy `note_type='user'` z `user_id` (FK→users SET NULL) + `author_name` (snapshot) + indeks `idx_order_notes_type_order`. `OrderNotesService` z autoryzacja DB-level (`WHERE user_id = :user_id`, rowCount=0 ⇒ 403). Sekcja `#notes` w "Wiadomosci i zalaczniki" w `/orders/{id}` z inline edit form + delete przez `OrderProAlerts.confirm`. Badge `[N]` (indigo neutralny) przy nr zamowienia na `/orders/list` (subquery `user_notes_count` w paginate). Brak admin override (brak systemu rol w aplikacji) — edit/delete tylko dla autora — Phase 129 - [x] Order User Notes module (Phase 129): pelen CRUD notatek autorskich operatora per zamowienie. Reuse `order_notes` przez nowy `note_type='user'` z `user_id` (FK→users SET NULL) + `author_name` (snapshot) + indeks `idx_order_notes_type_order`. `OrderNotesService` z autoryzacja DB-level (`WHERE user_id = :user_id`, rowCount=0 ⇒ 403). Sekcja `#notes` w "Wiadomosci i zalaczniki" w `/orders/{id}` z inline edit form + delete przez `OrderProAlerts.confirm`. Badge `[N]` (indigo neutralny) przy nr zamowienia na `/orders/list` (subquery `user_notes_count` w paginate). Brak admin override (brak systemu rol w aplikacji) — edit/delete tylko dla autora — Phase 129
@@ -150,7 +151,7 @@ Sprzedawca moĹĽe obsĹugiwać zamĂłwienia ze wszystkich kanaĹĂłw
### Active (In Progress) ### Active (In Progress)
- [ ] v3.9 Stabilizacja i splata dlugu technicznego — Phase 139 Sonar Critical/Major Cleanup ready to plan after Phase 138. - [ ] v3.9 Stabilizacja i splata dlugu technicznego — Phase 140 Performance Safeguards ready to plan after Phase 139.
### Planned (Next) ### Planned (Next)
@@ -272,6 +273,7 @@ PHP (XAMPP/Laravel), integracje z API marketplace'Ăłw (Allegro, Erli) oraz API
| SMTP mailbox TLS is strict by default | Phase 138: `ssl` and STARTTLS verify peer and host name; self-signed/unverified certificates require `SMTP_ALLOW_SELF_SIGNED_DEV=true` and local/dev/testing env. | 2026-05-17 | Active | | SMTP mailbox TLS is strict by default | Phase 138: `ssl` and STARTTLS verify peer and host name; self-signed/unverified certificates require `SMTP_ALLOW_SELF_SIGNED_DEV=true` and local/dev/testing env. | 2026-05-17 | Active |
| Unknown e-mail/SMS template placeholders are blocked on save | Phase 138: `TemplateVariableCatalog` is the shared catalog; create/edit rejects unknown `{{group.variable}}` keys while existing DB rows are not migrated. | 2026-05-17 | Active | | Unknown e-mail/SMS template placeholders are blocked on save | Phase 138: `TemplateVariableCatalog` is the shared catalog; create/edit rejects unknown `{{group.variable}}` keys while existing DB rows are not migrated. | 2026-05-17 | Active |
| Raw session access belongs only in `App\Core\Support\Session` | Phase 138 moved auth, CSRF, flash and Allegro OAuth state access behind `Session::get/set/has/forget/pull`. | 2026-05-17 | Active | | Raw session access belongs only in `App\Core\Support\Session` | Phase 138 moved auth, CSRF, flash and Allegro OAuth state access behind `Session::get/set/has/forget/pull`. | 2026-05-17 | Active |
| Phase 139 cleanup slices must stay behavior-preserving and leave god-class splits to Phase 141 | Phase 139 reduced Sonar BLOCKER/CRITICAL/MAJOR from 648 to 495 without DB/schema/business-flow changes; `php:S1448` remains a dedicated architecture concern. | 2026-05-17 | Active |
| polkurier startuje jako jedna globalna konfiguracja (single-instance, mirror Apaczka/HostedSMS/SMSPLANET) z realnym testowym wywolaniem `apimetod=test_auth_api` | Operator ma jedno konto polkurier; fundament musi byc zweryfikowany na zywym API zanim dolozymy `PolkurierShipmentService` | 2026-05-14 | Active | | polkurier startuje jako jedna globalna konfiguracja (single-instance, mirror Apaczka/HostedSMS/SMSPLANET) z realnym testowym wywolaniem `apimetod=test_auth_api` | Operator ma jedno konto polkurier; fundament musi byc zweryfikowany na zywym API zanim dolozymy `PolkurierShipmentService` | 2026-05-14 | Active |
| polkurier wymaga `login + token` razem w body `authorization` (nie samego tokena) | Zweryfikowane w SDK polkurier-sdk (`Auth.php`/`Request.php`); kolumna `login VARCHAR(190)` w `polkurier_integration_settings` mimo ze PLAN tego nie wymagal — kontrakt API to dyktuje | 2026-05-14 | Active | | polkurier wymaga `login + token` razem w body `authorization` (nie samego tokena) | Zweryfikowane w SDK polkurier-sdk (`Auth.php`/`Request.php`); kolumna `login VARCHAR(190)` w `polkurier_integration_settings` mimo ze PLAN tego nie wymagal — kontrakt API to dyktuje | 2026-05-14 | Active |
| polkurier API: top-level `status` === `'success'` (nie `'ok'`), tresc bledu w polu `response` envelope'a | `ResponseStatus::SUCCESS = 'success'` z `src/Type/ResponseStatus.php` SDK; bledy rzucane przez `ErrorException($response->get('response'))` w `PolkurierWebService.php`. Pattern dla wszystkich przyszlych metod polkurier API (`createShipment`, `getLabel`, `getStatus`, `cancelOrder`, etc.) | 2026-05-14 | Active | | polkurier API: top-level `status` === `'success'` (nie `'ok'`), tresc bledu w polu `response` envelope'a | `ResponseStatus::SUCCESS = 'success'` z `src/Type/ResponseStatus.php` SDK; bledy rzucane przez `ErrorException($response->get('response'))` w `PolkurierWebService.php`. Pattern dla wszystkich przyszlych metod polkurier API (`createShipment`, `getLabel`, `getStatus`, `cancelOrder`, etc.) | 2026-05-14 | Active |
@@ -319,6 +321,6 @@ Quick Reference:
--- ---
*PROJECT.md — Updated when requirements or context change* *PROJECT.md — Updated when requirements or context change*
*Last updated: 2026-05-17 after Phase 138 (Security and Legacy Hardening) closure* *Last updated: 2026-05-17 after Phase 139 (Sonar Critical/Major Cleanup) closure*

View File

@@ -12,7 +12,7 @@ Milestone porzadkujacy zbudowany z `.paul/codebase/todo.md` i `.paul/codebase/co
Rule for every phase/plan: przed implementacja sprawdzic w kodzie i dokumentacji, czy wpis nadal jest aktualny i czy nie zostal juz wdrozony; nastepnie przedstawic krotki plan operatorowi i zapytac o potwierdzenie. Dopiero po akceptacji wolno wprowadzac zmiany i uruchamiac testy. Jezeli wpis jest nieaktualny albo juz zrealizowany, faza/planu ma zamknac go dokumentacyjnie bez niepotrzebnej zmiany kodu. Rule for every phase/plan: przed implementacja sprawdzic w kodzie i dokumentacji, czy wpis nadal jest aktualny i czy nie zostal juz wdrozony; nastepnie przedstawic krotki plan operatorowi i zapytac o potwierdzenie. Dopiero po akceptacji wolno wprowadzac zmiany i uruchamiac testy. Jezeli wpis jest nieaktualny albo juz zrealizowany, faza/planu ma zamknac go dokumentacyjnie bez niepotrzebnej zmiany kodu.
Progress: 5 of 9 phases complete (56%). Progress: 6 of 9 phases complete (67%).
| Phase | Name | Plans | Status | | Phase | Name | Plans | Status |
|-------|------|-------|--------| |-------|------|-------|--------|
@@ -21,7 +21,7 @@ Progress: 5 of 9 phases complete (56%).
| 136 | Fakturownia Invoice Idempotency | 1/1 | Complete (2026-05-17; Fakturownia oid idempotency, migration/PHPUnit/Sonar env gaps documented) | | 136 | Fakturownia Invoice Idempotency | 1/1 | Complete (2026-05-17; Fakturownia oid idempotency, migration/PHPUnit/Sonar env gaps documented) |
| 137 | Delivery Status Backlog Verification | 1/1 | Complete (2026-05-17; verification-only closure, no stale automation keys found) | | 137 | Delivery Status Backlog Verification | 1/1 | Complete (2026-05-17; verification-only closure, no stale automation keys found) |
| 138 | Security and Legacy Hardening | 1/1 | Complete (2026-05-17; SMTP TLS/template/session/view hardening, PHPUnit/Sonar env gaps documented) | | 138 | Security and Legacy Hardening | 1/1 | Complete (2026-05-17; SMTP TLS/template/session/view hardening, PHPUnit/Sonar env gaps documented) |
| 139 | Sonar Critical/Major Cleanup | 1/TBD | Active (139-01 implemented 2026-05-17; total Sonar BLOCKER/CRITICAL/MAJOR 648 -> 605) | | 139 | Sonar Critical/Major Cleanup | 2/2 | Complete (2026-05-17; Sonar BLOCKER/CRITICAL/MAJOR reduced 648 -> 495 across two cleanup slices) |
| 140 | Performance Safeguards | TBD | Not started | | 140 | Performance Safeguards | TBD | Not started |
| 141 | God Classes and Duplication Refactor | TBD | Not started | | 141 | God Classes and Duplication Refactor | TBD | Not started |
| 142 | Architecture Guardrails | TBD | Not started | | 142 | Architecture Guardrails | TBD | Not started |
@@ -54,7 +54,7 @@ Plans: 138-01 (complete; `.paul/phases/138-security-and-legacy-hardening/138-01-
### Phase 139: Sonar Critical/Major Cleanup ### Phase 139: Sonar Critical/Major Cleanup
Focus: Zmniejszyc potwierdzone problemy SonarQube z `concerns.md`: generic exceptions, zbyt wiele returnow, powtarzajace sie literaly, cognitive complexity, unused parameters, use-namespace-import oraz accessibility (`aria-label`, `<output>`). Przed kazda grupa zmian odswiezyc stan skanu albo lokalnie potwierdzic wystepowanie problemu. Focus: Zmniejszyc potwierdzone problemy SonarQube z `concerns.md`: generic exceptions, zbyt wiele returnow, powtarzajace sie literaly, cognitive complexity, unused parameters, use-namespace-import oraz accessibility (`aria-label`, `<output>`). Przed kazda grupa zmian odswiezyc stan skanu albo lokalnie potwierdzic wystepowanie problemu.
Plans: 139-01 (implemented; `.paul/phases/139-sonar-critical-major-cleanup/139-01-SUMMARY.md`) Plans: 139-01 (complete; `.paul/phases/139-sonar-critical-major-cleanup/139-01-SUMMARY.md`); 139-02 (complete; `.paul/phases/139-sonar-critical-major-cleanup/139-02-SUMMARY.md`)
### Phase 140: Performance Safeguards ### Phase 140: Performance Safeguards
@@ -633,4 +633,4 @@ Archive: `.paul/milestones/v0.1-ROADMAP.md`
--- ---
*Roadmap created: 2026-03-12* *Roadmap created: 2026-03-12*
*Last updated: 2026-05-17 - Phase 139 plan 139-01 implemented* *Last updated: 2026-05-17 - Phase 139 complete; Phase 140 ready to plan*

View File

@@ -5,44 +5,51 @@
See: .paul/PROJECT.md (updated 2026-05-17) See: .paul/PROJECT.md (updated 2026-05-17)
**Core value:** Sprzedawca moze obslugiwac zamowienia ze wszystkich kanalow sprzedazy i nadawac przesylki bez przelaczania sie miedzy platformami. **Core value:** Sprzedawca moze obslugiwac zamowienia ze wszystkich kanalow sprzedazy i nadawac przesylki bez przelaczania sie miedzy platformami.
**Current focus:** v3.9 Stabilizacja i splata dlugu technicznego; Phase 139 Sonar Critical/Major Cleanup plan 139-01 implemented, ready for UNIFY/next slice. **Current focus:** v3.9 Stabilizacja i splata dlugu technicznego; Phase 139 Sonar Critical/Major Cleanup complete, Phase 140 Performance Safeguards ready to plan.
## Current Position ## Current Position
Milestone: v3.9 Stabilizacja i splata dlugu technicznego Milestone: v3.9 Stabilizacja i splata dlugu technicznego
Phase: 139 of 142 (Sonar Critical/Major Cleanup) - Applying Phase: 140 of 142 (Performance Safeguards)
Plan: 139-01 implemented Plan: Not started
Status: APPLY complete; final Sonar scan processed Status: Ready to plan
Last activity: 2026-05-17 19:58 - final Sonar scan processed; selected delivery-status files clean, statistics file has only remaining `php:S1448` Last activity: 2026-05-17 23:21 - Phase 139 complete; transitioned to Phase 140
Progress: Progress:
- Milestone v3.9: [######----] 56% (5 of 9 phases complete) - Milestone v3.9: [#######---] 67% (6 of 9 phases complete)
- Phase 139: [####------] 40% (fresh baseline + first cleanup slice complete) - Phase 140: [----------] 0% (ready to plan)
## Loop Position ## Loop Position
Current loop state: Current loop state:
``` ```
PLAN -> APPLY -> UNIFY PLAN -> APPLY -> UNIFY
done done open [ready for summary/UNIFY] done done done [Phase 139 loop complete; ready for Phase 140 PLAN]
``` ```
## Session Continuity ## Session Continuity
Last session: 2026-05-17 19:58 Last session: 2026-05-17 23:21
Stopped at: Plan 139-01 implemented; documentation and summary pending finalization Stopped at: Phase 139 complete, ready to plan Phase 140
Next action: Complete Phase 139-01 UNIFY/summary, then plan the next Sonar slice Next action: $paul-plan for Phase 140 Performance Safeguards
Resume file: .paul/phases/139-sonar-critical-major-cleanup/139-01-PLAN.md Resume file: .paul/ROADMAP.md
## Pending parallel work ## Pending parallel work
- None — Phase 118, 121, 122 wszystkie zacommitowane (8f14851, 360eef1). - None — Phase 118, 121, 122 wszystkie zacommitowane (8f14851, 360eef1).
## Git State ## Git State
Last phase commit: HEAD feat(138): security and legacy hardening Last phase commit: HEAD feat(139): sonar critical major cleanup
Previous: feat(136): fakturownia invoice idempotency Previous: feat(136): fakturownia invoice idempotency
Branch: main Branch: main
### Skill Audit (Phase 139)
| Expected | Invoked | Notes |
|----------|---------|-------|
| `sonar-scanner` | invoked | Local PATH did not contain the scanner, but the official Windows x64 scanner was downloaded to `%TEMP%` and used successfully before and after cleanup. |
| `sonar-scanner` 139-02 | invoked | Reused the official Windows x64 scanner from `%TEMP%`; final scan succeeded with analysis `2c18a5b3-40b4-41d8-b826-df88615749db` and 495 OPEN BLOCKER/CRITICAL/MAJOR issues. |
### Skill Audit (Phase 129) ### Skill Audit (Phase 129)
| Expected | Invoked | Notes | | Expected | Invoked | Notes |
@@ -114,6 +121,7 @@ Branch: main
- Phase 138 centralized raw `$_SESSION` access in `Session` and replaced targeted hard view `require`/inline `\App\...` patterns. - Phase 138 centralized raw `$_SESSION` access in `Session` and replaced targeted hard view `require`/inline `\App\...` patterns.
- Phase 139 is confirmed by operator. Plan 139-01 must run a fresh `sonar-scanner` before code cleanup; stale API-only results are not enough. Scope should fix as many confirmed issues as safely possible, split across multiple plans if needed. - Phase 139 is confirmed by operator. Plan 139-01 must run a fresh `sonar-scanner` before code cleanup; stale API-only results are not enough. Scope should fix as many confirmed issues as safely possible, split across multiple plans if needed.
- Phase 139-01 fresh scan found 648 OPEN BLOCKER/CRITICAL/MAJOR issues; final scan after cleanup found 605. Delivery status target files are clean; `OrdersStatisticsRepository` still needs a class split for `php:S1448`. - Phase 139-01 fresh scan found 648 OPEN BLOCKER/CRITICAL/MAJOR issues; final scan after cleanup found 605. Delivery status target files are clean; `OrdersStatisticsRepository` still needs a class split for `php:S1448`.
- Phase 139-02 final scan found 495 OPEN BLOCKER/CRITICAL/MAJOR issues. `php:S4833` dropped to 3 and selected `php:S112` generic exception clusters were replaced with typed exceptions.
### Blockers / Concerns ### Blockers / Concerns
@@ -133,7 +141,7 @@ Branch: main
- Phase 138 follow-up: run `vendor/bin/phpunit tests/Unit/SmtpSecurityContextFactoryTest.php tests/Unit/TemplateVariableCatalogTest.php` after dependencies are installed. - Phase 138 follow-up: run `vendor/bin/phpunit tests/Unit/SmtpSecurityContextFactoryTest.php tests/Unit/TemplateVariableCatalogTest.php` after dependencies are installed.
- Phase 139 follow-up: split `OrdersStatisticsRepository` (`php:S1448`, 43 methods) or include it in Phase 141 god-class refactor. - Phase 139 follow-up: split `OrdersStatisticsRepository` (`php:S1448`, 43 methods) or include it in Phase 141 god-class refactor.
- Phase 139 follow-up: continue with fresh confirmed groups `php:S1142`, `php:S1192`, `php:S4833`, `php:S3776`, `php:S1172`, `php:S112`, plus Web table/accessibility issues. - Phase 139 follow-up: continue with confirmed groups `php:S1142`, `php:S3776`, `php:S1172`, `php:S1192`, `php:S112`, plus Web table/accessibility issues. `php:S4833` is now only 3 core framework require issues.
- Phase 138 manual smoke: test a real SMTP SSL/STARTTLS mailbox in strict mode; test invalid and valid e-mail/SMS template saves in UI. - Phase 138 manual smoke: test a real SMTP SSL/STARTTLS mailbox in strict mode; test invalid and valid e-mail/SMS template saves in UI.
- Manualne testy AC-1..AC-7 dla Phase 112 na zywej bazie (XAMPP online). - Manualne testy AC-1..AC-7 dla Phase 112 na zywej bazie (XAMPP online).
- Backfill zamowienia #882 - operator robi recznie po wdrozeniu (poza zakresem planu). - Backfill zamowienia #882 - operator robi recznie po wdrozeniu (poza zakresem planu).

View File

@@ -8,6 +8,12 @@
- [Phase 138, Plan 138-01] Domknieto Security and Legacy Hardening: strict SMTP TLS, jawny local/dev self-signed override, walidacja zmiennych szablonow, centralizacja sesji i targeted view cleanup. - [Phase 138, Plan 138-01] Domknieto Security and Legacy Hardening: strict SMTP TLS, jawny local/dev self-signed override, walidacja zmiennych szablonow, centralizacja sesji i targeted view cleanup.
- Dodano `SmtpSecurityContextFactory`, `TemplateVariableCatalog`, testy jednostkowe dla obu polityk oraz helper `$component()` w `Template`. - Dodano `SmtpSecurityContextFactory`, `TemplateVariableCatalog`, testy jednostkowe dla obu polityk oraz helper `$component()` w `Template`.
- Udokumentowano brak zmian DB oraz luki srodowiskowe: `vendor/bin/phpunit` i `sonar-scanner` nie sa dostepne lokalnie. - Udokumentowano brak zmian DB oraz luki srodowiskowe: `vendor/bin/phpunit` i `sonar-scanner` nie sa dostepne lokalnie.
- [Phase 139, Plan 139-01] Odswiezono baseline SonarQube i wykonano pierwszy cleanup slice: OPEN BLOCKER/CRITICAL/MAJOR spadly z 648 do 605.
- Wyczyszczono wybrane pliki Delivery Status/settings/view z potwierdzonych problemow Sonar, uproszczono fragmenty `OrdersStatisticsRepository` i udokumentowano pozostaly `php:S1448`.
- Zweryfikowano `sonar-scanner` przez tymczasowo pobrany oficjalny scanner Windows; PHPUnit pozostaje zablokowany przez brak `vendor/` i Composera w PATH.
- [Phase 139, Plan 139-02] Domknieto drugi cleanup slice Sonar: OPEN BLOCKER/CRITICAL/MAJOR spadly z 605 do 495.
- Dodano typowane wyjatki dla wybranych klastrow Settings/Automation, zmigrowano targetowane alert includes na `$component()` i usunieto inline `\App\...` z widokow.
- Zamknieto Phase 139 jako kompletna; kolejne kroki to Phase 140 Performance Safeguards oraz pozniejsze Phase 141 god-class splits.
## Zmienione pliki ## Zmienione pliki
@@ -54,3 +60,39 @@
- `resources/views/users/index.php` - `resources/views/users/index.php`
- `tests/Unit/SmtpSecurityContextFactoryTest.php` - `tests/Unit/SmtpSecurityContextFactoryTest.php`
- `tests/Unit/TemplateVariableCatalogTest.php` - `tests/Unit/TemplateVariableCatalogTest.php`
- `.paul/phases/139-sonar-critical-major-cleanup/139-01-PLAN.md`
- `.paul/phases/139-sonar-critical-major-cleanup/139-01-SUMMARY.md`
- `.paul/phases/139-sonar-critical-major-cleanup/139-02-PLAN.md`
- `.paul/phases/139-sonar-critical-major-cleanup/139-02-SUMMARY.md`
- `.paul/phases/139-sonar-critical-major-cleanup/SONAR-BASELINE.md`
- `DOCS/todo.md`
- `src/Core/Exceptions/DeliveryStatusException.php`
- `src/Modules/Shipments/DeliveryStatus.php`
- `src/Modules/Shipments/DeliveryStatusRepository.php`
- `src/Modules/Settings/DeliveryStatusMappingController.php`
- `src/Modules/Settings/DeliveryStatusesController.php`
- `src/Modules/Statistics/OrdersStatisticsRepository.php`
- `resources/views/settings/_delivery-status-mappings-content.php`
- `resources/views/settings/delivery-status-mappings.php`
- `resources/views/settings/delivery-statuses.php`
- `tests/Unit/DeliveryStatusTest.php`
- `tests/Unit/OrdersStatisticsRepositoryTest.php`
- `src/Modules/Settings/FakturowniaApiException.php`
- `src/Modules/Settings/PolkurierApiException.php`
- `src/Modules/Settings/ErliOrderMappingException.php`
- `src/Modules/Automation/AutomationRuleException.php`
- `src/Modules/Settings/EmailTemplateException.php`
- `src/Modules/Settings/PolkurierApiClient.php`
- `src/Modules/Settings/ErliOrderMapper.php`
- `src/Modules/Automation/AutomationRepository.php`
- `src/Modules/Settings/EmailTemplateRepository.php`
- `src/Modules/Settings/SmsTemplateController.php`
- `src/Modules/Users/UsersController.php`
- `resources/views/layouts/app.php`
- `resources/views/layouts/auth.php`
- `resources/views/layouts/public.php`
- `resources/views/settings/erli.php`
- `resources/views/settings/polkurier.php`
- `resources/views/shipments/prepare.php`
- `tests/Unit/ErliOrderMapperTest.php`
- `tests/Unit/FakturowniaInvoiceIdempotencyTest.php`

View File

@@ -7,7 +7,7 @@ Szczegoly i dowody: `.paul/phases/134-backlog-reality-check/BACKLOG-AUDIT.md`.
| Group / item | Status po audycie | Krotki wniosek | | Group / item | Status po audycie | Krotki wniosek |
|--------------|-------------------|----------------| |--------------|-------------------|----------------|
| God Classes | **Active** | Klasy nadal sa duze; stare LOC/method counts sa nieaktualne, ale Phase 141 pozostaje zasadny. | | God Classes | **Active** | Klasy nadal sa duze; stare LOC/method counts sa nieaktualne, ale Phase 141 pozostaje zasadny. |
| SonarQube Issues | **Fresh baseline / active patterns** | Phase 139 odswiezyl baseline i zredukowal pierwsza fale issue; pozostale grupy sa aktualne po skanie z 2026-05-17. | | SonarQube Issues | **Fresh baseline / active patterns** | Phase 139 odswiezyl baseline i zredukowal dwie fale issue; pozostale grupy sa aktualne po skanie 139-02 z 2026-05-17. |
| Breaking: delivery status group keys | **Closed in Phase 137** | DB-driven statusy sa wdrozone, a read-only DB check nie znalazl starych ani niepoprawnych kluczy automatyzacji. | | Breaking: delivery status group keys | **Closed in Phase 137** | DB-driven statusy sa wdrozone, a read-only DB check nie znalazl starych ani niepoprawnych kluczy automatyzacji. |
| Breaking: `SHIPMENT_STATUS_OPTION_MAP` | **Implemented / stale** | Symbol nie wystepuje juz w runtime source. | | Breaking: `SHIPMENT_STATUS_OPTION_MAP` | **Implemented / stale** | Symbol nie wystepuje juz w runtime source. |
| Breaking: `_csrf_token` -> `_token` | **Implemented / stale** | Formularze/kontrolery uzywaja `_token`; wewnetrzny session key w `Csrf` nie jest problemem formularzy. | | Breaking: `_csrf_token` -> `_token` | **Implemented / stale** | Formularze/kontrolery uzywaja `_token`; wewnetrzny session key w `Csrf` nie jest problemem formularzy. |
@@ -18,7 +18,7 @@ Szczegoly i dowody: `.paul/phases/134-backlog-reality-check/BACKLOG-AUDIT.md`.
| Security: template variables | **Resolved in Phase 138** | Nowe/edytowane szablony e-mail/SMS blokuja nieznane `{{grupa.zmienna}}` przez wspolny `TemplateVariableCatalog`. | | Security: template variables | **Resolved in Phase 138** | Nowe/edytowane szablony e-mail/SMS blokuja nieznane `{{grupa.zmienna}}` przez wspolny `TemplateVariableCatalog`. |
| Architecture Concerns | **Active / low impact** | Zostawic do decyzji w Phase 142. | | Architecture Concerns | **Active / low impact** | Zostawic do decyzji w Phase 142. |
| Duplication Areas | **Mixed** | `SslCertificateResolver` i `RedirectPathResolver` sa czesciowo wdrozone; reszta wymaga selektywnej decyzji. | | Duplication Areas | **Mixed** | `SslCertificateResolver` i `RedirectPathResolver` sa czesciowo wdrozone; reszta wymaga selektywnej decyzji. |
| Legacy patterns | **Partly resolved in Phase 138** | Raw `$_SESSION` jest izolowany w `Session`; wskazane hard `require` i inline FQCN w widokach sa usuniete. Alert includes pozostaja zaakceptowanym patternem Phase 120. | | Legacy patterns | **Mostly resolved in Phase 139-02** | Raw `$_SESSION` jest izolowany w `Session`; targetowane hard `require`/alert includes i inline FQCN w widokach sa usuniete przez `$component()` i lokalne importy. |
| Performance Risks | **Active / needs profiling** | Return-risk indexes i cron backoff aktywne; `findDetails()` najpierw profilowac. | | Performance Risks | **Active / needs profiling** | Return-risk indexes i cron backoff aktywne; `findDetails()` najpierw profilowac. |
## God Classes (Priority Refactor Targets) ## God Classes (Priority Refactor Targets)
@@ -37,22 +37,22 @@ Szczegoly i dowody: `.paul/phases/134-backlog-reality-check/BACKLOG-AUDIT.md`.
## SonarQube Issues (new code since 2026-03-28) ## SonarQube Issues (new code since 2026-03-28)
Fresh Phase 139 baseline after plan 139-01: **605 OPEN BLOCKER/CRITICAL/MAJOR issues** (BLOCKER=0, CRITICAL=181, MAJOR=424). Fresh Phase 139 baseline after plan 139-02: **495 OPEN BLOCKER/CRITICAL/MAJOR issues** (BLOCKER=0, CRITICAL=178, MAJOR=317).
| Rule | Count | Severity | Examples | | Rule | Count | Severity | Examples |
|------|-------|----------|---------| |------|-------|----------|---------|
| `php:S1142` — Excess return statements | 148 | MAJOR | Many service/controller methods still have 4+ returns | | `php:S1142` — Excess return statements | 148 | MAJOR | Many service/controller methods still have 4+ returns |
| `php:S1192` Duplicated string literals | 101 | CRITICAL | Route paths, SQL fragments, status strings, HTTP headers | | `php:S1192` - Duplicated string literals | 98 | CRITICAL | Route paths, SQL fragments, status strings, HTTP headers |
| `php:S4833` Use namespace import / direct include patterns | 93 | MAJOR | Remaining source/view import/include cleanup outside Phase 139-01 target | | `php:S4833` - Use namespace import / direct include patterns | 3 | MAJOR | Remaining issues are core framework `require` calls in Application/Translator/Template |
| `php:S3776` — Cognitive complexity > 15 | 54 | CRITICAL | Mapper/service/reporting methods needing focused refactor | | `php:S3776` — Cognitive complexity > 15 | 54 | CRITICAL | Mapper/service/reporting methods needing focused refactor |
| `php:S1172` — Unused parameters | 41 | MAJOR | Handler payload/request params | | `php:S1172` — Unused parameters | 41 | MAJOR | Handler payload/request params |
| `php:S112` Generic exceptions | 40 | MAJOR | Remaining generic exceptions outside delivery-status repository | | `php:S112` - Generic exceptions | 23 | MAJOR | Remaining generic exceptions outside selected compact Settings/Automation clusters |
| `php:S1448` — Class too large | 16 | MAJOR | See god classes above | | `php:S1448` — Class too large | 16 | MAJOR | See god classes above |
| `php:S4423` — Weak TLS protocol | stale | **CRITICAL** | Resolved in Phase 138: `EmailMailboxController::testConnection()` uzywa strict SSL context i STARTTLS | | `php:S4423` — Weak TLS protocol | stale | **CRITICAL** | Resolved in Phase 138: `EmailMailboxController::testConnection()` uzywa strict SSL context i STARTTLS |
| `Web:TableHeaderHasIdOrScopeCheck` | 16 | MAJOR | Tables without explicit header scope/id | | `Web:TableHeaderHasIdOrScopeCheck` | 16 | MAJOR | Tables without explicit header scope/id |
| `Web:S6819` — Accessibility | 5 | MAJOR | Use semantic output/status elements where applicable | | `Web:S6819` — Accessibility | 5 | MAJOR | Use semantic output/status elements where applicable |
Phase 139-01 reduced the fresh total by 43 issues and cleared all selected delivery-status files. Remaining detailed baseline: `.paul/phases/139-sonar-critical-major-cleanup/SONAR-BASELINE.md`. Phase 139-01 reduced the fresh total by 43 issues and cleared all selected delivery-status files. Phase 139-02 reduced the post-139-01 total by 110, mainly through broad `$component()` alert rendering and typed exceptions. Remaining detailed baseline: `.paul/phases/139-sonar-critical-major-cleanup/SONAR-BASELINE.md`.
## Breaking Changes ## Breaking Changes
@@ -112,7 +112,7 @@ These support the correlated subquery in `OrdersRepository` used for return-risk
| Pattern | Location | Status | | Pattern | Location | Status |
|---------|----------|--------| |---------|----------|--------|
| `fsockopen('ssl://')` / weak SMTP TLS | `EmailMailboxController::testConnection()` | Resolved in Phase 138; strict stream context + STARTTLS, local dev override only. | | `fsockopen('ssl://')` / weak SMTP TLS | `EmailMailboxController::testConnection()` | Resolved in Phase 138; strict stream context + STARTTLS, local dev override only. |
| `require` in targeted views | `resources/views/accounting/index.php`, `orders/list.php`, `orders/show.php`, `users/index.php` | Resolved in Phase 138 through `$component()` helper. Other alert includes remain accepted Phase 120 pattern. | | `require` / direct alert includes in targeted views | `resources/views/...` targeted by Phase 139-02 | Resolved through `$component()` helper. Remaining `php:S4833` issues are core framework file loading, not alert components. |
| Raw `$_SESSION` access | Auth/Flash/Csrf/OAuth before Phase 138 | Resolved in Phase 138; raw access is isolated in `App\Core\Support\Session`. | | Raw `$_SESSION` access | Auth/Flash/Csrf/OAuth before Phase 138 | Resolved in Phase 138; raw access is isolated in `App\Core\Support\Session`. |
## Performance Risks ## Performance Risks

View File

@@ -1,5 +1,19 @@
# Technical Changelog # Technical Changelog
## 2026-05-17 - Phase 139 Plan 02: Sonar Critical/Major Cleanup
**Co zrobiono:**
- Finalny Sonar: 495 OPEN BLOCKER/CRITICAL/MAJOR po 139-02 (spadek z 605 po 139-01).
- Dodano typowane wyjatki w selected Settings/Automation clusters: Fakturownia, Polkurier, Erli mapping, automation duplicate i email template duplicate.
- Targetowane widoki/layouty renderuja alert component przez `$component()`; `messageHtml` pozostaje tylko dla zaufanego gotowego HTML.
- `SmsTemplateController` i `UsersController` maja stale tras/flashy oraz male helpery walidacyjne, bez zmiany routingu i UX.
**Dlaczego:**
- Najwiekszy bezpieczny zysk po 139-01 byl w `php:S4833` i `php:S112`; plan nie mial ruszac schematu DB ani god-class splitow.
**BREAKING / migracja:**
- Brak migracji DB i brak breaking changes.
## 2026-05-17 - Phase 139 Plan 01: Sonar Critical/Major Cleanup ## 2026-05-17 - Phase 139 Plan 01: Sonar Critical/Major Cleanup
**Co zrobiono:** **Co zrobiono:**

View File

@@ -0,0 +1,275 @@
---
phase: 139-sonar-critical-major-cleanup
plan: 02
type: execute
wave: 1
depends_on: ["139-01"]
files_modified:
- src/Modules/Settings/FakturowniaApiException.php
- src/Modules/Settings/FakturowniaApiClient.php
- src/Modules/Settings/PolkurierApiException.php
- src/Modules/Settings/PolkurierApiClient.php
- src/Modules/Settings/ErliOrderMappingException.php
- src/Modules/Settings/ErliOrderMapper.php
- src/Modules/Automation/AutomationRuleException.php
- src/Modules/Automation/AutomationRepository.php
- src/Modules/Settings/EmailTemplateException.php
- src/Modules/Settings/EmailTemplateRepository.php
- src/Modules/Settings/SmsTemplateController.php
- src/Modules/Users/UsersController.php
- resources/views/layouts/app.php
- resources/views/layouts/auth.php
- resources/views/layouts/public.php
- resources/views/auth/login.php
- resources/views/users/index.php
- resources/views/orders/list.php
- resources/views/orders/show.php
- resources/views/orders/receipt-create.php
- resources/views/automation/index.php
- resources/views/automation/form.php
- resources/views/statistics/orders.php
- resources/views/accounting/index.php
- resources/views/accounting/invoice_form.php
- resources/views/shipments/prepare.php
- resources/views/settings/accounting.php
- resources/views/settings/accounting-invoices.php
- resources/views/settings/accounting-invoice-edit.php
- resources/views/settings/accounting-receipts.php
- resources/views/settings/accounting-receipt-edit.php
- resources/views/settings/allegro.php
- resources/views/settings/apaczka.php
- resources/views/settings/company.php
- resources/views/settings/cron.php
- resources/views/settings/database.php
- resources/views/settings/delivery-status-form.php
- resources/views/settings/email-mailboxes.php
- resources/views/settings/email-templates.php
- resources/views/settings/email-templates-form.php
- resources/views/settings/erli.php
- resources/views/settings/fakturownia.php
- resources/views/settings/hostedsms.php
- resources/views/settings/inpost.php
- resources/views/settings/integrations.php
- resources/views/settings/polkurier.php
- resources/views/settings/printing.php
- resources/views/settings/project-mappings.php
- resources/views/settings/shoppro.php
- resources/views/settings/sms-templates.php
- resources/views/settings/sms-templates-form.php
- resources/views/settings/smsplanet.php
- resources/views/settings/statuses.php
- tests/Unit/ErliOrderMapperTest.php
- tests/Unit/FakturowniaInvoiceIdempotencyTest.php
- tests/Unit/OrdersStatisticsRepositoryTest.php
- DOCS/ARCHITECTURE.md
- DOCS/TECH_CHANGELOG.md
- DOCS/todo.md
- .paul/codebase/concerns.md
- .paul/codebase/tech_changelog.md
- .paul/phases/139-sonar-critical-major-cleanup/SONAR-BASELINE.md
autonomous: true
delegation: off
---
<objective>
## Goal
Reduce the largest safe set of remaining Phase 139 Sonar `BLOCKER,CRITICAL,MAJOR` issues after plan 139-01, prioritizing high-count rules `php:S4833`, `php:S112`, `php:S1142`, `php:S1192`, and `php:S1172` without doing the `OrdersStatisticsRepository`/god-class split reserved for Phase 141.
## Purpose
Plan 139-01 already reduced the fresh Sonar baseline from 648 to 605 issues and cleared the selected delivery-status cluster. This plan takes the operator-approved aggressive path for the next highest-impact cleanup: remove broad mechanical import/include issues, replace generic exceptions in compact API/repository clusters, and flatten small controllers where the behavior is easy to preserve.
## Output
Typed exception classes for selected Settings/Automation failures, remaining alert component includes migrated to `$component()`, small controller/repository return/literal cleanup, updated Sonar/debt documentation, and a final Sonar scan showing the remaining count and next slice.
</objective>
<context>
<clarifications>
- **Zakres** - Jaki ma byc glowny zakres planu 139-02?
-> Odpowiedz: Najwiekszy efekt.
- **Skan** - Czy 139-02 ma ponownie zaczynac od pelnego `sonar-scanner`, czy bazowac na finalnym skanie z 139-01?
-> Odpowiedz: Bazuj na 139-01.
- **Ryzyko** - Jak agresywny ma byc refaktor w tym slice?
-> Odpowiedz: Agresywny.
</clarifications>
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md
@AGENTS.md
@DOCS/ARCHITECTURE.md
@DOCS/DB_SCHEMA.md
@DOCS/TECH_CHANGELOG.md
## Prior Work
@.paul/phases/139-sonar-critical-major-cleanup/139-01-SUMMARY.md
@.paul/phases/139-sonar-critical-major-cleanup/SONAR-BASELINE.md
## Source Files
@.paul/SPECIAL-FLOWS.md
@.paul/codebase/concerns.md
@DOCS/todo.md
@src/Modules/Settings/FakturowniaApiClient.php
@src/Modules/Settings/PolkurierApiClient.php
@src/Modules/Settings/ErliOrderMapper.php
@src/Modules/Automation/AutomationRepository.php
@src/Modules/Settings/EmailTemplateRepository.php
@src/Modules/Settings/SmsTemplateController.php
@src/Modules/Users/UsersController.php
@resources/views/layouts/app.php
@resources/views/layouts/auth.php
@resources/views/layouts/public.php
@resources/views/shipments/prepare.php
@resources/views/settings/*.php
@resources/views/orders/*.php
@resources/views/automation/*.php
@resources/views/accounting/*.php
@resources/views/users/index.php
@resources/views/auth/login.php
@resources/views/statistics/orders.php
</context>
<skills>
## Required Skills (from SPECIAL-FLOWS.md)
| Skill | Priority | When to Invoke | Loaded? |
|-------|----------|----------------|---------|
| `sonar-scanner` | required | After APPLY, before UNIFY | o |
| /code-review | optional | Before UNIFY if the broad view/repository cleanup touches shared contracts | o |
| /simplify | optional | After APPLY if aggressive refactor leaves duplicated helper patterns | o |
**BLOCKING for UNIFY:** `sonar-scanner` must be run after implementation. Because plan 139-02 intentionally bases initial target selection on the final 139-01 scan, APPLY does not need a new pre-edit scanner run. If `sonar-scanner` is still not in PATH, reuse the official Windows scanner downloaded during 139-01 or download the same official Windows x64 scanner to `%TEMP%`.
</skills>
<acceptance_criteria>
## AC-1: Plan uses the 139-01 final baseline, not stale counts
```gherkin
Given plan 139-01 ended with 605 OPEN BLOCKER/CRITICAL/MAJOR issues
When 139-02 implementation starts
Then target selection is based on `.paul/phases/139-sonar-critical-major-cleanup/SONAR-BASELINE.md` final scan and local code inspection, without requiring a new pre-edit scanner run
```
## AC-2: Generic exceptions are reduced in selected compact clusters
```gherkin
Given Sonar still reports `php:S112` outside the delivery-status repository
When the selected Settings/Automation clusters are cleaned
Then `FakturowniaApiClient`, `PolkurierApiClient`, `ErliOrderMapper`, `AutomationRepository::duplicate()`, and `EmailTemplateRepository::duplicate()` use typed domain exceptions instead of generic `RuntimeException`
```
## AC-3: Direct component includes/import-pattern issues are reduced broadly
```gherkin
Given many views still include `components/alert.php` directly or reference fully qualified `\App\...` classes inline
When the view cleanup finishes
Then targeted views use the established `$component('components/alert', [...])` helper and inline `\App\...` references are moved to controller-prepared variables or local imports where practical
```
## AC-4: Small controller/repository return and literal issues are reduced
```gherkin
Given selected controllers have repeated redirect paths, repeated flash keys, unused request parameters, or excessive early returns
When the cleanup completes
Then `SmsTemplateController` and `UsersController` have narrow helpers/constants that preserve the same redirects, messages, validation order, and response statuses while reducing confirmed `php:S1142`, `php:S1192`, and `php:S1172` issues
```
## AC-5: Behavior remains unchanged
```gherkin
Given the plan touches shared settings APIs, automation duplication, user creation, SMS templates, and view rendering
When verification runs
Then PHP lint passes for every touched PHP file/view, existing targeted runtime smokes still pass where available, and no route, form action, CSRF, flash, status mapping, invoice idempotency, or shipment behavior changes are introduced
```
## AC-6: Remaining Sonar work is documented
```gherkin
Given this is still not the final Phase 139 slice
When the final Sonar scan completes
Then `SONAR-BASELINE.md`, `DOCS/todo.md`, `.paul/codebase/concerns.md`, and technical changelogs record the new totals, fixed groups, remaining top groups, and the recommended next plan
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Replace selected generic exceptions with typed domain exceptions</name>
<files>src/Modules/Settings/FakturowniaApiException.php, src/Modules/Settings/FakturowniaApiClient.php, src/Modules/Settings/PolkurierApiException.php, src/Modules/Settings/PolkurierApiClient.php, src/Modules/Settings/ErliOrderMappingException.php, src/Modules/Settings/ErliOrderMapper.php, src/Modules/Automation/AutomationRuleException.php, src/Modules/Automation/AutomationRepository.php, src/Modules/Settings/EmailTemplateException.php, src/Modules/Settings/EmailTemplateRepository.php, tests/Unit/ErliOrderMapperTest.php, tests/Unit/FakturowniaInvoiceIdempotencyTest.php</files>
<action>
Reduce confirmed `php:S112` in compact, high-confidence clusters.
- Add final typed exception classes extending the nearest existing project exception base when available, otherwise `RuntimeException`: `FakturowniaApiException`, `PolkurierApiException`, `ErliOrderMappingException`, `AutomationRuleException`, `EmailTemplateException`.
- Replace `throw new RuntimeException` / `throw new \RuntimeException` only in the selected files listed above. Preserve exception messages, codes, previous exceptions, and catch semantics.
- Update `catch (RuntimeException ...)` in `PolkurierApiClient::testConnection()` to catch the new typed exception if the call path now throws it.
- Do not convert broad `catch (RuntimeException)` blocks in integration sync services unless they catch the selected new typed exception without changing external behavior.
- Adjust tests only where they assert exception class or message. Do not add schema changes or new dependencies.
</action>
<verify>`C:\xampp\php\php.exe -l` on all selected source/test files; targeted ad-hoc runtime smoke for Erli mapper invalid payload; existing invoice idempotency smoke if available; final Sonar file-level check after scanner</verify>
<done>AC-2 and AC-5 satisfied for selected generic exception clusters.</done>
</task>
<task type="auto">
<name>Task 2: Migrate remaining alert includes and inline view imports</name>
<files>resources/views/layouts/app.php, resources/views/layouts/auth.php, resources/views/layouts/public.php, resources/views/auth/login.php, resources/views/users/index.php, resources/views/orders/list.php, resources/views/orders/show.php, resources/views/orders/receipt-create.php, resources/views/automation/index.php, resources/views/automation/form.php, resources/views/statistics/orders.php, resources/views/accounting/index.php, resources/views/accounting/invoice_form.php, resources/views/shipments/prepare.php, resources/views/settings/accounting.php, resources/views/settings/accounting-invoices.php, resources/views/settings/accounting-invoice-edit.php, resources/views/settings/accounting-receipts.php, resources/views/settings/accounting-receipt-edit.php, resources/views/settings/allegro.php, resources/views/settings/apaczka.php, resources/views/settings/company.php, resources/views/settings/cron.php, resources/views/settings/database.php, resources/views/settings/delivery-status-form.php, resources/views/settings/email-mailboxes.php, resources/views/settings/email-templates.php, resources/views/settings/email-templates-form.php, resources/views/settings/erli.php, resources/views/settings/fakturownia.php, resources/views/settings/hostedsms.php, resources/views/settings/inpost.php, resources/views/settings/integrations.php, resources/views/settings/polkurier.php, resources/views/settings/printing.php, resources/views/settings/project-mappings.php, resources/views/settings/shoppro.php, resources/views/settings/sms-templates.php, resources/views/settings/sms-templates-form.php, resources/views/settings/smsplanet.php, resources/views/settings/statuses.php</files>
<action>
Reduce `php:S4833` and repeated include patterns aggressively but mechanically.
- Replace direct `include dirname(__DIR__) . '/components/alert.php'` and `include __DIR__ . '/../components/alert.php'` alert rendering with `$component('components/alert', [...])`.
- Preserve `messageHtml` behavior by passing `messageHtml` only for trusted prebuilt HTML and preserving `dismissible`/`role` values. Do not double-escape trusted HTML.
- In layouts, keep central flash rendering behavior exactly the same while avoiding direct component include.
- In `shipments/prepare.php`, move repeated inline `\App\Modules\Shipments\DeliveryStatus::*` and `\App\Core\Support\StringHelper::*` access to local prepared variables/imports at the top of the view or to controller-provided variables if already easy. Do not alter visible shipment rows, links, status colors, or tracking URLs.
- Leave JavaScript-generated alert class names alone; they are not PHP component includes and should remain compatible with existing CSS.
</action>
<verify>`C:\xampp\php\php.exe -l` on every touched view/layout; `rg -n "include dirname\\(__DIR__\\) . '/components/alert.php'|include __DIR__ . '/../components/alert.php'|\\\\App\\\\" resources/views` shows only documented intentional leftovers; manual browser smoke deferred but rendering syntax is valid</verify>
<done>AC-3 and AC-5 satisfied for targeted view/component cleanup.</done>
</task>
<task type="auto">
<name>Task 3: Flatten small controllers and update Sonar documentation</name>
<files>src/Modules/Settings/SmsTemplateController.php, src/Modules/Users/UsersController.php, DOCS/ARCHITECTURE.md, DOCS/TECH_CHANGELOG.md, DOCS/todo.md, .paul/codebase/concerns.md, .paul/codebase/tech_changelog.md, .paul/phases/139-sonar-critical-major-cleanup/SONAR-BASELINE.md</files>
<action>
Reduce local `php:S1142`, `php:S1192`, and `php:S1172` where the selected controllers are simple enough to preserve exactly.
- In `SmsTemplateController`, introduce constants for routes/flash keys/messages, extract CSRF/id validation helpers that return a `Response` or normalized data, and keep JSON status codes/messages unchanged.
- In `UsersController`, extract validation into small methods or a validation-result helper so `store()` has fewer early returns while preserving old form flash values, validation order, redirects, and password hashing behavior.
- Remove unused request parameters only where the method does not need them and route invocation supports the same signature pattern in this project. If unsure, keep the parameter and document it as intentionally retained.
- Run the final Sonar scanner after all code changes. Update baseline/docs with before/after totals, fixed groups, remaining top groups, and whether the next slice should address Web accessibility or Phase 141 god-class work.
</action>
<verify>`C:\xampp\php\php.exe -l src/Modules/Settings/SmsTemplateController.php src/Modules/Users/UsersController.php`; `git diff --check`; final `sonar-scanner` using PATH or the downloaded Windows scanner; `vendor/bin/phpunit` only if dependencies exist, otherwise document the environment gap</verify>
<done>AC-1, AC-4, AC-5, and AC-6 satisfied.</done>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- Do not split `OrdersStatisticsRepository`, `OrdersRepository`, `OrdersController`, `AutomationService`, or other god classes in this plan; that belongs to Phase 141 or a dedicated later slice.
- Do not change database schema, migrations, SQL semantics, status mappings, cron behavior, payment/invoice/shipment business behavior, or route URLs.
- Do not add native `alert()` / `confirm()` or CSS in views.
- Do not replace JS-generated alert markup that intentionally only uses `.alert` classes.
- Do not use `DB_HOST_REMOTE`; no DB writes or migrations are needed.
## SCOPE LIMITS
- This plan may be broad but must stay behavior-preserving.
- Initial target selection is based on the final 139-01 scan plus local code inspection; only the final scanner run is required.
- If a file is riskier than expected, skip that file, document the skip in `SONAR-BASELINE.md`, and continue with the other selected files.
- If `vendor/bin/phpunit` is missing, use PHP lint plus existing ad-hoc runtime smokes and document the gap.
</boundaries>
<verification>
Before declaring plan complete:
- [ ] PHP lint passes for all touched PHP source files and views.
- [ ] `rg` confirms targeted direct alert includes and selected generic `RuntimeException` throws were removed or explicitly documented as intentional leftovers.
- [ ] Existing targeted ad-hoc runtime smokes pass where available.
- [ ] `git diff --check` passes.
- [ ] Final `sonar-scanner` runs successfully using PATH or the downloaded Windows scanner from `%TEMP%`.
- [ ] `SONAR-BASELINE.md`, `DOCS/todo.md`, `DOCS/ARCHITECTURE.md`, `DOCS/TECH_CHANGELOG.md`, `.paul/codebase/concerns.md`, and `.paul/codebase/tech_changelog.md` are updated.
</verification>
<success_criteria>
- 139-02 reduces the total OPEN `BLOCKER,CRITICAL,MAJOR` Sonar count from the 139-01 final baseline of 605.
- Selected generic exceptions are replaced by typed domain exceptions without changing caller behavior.
- Targeted views use `$component()` for alert rendering and no longer rely on hard includes.
- Small controller cleanup reduces confirmed return/literal/unused-parameter issues while preserving UX and response contracts.
- Remaining high-count issue groups are documented for the next Phase 139 plan or Phase 141.
</success_criteria>
<output>
After completion, create `.paul/phases/139-sonar-critical-major-cleanup/139-02-SUMMARY.md`.
</output>

View File

@@ -0,0 +1,150 @@
---
phase: 139-sonar-critical-major-cleanup
plan: 139-02
subsystem: quality
tags: [sonar, php, views, exceptions, refactor]
requires:
- phase: 139-01
provides: Fresh Sonar baseline reduced from 648 to 605 OPEN BLOCKER/CRITICAL/MAJOR issues
provides:
- Typed exceptions for selected Settings/Automation failure paths
- Broad alert component include migration to Template component helper
- Updated Phase 139 Sonar baseline and debt documentation
affects: [phase-139, phase-141, sonar-cleanup, ui-alerts]
tech-stack:
added: []
patterns:
- Typed domain exceptions for compact API/repository failure clusters
- Alert rendering through `$component('components/alert', ...)`
key-files:
created:
- src/Modules/Settings/FakturowniaApiException.php
- src/Modules/Settings/PolkurierApiException.php
- src/Modules/Settings/ErliOrderMappingException.php
- src/Modules/Automation/AutomationRuleException.php
- src/Modules/Settings/EmailTemplateException.php
modified:
- .paul/phases/139-sonar-critical-major-cleanup/SONAR-BASELINE.md
- DOCS/ARCHITECTURE.md
- DOCS/TECH_CHANGELOG.md
- DOCS/todo.md
key-decisions:
- "Use 139-01 final Sonar result as the 139-02 baseline; no pre-edit scanner rerun."
- "Keep Phase 141 god-class splits out of this cleanup slice."
patterns-established:
- "Trusted prebuilt alert HTML must be passed as `messageHtml`; normal alert text uses `message`."
duration: ~35min
started: 2026-05-17T23:09:00+02:00
completed: 2026-05-17T23:21:00+02:00
---
# Phase 139 Plan 02: Sonar Critical/Major Cleanup Summary
Plan 139-02 removed the largest safe cluster after 139-01: broad view include/import issues, selected generic exceptions, and small controller literal/return cleanup.
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~35min |
| Started | 2026-05-17 23:09 |
| Completed | 2026-05-17 23:21 |
| Tasks | 3 completed |
| Final Sonar total | 495 OPEN BLOCKER/CRITICAL/MAJOR |
## Acceptance Criteria Results
| Criterion | Status | Evidence |
|-----------|--------|----------|
| AC-1: 139-01 baseline used | Pass | Implementation used the documented 139-01 final baseline of 605 and only ran scanner after code changes. |
| AC-2: Selected generic exceptions reduced | Pass | New typed exceptions replaced target `RuntimeException` throws/catches; `php:S112` dropped from 40 to 23. |
| AC-3: Direct alert includes/import patterns reduced | Pass | `rg` found no target direct alert includes or inline `\App\...` in `resources/views`; `php:S4833` dropped from 93 to 3. |
| AC-4: Small controller cleanup | Pass | `SmsTemplateController` and `UsersController` now centralize routes, flash keys and validation helpers without route/UX changes. |
| AC-5: Behavior unchanged | Pass | PHP lint passed; ad-hoc Erli invalid-payload smoke passed; no DB/schema/routes were changed. |
| AC-6: Remaining Sonar work documented | Pass | `SONAR-BASELINE.md`, `DOCS/todo.md`, `DOCS/ARCHITECTURE.md`, `DOCS/TECH_CHANGELOG.md`, `.paul/codebase/concerns.md`, and `.paul/codebase/tech_changelog.md` were updated. |
## Accomplishments
- Reduced OPEN `BLOCKER,CRITICAL,MAJOR` Sonar issues from 605 to 495.
- Added typed exceptions for Fakturownia, Polkurier, Erli mapping, automation duplication, and email template duplication.
- Migrated target view/layout alert rendering to `$component('components/alert', ...)`, preserving trusted `messageHtml` behavior.
- Removed target inline fully qualified `\App\...` references from views by using local imports.
- Documented remaining Phase 139 work and the Phase 141 boundary for god-class splits.
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `src/Modules/Settings/FakturowniaApiException.php` | Created | Typed exception for Fakturownia API failures. |
| `src/Modules/Settings/PolkurierApiException.php` | Created | Typed exception for Polkurier API failures. |
| `src/Modules/Settings/ErliOrderMappingException.php` | Created | Typed exception for Erli inbox mapping failures. |
| `src/Modules/Automation/AutomationRuleException.php` | Created | Typed exception for automation rule duplication failures. |
| `src/Modules/Settings/EmailTemplateException.php` | Created | Typed exception for e-mail template duplication failures. |
| `src/Modules/Settings/FakturowniaApiClient.php` | Modified | Replaced selected generic runtime exceptions. |
| `src/Modules/Settings/PolkurierApiClient.php` | Modified | Replaced selected generic runtime exceptions and catch type. |
| `src/Modules/Settings/ErliOrderMapper.php` | Modified | Replaced mapping guard exceptions. |
| `src/Modules/Automation/AutomationRepository.php` | Modified | Replaced duplicate missing-rule exception. |
| `src/Modules/Settings/EmailTemplateRepository.php` | Modified | Replaced duplicate missing-template exception. |
| `src/Modules/Settings/SmsTemplateController.php` | Modified | Extracted constants/helpers for flash, routes and redirects. |
| `src/Modules/Users/UsersController.php` | Modified | Extracted user validation and flash helpers. |
| `resources/views/**/*.php` | Modified | Migrated target alerts to `$component()` and view imports where applicable. |
| `tests/Unit/ErliOrderMapperTest.php` | Modified | Updated exception assertion to typed mapper exception. |
| `tests/Unit/FakturowniaInvoiceIdempotencyTest.php` | Modified | Updated API failure mocks to typed exception. |
| `DOCS/ARCHITECTURE.md` | Modified | Documented 139-02 architecture cleanup. |
| `DOCS/TECH_CHANGELOG.md` | Modified | Added 139-02 technical changelog. |
| `DOCS/todo.md` | Modified | Updated Sonar totals and next slices. |
| `.paul/codebase/concerns.md` | Modified | Updated Sonar debt counts and legacy-pattern status. |
| `.paul/codebase/tech_changelog.md` | Modified | Added 139-02 PAUL technical changelog. |
| `.paul/phases/139-sonar-critical-major-cleanup/SONAR-BASELINE.md` | Modified | Recorded final Sonar analysis and remaining groups. |
## Verification Results
| Check | Result |
|-------|--------|
| PHP lint on touched source/test/view/layout files | Pass |
| `rg` target direct alert includes | Pass: no target leftovers |
| `rg` inline `\App\...` in views | Pass: no leftovers |
| `rg` selected generic `RuntimeException` leftovers | Pass: no leftovers |
| Ad-hoc Erli mapper invalid payload smoke | Pass: typed `ErliOrderMappingException` with preserved message |
| `git diff --check` | Pass |
| Final `sonar-scanner` | Pass, analysis `2c18a5b3-40b4-41d8-b826-df88615749db` |
| PHPUnit | Not run: `vendor/` and `vendor/bin/phpunit` are missing |
## Deviations from Plan
| Type | Count | Impact |
|------|-------|--------|
| Environment gaps | 1 | PHPUnit could not run because dependencies are not installed in checkout. |
| Scope additions | 0 | None. |
| Deferred | 0 | No new deferred issues beyond documented remaining Sonar groups. |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| Reuse `%TEMP%` SonarScanner | Scanner is still not in PATH, but the official Windows x64 scanner from 139-01 exists and works. | Final scan completed without adding dependencies. |
| Do not split god classes | The plan explicitly reserved `OrdersStatisticsRepository` and other god-class work for Phase 141. | Phase 139 remains focused on safe Sonar reductions. |
## Issues Encountered
| Issue | Resolution |
|-------|------------|
| `vendor/bin/phpunit` unavailable | Documented as environment gap; used PHP lint, ad-hoc smoke and final Sonar. |
| `sonar-scanner` not in PATH | Reused official scanner from `%TEMP%`. |
## Next Phase Readiness
**Ready:**
- Phase 140 can plan performance safeguards from current roadmap.
- Phase 141 has a clearer boundary for `php:S1448` class splits.
- Remaining Phase 139 debt is documented for optional future slices.
**Concerns:**
- PHPUnit still requires `composer install`/`vendor/`.
- `php:S1142`, `php:S3776`, `php:S1172`, and accessibility groups remain substantial.
**Blockers:** None for moving to Phase 140 planning.
---
*Phase: 139-sonar-critical-major-cleanup, Plan: 139-02*
*Completed: 2026-05-17*

View File

@@ -78,6 +78,49 @@ Top rules after cleanup:
- `src/Modules/Statistics/OrdersStatisticsRepository.php`: `php:S1448`, 43 methods. This requires a class split and belongs with the Phase 141 god-class work or a dedicated Phase 139 follow-up slice. - `src/Modules/Statistics/OrdersStatisticsRepository.php`: `php:S1448`, 43 methods. This requires a class split and belongs with the Phase 141 god-class work or a dedicated Phase 139 follow-up slice.
## Final Scan After Plan 139-02
Analysis id: `2c18a5b3-40b4-41d8-b826-df88615749db`
CE task id: `de0e6b08-015c-4651-ae02-d28ada99b393`
OPEN `BLOCKER,CRITICAL,MAJOR` issues:
| Severity | After 139-01 | After 139-02 | Delta |
|----------|--------------|--------------|-------|
| BLOCKER | 0 | 0 | 0 |
| CRITICAL | 181 | 178 | -3 |
| MAJOR | 424 | 317 | -107 |
| Total | 605 | 495 | -110 |
Top rules after 139-02:
| Rule | Count |
|------|-------|
| `php:S1142` | 148 |
| `php:S1192` | 98 |
| `php:S3776` | 54 |
| `php:S1172` | 41 |
| `php:S121` | 24 |
| `php:S3358` | 23 |
| `php:S112` | 23 |
| `Web:TableHeaderHasIdOrScopeCheck` | 16 |
| `php:S1448` | 16 |
| `php:S2681` | 9 |
Selected 139-02 fixes:
| Group | Before | After | Notes |
|-------|--------|-------|-------|
| `php:S4833` | 93 | 3 | Broad alert include migration to `$component()` and view FQCN cleanup. Remaining issues are core framework `require` calls in `Application`, `Translator`, and `Template`. |
| `php:S112` | 40 | 23 | Typed exceptions added for Fakturownia, Polkurier, Erli mapping, automation duplication, and email template duplication. |
| `php:S1192` | 101 | 98 | Small controller constants reduced repeated route/flash literals. |
Recommended next slice:
- Continue Phase 139 with `php:S1142`, `php:S3776`, and `php:S1172` in compact service/controller methods.
- Treat Web accessibility issues as a separate UI verification slice.
- Keep `php:S1448` god-class splits for Phase 141 or a dedicated architecture plan.
## Tooling Notes ## Tooling Notes
- `vendor/bin/phpunit` is not available because `vendor/` is missing and Composer is not installed in PATH. - `vendor/bin/phpunit` is not available because `vendor/` is missing and Composer is not installed in PATH.

View File

@@ -89,6 +89,13 @@ HTTP Request
- Amount SQL generation is split into small helpers while preserving the Phase 135 contract: source-level net first, VAT-aware item fallback second, legacy gross `/1.23` last, and delivery net at 23% only when source net is missing. - Amount SQL generation is split into small helpers while preserving the Phase 135 contract: source-level net first, VAT-aware item fallback second, legacy gross `/1.23` last, and delivery net at 23% only when source net is missing.
- Remaining architecture debt: `OrdersStatisticsRepository` still exceeds the Sonar method-count threshold and should be split in a later god-class/refactor slice. - Remaining architecture debt: `OrdersStatisticsRepository` still exceeds the Sonar method-count threshold and should be split in a later god-class/refactor slice.
### Critical/major cleanup slice 139-02
- Settings/Automation compact failure paths now use typed exceptions: `FakturowniaApiException`, `PolkurierApiException`, `ErliOrderMappingException`, `AutomationRuleException`, and `EmailTemplateException`.
- Alert rendering in targeted layouts/views uses `Template::$component('components/alert', ...)` instead of direct component includes. Trusted prebuilt alert HTML is still passed as `messageHtml`.
- `SmsTemplateController` and `UsersController` centralize repeated routes, flash keys and validation helpers without changing request routes, flash UX or response status contracts.
- `resources/views/shipments/prepare.php` imports `StringHelper` and `DeliveryStatus` locally instead of using inline fully qualified class names.
- Phase 139-02 final Sonar scan reduced OPEN BLOCKER/CRITICAL/MAJOR issues from 605 to 495. Remaining high-impact work is mostly return/cognitive-complexity cleanup, accessibility, and Phase 141 god-class splits.
## Frontend Enhancement Modules ## Frontend Enhancement Modules
### Checkbox Multiselect (`public/assets/js/modules/checkbox-multiselect.js`) ### Checkbox Multiselect (`public/assets/js/modules/checkbox-multiselect.js`)

View File

@@ -1,5 +1,20 @@
# Technical Changelog # Technical Changelog
## 2026-05-17 - Phase 139 Plan 02: Sonar Critical/Major Cleanup
**Co zrobiono:**
- Finalny Sonar po planie 139-02: 495 OPEN BLOCKER/CRITICAL/MAJOR (`BLOCKER=0`, `CRITICAL=178`, `MAJOR=317`), spadek z 605 po 139-01.
- Dodano typowane wyjatki dla kompaktowych klastrow: Fakturownia API, Polkurier API, Erli order mapping, automation rule duplication i email template duplication.
- Zmigrowano szeroki zestaw alertow w layoutach i widokach na `$component('components/alert', ...)`, zachowujac `messageHtml` dla zaufanego gotowego HTML.
- Usunieto inline `\App\...` z targetowanych widokow/layoutow przez lokalne importy.
- `SmsTemplateController` i `UsersController` dostaly stale dla tras/flashy oraz male helpery walidacji/redirectow bez zmiany kontraktu formularzy.
**Dlaczego:**
- Plan 139-02 mial agresywnie zredukowac najwieksze bezpieczne grupy Sonar po 139-01, szczegolnie `php:S4833` i `php:S112`, bez ruszania schematu DB ani god-class splitow z Phase 141.
**BREAKING / migracja:**
- Brak migracji DB i brak zmian breaking. PHPUnit nadal nie jest dostepny w checkoutcie przez brak `vendor/`; wykonano lint, ad-hoc smoke mappera Erli i finalny SonarScanner.
## 2026-05-17 - Phase 139 Plan 01: Sonar Critical/Major Cleanup ## 2026-05-17 - Phase 139 Plan 01: Sonar Critical/Major Cleanup
**Co zrobiono:** **Co zrobiono:**

View File

@@ -2,13 +2,19 @@
## SonarQube - 2026-05-17 ## SonarQube - 2026-05-17
Fresh Phase 139 scan after plan 139-01: 605 OPEN `BLOCKER,CRITICAL,MAJOR` issues remain (`BLOCKER=0`, `CRITICAL=181`, `MAJOR=424`). Fresh Phase 139 scan after plan 139-02: 495 OPEN `BLOCKER,CRITICAL,MAJOR` issues remain (`BLOCKER=0`, `CRITICAL=178`, `MAJOR=317`).
Next recommended slices: Next recommended slices:
- Split `src/Modules/Statistics/OrdersStatisticsRepository.php` (`php:S1448`, 43 methods) into query/amount/schema helpers before making deeper statistics changes. - Continue with the largest confirmed rule groups: `php:S1142`, `php:S3776`, `php:S1172`, `php:S1192`, `php:S112`.
- Continue with the largest confirmed rule groups: `php:S1142`, `php:S1192`, `php:S4833`, `php:S3776`, `php:S1172`, `php:S112`.
- Handle Web accessibility groups separately so table headers, `<output>` semantics and icon-only labels can be checked in the UI. - Handle Web accessibility groups separately so table headers, `<output>` semantics and icon-only labels can be checked in the UI.
- Split `src/Modules/Statistics/OrdersStatisticsRepository.php` and other `php:S1448` god classes in Phase 141 or a dedicated architecture slice.
Resolved in Phase 139-02:
- Broad direct alert include/import-pattern cleanup: `php:S4833` reduced from 93 to 3.
- Selected generic exception cleanup: `php:S112` reduced from 40 to 23.
- Small controller literal cleanup in SMS templates and users.
Resolved in Phase 139-01: Resolved in Phase 139-01:

View File

@@ -30,7 +30,7 @@ $buyerEmailDefault = trim((string) ($buyerAddr['email'] ?? $orderRow['buyer_emai
</div> </div>
<?php if ($errorMsg !== ''): ?> <?php if ($errorMsg !== ''): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMsg; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMsg, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if ($hasExistingInvoices): ?> <?php if ($hasExistingInvoices): ?>
@@ -51,7 +51,7 @@ $buyerEmailDefault = trim((string) ($buyerAddr['email'] ?? $orderRow['buyer_emai
<?php <?php
$existingInvoicesHtml = ob_get_clean(); $existingInvoicesHtml = ob_get_clean();
?> ?>
<div class="mt-12"><?php $type='warning'; $messageHtml = $existingInvoicesHtml; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; unset($messageHtml); ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'warning', 'messageHtml' => $existingInvoicesHtml, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<form id="invoice-create-form" method="post" action="/orders/<?= $e((string) $orderIdVal) ?>/invoice/store" class="mt-16"> <form id="invoice-create-form" method="post" action="/orders/<?= $e((string) $orderIdVal) ?>/invoice/store" class="mt-16">

View File

@@ -6,7 +6,7 @@
</header> </header>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="login-alert"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="login-alert"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<form class="login-form" action="/login" method="post" novalidate> <form class="login-form" action="/login" method="post" novalidate>

View File

@@ -41,7 +41,7 @@ $orderStatusOptions = is_array($orderStatusOptions ?? null) ? $orderStatusOption
<h2 class="section-title"><?= $isEdit ? 'Edytuj zadanie automatyczne' : 'Nowe zadanie automatyczne' ?></h2> <h2 class="section-title"><?= $isEdit ? 'Edytuj zadanie automatyczne' : 'Nowe zadanie automatyczne' ?></h2>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<form action="<?= $isEdit ? '/settings/automation/update' : '/settings/automation/store' ?>" method="post" novalidate class="mt-12" id="js-automation-form"> <form action="<?= $isEdit ? '/settings/automation/update' : '/settings/automation/store' ?>" method="post" novalidate class="mt-12" id="js-automation-form">

View File

@@ -56,10 +56,10 @@ $buildHistoryUrl = static function (array $overrides = []) use ($historyFiltersD
<p class="muted mt-8">Reguly automatyzacji wykonywane po wystapieniu zdarzenia.</p> <p class="muted mt-8">Reguly automatyzacji wykonywane po wystapieniu zdarzenia.</p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<nav class="content-tabs-nav mt-12" aria-label="Zakladki automatyzacji"> <nav class="content-tabs-nav mt-12" aria-label="Zakladki automatyzacji">

View File

@@ -1,3 +1,8 @@
<?php
use App\Core\Application;
use App\Core\Support\Flash;
use App\Modules\Shipments\DeliveryStatusMappingRepository;
?>
<!doctype html> <!doctype html>
<html lang="pl"> <html lang="pl">
<head> <head>
@@ -136,12 +141,12 @@
<?php <?php
$dsmUnmappedCount = 0; $dsmUnmappedCount = 0;
try { try {
$appInstance = \App\Core\Application::instance(); $appInstance = Application::instance();
if ($appInstance !== null) { if ($appInstance !== null) {
$dsmRepo = new \App\Modules\Shipments\DeliveryStatusMappingRepository($appInstance->db()); $dsmRepo = new DeliveryStatusMappingRepository($appInstance->db());
$dsmUnmappedCount = $dsmRepo->countAllUnmappedForBadge(); $dsmUnmappedCount = $dsmRepo->countAllUnmappedForBadge();
} }
} catch (\Throwable) { } catch (Throwable) {
$dsmUnmappedCount = 0; $dsmUnmappedCount = 0;
} }
?> ?>
@@ -194,14 +199,14 @@
</header> </header>
<main class="container"> <main class="container">
<?php $flashEntries = \App\Core\Support\Flash::all(); ?> <?php $flashEntries = Flash::all(); ?>
<?php if (!empty($flashEntries)): ?> <?php if (!empty($flashEntries)): ?>
<div class="alerts-stack" data-flash-stack> <div class="alerts-stack" data-flash-stack>
<?php foreach ($flashEntries as $flashEntry): ?> <?php foreach ($flashEntries as $flashEntry): ?>
<?php $type = (string) ($flashEntry['type'] ?? 'info'); ?> <?php $type = (string) ($flashEntry['type'] ?? 'info'); ?>
<?php $message = (string) ($flashEntry['message'] ?? ''); ?> <?php $message = (string) ($flashEntry['message'] ?? ''); ?>
<?php $dismissible = true; ?> <?php $dismissible = true; ?>
<?php include __DIR__ . '/../components/alert.php'; ?> <?php $component('components/alert', ['type' => $type, 'message' => $message, 'dismissible' => $dismissible]); ?>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<?php endif; ?> <?php endif; ?>

View File

@@ -1,3 +1,6 @@
<?php
use App\Core\Support\Flash;
?>
<!doctype html> <!doctype html>
<html lang="pl"> <html lang="pl">
<head> <head>
@@ -14,14 +17,14 @@
<div class="bg-orb bg-orb-right" aria-hidden="true"></div> <div class="bg-orb bg-orb-right" aria-hidden="true"></div>
<main class="login-page"> <main class="login-page">
<?php $flashEntries = \App\Core\Support\Flash::all(); ?> <?php $flashEntries = Flash::all(); ?>
<?php if (!empty($flashEntries)): ?> <?php if (!empty($flashEntries)): ?>
<div class="alerts-stack" data-flash-stack> <div class="alerts-stack" data-flash-stack>
<?php foreach ($flashEntries as $flashEntry): ?> <?php foreach ($flashEntries as $flashEntry): ?>
<?php $type = (string) ($flashEntry['type'] ?? 'info'); ?> <?php $type = (string) ($flashEntry['type'] ?? 'info'); ?>
<?php $message = (string) ($flashEntry['message'] ?? ''); ?> <?php $message = (string) ($flashEntry['message'] ?? ''); ?>
<?php $dismissible = true; ?> <?php $dismissible = true; ?>
<?php include __DIR__ . '/../components/alert.php'; ?> <?php $component('components/alert', ['type' => $type, 'message' => $message, 'dismissible' => $dismissible]); ?>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<?php endif; ?> <?php endif; ?>

View File

@@ -1,3 +1,6 @@
<?php
use App\Core\Support\Flash;
?>
<!doctype html> <!doctype html>
<html lang="pl"> <html lang="pl">
<head> <head>
@@ -28,14 +31,14 @@
</head> </head>
<body> <body>
<main class="public-page"> <main class="public-page">
<?php $flashEntries = \App\Core\Support\Flash::all(); ?> <?php $flashEntries = Flash::all(); ?>
<?php if (!empty($flashEntries)): ?> <?php if (!empty($flashEntries)): ?>
<div class="alerts-stack" data-flash-stack> <div class="alerts-stack" data-flash-stack>
<?php foreach ($flashEntries as $flashEntry): ?> <?php foreach ($flashEntries as $flashEntry): ?>
<?php $type = (string) ($flashEntry['type'] ?? 'info'); ?> <?php $type = (string) ($flashEntry['type'] ?? 'info'); ?>
<?php $message = (string) ($flashEntry['message'] ?? ''); ?> <?php $message = (string) ($flashEntry['message'] ?? ''); ?>
<?php $dismissible = true; ?> <?php $dismissible = true; ?>
<?php include __DIR__ . '/../components/alert.php'; ?> <?php $component('components/alert', ['type' => $type, 'message' => $message, 'dismissible' => $dismissible]); ?>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<?php endif; ?> <?php endif; ?>

View File

@@ -16,7 +16,7 @@
</div> </div>
</div> </div>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='warning'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'warning', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -40,7 +40,7 @@ $hasExistingReceipts = $existingReceiptsList !== [];
<?php <?php
$existingReceiptsHtml = ob_get_clean(); $existingReceiptsHtml = ob_get_clean();
?> ?>
<div class="mt-12"><?php $type='warning'; $messageHtml = $existingReceiptsHtml; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; unset($messageHtml); ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'warning', 'messageHtml' => $existingReceiptsHtml, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<form id="receipt-create-form" method="post" action="/orders/<?= $e((string) $orderIdVal) ?>/receipt/store" class="mt-16"> <form id="receipt-create-form" method="post" action="/orders/<?= $e((string) $orderIdVal) ?>/receipt/store" class="mt-16">

View File

@@ -147,10 +147,10 @@ foreach ($addressesList as $address) {
</div> </div>
<?php endif; ?> <?php endif; ?>
<?php if ($flashSuccessMsg !== ''): ?> <?php if ($flashSuccessMsg !== ''): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $flashSuccessMsg; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $flashSuccessMsg, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if ($flashErrorMsg !== ''): ?> <?php if ($flashErrorMsg !== ''): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $flashErrorMsg; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $flashErrorMsg, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<div class="order-status-change mt-12"> <div class="order-status-change mt-12">

View File

@@ -27,10 +27,10 @@ $error = trim((string) ($errorMessage ?? ''));
<h2 class="section-title"><?= $isEdit ? 'Edycja konfiguracji faktury' : 'Nowa konfiguracja faktury' ?></h2> <h2 class="section-title"><?= $isEdit ? 'Edycja konfiguracji faktury' : 'Nowa konfiguracja faktury' ?></h2>
<?php if ($error !== ''): ?> <?php if ($error !== ''): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $error; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $error, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if ($success !== ''): ?> <?php if ($success !== ''): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $success; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $success, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -11,10 +11,10 @@ $error = trim((string) ($errorMessage ?? ''));
<p class="muted mt-12">Zarzadzaj konfiguracjami wystawiania faktur. Mozesz dodac wiele konfiguracji (np. dla roznych dzialalnosci) i opcjonalnie delegowac wystawianie do Fakturowni.</p> <p class="muted mt-12">Zarzadzaj konfiguracjami wystawiania faktur. Mozesz dodac wiele konfiguracji (np. dla roznych dzialalnosci) i opcjonalnie delegowac wystawianie do Fakturowni.</p>
<?php if ($error !== ''): ?> <?php if ($error !== ''): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $error; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $error, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if ($success !== ''): ?> <?php if ($success !== ''): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $success; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $success, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -21,10 +21,10 @@ $error = trim((string) ($errorMessage ?? ''));
<h2 class="section-title"><?= $isEdit ? 'Edycja konfiguracji paragonu' : 'Nowa konfiguracja paragonu' ?></h2> <h2 class="section-title"><?= $isEdit ? 'Edycja konfiguracji paragonu' : 'Nowa konfiguracja paragonu' ?></h2>
<?php if ($error !== ''): ?> <?php if ($error !== ''): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $error; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $error, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if ($success !== ''): ?> <?php if ($success !== ''): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $success; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $success, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -11,10 +11,10 @@ $error = trim((string) ($errorMessage ?? ''));
<p class="muted mt-12">Zarzadzaj konfiguracjami wystawiania paragonow.</p> <p class="muted mt-12">Zarzadzaj konfiguracjami wystawiania paragonow.</p>
<?php if ($error !== ''): ?> <?php if ($error !== ''): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $error; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $error, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if ($success !== ''): ?> <?php if ($success !== ''): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $success; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $success, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -8,10 +8,10 @@ $error = trim((string) ($errorMessage ?? ''));
<p class="muted mt-12">Wybierz typ dokumentu ktorego konfiguracje chcesz zarzadzac.</p> <p class="muted mt-12">Wybierz typ dokumentu ktorego konfiguracje chcesz zarzadzac.</p>
<?php if ($error !== ''): ?> <?php if ($error !== ''): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $error; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $error, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if ($success !== ''): ?> <?php if ($success !== ''): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $success; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $success, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -40,15 +40,15 @@ foreach ($pullStatusMappings as $pm) {
<p class="muted mt-12"><?= $e($t('settings.allegro.description')) ?></p> <p class="muted mt-12"><?= $e($t('settings.allegro.description')) ?></p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($warningMessage)): ?> <?php if (!empty($warningMessage)): ?>
<div class="mt-12"><?php $type='warning'; $message=(string) $warningMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'warning', 'message' => (string) $warningMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>
@@ -359,7 +359,7 @@ foreach ($pullStatusMappings as $pm) {
<p class="muted mt-12"><?= $e($t('settings.allegro.delivery.description')) ?></p> <p class="muted mt-12"><?= $e($t('settings.allegro.delivery.description')) ?></p>
<?php if ($dmServicesError !== ''): ?> <?php if ($dmServicesError !== ''): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $dmServicesError; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $dmServicesError, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if ($dmOrderMethods === []): ?> <?php if ($dmOrderMethods === []): ?>

View File

@@ -11,11 +11,11 @@ $updatedAt = trim((string) ($integration['updated_at'] ?? ''));
<p class="muted mt-12"><?= $e($t('settings.apaczka.description')) ?></p> <p class="muted mt-12"><?= $e($t('settings.apaczka.description')) ?></p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -7,10 +7,10 @@ $s = is_array($settings ?? null) ? $settings : [];
<p class="muted mt-12"><?= $e($t('settings.company.description')) ?></p> <p class="muted mt-12"><?= $e($t('settings.company.description')) ?></p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -12,11 +12,11 @@ $pastTotal = max(0, (int) ($pastPagination['total'] ?? 0));
<h2 class="section-title"><?= $e($t('settings.cron.title')) ?></h2> <h2 class="section-title"><?= $e($t('settings.cron.title')) ?></h2>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<nav class="content-tabs-nav mt-12" aria-label="Zakładki crona"> <nav class="content-tabs-nav mt-12" aria-label="Zakładki crona">

View File

@@ -11,11 +11,11 @@ $logs = (array) ($runLogs ?? []);
<h2 class="section-title"><?= $e($t('settings.database.title')) ?></h2> <h2 class="section-title"><?= $e($t('settings.database.title')) ?></h2>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<div class="settings-grid mt-16"> <div class="settings-grid mt-16">
@@ -34,14 +34,14 @@ $logs = (array) ($runLogs ?? []);
</div> </div>
<?php if ($pending > 0): ?> <?php if ($pending > 0): ?>
<div class="mt-16"><?php $type='warning'; $message=(string) $t('settings.database.state.needs_update'); $dismissible=false; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-16"><?php $component('components/alert', ['type' => 'warning', 'message' => (string) $t('settings.database.state.needs_update'), 'dismissible' => false]); ?></div>
<form class="mt-16" action="/settings/database/migrate" method="post"> <form class="mt-16" action="/settings/database/migrate" method="post">
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>"> <input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
<button type="submit" class="btn btn--primary"><?= $e($t('settings.database.actions.run_update')) ?></button> <button type="submit" class="btn btn--primary"><?= $e($t('settings.database.actions.run_update')) ?></button>
</form> </form>
<?php else: ?> <?php else: ?>
<div class="mt-16"><?php $type='success'; $message=(string) $t('settings.database.state.up_to_date'); $dismissible=false; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-16"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $t('settings.database.state.up_to_date'), 'dismissible' => false]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -20,7 +20,7 @@ $action = $isEdit
<h2 class="section-title"><?= $isEdit ? 'Edytuj status przesyłki' : 'Nowy status przesyłki' ?></h2> <h2 class="section-title"><?= $isEdit ? 'Edytuj status przesyłki' : 'Nowy status przesyłki' ?></h2>
<?php if ($errorMessage !== ''): ?> <?php if ($errorMessage !== ''): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<form action="<?= $e($action) ?>" method="post" novalidate class="mt-12"> <form action="<?= $e($action) ?>" method="post" novalidate class="mt-12">

View File

@@ -17,10 +17,10 @@ $isEdit = $em !== null;
<p class="muted mt-12">Konfiguracja skrzynek SMTP do wysylki wiadomosci e-mail.</p> <p class="muted mt-12">Konfiguracja skrzynek SMTP do wysylki wiadomosci e-mail.</p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -11,10 +11,10 @@ $attachmentTypes = is_array($attachmentTypes ?? null) ? $attachmentTypes : [];
<p class="muted mt-12">Skonfiguruj temat, tresc i zmienne, ktore beda podstawiane podczas wysylki.</p> <p class="muted mt-12">Skonfiguruj temat, tresc i zmienne, ktore beda podstawiane podczas wysylki.</p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -11,10 +11,10 @@ $attachmentTypes = is_array($attachmentTypes ?? null) ? $attachmentTypes : [];
<p class="muted mt-12">Szablony wiadomosci e-mail z edytorem i systemem zmiennych.</p> <p class="muted mt-12">Szablony wiadomosci e-mail z edytorem i systemem zmiennych.</p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -37,19 +37,19 @@ $activeTab = (string) ($activeTab ?? 'integration');
<p class="muted mt-12"><?= $e($t('settings.erli.description')) ?></p> <p class="muted mt-12"><?= $e($t('settings.erli.description')) ?></p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($testMessage)): ?> <?php if (!empty($testMessage)): ?>
<div class="mt-12"><?php $type='info'; $message=(string) $testMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'info', 'message' => (string) $testMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($importMessage)): ?> <?php if (!empty($importMessage)): ?>
<div class="mt-12"><?php $type='info'; $message=(string) $importMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'info', 'message' => (string) $importMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>
@@ -284,7 +284,7 @@ $activeTab = (string) ($activeTab ?? 'integration');
<p class="muted mt-12"><?= $e($t('settings.erli.delivery.description')) ?></p> <p class="muted mt-12"><?= $e($t('settings.erli.delivery.description')) ?></p>
<?php if ($erliDeliveryMetadataError !== ''): ?> <?php if ($erliDeliveryMetadataError !== ''): ?>
<div class="mt-12"><?php $type='warning'; $message=(string) $erliDeliveryMetadataError; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'warning', 'message' => (string) $erliDeliveryMetadataError, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<form action="/settings/integrations/erli/delivery/save" method="post" class="mt-12"> <form action="/settings/integrations/erli/delivery/save" method="post" class="mt-12">
@@ -455,8 +455,7 @@ $activeTab = (string) ($activeTab ?? 'integration');
. ($lastTestStatus !== '' ? ' <span class="badge badge--' . ($lastTestStatus === 'ok' ? 'success' : 'muted') . '">' . $e(strtoupper($lastTestStatus)) . '</span>' : '') . ($lastTestStatus !== '' ? ' <span class="badge badge--' . ($lastTestStatus === 'ok' ? 'success' : 'muted') . '">' . $e(strtoupper($lastTestStatus)) . '</span>' : '')
. ($lastTestHttpCode !== null ? ' <span class="badge badge--muted">HTTP ' . $e((string) $lastTestHttpCode) . '</span>' : '') . ($lastTestHttpCode !== null ? ' <span class="badge badge--muted">HTTP ' . $e((string) $lastTestHttpCode) . '</span>' : '')
. ($lastTestMessage !== '' ? '<div class="mt-12">' . $e($lastTestMessage) . '</div>' : ''); . ($lastTestMessage !== '' ? '<div class="mt-12">' . $e($lastTestMessage) . '</div>' : '');
$dismissible = false; $component('components/alert', ['type' => $type, 'messageHtml' => $messageHtml, 'dismissible' => false]);
include dirname(__DIR__) . '/components/alert.php';
unset($messageHtml); unset($messageHtml);
?></div> ?></div>
<?php endif; ?> <?php endif; ?>

View File

@@ -22,13 +22,13 @@ $flashError = trim((string) ($flashError ?? ''));
<p class="muted mt-12">Jedna globalna konfiguracja konta Fakturowni do wystawiania faktur delegowanych.</p> <p class="muted mt-12">Jedna globalna konfiguracja konta Fakturowni do wystawiania faktur delegowanych.</p>
<?php if ($flashError !== ''): ?> <?php if ($flashError !== ''): ?>
<div class="mt-12"><?php $type='danger'; $message=$flashError; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => $flashError, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if ($flashSave !== ''): ?> <?php if ($flashSave !== ''): ?>
<div class="mt-12"><?php $type='success'; $message=$flashSave; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => $flashSave, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if ($flashTest !== ''): ?> <?php if ($flashTest !== ''): ?>
<div class="mt-12"><?php $type='info'; $message=$flashTest; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'info', 'message' => $flashTest, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -20,15 +20,15 @@ if (str_starts_with($lastTestMessage, 'MessageId:')) {
<p class="muted mt-12"><?= $e($t('settings.hostedsms.description')) ?></p> <p class="muted mt-12"><?= $e($t('settings.hostedsms.description')) ?></p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($testMessage)): ?> <?php if (!empty($testMessage)): ?>
<div class="mt-12"><?php $type='info'; $message=(string) $testMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'info', 'message' => (string) $testMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -12,11 +12,11 @@ $labelFormat = (string) ($s['label_format'] ?? 'Pdf');
<p class="muted mt-12"><?= $e($t('settings.inpost.description')) ?></p> <p class="muted mt-12"><?= $e($t('settings.inpost.description')) ?></p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -7,11 +7,11 @@ $items = is_array($rows ?? null) ? $rows : [];
<p class="muted mt-12"><?= $e($t('settings.integrations_hub.description')) ?></p> <p class="muted mt-12"><?= $e($t('settings.integrations_hub.description')) ?></p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -16,15 +16,15 @@ $labelFormats = ['PDF', 'ZPL', 'EPL'];
<p class="muted mt-12"><?= $e($t('settings.polkurier.description')) ?></p> <p class="muted mt-12"><?= $e($t('settings.polkurier.description')) ?></p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($testMessage)): ?> <?php if (!empty($testMessage)): ?>
<div class="mt-12"><?php $type='info'; $message=(string) $testMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'info', 'message' => (string) $testMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>
@@ -101,8 +101,7 @@ $labelFormats = ['PDF', 'ZPL', 'EPL'];
$parts[] = $e($lastTestMessage); $parts[] = $e($lastTestMessage);
} }
$messageHtml = implode(' &middot; ', $parts); $messageHtml = implode(' &middot; ', $parts);
$dismissible = false; $component('components/alert', ['type' => $type, 'messageHtml' => $messageHtml, 'dismissible' => false]);
include dirname(__DIR__) . '/components/alert.php';
unset($messageHtml); unset($messageHtml);
?> ?>
</div> </div>

View File

@@ -10,10 +10,10 @@ $currentStatusFilter = (string) ($printStatusFilter ?? '');
<p class="muted mt-12">Klucze API do uwierzytelniania aplikacji drukujacej (Windows Client).</p> <p class="muted mt-12">Klucze API do uwierzytelniania aplikacji drukujacej (Windows Client).</p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if ($newKey !== ''): ?> <?php if ($newKey !== ''): ?>
@@ -27,7 +27,7 @@ $currentStatusFilter = (string) ($printStatusFilter ?? '');
<?php <?php
$newKeyAlertHtml = ob_get_clean(); $newKeyAlertHtml = ob_get_clean();
?> ?>
<div class="mt-12"><?php $type='warning'; $messageHtml=$newKeyAlertHtml; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; unset($messageHtml); ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'warning', 'messageHtml' => $newKeyAlertHtml, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -8,10 +8,10 @@ $scripts = is_array($scripts ?? null) ? $scripts : [];
<p class="muted mt-4"><?= $e($t('settings.project_mapping.description')) ?></p> <p class="muted mt-4"><?= $e($t('settings.project_mapping.description')) ?></p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -32,11 +32,11 @@ foreach ($dmMappings as $dm) {
<p class="muted mt-12"><?= $e($t('settings.integrations.description')) ?></p> <p class="muted mt-12"><?= $e($t('settings.integrations.description')) ?></p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>
@@ -452,7 +452,7 @@ foreach ($dmMappings as $dm) {
<p class="muted mt-12"><?= $e($t('settings.integrations.delivery.select_integration_first')) ?></p> <p class="muted mt-12"><?= $e($t('settings.integrations.delivery.select_integration_first')) ?></p>
<?php else: ?> <?php else: ?>
<?php if ($dmServicesError !== ''): ?> <?php if ($dmServicesError !== ''): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $dmServicesError; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $dmServicesError, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if ($dmOrderMethods === []): ?> <?php if ($dmOrderMethods === []): ?>

View File

@@ -9,10 +9,10 @@ $variableGroups = is_array($variableGroups ?? null) ? $variableGroups : [];
<p class="muted mt-12">Wpisz tresc wiadomosci ze zmiennymi typu <code>{{zamowienie.numer}}</code>. Stopka SMSPLANET jest doklejana automatycznie przy wysylce, nie dopisuj jej w szablonie.</p> <p class="muted mt-12">Wpisz tresc wiadomosci ze zmiennymi typu <code>{{zamowienie.numer}}</code>. Stopka SMSPLANET jest doklejana automatycznie przy wysylce, nie dopisuj jej w szablonie.</p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -10,10 +10,10 @@ $templates = is_array($templates ?? null) ? $templates : [];
<p class="muted mt-12">Szybkie szablony wiadomosci SMS do wstawiania z zakladki SMS w szczegolach zamowienia. Stopka SMSPLANET jest doklejana automatycznie.</p> <p class="muted mt-12">Szybkie szablony wiadomosci SMS do wstawiania z zakladki SMS w szczegolach zamowienia. Stopka SMSPLANET jest doklejana automatycznie.</p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -26,15 +26,15 @@ if (str_starts_with($lastTestMessage, 'messageId:')) {
<p class="muted mt-12"><?= $e($t('settings.smsplanet.description')) ?></p> <p class="muted mt-12"><?= $e($t('settings.smsplanet.description')) ?></p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($testMessage)): ?> <?php if (!empty($testMessage)): ?>
<div class="mt-12"><?php $type='info'; $message=(string) $testMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'info', 'message' => (string) $testMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>

View File

@@ -26,11 +26,11 @@ foreach ($statusesList as $statusRow) {
<p class="muted mt-12"><?= $e($t('settings.statuses.description')) ?></p> <p class="muted mt-12"><?= $e($t('settings.statuses.description')) ?></p>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>
@@ -76,7 +76,7 @@ foreach ($statusesList as $statusRow) {
<h2 class="section-title mt-16"><?= $e($t('settings.statuses.statuses.list_title')) ?></h2> <h2 class="section-title mt-16"><?= $e($t('settings.statuses.statuses.list_title')) ?></h2>
<?php if ($groupsList === []): ?> <?php if ($groupsList === []): ?>
<div class="mt-12"><?php $type='warning'; $message=(string) $t('settings.statuses.groups.empty'); $dismissible=false; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'warning', 'message' => (string) $t('settings.statuses.groups.empty'), 'dismissible' => false]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php foreach ($groupsList as $group): ?> <?php foreach ($groupsList as $group): ?>

View File

@@ -1,4 +1,7 @@
<?php <?php
use App\Core\Support\StringHelper;
use App\Modules\Shipments\DeliveryStatus;
$orderRow = is_array($order ?? null) ? $order : []; $orderRow = is_array($order ?? null) ? $order : [];
$itemsList = is_array($items ?? null) ? $items : []; $itemsList = is_array($items ?? null) ? $items : [];
$receiver = is_array($receiverAddr ?? null) ? $receiverAddr : []; $receiver = is_array($receiverAddr ?? null) ? $receiverAddr : [];
@@ -43,7 +46,7 @@ $pointId = trim((string) ($receiver['parcel_external_id'] ?? ''));
$pointName = trim((string) ($receiver['parcel_name'] ?? '')); $pointName = trim((string) ($receiver['parcel_name'] ?? ''));
$totalWithTax = (float) ($orderRow['total_with_tax'] ?? 0); $totalWithTax = (float) ($orderRow['total_with_tax'] ?? 0);
$currency = strtoupper(trim((string) ($orderRow['currency'] ?? 'PLN'))); $currency = strtoupper(trim((string) ($orderRow['currency'] ?? 'PLN')));
$isCod = \App\Core\Support\StringHelper::isCodPayment((string) ($orderRow['external_payment_type_id'] ?? '')); $isCod = StringHelper::isCodPayment((string) ($orderRow['external_payment_type_id'] ?? ''));
$defaultCodAmount = $isCod ? number_format($totalWithTax, 2, '.', '') : '0'; $defaultCodAmount = $isCod ? number_format($totalWithTax, 2, '.', '') : '0';
?> ?>
@@ -59,10 +62,10 @@ $defaultCodAmount = $isCod ? number_format($totalWithTax, 2, '.', '') : '0';
</div> </div>
<?php if ($flashSuccessMsg !== ''): ?> <?php if ($flashSuccessMsg !== ''): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $flashSuccessMsg; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $flashSuccessMsg, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if ($flashErrorMsg !== ''): ?> <?php if ($flashErrorMsg !== ''): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $flashErrorMsg; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $flashErrorMsg, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</section> </section>
@@ -96,7 +99,7 @@ $defaultCodAmount = $isCod ? number_format($totalWithTax, 2, '.', '') : '0';
<h3 class="section-title">Przesylka</h3> <h3 class="section-title">Przesylka</h3>
<?php if ($servicesError !== ''): ?> <?php if ($servicesError !== ''): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $servicesError; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $servicesError, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<div class="form-field mt-12"> <div class="form-field mt-12">
@@ -112,7 +115,7 @@ $defaultCodAmount = $isCod ? number_format($totalWithTax, 2, '.', '') : '0';
<div class="muted mt-4" style="font-size:12px">Metoda z zamowienia: <strong><?= $e($deliveryMethodName) ?></strong><?php if ($mappedServiceName !== ''): ?> &rarr; <?= $e($mappedCarrier === 'inpost' ? 'InPost' : ($mappedCarrier === 'apaczka' ? 'Apaczka' : 'Allegro')) ?>: <?= $e($mappedServiceName) ?><?php endif; ?></div> <div class="muted mt-4" style="font-size:12px">Metoda z zamowienia: <strong><?= $e($deliveryMethodName) ?></strong><?php if ($mappedServiceName !== ''): ?> &rarr; <?= $e($mappedCarrier === 'inpost' ? 'InPost' : ($mappedCarrier === 'apaczka' ? 'Apaczka' : 'Allegro')) ?>: <?= $e($mappedServiceName) ?><?php endif; ?></div>
<?php endif; ?> <?php endif; ?>
<?php if ($deliveryMappingDiagnostic !== ''): ?> <?php if ($deliveryMappingDiagnostic !== ''): ?>
<div class="mt-8"><?php $type='danger'; $message=(string) $deliveryMappingDiagnostic; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-8"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $deliveryMappingDiagnostic, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
</div> </div>
@@ -122,7 +125,7 @@ $defaultCodAmount = $isCod ? number_format($totalWithTax, 2, '.', '') : '0';
<div id="shipment-allegro-panel" style="<?= $preselectedCarrier !== 'allegro' ? 'display:none' : '' ?>"> <div id="shipment-allegro-panel" style="<?= $preselectedCarrier !== 'allegro' ? 'display:none' : '' ?>">
<?php if ($servicesError !== ''): ?> <?php if ($servicesError !== ''): ?>
<div class="mt-4"><?php $type='danger'; $message=(string) $servicesError; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-4"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $servicesError, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<div class="searchable-select" id="shipment-service-wrapper" data-match-id="<?= $e($deliveryMethodId) ?>"> <div class="searchable-select" id="shipment-service-wrapper" data-match-id="<?= $e($deliveryMethodId) ?>">
<input type="text" class="form-control" id="shipment-service-search" placeholder="Szukaj uslugi dostawy Allegro..." autocomplete="off"> <input type="text" class="form-control" id="shipment-service-search" placeholder="Szukaj uslugi dostawy Allegro..." autocomplete="off">
@@ -441,19 +444,19 @@ $defaultCodAmount = $isCod ? number_format($totalWithTax, 2, '.', '') : '0';
$pkgProvider = trim((string) ($pkg['provider'] ?? '')); $pkgProvider = trim((string) ($pkg['provider'] ?? ''));
$pkgDeliveryStatus = (string) ($pkg['delivery_status'] ?? 'unknown'); $pkgDeliveryStatus = (string) ($pkg['delivery_status'] ?? 'unknown');
$pkgDeliveryRaw = trim((string) ($pkg['delivery_status_raw'] ?? '')); $pkgDeliveryRaw = trim((string) ($pkg['delivery_status_raw'] ?? ''));
$pkgDeliveryLabel = \App\Modules\Shipments\DeliveryStatus::label($pkgDeliveryStatus); $pkgDeliveryLabel = DeliveryStatus::label($pkgDeliveryStatus);
$pkgDeliveryDesc = $pkgDeliveryRaw !== '' ? \App\Modules\Shipments\DeliveryStatus::description($pkgProvider, $pkgDeliveryRaw) : ''; $pkgDeliveryDesc = $pkgDeliveryRaw !== '' ? DeliveryStatus::description($pkgProvider, $pkgDeliveryRaw) : '';
$pkgDeliveryTitle = $pkgDeliveryRaw !== '' ? ($pkgDeliveryRaw . ' — ' . $pkgDeliveryDesc) : ''; $pkgDeliveryTitle = $pkgDeliveryRaw !== '' ? ($pkgDeliveryRaw . ' — ' . $pkgDeliveryDesc) : '';
?> ?>
<?php <?php
$pkgIsSystem = in_array($pkgDeliveryStatus, \App\Modules\Shipments\DeliveryStatus::ALL_STATUSES, true); $pkgIsSystem = in_array($pkgDeliveryStatus, DeliveryStatus::ALL_STATUSES, true);
$pkgColor = $pkgIsSystem ? '' : \App\Modules\Shipments\DeliveryStatus::getColor($pkgDeliveryStatus); $pkgColor = $pkgIsSystem ? '' : DeliveryStatus::getColor($pkgDeliveryStatus);
?> ?>
<span class="delivery-badge<?= $pkgIsSystem ? ' delivery-badge--' . $e($pkgDeliveryStatus) : ' delivery-badge--custom' ?>" title="<?= $e($pkgDeliveryTitle) ?>"<?= $pkgColor !== '' ? ' style="--status-color: ' . $e($pkgColor) . '"' : '' ?>><?= $e($pkgDeliveryLabel) ?></span> <span class="delivery-badge<?= $pkgIsSystem ? ' delivery-badge--' . $e($pkgDeliveryStatus) : ' delivery-badge--custom' ?>" title="<?= $e($pkgDeliveryTitle) ?>"<?= $pkgColor !== '' ? ' style="--status-color: ' . $e($pkgColor) . '"' : '' ?>><?= $e($pkgDeliveryLabel) ?></span>
</td> </td>
<td style="white-space:nowrap"> <td style="white-space:nowrap">
<?= $e($pkgTracking !== '' ? $pkgTracking : '-') ?><?php <?= $e($pkgTracking !== '' ? $pkgTracking : '-') ?><?php
$pkgTrackUrl = \App\Modules\Shipments\DeliveryStatus::trackingUrl($pkgProvider, $pkgTracking, trim((string) ($pkg['carrier_id'] ?? ''))); $pkgTrackUrl = DeliveryStatus::trackingUrl($pkgProvider, $pkgTracking, trim((string) ($pkg['carrier_id'] ?? '')));
if ($pkgTrackUrl !== null): ?> <a href="<?= $e($pkgTrackUrl) ?>" target="_blank" class="tracking-link" title="Sledz przesylke">&#128279;</a><?php endif; ?> if ($pkgTrackUrl !== null): ?> <a href="<?= $e($pkgTrackUrl) ?>" target="_blank" class="tracking-link" title="Sledz przesylke">&#128279;</a><?php endif; ?>
</td> </td>
<td><?= $e($pkgCarrier !== '' ? $pkgCarrier : '-') ?></td> <td><?= $e($pkgCarrier !== '' ? $pkgCarrier : '-') ?></td>

View File

@@ -120,7 +120,7 @@ foreach ($channelOptions as $channelOption) {
<?php <?php
$statisticsDebugHtml = ob_get_clean(); $statisticsDebugHtml = ob_get_clean();
?> ?>
<?php $type='warning'; $messageHtml=$statisticsDebugHtml; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; unset($messageHtml); ?> <?php $component('components/alert', ['type' => 'warning', 'messageHtml' => $statisticsDebugHtml, 'dismissible' => true]); ?>
<?php endif; ?> <?php endif; ?>
<?php if (!$hasData): ?> <?php if (!$hasData): ?>

View File

@@ -7,11 +7,11 @@
<h2 class="section-title"><?= $e($t('users.create_title')) ?></h2> <h2 class="section-title"><?= $e($t('users.create_title')) ?></h2>
<?php if (!empty($errorMessage)): ?> <?php if (!empty($errorMessage)): ?>
<div class="mt-12"><?php $type='danger'; $message=(string) $errorMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'danger', 'message' => (string) $errorMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($successMessage)): ?> <?php if (!empty($successMessage)): ?>
<div class="mt-12"><?php $type='success'; $message=(string) $successMessage; $dismissible=true; include dirname(__DIR__) . '/components/alert.php'; ?></div> <div class="mt-12"><?php $component('components/alert', ['type' => 'success', 'message' => (string) $successMessage, 'dismissible' => true]); ?></div>
<?php endif; ?> <?php endif; ?>
<form class="users-form mt-16" action="/settings/users" method="post" novalidate> <form class="users-form mt-16" action="/settings/users" method="post" novalidate>

View File

@@ -135,7 +135,7 @@ final class AutomationRepository
{ {
$source = $this->findById($id); $source = $this->findById($id);
if ($source === null) { if ($source === null) {
throw new \RuntimeException('Zadanie nie istnieje'); throw new AutomationRuleException('Zadanie nie istnieje');
} }
$conditions = array_map(static function (array $c): array { $conditions = array_map(static function (array $c): array {

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace App\Modules\Automation;
use RuntimeException;
final class AutomationRuleException extends RuntimeException
{
}

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace App\Modules\Settings;
use RuntimeException;
final class EmailTemplateException extends RuntimeException
{
}

View File

@@ -123,7 +123,7 @@ final class EmailTemplateRepository
{ {
$source = $this->findById($id); $source = $this->findById($id);
if ($source === null) { if ($source === null) {
throw new \RuntimeException('Szablon nie istnieje'); throw new EmailTemplateException('Szablon nie istnieje');
} }
$statement = $this->pdo->prepare( $statement = $this->pdo->prepare(

View File

@@ -5,7 +5,6 @@ namespace App\Modules\Settings;
use App\Core\Constants\IntegrationSources; use App\Core\Constants\IntegrationSources;
use App\Core\Support\StringHelper; use App\Core\Support\StringHelper;
use RuntimeException;
final class ErliOrderMapper final class ErliOrderMapper
{ {
@@ -43,12 +42,12 @@ final class ErliOrderMapper
$payload = is_array($message['payload'] ?? null) ? $message['payload'] : []; $payload = is_array($message['payload'] ?? null) ? $message['payload'] : [];
if ($payload === []) { if ($payload === []) {
throw new RuntimeException('Wiadomosc Erli nie zawiera payloadu zamowienia.'); throw new ErliOrderMappingException('Wiadomosc Erli nie zawiera payloadu zamowienia.');
} }
$sourceOrderId = $this->normalizeOrderId($this->readPath($payload, ['id', 'orderId', 'externalOrderId'])); $sourceOrderId = $this->normalizeOrderId($this->readPath($payload, ['id', 'orderId', 'externalOrderId']));
if ($sourceOrderId === '') { if ($sourceOrderId === '') {
throw new RuntimeException('Payload Erli nie zawiera ID zamowienia.'); throw new ErliOrderMappingException('Payload Erli nie zawiera ID zamowienia.');
} }
$rawStatus = strtolower(trim((string) $this->readPath($payload, ['status', 'order.status']))); $rawStatus = strtolower(trim((string) $this->readPath($payload, ['status', 'order.status'])));

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace App\Modules\Settings;
use RuntimeException;
final class ErliOrderMappingException extends RuntimeException
{
}

View File

@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace App\Modules\Settings; namespace App\Modules\Settings;
use App\Core\Http\SslCertificateResolver; use App\Core\Http\SslCertificateResolver;
use RuntimeException;
final class FakturowniaApiClient final class FakturowniaApiClient
{ {
@@ -72,7 +71,7 @@ final class FakturowniaApiClient
$apiToken = trim((string) ($settings['api_token'] ?? '')); $apiToken = trim((string) ($settings['api_token'] ?? ''));
if ($prefix === '' || $apiToken === '') { if ($prefix === '' || $apiToken === '') {
throw new RuntimeException('Brak prefiksu konta lub tokenu API.'); throw new FakturowniaApiException('Brak prefiksu konta lub tokenu API.');
} }
$url = $this->buildUrl($prefix, '/invoices.json'); $url = $this->buildUrl($prefix, '/invoices.json');
@@ -82,13 +81,13 @@ final class FakturowniaApiClient
], JSON_UNESCAPED_UNICODE); ], JSON_UNESCAPED_UNICODE);
if ($body === false) { if ($body === false) {
throw new RuntimeException('Nie udalo sie zakodowac payloadu (json_encode).'); throw new FakturowniaApiException('Nie udalo sie zakodowac payloadu (json_encode).');
} }
[$rawBody, $httpCode, $curlError] = $this->httpPostJson($url, $body); [$rawBody, $httpCode, $curlError] = $this->httpPostJson($url, $body);
if ($curlError !== null) { if ($curlError !== null) {
throw new RuntimeException('Blad polaczenia: ' . $curlError); throw new FakturowniaApiException('Blad polaczenia: ' . $curlError);
} }
if ($httpCode < 200 || $httpCode >= 300) { if ($httpCode < 200 || $httpCode >= 300) {
@@ -97,7 +96,7 @@ final class FakturowniaApiClient
$msg = 'raw body: ' . substr($rawBody, 0, 500); $msg = 'raw body: ' . substr($rawBody, 0, 500);
} }
error_log('[Fakturownia] createInvoice HTTP ' . $httpCode . ' | body=' . substr($rawBody, 0, 1000)); error_log('[Fakturownia] createInvoice HTTP ' . $httpCode . ' | body=' . substr($rawBody, 0, 1000));
throw new RuntimeException('HTTP ' . $httpCode . ': ' . $msg); throw new FakturowniaApiException('HTTP ' . $httpCode . ': ' . $msg);
} }
return $this->normalizeInvoiceResponse($rawBody, $prefix, $apiToken); return $this->normalizeInvoiceResponse($rawBody, $prefix, $apiToken);
@@ -116,7 +115,7 @@ final class FakturowniaApiClient
$oid = trim($oid); $oid = trim($oid);
if ($prefix === '' || $apiToken === '') { if ($prefix === '' || $apiToken === '') {
throw new RuntimeException('Brak prefiksu konta lub tokenu API.'); throw new FakturowniaApiException('Brak prefiksu konta lub tokenu API.');
} }
if ($oid === '') { if ($oid === '') {
return null; return null;
@@ -128,7 +127,7 @@ final class FakturowniaApiClient
[$rawBody, $httpCode, $curlError] = $this->httpGet($url); [$rawBody, $httpCode, $curlError] = $this->httpGet($url);
if ($curlError !== null) { if ($curlError !== null) {
throw new RuntimeException('Blad polaczenia: ' . $curlError); throw new FakturowniaApiException('Blad polaczenia: ' . $curlError);
} }
if ($httpCode < 200 || $httpCode >= 300) { if ($httpCode < 200 || $httpCode >= 300) {
@@ -136,12 +135,12 @@ final class FakturowniaApiClient
if ($msg === '') { if ($msg === '') {
$msg = 'HTTP ' . $httpCode; $msg = 'HTTP ' . $httpCode;
} }
throw new RuntimeException('HTTP ' . $httpCode . ': ' . $msg); throw new FakturowniaApiException('HTTP ' . $httpCode . ': ' . $msg);
} }
$decoded = json_decode($rawBody, true); $decoded = json_decode($rawBody, true);
if (!is_array($decoded)) { if (!is_array($decoded)) {
throw new RuntimeException('Niepoprawna odpowiedz JSON od Fakturowni.'); throw new FakturowniaApiException('Niepoprawna odpowiedz JSON od Fakturowni.');
} }
$invoice = $this->firstInvoiceFromListResponse($decoded); $invoice = $this->firstInvoiceFromListResponse($decoded);
@@ -174,7 +173,7 @@ final class FakturowniaApiClient
{ {
$decoded = json_decode($rawBody, true); $decoded = json_decode($rawBody, true);
if (!is_array($decoded)) { if (!is_array($decoded)) {
throw new RuntimeException('Niepoprawna odpowiedz JSON od Fakturowni.'); throw new FakturowniaApiException('Niepoprawna odpowiedz JSON od Fakturowni.');
} }
return $this->normalizeInvoiceArray($decoded, $prefix, $apiToken); return $this->normalizeInvoiceArray($decoded, $prefix, $apiToken);
@@ -190,7 +189,7 @@ final class FakturowniaApiClient
$number = $invoice['number'] ?? $invoice['full_number'] ?? null; $number = $invoice['number'] ?? $invoice['full_number'] ?? null;
if ($id === null || $number === null) { if ($id === null || $number === null) {
throw new RuntimeException('Odpowiedz Fakturowni nie zawiera id/number.'); throw new FakturowniaApiException('Odpowiedz Fakturowni nie zawiera id/number.');
} }
$viewUrl = (string) ($invoice['view_url'] ?? ''); $viewUrl = (string) ($invoice['view_url'] ?? '');

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace App\Modules\Settings;
use RuntimeException;
final class FakturowniaApiException extends RuntimeException
{
}

View File

@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace App\Modules\Settings; namespace App\Modules\Settings;
use App\Core\Http\SslCertificateResolver; use App\Core\Http\SslCertificateResolver;
use RuntimeException;
/** /**
* polkurier.pl Web Service API client. * polkurier.pl Web Service API client.
@@ -39,7 +38,7 @@ final class PolkurierApiClient
'platform' => self::PLATFORM, 'platform' => self::PLATFORM,
'platform_version' => self::PLATFORM_VERSION, 'platform_version' => self::PLATFORM_VERSION,
], $login, $apiToken); ], $login, $apiToken);
} catch (RuntimeException $exception) { } catch (PolkurierApiException $exception) {
return [ return [
'ok' => false, 'ok' => false,
'http_code' => $this->lastHttpCode, 'http_code' => $this->lastHttpCode,
@@ -219,7 +218,7 @@ final class PolkurierApiClient
/** /**
* Wspolny wrapper dla wszystkich apimetod. * Wspolny wrapper dla wszystkich apimetod.
* Sukces -> zwraca tresc pola 'response'. Blad -> rzuca RuntimeException z trescia z 'response'. * Sukces -> zwraca tresc pola 'response'. Blad -> rzuca PolkurierApiException z trescia z 'response'.
* *
* @param array<string, mixed> $data * @param array<string, mixed> $data
* @return mixed Tresc pola 'response' z API. * @return mixed Tresc pola 'response' z API.
@@ -239,13 +238,13 @@ final class PolkurierApiClient
$this->lastHttpCode = $httpCode; $this->lastHttpCode = $httpCode;
if ($curlError !== null) { if ($curlError !== null) {
throw new RuntimeException('Blad polaczenia z polkurier: ' . $curlError); throw new PolkurierApiException('Blad polaczenia z polkurier: ' . $curlError);
} }
$decoded = json_decode(ltrim($body, "\xEF\xBB\xBF \t\n\r\0\x0B"), true); $decoded = json_decode(ltrim($body, "\xEF\xBB\xBF \t\n\r\0\x0B"), true);
if (!is_array($decoded)) { if (!is_array($decoded)) {
$snippet = substr(trim(strip_tags($body)), 0, 240); $snippet = substr(trim(strip_tags($body)), 0, 240);
throw new RuntimeException('Niepoprawna odpowiedz JSON polkurier (HTTP ' . $httpCode . '): ' . $snippet); throw new PolkurierApiException('Niepoprawna odpowiedz JSON polkurier (HTTP ' . $httpCode . '): ' . $snippet);
} }
$status = strtolower(trim((string) ($decoded['status'] ?? ''))); $status = strtolower(trim((string) ($decoded['status'] ?? '')));
@@ -257,7 +256,7 @@ final class PolkurierApiClient
// Error path // Error path
$errorMessage = $this->extractErrorMessage($responseField, $decoded, $status, $httpCode); $errorMessage = $this->extractErrorMessage($responseField, $decoded, $status, $httpCode);
throw new RuntimeException($errorMessage); throw new PolkurierApiException($errorMessage);
} }
private int $lastHttpCode = 0; private int $lastHttpCode = 0;

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace App\Modules\Settings;
use RuntimeException;
final class PolkurierApiException extends RuntimeException
{
}

View File

@@ -16,6 +16,15 @@ use Throwable;
final class SmsTemplateController final class SmsTemplateController
{ {
private const ROUTE_INDEX = '/settings/sms-templates';
private const ROUTE_CREATE = '/settings/sms-templates/create';
private const ROUTE_EDIT = '/settings/sms-templates/edit?id=';
private const FLASH_SUCCESS = 'settings.sms_templates.success';
private const FLASH_ERROR = 'settings.sms_templates.error';
private const MSG_CSRF = 'Nieprawidlowy token CSRF';
private const MSG_NOT_FOUND = 'Nie znaleziono szablonu';
private const MSG_REQUIRED = 'Nazwa i tresc sa wymagane';
public function __construct( public function __construct(
private readonly Template $template, private readonly Template $template,
private readonly Translator $translator, private readonly Translator $translator,
@@ -35,8 +44,8 @@ final class SmsTemplateController
'user' => $this->auth->user(), 'user' => $this->auth->user(),
'csrfToken' => Csrf::token(), 'csrfToken' => Csrf::token(),
'templates' => $templates, 'templates' => $templates,
'successMessage' => Flash::get('settings.sms_templates.success', ''), 'successMessage' => Flash::get(self::FLASH_SUCCESS, ''),
'errorMessage' => Flash::get('settings.sms_templates.error', ''), 'errorMessage' => Flash::get(self::FLASH_ERROR, ''),
], 'layouts/app'); ], 'layouts/app');
return Response::html($html); return Response::html($html);
@@ -53,8 +62,7 @@ final class SmsTemplateController
$template = $id > 0 ? $this->repository->findById($id) : null; $template = $id > 0 ? $this->repository->findById($id) : null;
if ($template === null) { if ($template === null) {
Flash::set('settings.sms_templates.error', 'Nie znaleziono szablonu'); return $this->redirectWithError(self::ROUTE_INDEX, self::MSG_NOT_FOUND);
return Response::redirect('/settings/sms-templates');
} }
return $this->renderForm($template); return $this->renderForm($template);
@@ -66,25 +74,22 @@ final class SmsTemplateController
$formPath = $this->buildFormPath($templateId > 0 ? $templateId : null); $formPath = $this->buildFormPath($templateId > 0 ? $templateId : null);
if (!Csrf::validate((string) $request->input('_token', ''))) { if (!Csrf::validate((string) $request->input('_token', ''))) {
Flash::set('settings.sms_templates.error', 'Nieprawidlowy token CSRF'); return $this->redirectWithError($formPath, self::MSG_CSRF);
return Response::redirect($formPath);
} }
$name = trim((string) $request->input('name', '')); $name = trim((string) $request->input('name', ''));
$body = (string) $request->input('body', ''); $body = (string) $request->input('body', '');
if ($name === '' || trim($body) === '') { if ($name === '' || trim($body) === '') {
Flash::set('settings.sms_templates.error', 'Nazwa i tresc sa wymagane'); return $this->redirectWithError($formPath, self::MSG_REQUIRED);
return Response::redirect($formPath);
} }
$unknownVariables = TemplateVariableCatalog::findUnknownPlaceholders($body); $unknownVariables = TemplateVariableCatalog::findUnknownPlaceholders($body);
if ($unknownVariables !== []) { if ($unknownVariables !== []) {
Flash::set( return $this->redirectWithError(
'settings.sms_templates.error', $formPath,
'Nieznane zmienne w szablonie: ' . implode(', ', $unknownVariables) 'Nieznane zmienne w szablonie: ' . implode(', ', $unknownVariables)
); );
return Response::redirect($formPath);
} }
try { try {
@@ -95,36 +100,33 @@ final class SmsTemplateController
'is_active' => $request->input('is_active', null), 'is_active' => $request->input('is_active', null),
]); ]);
Flash::set('settings.sms_templates.success', 'Szablon SMS zapisany'); Flash::set(self::FLASH_SUCCESS, 'Szablon SMS zapisany');
} catch (Throwable $exception) { } catch (Throwable $exception) {
Flash::set('settings.sms_templates.error', 'Blad zapisu szablonu: ' . $exception->getMessage()); return $this->redirectWithError($formPath, 'Blad zapisu szablonu: ' . $exception->getMessage());
return Response::redirect($formPath);
} }
return Response::redirect('/settings/sms-templates'); return Response::redirect(self::ROUTE_INDEX);
} }
public function delete(Request $request): Response public function delete(Request $request): Response
{ {
if (!Csrf::validate((string) $request->input('_token', ''))) { if (!Csrf::validate((string) $request->input('_token', ''))) {
Flash::set('settings.sms_templates.error', 'Nieprawidlowy token CSRF'); return $this->redirectWithError(self::ROUTE_INDEX, self::MSG_CSRF);
return Response::redirect('/settings/sms-templates');
} }
$id = (int) $request->input('id', '0'); $id = (int) $request->input('id', '0');
if ($id <= 0) { if ($id <= 0) {
Flash::set('settings.sms_templates.error', 'Nieprawidlowy identyfikator szablonu'); return $this->redirectWithError(self::ROUTE_INDEX, 'Nieprawidlowy identyfikator szablonu');
return Response::redirect('/settings/sms-templates');
} }
try { try {
$this->repository->delete($id); $this->repository->delete($id);
Flash::set('settings.sms_templates.success', 'Szablon SMS usuniety'); Flash::set(self::FLASH_SUCCESS, 'Szablon SMS usuniety');
} catch (Throwable) { } catch (Throwable) {
Flash::set('settings.sms_templates.error', 'Blad usuwania szablonu'); Flash::set(self::FLASH_ERROR, 'Blad usuwania szablonu');
} }
return Response::redirect('/settings/sms-templates'); return Response::redirect(self::ROUTE_INDEX);
} }
public function toggleStatus(Request $request): Response public function toggleStatus(Request $request): Response
@@ -164,8 +166,8 @@ final class SmsTemplateController
'csrfToken' => Csrf::token(), 'csrfToken' => Csrf::token(),
'template' => $template, 'template' => $template,
'variableGroups' => TemplateVariableCatalog::groups(), 'variableGroups' => TemplateVariableCatalog::groups(),
'successMessage' => Flash::get('settings.sms_templates.success', ''), 'successMessage' => Flash::get(self::FLASH_SUCCESS, ''),
'errorMessage' => Flash::get('settings.sms_templates.error', ''), 'errorMessage' => Flash::get(self::FLASH_ERROR, ''),
], 'layouts/app'); ], 'layouts/app');
return Response::html($html); return Response::html($html);
@@ -174,9 +176,15 @@ final class SmsTemplateController
private function buildFormPath(?int $templateId): string private function buildFormPath(?int $templateId): string
{ {
if ($templateId !== null && $templateId > 0) { if ($templateId !== null && $templateId > 0) {
return '/settings/sms-templates/edit?id=' . $templateId; return self::ROUTE_EDIT . $templateId;
} }
return '/settings/sms-templates/create'; return self::ROUTE_CREATE;
}
private function redirectWithError(string $path, string $message): Response
{
Flash::set(self::FLASH_ERROR, $message);
return Response::redirect($path);
} }
} }

View File

@@ -13,6 +13,12 @@ use App\Modules\Auth\AuthService;
final class UsersController final class UsersController
{ {
private const ROUTE_INDEX = '/settings/users';
private const FLASH_ERROR = 'users_error';
private const FLASH_SUCCESS = 'users_success';
private const FLASH_OLD_NAME = 'users_old_name';
private const FLASH_OLD_EMAIL = 'users_old_email';
public function __construct( public function __construct(
private readonly Template $template, private readonly Template $template,
private readonly Translator $translator, private readonly Translator $translator,
@@ -41,7 +47,7 @@ final class UsersController
'csrfToken' => Csrf::token(), 'csrfToken' => Csrf::token(),
'tableList' => [ 'tableList' => [
'list_key' => 'users', 'list_key' => 'users',
'base_path' => '/settings/users', 'base_path' => self::ROUTE_INDEX,
'query' => $filters, 'query' => $filters,
'filters' => [ 'filters' => [
[ [
@@ -68,10 +74,10 @@ final class UsersController
'empty_message' => $this->translator->get('users.empty'), 'empty_message' => $this->translator->get('users.empty'),
'show_actions' => false, 'show_actions' => false,
], ],
'errorMessage' => (string) Flash::get('users_error', ''), 'errorMessage' => (string) Flash::get(self::FLASH_ERROR, ''),
'successMessage' => (string) Flash::get('users_success', ''), 'successMessage' => (string) Flash::get(self::FLASH_SUCCESS, ''),
'oldName' => (string) Flash::get('users_old_name', ''), 'oldName' => (string) Flash::get(self::FLASH_OLD_NAME, ''),
'oldEmail' => (string) Flash::get('users_old_email', ''), 'oldEmail' => (string) Flash::get(self::FLASH_OLD_EMAIL, ''),
], 'layouts/app'); ], 'layouts/app');
return Response::html($html); return Response::html($html);
@@ -81,44 +87,56 @@ final class UsersController
{ {
$csrfToken = (string) $request->input('_token', ''); $csrfToken = (string) $request->input('_token', '');
if (!Csrf::validate($csrfToken)) { if (!Csrf::validate($csrfToken)) {
Flash::set('users_error', $this->translator->get('auth.errors.csrf_expired')); return $this->redirectWithError($this->translator->get('auth.errors.csrf_expired'));
return Response::redirect('/settings/users');
} }
$name = trim((string) $request->input('name', '')); $name = trim((string) $request->input('name', ''));
$email = strtolower(trim((string) $request->input('email', ''))); $email = strtolower(trim((string) $request->input('email', '')));
$password = (string) $request->input('password', ''); $password = (string) $request->input('password', '');
Flash::set('users_old_name', $name); $this->rememberOldInput($name, $email);
Flash::set('users_old_email', $email);
if (mb_strlen($name) < 2) { $validationError = $this->validateNewUser($name, $email, $password);
Flash::set('users_error', $this->translator->get('users.validation.name_min')); if ($validationError !== null) {
return Response::redirect('/settings/users'); return $this->redirectWithError($validationError);
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
Flash::set('users_error', $this->translator->get('users.validation.email_invalid'));
return Response::redirect('/settings/users');
}
if (strlen($password) < 8) {
Flash::set('users_error', $this->translator->get('users.validation.password_min'));
return Response::redirect('/settings/users');
}
if ($this->users->findByEmail($email) !== null) {
Flash::set('users_error', $this->translator->get('users.validation.email_taken'));
return Response::redirect('/settings/users');
} }
$passwordHash = password_hash($password, PASSWORD_DEFAULT); $passwordHash = password_hash($password, PASSWORD_DEFAULT);
$this->users->create($name, $email, $passwordHash); $this->users->create($name, $email, $passwordHash);
Flash::set('users_success', $this->translator->get('users.flash.created')); Flash::set(self::FLASH_SUCCESS, $this->translator->get('users.flash.created'));
Flash::set('users_old_name', ''); $this->rememberOldInput('', '');
Flash::set('users_old_email', '');
return Response::redirect('/settings/users'); return Response::redirect(self::ROUTE_INDEX);
}
private function validateNewUser(string $name, string $email, string $password): ?string
{
if (mb_strlen($name) < 2) {
return $this->translator->get('users.validation.name_min');
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return $this->translator->get('users.validation.email_invalid');
}
if (strlen($password) < 8) {
return $this->translator->get('users.validation.password_min');
}
if ($this->users->findByEmail($email) !== null) {
return $this->translator->get('users.validation.email_taken');
}
return null;
}
private function rememberOldInput(string $name, string $email): void
{
Flash::set(self::FLASH_OLD_NAME, $name);
Flash::set(self::FLASH_OLD_EMAIL, $email);
}
private function redirectWithError(string $message): Response
{
Flash::set(self::FLASH_ERROR, $message);
return Response::redirect(self::ROUTE_INDEX);
} }
} }

View File

@@ -4,10 +4,10 @@ declare(strict_types=1);
namespace Tests\Unit; namespace Tests\Unit;
use App\Core\Constants\IntegrationSources; use App\Core\Constants\IntegrationSources;
use App\Modules\Settings\ErliOrderMappingException;
use App\Modules\Settings\ErliOrderMapper; use App\Modules\Settings\ErliOrderMapper;
use App\Modules\Settings\ErliPullStatusMappingRepository; use App\Modules\Settings\ErliPullStatusMappingRepository;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use RuntimeException;
final class ErliOrderMapperTest extends TestCase final class ErliOrderMapperTest extends TestCase
{ {
@@ -111,7 +111,7 @@ final class ErliOrderMapperTest extends TestCase
$message = $this->message('purchased'); $message = $this->message('purchased');
unset($message['payload']['id']); unset($message['payload']['id']);
$this->expectException(RuntimeException::class); $this->expectException(ErliOrderMappingException::class);
$this->expectExceptionMessage('ID zamowienia'); $this->expectExceptionMessage('ID zamowienia');
$this->mapper->mapInboxMessage(7, $message); $this->mapper->mapInboxMessage(7, $message);

View File

@@ -8,12 +8,12 @@ use App\Modules\Accounting\InvoiceRepository;
use App\Modules\Accounting\InvoiceService; use App\Modules\Accounting\InvoiceService;
use App\Modules\Orders\OrdersRepository; use App\Modules\Orders\OrdersRepository;
use App\Modules\Settings\CompanySettingsRepository; use App\Modules\Settings\CompanySettingsRepository;
use App\Modules\Settings\FakturowniaApiException;
use App\Modules\Settings\FakturowniaApiClient; use App\Modules\Settings\FakturowniaApiClient;
use App\Modules\Settings\FakturowniaIntegrationRepository; use App\Modules\Settings\FakturowniaIntegrationRepository;
use App\Modules\Settings\InvoiceConfigRepository; use App\Modules\Settings\InvoiceConfigRepository;
use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use RuntimeException;
final class FakturowniaInvoiceIdempotencyTest extends TestCase final class FakturowniaInvoiceIdempotencyTest extends TestCase
{ {
@@ -124,7 +124,7 @@ final class FakturowniaInvoiceIdempotencyTest extends TestCase
$this->api $this->api
->expects($this->once()) ->expects($this->once())
->method('createInvoice') ->method('createInvoice')
->willThrowException(new RuntimeException('Blad polaczenia: timeout')); ->willThrowException(new FakturowniaApiException('Blad polaczenia: timeout'));
$this->invoices $this->invoices
->expects($this->once()) ->expects($this->once())
->method('finalizeDelegatedExternal') ->method('finalizeDelegatedExternal')
@@ -158,7 +158,7 @@ final class FakturowniaInvoiceIdempotencyTest extends TestCase
$this->api $this->api
->expects($this->once()) ->expects($this->once())
->method('createInvoice') ->method('createInvoice')
->willThrowException(new RuntimeException('Blad polaczenia: timeout')); ->willThrowException(new FakturowniaApiException('Blad polaczenia: timeout'));
$this->invoices $this->invoices
->expects($this->once()) ->expects($this->once())
->method('markDelegatedExternalFailed') ->method('markDelegatedExternalFailed')