# Architecture ## Status - Projekt po resecie do trybu `users-only`. - UI korzysta z globalnego standardu naglowkow sekcji (`h2/h3/h4.section-title`) definiowanego centralnie w `resources/scss/app.scss` i buildowanego do `public/assets/css/app.css`. ## Moduly aktywne - `App\Modules\Auth` - `App\Modules\Orders` - `App\Modules\Users` - `App\Modules\Settings` ## Routing - `GET /login`, `POST /login`, `POST /logout` - `GET /settings/users`, `POST /settings/users` - `GET /orders` (redirect do `/orders/list`) - `GET /orders/list` - `GET /orders/{id}` - `POST /orders/{id}/status` - `GET /users` (redirect do `/settings/users`) - `POST /users` (compat route) - `GET /settings` (redirect do `/settings/users`) - `GET /settings/database` - `POST /settings/database/migrate` - `GET /settings/statuses` - `POST /settings/status-groups` - `POST /settings/status-groups/update` - `POST /settings/status-groups/delete` - `POST /settings/status-groups/reorder` - `POST /settings/statuses/create` - `POST /settings/statuses/update` - `POST /settings/statuses/delete` - `POST /settings/statuses/reorder` - `GET /settings/cron` - `POST /settings/cron` - `GET /settings/integrations` - `GET /settings/integrations/allegro` - `POST /settings/integrations/allegro/save` - `POST /settings/integrations/allegro/oauth/start` - `POST /settings/integrations/allegro/import-single` - `POST /settings/integrations/allegro/statuses/save` - `POST /settings/integrations/allegro/statuses/save-bulk` - `POST /settings/integrations/allegro/statuses/delete` - `POST /settings/integrations/allegro/statuses/sync` - `GET /settings/integrations/allegro/oauth/callback` - `GET /settings/integrations/apaczka` - `POST /settings/integrations/apaczka/save` - `POST /settings/integrations/apaczka/test` - `GET /settings/integrations/inpost` - `POST /settings/integrations/inpost/save` - `GET /settings/integrations/shoppro` - `POST /settings/integrations/shoppro/save` - `POST /settings/integrations/shoppro/test` - `POST /settings/integrations/shoppro/statuses/save` - `POST /settings/integrations/shoppro/statuses/sync` - `POST /settings/integrations/shoppro/delivery/save` - `GET /health` - `GET /` (redirect) ## Korekta logowania - `AuthController::showLogin(Request): Response`: - dla zalogowanego usera redirect na `/settings/users` (zamiast nieistniejacego `/dashboard`). - `AuthController::login(Request): Response`: - po poprawnym logowaniu redirect na `/settings/users`. ## Kluczowe klasy - `App\Core\Application` - `App\Modules\Auth\AuthController` - `App\Modules\Auth\AuthService` - `App\Modules\Orders\OrdersController` - `App\Modules\Orders\OrdersRepository` - `App\Modules\Settings\SettingsController` - `App\Modules\Settings\OrderStatusRepository` - `App\Modules\Settings\AllegroIntegrationController` - `App\Modules\Settings\AllegroIntegrationRepository` - `App\Modules\Settings\AllegroOAuthClient` - `App\Modules\Settings\AllegroTokenManager` - `App\Modules\Settings\AllegroApiClient` - `App\Modules\Settings\AllegroOrderImportService` - `App\Modules\Settings\AllegroStatusMappingRepository` - `App\Modules\Settings\AllegroStatusDiscoveryService` - `App\Modules\Settings\IntegrationsRepository` - `App\Modules\Settings\IntegrationSecretCipher` - `App\Modules\Orders\OrderImportRepository` - `App\Modules\Settings\CronSettingsController` - `App\Modules\Cron\CronRepository` - `App\Modules\Cron\CronRunner` - `App\Modules\Cron\AllegroTokenRefreshHandler` - `App\Modules\Cron\AllegroOrdersImportHandler` - `App\Modules\Cron\AllegroStatusSyncHandler` - `App\Modules\Cron\ShopproOrdersImportHandler` - `App\Modules\Cron\ShopproStatusSyncHandler` - `App\Modules\Cron\ShopproPaymentStatusSyncHandler` - `App\Modules\Users\UsersController` - `App\Modules\Users\UserRepository` - `App\Modules\Settings\ApaczkaIntegrationController` - `App\Modules\Settings\ApaczkaIntegrationRepository` - `App\Modules\Settings\ApaczkaApiClient` - `App\Modules\Settings\CarrierDeliveryMethodMappingRepository` - `App\Modules\Settings\InpostIntegrationController` - `App\Modules\Settings\InpostIntegrationRepository` - `App\Modules\Settings\IntegrationsHubController` - `App\Modules\Settings\ShopproIntegrationsController` - `App\Modules\Settings\ShopproIntegrationsRepository` - `App\Modules\Settings\ShopproStatusMappingRepository` - `App\Modules\Settings\ShopproDeliveryMethodMappingRepository` - `App\Modules\Settings\ShopproApiClient` - `App\Modules\Settings\ShopproOrdersSyncService` - `App\Modules\Settings\ShopproStatusSyncService` - `App\Modules\Settings\ShopproPaymentStatusSyncService` - `App\Modules\Settings\AllegroOrdersSyncService` - `App\Modules\Settings\AllegroOrderSyncStateRepository` - `App\Modules\Settings\AllegroStatusSyncService` - `App\Modules\Shipments\ShipmentProviderInterface` - `App\Modules\Shipments\ShipmentProviderRegistry` - `App\Modules\Shipments\ApaczkaShipmentService` ## Przeplyw Zamowienia > Lista zamowien - `GET /orders/list`: - `OrdersController::index(Request): Response` - pobiera dane listy przez `OrdersRepository::paginate(...)`, - data zamowienia na liscie korzysta z fallbacku: `orders.ordered_at` -> `orders.source_created_at` -> `orders.source_updated_at` -> `orders.fetched_at`, - domyslne sortowanie i filtry zakresu dat (`date_from`, `date_to`) korzystaja z tej samej daty efektywnej, - pobiera slowniki filtrow (`sourceOptions()`, `statusOptions()`), statystyki (`quickStats()`), agregaty statusow (`statusCounts()`) i konfiguracje grup/statusow (`statusPanelConfig()`), - buduje panel statusow z grupami i licznikami (`buildStatusPanel(...)`) z linkami filtrujacymi po statusie, - panel statusow i etykiety statusow sa zgodne z konfiguracja z `Ustawienia > Statusy` (z fallbackiem `Pozostale`), - renderuje podglad pozycji zamowienia (nazwa, miniatura, ilosc) na bazie `order_items`, - miniatura pozycji jest rozwiazywana priorytetowo: `order_items.media_url` -> glowny obraz powiazanego produktu orderPRO (`product_channel_map` + `sales_channels[allegro]` + `product_images`), - obsluguje modal podgladu zdjecia pozycji po kliknieciu miniatury, - normalizuje status techniczny na etykiete biznesowa (bez kodu statusu), - renderuje widok `resources/views/orders/list.php` i komponent tabeli `resources/views/components/table-list.php`. - `GET /orders/{id}`: - `OrdersController::show(Request): Response` - pobiera szczegoly przez `OrdersRepository::findDetails(int $orderId)`, statystyke statusow przez `statusCounts()` oraz konfiguracje przez `statusPanelConfig()`, - pozycje zamowienia maja ten sam mechanizm rozwiazywania miniatur co lista (`media_url` z zamowienia lub obraz z mapowania produktu), - buduje panel statusow z grupami i licznikami (`buildStatusPanel(...)`), - renderuje klikalne taby sekcji i przelaczanie paneli po stronie klienta (JS w `orders/show.php`), - renderuje widok `resources/views/orders/show.php` z sekcjami: - pozycje zamowienia, - szczegoly zamowienia, - platnosc i wysylka, - adresy (`customer`, `invoice`, `delivery`), - notatki i historia statusow. - Sidebar ma oddzielna grupe nawigacyjna: - `Zamowienia` -> `Lista zamowien`. ## Skrypty techniczne (CLI) - `bin/fix_status_codes.php` - naprawa kodow grup/statusow (transliteracja PL -> ASCII, tryb `--dry-run`, opcja `--use-remote`). - `bin/deploy_and_seed_orders.php` - aplikuje generyczny schema zamowien z `database/drafts/20260302_orders_schema_v1.sql`, - seeduje dane testowe (`--count`, `--append`, `--use-remote`, `--profile=default|realistic`), - profil `realistic` utrzymuje spojne zaleznosci miedzy: - statusem zamowienia, - statusem i kwota platnosci, - obecnoscia wysylek i dokumentow, - historia przejsc statusow (deterministyczne sciezki zamiast losowych przeskokow). ## Przeplyw Ustawienia > Statusy - `GET /settings/statuses`: - `SettingsController::statuses(Request): Response` - pobiera dane przez `OrderStatusRepository::listGroups()` i `OrderStatusRepository::listStatuses()`, - renderuje widok `resources/views/settings/statuses.php`. - `POST /settings/status-groups`: - `SettingsController::createStatusGroup(Request): Response` - waliduje CSRF i dane (`name`, `color_hex`, `is_active`), - `code` jest generowany automatycznie z `name` i nie jest edytowany z UI, - zapisuje przez `OrderStatusRepository::createGroup(...)`. - `POST /settings/status-groups/update`: - `SettingsController::updateStatusGroup(Request): Response` - waliduje istnienie grupy i aktualizuje rekord przez `updateGroup(...)`, - `code` pozostaje bez zmian (read-only po utworzeniu). - `POST /settings/status-groups/delete`: - `SettingsController::deleteStatusGroup(Request): Response` - usuwa grupe przez `deleteGroup(...)`; statusy z tej grupy usuwane sa kaskadowo (FK). - `POST /settings/status-groups/reorder`: - `SettingsController::reorderStatusGroups(Request): Response` - zapisuje kolejnosc drag-and-drop grup przez `OrderStatusRepository::reorderGroups(...)`, - endpoint jest wywolywany automatycznie po upuszczeniu elementu listy (auto-save). - `POST /settings/statuses/create`: - `SettingsController::createStatus(Request): Response` - waliduje grupe, pola statusu i zapisuje przez `createStatus(...)`. - `POST /settings/statuses/update`: - `SettingsController::updateStatus(Request): Response` - waliduje dane i aktualizuje status przez `updateStatus(...)`, - `code` pozostaje bez zmian (read-only po utworzeniu). - `POST /settings/statuses/delete`: - `SettingsController::deleteStatus(Request): Response` - usuwa status przez `deleteStatus(...)`. - `POST /settings/statuses/reorder`: - `SettingsController::reorderStatuses(Request): Response` - zapisuje kolejnosc drag-and-drop statusow w ramach grupy przez `OrderStatusRepository::reorderStatusesByGroup(...)`, - endpoint jest wywolywany automatycznie po upuszczeniu elementu listy (auto-save). ## Nawigacja ustawien - Sidebar (`resources/views/layouts/app.php`) ma nowy podlink: - `Statusy` (`/settings/statuses`). - `Cron` (`/settings/cron`). - `Integracje` (`/settings/integrations`) - wspolny hub konfiguracji providerow. ## Przeplyw Ustawienia > Cron - `GET /settings/cron`: - `CronSettingsController::index(Request): Response` - pobiera ustawienia `cron_run_on_web`, `cron_web_limit`, - renderuje harmonogramy (`cron_schedules`) oraz kolejke/historie (`cron_jobs`), - historia (`past_jobs`) ma stronicowanie po parametrze query `past_page` (25 rekordow na strone). - `POST /settings/cron`: - `CronSettingsController::save(Request): Response` - waliduje CSRF, - zapisuje `cron_run_on_web` i `cron_web_limit` do `app_settings`. ## Przeplyw wykonania crona - `bin/cron.php`: - laduje aplikacje i uruchamia `CronRunner::run($limit)`. - `App\Core\Application::maybeRunCronOnWeb(Request): void`: - przy wlaczonej opcji `cron_run_on_web=1` uruchamia `CronRunner` podczas requestu HTTP, - stosuje throttling sesyjny i lock DB (`GET_LOCK`) zeby uniknac wielu rownoleglych workerow. - `CronRunner`: - dispatchuje due schedule z `cron_schedules` do `cron_jobs`, - pobiera pending joby wg priorytetu i czasu, - wykonuje handler po `job_type`. - Pierwszy aktywny handler: - `allegro_token_refresh` -> `AllegroTokenRefreshHandler::handle(...)` (odswiezenie tokenu OAuth Allegro). - Dodatkowy handler: - `allegro_orders_import` -> `AllegroOrdersImportHandler::handle(...)` (automatyczny import zamowien Allegro). - `allegro_status_sync` -> `AllegroStatusSyncHandler::handle(...)` (synchronizacja statusow wg kierunku z ustawien integracji Allegro). - `shoppro_orders_import` -> `ShopproOrdersImportHandler::handle(...)` (automatyczny import zamowien z aktywnych integracji `shopPRO` z wlaczonym pobieraniem). - `shoppro_order_status_sync` -> `ShopproStatusSyncHandler::handle(...)` (synchronizacja statusow shopPRO wg kierunku ustawionego per instancja). - `shoppro_payment_status_sync` -> `ShopproPaymentStatusSyncHandler::handle(...)` (odswiezanie statusu platnosci zamowien shopPRO na podstawie flagi `paid`). ## Przeplyw tworzenia przesylki - `GET /orders/{id}/shipment/prepare`: - `ShipmentController::prepare(Request): Response`, - laduje uslugi dostawy providerow z `ShipmentProviderRegistry` (aktualnie: `allegro_wza`, `apaczka`), - pobiera automatyczne mapowanie formy dostawy przez `CarrierDeliveryMethodMappingRepository` (`source_system` + `source_integration_id` + `order_delivery_method`), - dla dostaw punktowych (`parcel_external_id`/`parcel_name`) prefillem `receiver_name` sa dane klienta (a nie nazwa punktu/metody dostawy), - gdy mapowanie nie zostanie znalezione, buduje komunikat diagnostyczny (brak mapowan dla instancji lub brak mapowania konkretnej metody) i przekazuje go do widoku. - `POST /orders/{id}/shipment/create`: - `ShipmentController::create(Request): Response`, - wybiera providera dynamicznie po `provider_code` i deleguje do `ShipmentProviderInterface::createShipment(...)`, - dla `apaczka` waliduje wymagane punkty odbioru/nadania wg definicji uslugi (`service_structure`) i przy bledzie wyceny zwraca rozszerzona diagnostyke parametrow, - `apaczka` uzupelnia i wysyla `contact_person` dla nadawcy (z `Ustawienia > Dane firmy`) i odbiorcy (fallback z danych zamowienia), - `apaczka` ustawia jawnie `pickup.type` (`SELF`/`COURIER`) na podstawie uslugi i obecnosci `sender_point_id`; dla `COURIER` dopelnia tez `pickup.date`, `pickup.hours_from`, `pickup.hours_to`, - dla uslug punktowych `apaczka` payload adresu zawiera aliasy identyfikatora punktu (`point`, `foreign_address_id`, `point_id`) dla nadania i odbioru, - `ApaczkaShipmentService::buildReceiverAddress(...)` sklada dane odbiorcy z fallbackami (formularz -> delivery -> punkt odbioru z `parcel_name` -> customer), a dla przesylek punktowych dodatkowo probuje uzupelnic adres punktu przez API `points`; przy dalszych brakach dopelnia minimum techniczne, aby nie blokowac tworzenia. - `GET /orders/{id}/shipment/{packageId}/status`: - `ShipmentController::checkStatus(Request): Response`, - wybiera providera po `shipment_packages.provider` i deleguje `checkCreationStatus(...)`. - `POST /orders/{id}/shipment/{packageId}/label`: - `ShipmentController::label(Request): Response`, - wybiera providera po `shipment_packages.provider` i deleguje `downloadLabel(...)`, - dla Apaczka bledy typu `Label is not available for this order` oznaczaja paczke jako `error`, aby nie ponawiac nieskutecznych prob pobrania. ## Przeplyw Ustawienia > Integracje > Allegro - `GET /settings/integrations/allegro`: - `AllegroIntegrationController::index(Request): Response` - odczytuje konfiguracje przez `AllegroIntegrationRepository::getSettings()`, - renderuje `resources/views/settings/allegro.php` z domyslnym callback URL. - `POST /settings/integrations/allegro/save`: - `AllegroIntegrationController::save(Request): Response` - waliduje CSRF, srodowisko, `redirect_uri` i date startu, - zapisuje ustawienia przez `AllegroIntegrationRepository::saveSettings(...)`. - `POST /settings/integrations/allegro/settings/save`: - `AllegroIntegrationController::saveImportSettings(Request): Response` - zapisuje interwal harmonogramu `allegro_orders_import` (w minutach) do `cron_schedules.interval_seconds`, - zapisuje kierunek synchronizacji statusow i interwal synchronizacji statusow do `app_settings`, - zapisuje interwal joba `allegro_status_sync` do `cron_schedules.interval_seconds`. - `POST /settings/integrations/allegro/oauth/start`: - `AllegroIntegrationController::startOAuth(Request): Response` - waliduje CSRF i komplet danych OAuth (`client_id`, `client_secret`, `redirect_uri`), - buduje URL autoryzacji przez `AllegroOAuthClient::buildAuthorizeUrl(...)` (scope: `orders:read` + `sale:offers:read`), - zapisuje `state` w sesji i przekierowuje do Allegro. - `POST /settings/integrations/allegro/import-single`: - `AllegroIntegrationController::importSingleOrder(Request): Response` - waliduje CSRF i `checkout_form_id`, - uruchamia `AllegroOrderImportService::importSingleOrder(...)`, - po imporcie pokazuje diagnostyke miniatur pozycji (ile pozycji ma obrazek i przyczyny brakow). - `POST /settings/integrations/allegro/statuses/save`: - `AllegroIntegrationController::saveStatusMapping(Request): Response` - zapisuje mapowanie `allegro_status_code -> orderpro_status_code`. - `POST /settings/integrations/allegro/statuses/save-bulk`: - `AllegroIntegrationController::saveStatusMappingsBulk(Request): Response` - zapisuje mapowania zbiorczo dla wszystkich wierszy tabeli mapowan. - `POST /settings/integrations/allegro/statuses/delete`: - `AllegroIntegrationController::deleteStatusMapping(Request): Response` - usuwa mapowanie po `mapping_id`. - `POST /settings/integrations/allegro/statuses/sync`: - `AllegroIntegrationController::syncStatusesFromAllegro(Request): Response` - pobiera statusy z API Allegro (`checkout-forms`) i dopisuje je do tabeli mapowan. - `GET /settings/integrations/allegro/oauth/callback`: - `AllegroIntegrationController::oauthCallback(Request): Response` - waliduje `state` i `code`, - wymienia `code` na tokeny przez `AllegroOAuthClient::exchangeAuthorizationCode(...)`, - zapisuje tokeny przez `AllegroIntegrationRepository::saveTokens(...)`. - `AllegroTokenManager`: - Shared Allegro OAuth token resolver. Checks expiry and refreshes via `AllegroOAuthClient` when needed. Injected into all Allegro service classes (`AllegroOrderImportService`, `AllegroOrdersSyncService`, `AllegroStatusDiscoveryService`, `AllegroShipmentService`). - `AllegroOrderImportService`: - pilnuje waznosci tokenu przez `AllegroTokenManager::resolveToken()` (refresh przed requestem lub retry po `401`), - pobiera zamowienie `GET /order/checkout-forms/{id}` przez `AllegroApiClient`, - pobiera przesylki zamowienia `GET /order/checkout-forms/{id}/shipments` przez `AllegroApiClient::getCheckoutFormShipments(...)`, - dla pozycji bez obrazka w checkout-form pobiera szczegoly oferty `GET /sale/product-offers/{offerId}` i uzupelnia `order_items.media_url`, - mapuje forme wysylki Allegro (`delivery.method.name`/`delivery.method.id`) do pol zamowienia (`external_carrier_id`, `external_carrier_account_id`), - dla dostawy do punktu odbioru mapuje adres `delivery.pickupPoint.address` i nazwe punktu do adresu typu `delivery`, - mapuje terminy wysylki z `delivery.time.dispatch` do `send_date_min` / `send_date_max`, - buduje diagnostyke importu miniatur (statystyki + przyczyny brakow), - mapuje status Allegro na status orderPRO na podstawie `allegro_order_status_mappings`, - mapuje payload Allegro na neutralny model tabel zamowien (z `integration_id` aktywnej instancji Allegro), - zapisuje aggregate przez `OrderImportRepository::upsertOrderAggregate(...)`. - `AllegroOrdersSyncService`: - uruchamiany z crona (`allegro_orders_import`), - korzysta z dynamicznego `integration_id` aktywnego srodowiska Allegro (zamiast stalej), - respektuje ustawienia integracji (`orders_fetch_enabled`, `orders_fetch_start_date`), - pobiera listy checkout forms (`GET /order/checkout-forms?sort=-updatedAt`) i importuje nowe/zmienione zamowienia, - utrzymuje kursor sync i status ostatniego wykonania w `integration_order_sync_state`. - `AllegroStatusSyncService`: - uruchamiany z crona (`allegro_status_sync`), - respektuje ustawienie kierunku `allegro_status_sync_direction`, - dla kierunku `allegro_to_orderpro` wykorzystuje mechanizm importu zamowien do aktualizacji statusow, - dla kierunku `orderpro_to_allegro` zwraca wynik informacyjny (tryb przygotowany pod kolejny etap). ## Log aktywnosci zamowien - Tabela `order_activity_log` rejestruje wszystkie zdarzenia dotyczace zamowienia. - Typy zdarzen: `status_change`, `payment`, `invoice`, `shipment`, `message`, `document`, `import`, `note`. - Rejestracja zdarzen: `OrdersRepository::recordActivity(...)`. - Zmiana statusu: `OrdersRepository::updateOrderStatus(...)` — aktualizuje `orders.external_status_id`, wpisuje do `order_status_history` i `order_activity_log`. - Import zamowienia: `AllegroOrderImportService::importSingleOrder(...)` — po upsert zamowienia rejestruje zdarzenie `import` w `order_activity_log` (nowy import lub re-import/aktualizacja), actor_type `import`, actor_name `Allegro`. - Widok szczegolow zamowienia (`GET /orders/{id}`) wyswietla log aktywnosci w zakladce `Historia zmian`. ## Zmiana statusu zamowienia z widoku szczegolow - `POST /orders/{id}/status`: - `OrdersController::updateStatus(Request): Response` - waliduje CSRF i wybrany status, - wywoluje `OrdersRepository::updateOrderStatus(...)` (aktualizuje `orders.external_status_id`, wpisuje do `order_status_history` i `order_activity_log`), - actor_type: `user`, actor_name: nazwa zalogowanego uzytkownika, - po zapisie redirect do `GET /orders/{id}` z flash message (sukces/blad). - Widok szczegolow zamowienia wyswietla dropdown ze wszystkimi aktywnymi statusami (pogrupowanymi wg grup statusow) obok aktualnego statusu. ## Przeplyw Ustawienia > Integracja Apaczka - `GET /settings/integrations/apaczka`: - `ApaczkaIntegrationController::index(Request): Response` - odczytuje konfiguracje przez `ApaczkaIntegrationRepository::getSettings()`, - renderuje `resources/views/settings/apaczka.php`. - `POST /settings/integrations/apaczka/save`: - `ApaczkaIntegrationController::save(Request): Response` - waliduje CSRF i klucz API, - zapisuje zaszyfrowany klucz API przez `ApaczkaIntegrationRepository::saveSettings(...)` do tabeli bazowej `integrations` (`type=apaczka`). ## Przeplyw Ustawienia > Integracja InPost - `GET /settings/integrations/inpost`: - `InpostIntegrationController::index(Request): Response` - odczytuje konfiguracje przez `InpostIntegrationRepository::getSettings()`, - renderuje `resources/views/settings/inpost.php`. - `POST /settings/integrations/inpost/save`: - `InpostIntegrationController::save(Request): Response` - waliduje CSRF, - zapisuje ustawienia przez `InpostIntegrationRepository::saveSettings(...)`: - token API w `integrations.api_key_encrypted` (`type=inpost`), - parametry specyficzne przewoznika w `inpost_integration_settings`. ## Przeplyw Ustawienia > Baza danych - `GET /settings/database`: - `SettingsController::database(Request): Response` - pobiera `Migrator::status()`, przekazuje statystyki i liste pending migracji do widoku `resources/views/settings/database.php`. - `POST /settings/database/migrate`: - `SettingsController::migrate(Request): Response` - waliduje CSRF, - uruchamia `Migrator::runPending()`, - zapisuje wynik do flash (`settings_success` / `settings_error`, `settings_migrate_logs`), - wykonuje redirect do `GET /settings/database`. ## Zmiany nawigacji - Sidebar ma teraz grupe `Ustawienia` z podlinkami: - `Uzytkownicy` (`/settings/users`) - `Baza danych` (`/settings/database`) - `UsersController::index(Request): Response` ustawia: - `activeMenu = settings` - `activeSettings = users` - Usunieto wewnetrzny pasek `settings-nav` z widokow podstron ustawien. ## Zasady aktualizacji - Przy kazdej zmianie dopisz: - nowe klasy i metody (sygnatury + odpowiedzialnosc), - zmiany przeplywu request -> controller -> repository, - kontrakty wejscia/wyjscia istotnych metod. ## Przeplyw Ustawienia > Integracje (hub) - `GET /settings/integrations`: - `IntegrationsHubController::index(Request): Response` - buduje liste instancji providerow (Allegro sandbox/production, Apaczka, InPost, shopPRO), - pokazuje tabele podsumowania i przycisk `Ustawienia` w kazdym wierszu, - przycisk `Ustawienia` prowadzi do dedykowanego ekranu providera (`/settings/integrations/allegro|apaczka|inpost|shoppro`), - renderuje `resources/views/settings/integrations.php`. ## Przeplyw Ustawienia > Integracje > shopPRO - `GET /settings/integrations/shoppro`: - `ShopproIntegrationsController::index(Request): Response` - pobiera liste instancji przez `ShopproIntegrationsRepository::listIntegrations()`, - opcjonalnie laduje wskazana instancje (`?id=`) przez `findIntegration(...)`, - renderuje `resources/views/settings/shoppro.php` z zakladkami: `Integracja`, `Statusy`, `Ustawienia`, `Formy dostawy`. - `POST /settings/integrations/shoppro/save`: - `ShopproIntegrationsController::save(Request): Response` - waliduje CSRF, nazwe, URL (`http|https`), klucz API (wymagany przy nowej konfiguracji) oraz format daty `orders_fetch_start_date` (`Y-m-d`), - zapisuje konfiguracje przez `ShopproIntegrationsRepository::saveIntegration(...)` do tabeli bazowej `integrations` (`type=shoppro`), - zapisuje interwal joba `shoppro_orders_import` (minuty) do `cron_schedules.interval_seconds`, - zapisuje kierunek synchronizacji statusow per instancja (`integrations.order_status_sync_direction`), - zapisuje interwal joba `shoppro_order_status_sync` (minuty) do `cron_schedules.interval_seconds`, - zapisuje interwal joba `shoppro_payment_status_sync` (minuty) do `cron_schedules.interval_seconds`, - zapisuje liste statusow orderPRO (per instancja) dla kontroli platnosci (`integrations.payment_sync_status_codes_json`). - `POST /settings/integrations/shoppro/test`: - `ShopproIntegrationsController::test(Request): Response` - waliduje CSRF i `integration_id`, - wykonuje test API przez `ShopproIntegrationsRepository::testConnection(...)`, - zapisuje wynik testu w `integrations.last_test_*` i `integration_test_logs`. - `POST /settings/integrations/shoppro/statuses/sync`: - `ShopproIntegrationsController::syncStatuses(Request): Response` - pobiera slownik statusow z API (`dictionaries/statuses`) przez `ShopproIntegrationsRepository::fetchOrderStatuses(...)`, - przekazuje odkryte statusy do widoku zakladki `Statusy` (flash/sesja). - `POST /settings/integrations/shoppro/statuses/save`: - `ShopproIntegrationsController::saveStatusMappings(Request): Response` - waliduje CSRF, `integration_id` i kody statusow orderPRO, - zapisuje mapowania per instancja shopPRO przez `ShopproStatusMappingRepository::replaceForIntegration(...)` do `order_status_mappings`. - `POST /settings/integrations/shoppro/delivery/save`: - `ShopproIntegrationsController::saveDeliveryMappings(Request): Response` - waliduje CSRF i `integration_id`, - zapisuje mapowania form dostawy przez `ShopproDeliveryMethodMappingRepository::saveMappings(...)` (per instancja). - `ShopproOrdersSyncService`: - uruchamiany z crona (`shoppro_orders_import`), - pobiera liste zamowien i (opcjonalnie) szczegoly zamowienia z API shopPRO, - mapuje kwoty z fallbackami (`summary`, `paid`, `transport_cost`) oraz ceny pozycji (`price_brutto`), - uzupelnia `order_items.media_url` przez pobranie `products/get` po `product_id`, gdy zamowienie nie zawiera obrazu. - wykrywa zadanie faktury takze po obecnosci danych firmowych (`firm_*`) i ustawia `orders.is_invoice`, - mapuje adres faktury do `order_addresses.address_type=invoice` (firma/NIP/adres) na podstawie pol `invoice`/`billing*`/`firm_*`, - mapuje punkty odbioru (`inpost_paczkomat` / `orlen_point`) do adresu `delivery` (`parcel_external_id`, `parcel_name`, ulica/kod/miasto), - uzupelnia `delivery` o telefon/e-mail klienta i etykiete metody dostawy z kosztem (`transport_cost`). - `ShopproStatusSyncService`: - uruchamiany z crona (`shoppro_order_status_sync`), - filtruje aktywne instancje `shopPRO` po kierunku synchronizacji statusow (`shoppro_to_orderpro`), - dla wspieranego kierunku wykorzystuje `ShopproOrdersSyncService` do odswiezenia statusow/importu danych, - dla kierunku `orderpro_to_shoppro` pomija instancje i zwraca wynik informacyjny (tryb przygotowany pod kolejny etap). - `ShopproPaymentStatusSyncService`: - uruchamiany z crona (`shoppro_payment_status_sync`), - pobiera zamowienia shopPRO nieoznaczone jako oplacone (`orders.payment_status != 2`) i nie-finalne, - dla kazdego zamowienia odpytuje API `orders/get|details` i odczytuje flage `paid`, - aktualizuje `orders.payment_status`, `orders.total_paid` i `order_payments`, - zapisuje log `payment` do `order_activity_log`, - respektuje liste statusow z `integrations.payment_sync_status_codes_json` (gdy pusta: fallback na pomijanie statusow finalnych). - Zakladka `Formy dostawy` (shopPRO): - laduje formy dostawy wykryte w zamowieniach danej instancji (`orders.source=shoppro` + `orders.integration_id`), - laduje uslugi dostawy z Allegro API (`delivery-services`) z fallbackiem na odswiezenie tokenu OAuth, - zapisuje mapowanie: forma dostawy shopPRO -> usluga Allegro/InPost WZA.