8fc9d3ec8a
Компоненты: - SupplierProjectDto (readonly DTO, fromModel + equals) - SupplierException иерархия (Auth/Transient/Client + abstract base) - SupplierAuthException: 401/403 sticky после refresh-retry - SupplierTransientException: 5xx/network/timeout (retryable) - SupplierClientException: 4xx 400/404/422 (наша ошибка payload) - SupplierPortalClient: list/save/update/delete projects через Http facade + Redis cache cookie/CSRF (key 'supplier:session', TTL 6h) + auto-retry на 401/403 через dispatch_sync(RefreshSupplierSessionJob) + classification HTTP errors → 3 exception types - RefreshSupplierSessionJob stub (handle() пустой; full impl в Task 5) - config/services.supplier (login/password/portal_url/alert_email) +9 тестов через Http::fake() в Unit/Supplier/SupplierPortalClientTest.php: - cookie/CSRF attach - 401 retry flow (single retry, sticky 401 → SupplierAuthException) - 5xx → SupplierTransientException - 4xx → SupplierClientException - network error → SupplierTransientException - save/update/delete payload shapes + responses NOTE: toPayload() shape — placeholder; точные поля адаптируются после Task 1 discovery + Task 2 spec §4.4 (отдельный fixup commit перед Task 6 при расхождении наблюдаемого формата с предполагаемым).