From 8fdaa0ba1def20ce3b0b31d91026fb5af7c5a2f4 Mon Sep 17 00:00:00 2001 From: Kevin Adametz Date: Fri, 22 Aug 2025 18:18:26 +0200 Subject: [PATCH] DHL Modul v0.5 Shipping Label ok --- .env | 64 +- .phpstorm.meta.php | 1325 ++++--- _ide_helper.php | 1032 +---- app/Console/Commands/CheckPaymentsAccount.php | 213 ++ app/Console/Kernel.php | 17 +- .../Controllers/DhlShipmentController.php | 662 ++++ app/Http/Controllers/FileController.php | 15 +- app/Http/Controllers/ModalController.php | 55 + app/Http/Controllers/Portal/InController.php | 14 +- app/Http/Controllers/SettingController.php | 89 +- app/Http/Controllers/UserShopController.php | 66 +- app/Http/Middleware/DomainResolver.php | 30 +- app/Jobs/CancelShipmentJob.php | 144 + app/Jobs/CreateReturnLabelJob.php | 148 + app/Jobs/CreateShipmentJob.php | 179 + app/Jobs/TrackShipmentJob.php | 194 + app/Mail/MailAutoReleaseAccount.php | 2 +- app/Mail/MailInfo.php | 6 +- app/Mail/MailReleaseAccount.php | 2 +- app/Mail/MailReleaseDocument.php | 2 +- app/Models/DhlShipment.php | 407 ++ app/Models/ShoppingOrder.php | 46 + app/Providers/AppServiceProvider.php | 7 +- app/Providers/AuthServiceProvider.php | 0 app/Providers/BroadcastServiceProvider.php | 0 app/Providers/DomainServiceProvider.php | 10 +- app/Providers/EventServiceProvider.php | 0 app/Providers/RouteServiceProvider.php | 0 app/Providers/YardServiceProvider.php | 0 app/Services/DhlApiService.php | 1312 +++++++ app/Services/DhlDataHelper.php | 89 + app/Services/DhlModalService.php | 439 +++ app/Services/DhlShipmentService.php | 147 + app/Services/DomainService.php | 4 +- app/Services/Payment.php | 8 + app/Services/Util.php | 309 +- app/User.php | 2 + app/helpers.php | 58 - bootstrap/cache/packages.php | 11 - bootstrap/cache/services.php | 93 +- composer.json | 21 +- composer.lock | 845 +++-- config/app.php | 14 +- config/dhl.php | 108 + config/profanity.php | 11 + config/queue.php | 2 +- ...8_19_155158_create_dhl_shipments_table.php | 109 + ...ng_code_to_dhl_package_shipments_table.php | 28 + dev/dhl-modul/AKTUALISIERUNG-PAKET-ANSATZ.md | 151 + dev/dhl-modul/OPTIMIERUNGEN.md | 33 + dev/dhl-modul/PAKET-INSTALLATION.md | 257 ++ dev/dhl-modul/PLAN-OPTIMIERT.md | 195 + dev/dhl-modul/README.md | 120 + dev/dhl-modul/SCHRITT-3-COMPLETED.md | 136 + dev/dhl-modul/parcel-de-shipping-v2_2.yaml | 2273 +++++++++++ packages/acme-laravel-dhl/README.md | 199 + packages/acme-laravel-dhl/composer.json | 30 + packages/acme-laravel-dhl/config/dhl.php | 13 + ...1_01_000000_create_dhl_shipments_table.php | 53 + ...25_01_01_000000_create_shipments_table.php | 29 + ...01_000100_create_shipment_labels_table.php | 22 + ...00200_create_dhl_tracking_events_table.php | 30 + ...01_000200_create_tracking_events_table.php | 24 + ...1_01_000300_create_return_labels_table.php | 22 + packages/acme-laravel-dhl/routes/api.php | 6 + packages/acme-laravel-dhl/src/DhlManager.php | 40 + .../src/DhlServiceProvider.php | 55 + .../src/Exceptions/DhlApiException.php | 8 + .../Exceptions/DhlAuthenticationException.php | 8 + .../src/Exceptions/DhlValidationException.php | 8 + packages/acme-laravel-dhl/src/Facades/DHL.php | 13 + .../Controllers/TrackingWebhookController.php | 43 + .../src/Jobs/CreateReturnLabelJob.php | 29 + .../src/Jobs/CreateShipmentJob.php | 29 + .../src/Jobs/SyncTrackingJob.php | 29 + .../src/Models/DhlShipment.php | 149 + .../src/Models/DhlTrackingEvent.php | 52 + .../src/Models/ReturnLabel.php | 10 + .../acme-laravel-dhl/src/Models/Shipment.php | 19 + .../src/Models/ShipmentLabel.php | 14 + .../src/Models/TrackingEvent.php | 16 + .../src/Services/ReturnsService.php | 180 + .../src/Services/ShippingService.php | 480 +++ .../src/Services/TrackingService.php | 180 + .../src/Support/DhlClient.php | 198 + .../tests/Unit/AddressParserTest.php | 114 + .../.editorconfig | 15 + .../.gitignore | 1 + .../CONTRIBUTING.md | 23 + .../LICENCE.md | 9 + .../README.md | 66 + .../composer.json | 39 + .../composer.lock | 3375 +++++++++++++++++ .../src/FormAdapter.php | 218 ++ .../src/FormFacade.php | 22 + .../src/HtmlAdapter.php | 7 + .../src/HtmlFacade.php | 18 + .../src/ServiceProvider.php | 46 + .../src/Traits/AttributesUtils.php | 35 + resources/lang/de/navigation.php | 2 + resources/lang/de/shop.php | 14 +- resources/views/admin/dhl/cockpit.blade.php | 578 +++ resources/views/admin/dhl/create.blade.php | 428 +++ .../admin/dhl/modal_create_shipment.blade.php | 498 +++ resources/views/admin/dhl/show.blade.php | 600 +++ .../views/admin/settings/index.blade.php | 214 ++ resources/views/auth/change.blade.php | 4 +- .../layouts/includes/layout-sidenav.blade.php | 6 + resources/views/status/not_found.blade.php | 2 +- resources/views/status/status_error.blade.php | 2 +- .../views/status/status_register.blade.php | 2 +- .../views/status/status_verify.blade.php | 2 +- .../user/components/user_shop_edit.blade.php | 27 +- .../components/user_shop_edit_name.blade.php | 174 + .../components/user_shop_register.blade.php | 4 +- resources/views/user/shop_edit_name.blade.php | 23 + .../web/layouts/includes/footer.blade.php | 5 +- .../user/layouts/includes/footer.blade.php | 2 +- routes/domains/crm.php | 39 +- routes/domains/main.php | 13 +- routes/domains/portal.php | 8 +- routes/shared/common.php | 50 +- 122 files changed, 17938 insertions(+), 2239 deletions(-) create mode 100644 app/Console/Commands/CheckPaymentsAccount.php create mode 100644 app/Http/Controllers/DhlShipmentController.php create mode 100644 app/Jobs/CancelShipmentJob.php create mode 100644 app/Jobs/CreateReturnLabelJob.php create mode 100644 app/Jobs/CreateShipmentJob.php create mode 100644 app/Jobs/TrackShipmentJob.php create mode 100644 app/Models/DhlShipment.php mode change 100755 => 100644 app/Providers/AppServiceProvider.php mode change 100755 => 100644 app/Providers/AuthServiceProvider.php mode change 100755 => 100644 app/Providers/BroadcastServiceProvider.php mode change 100755 => 100644 app/Providers/EventServiceProvider.php mode change 100755 => 100644 app/Providers/RouteServiceProvider.php mode change 100755 => 100644 app/Providers/YardServiceProvider.php create mode 100644 app/Services/DhlApiService.php create mode 100644 app/Services/DhlDataHelper.php create mode 100644 app/Services/DhlModalService.php create mode 100644 app/Services/DhlShipmentService.php create mode 100644 config/dhl.php create mode 100644 database/migrations/2025_08_19_155158_create_dhl_shipments_table.php create mode 100644 database/migrations/2025_08_22_172138_add_routing_code_to_dhl_package_shipments_table.php create mode 100644 dev/dhl-modul/AKTUALISIERUNG-PAKET-ANSATZ.md create mode 100644 dev/dhl-modul/OPTIMIERUNGEN.md create mode 100644 dev/dhl-modul/PAKET-INSTALLATION.md create mode 100644 dev/dhl-modul/PLAN-OPTIMIERT.md create mode 100644 dev/dhl-modul/README.md create mode 100644 dev/dhl-modul/SCHRITT-3-COMPLETED.md create mode 100644 dev/dhl-modul/parcel-de-shipping-v2_2.yaml create mode 100644 packages/acme-laravel-dhl/README.md create mode 100644 packages/acme-laravel-dhl/composer.json create mode 100644 packages/acme-laravel-dhl/config/dhl.php create mode 100644 packages/acme-laravel-dhl/database/migrations/2025_01_01_000000_create_dhl_shipments_table.php create mode 100644 packages/acme-laravel-dhl/database/migrations/2025_01_01_000000_create_shipments_table.php create mode 100644 packages/acme-laravel-dhl/database/migrations/2025_01_01_000100_create_shipment_labels_table.php create mode 100644 packages/acme-laravel-dhl/database/migrations/2025_01_01_000200_create_dhl_tracking_events_table.php create mode 100644 packages/acme-laravel-dhl/database/migrations/2025_01_01_000200_create_tracking_events_table.php create mode 100644 packages/acme-laravel-dhl/database/migrations/2025_01_01_000300_create_return_labels_table.php create mode 100644 packages/acme-laravel-dhl/routes/api.php create mode 100644 packages/acme-laravel-dhl/src/DhlManager.php create mode 100644 packages/acme-laravel-dhl/src/DhlServiceProvider.php create mode 100644 packages/acme-laravel-dhl/src/Exceptions/DhlApiException.php create mode 100644 packages/acme-laravel-dhl/src/Exceptions/DhlAuthenticationException.php create mode 100644 packages/acme-laravel-dhl/src/Exceptions/DhlValidationException.php create mode 100644 packages/acme-laravel-dhl/src/Facades/DHL.php create mode 100644 packages/acme-laravel-dhl/src/Http/Controllers/TrackingWebhookController.php create mode 100644 packages/acme-laravel-dhl/src/Jobs/CreateReturnLabelJob.php create mode 100644 packages/acme-laravel-dhl/src/Jobs/CreateShipmentJob.php create mode 100644 packages/acme-laravel-dhl/src/Jobs/SyncTrackingJob.php create mode 100644 packages/acme-laravel-dhl/src/Models/DhlShipment.php create mode 100644 packages/acme-laravel-dhl/src/Models/DhlTrackingEvent.php create mode 100644 packages/acme-laravel-dhl/src/Models/ReturnLabel.php create mode 100644 packages/acme-laravel-dhl/src/Models/Shipment.php create mode 100644 packages/acme-laravel-dhl/src/Models/ShipmentLabel.php create mode 100644 packages/acme-laravel-dhl/src/Models/TrackingEvent.php create mode 100644 packages/acme-laravel-dhl/src/Services/ReturnsService.php create mode 100644 packages/acme-laravel-dhl/src/Services/ShippingService.php create mode 100644 packages/acme-laravel-dhl/src/Services/TrackingService.php create mode 100644 packages/acme-laravel-dhl/src/Support/DhlClient.php create mode 100644 packages/acme-laravel-dhl/tests/Unit/AddressParserTest.php create mode 100644 packages/laravel-collective-spatie-html-parser/.editorconfig create mode 100644 packages/laravel-collective-spatie-html-parser/.gitignore create mode 100644 packages/laravel-collective-spatie-html-parser/CONTRIBUTING.md create mode 100644 packages/laravel-collective-spatie-html-parser/LICENCE.md create mode 100644 packages/laravel-collective-spatie-html-parser/README.md create mode 100644 packages/laravel-collective-spatie-html-parser/composer.json create mode 100644 packages/laravel-collective-spatie-html-parser/composer.lock create mode 100644 packages/laravel-collective-spatie-html-parser/src/FormAdapter.php create mode 100644 packages/laravel-collective-spatie-html-parser/src/FormFacade.php create mode 100644 packages/laravel-collective-spatie-html-parser/src/HtmlAdapter.php create mode 100644 packages/laravel-collective-spatie-html-parser/src/HtmlFacade.php create mode 100644 packages/laravel-collective-spatie-html-parser/src/ServiceProvider.php create mode 100644 packages/laravel-collective-spatie-html-parser/src/Traits/AttributesUtils.php create mode 100644 resources/views/admin/dhl/cockpit.blade.php create mode 100644 resources/views/admin/dhl/create.blade.php create mode 100644 resources/views/admin/dhl/modal_create_shipment.blade.php create mode 100644 resources/views/admin/dhl/show.blade.php create mode 100644 resources/views/user/components/user_shop_edit_name.blade.php create mode 100644 resources/views/user/shop_edit_name.blade.php diff --git a/.env b/.env index 5355081..4e3b0e6 100644 --- a/.env +++ b/.env @@ -3,6 +3,7 @@ APP_ENV=local APP_DEBUG=true APP_KEY=base64:HrWQ9AV3Zt2TU0iq1OeUUpTUaXwNUdh8xHmx7RXTif4= APP_URL=https://mivita.test/ +APP_URL_CRM=https://my.mivita.test APP_DOMAIN=mivita APP_TLD_CARE=.test APP_TLD_SHOP=.lshop @@ -57,7 +58,7 @@ BROADCAST_DRIVER=log CACHE_DRIVER=file SESSION_DRIVER=file SESSION_LIFETIME=120 -QUEUE_DRIVER=sync +QUEUE_DRIVER=database REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null @@ -85,4 +86,63 @@ MIVITA_REMIND_SEC_DAYS=14 MIVITA_ABO_BOOKING_DAYS=7 MIVITA_REMIND_LAST_DAYS=2 MIVITA_EDIT_DATA_PASS=mivita -MIVITA_ADD_NUMBER_ID=946 \ No newline at end of file +MIVITA_ADD_NUMBER_ID=946 + +# ============================================================================= +# ============================================================================= +# DHL VERSANDMODUL KONFIGURATION (acme/laravel-dhl) +# ============================================================================= + +# DHL API Zugangsdaten (konsolidiert) +DHL_BASE_URL=https://api-eu.dhl.com +DHL_API_KEY=AxGBdF8DBdIAmuhqvG0ASBRKFvyV7ypX +DHL_USERNAME=riwa-tec +DHL_PASSWORD=MivitaCare!!2025 +DHL_BILLING_NUMBER=63144073550101 + +# DHL Standard-Einstellungen +DHL_PRODUCT=V01PAK +DHL_LABEL_FORMAT=PDF +DHL_PRINT_FORMAT=910-300-710 +DHL_RETOURE_PRINT_FORMAT=910-300-700 +DHL_PROFILE=STANDARD_GRUPPENPROFIL + +# DHL Queue Configuration +# Set to true for background processing (requires queue worker) +# Set to false for immediate processing +DHL_USE_QUEUE=false + +# DHL Account Numbers (für verschiedene Produkte) +DHL_ACCOUNT_NUMBER_DEFAULT=63144073550101 +DHL_ACCOUNT_NUMBER_V01PAK=63144073550101 # DHL Paket National +DHL_ACCOUNT_NUMBER_V62WP=63144073556201 # Warenpost National +DHL_ACCOUNT_NUMBER_V53PAK=63144073555301 # DHL Paket International +DHL_ACCOUNT_NUMBER_V07PAK=63144073550701 # DHL Retoure Online +#sandbox +DHL_ACCOUNT_NUMBER_DEFAULT=33333333330101 +DHL_ACCOUNT_NUMBER_V01PAK=33333333330102 # DHL Paket National +DHL_ACCOUNT_NUMBER_V62WP=33333333336601 # Warenpost National +DHL_ACCOUNT_NUMBER_V53PAK=33333333335301 # DHL Paket International +DHL_ACCOUNT_NUMBER_V07PAK=33333333330702 # DHL Retoure Online + +#V66WPI|33333333336601 +#VO1PAK 33333333330102 33333333330101 +#V53WPAK 33333333335301 +#VO7PAK 33333333330702 + +# DHL Absenderadresse +DHL_SENDER_COMPANY="mivita care gmbh" +DHL_SENDER_NAME="" +DHL_SENDER_STREET="Leinfeld" +DHL_SENDER_STREET_NUMBER=2 +DHL_SENDER_POSTAL_CODE=87755 +DHL_SENDER_CITY=Kirchhaslach +DHL_SENDER_COUNTRY=DE +DHL_SENDER_EMAIL=versand@mivita.care +DHL_SENDER_PHONE="+49 123 456789" + +# DHL Legacy/Compatibility Settings +DHL_API_TYPE=developer +DHL_API_SECRET=OyoeePEbYmY1EuOG +DHL_SANDBOX=true +DHL_TEST_MODE=true diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php index 9a7990f..6a6485a 100644 --- a/.phpstorm.meta.php +++ b/.phpstorm.meta.php @@ -63,7 +63,6 @@ namespace PHPSTORM_META { 'filesystem.disk' => \Illuminate\Filesystem\LocalFilesystemAdapter::class, 'flare.logger' => \Monolog\Logger::class, 'flash' => \Laracasts\Flash\FlashNotifier::class, - 'form' => \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter::class, 'hash' => \Illuminate\Hashing\HashManager::class, 'hash.driver' => \Illuminate\Hashing\BcryptHasher::class, 'log' => \Illuminate\Log\LogManager::class, @@ -75,7 +74,7 @@ namespace PHPSTORM_META { 'migrator' => \Illuminate\Database\Migrations\Migrator::class, 'pipeline' => \Illuminate\Pipeline\Pipeline::class, 'queue' => \Illuminate\Queue\QueueManager::class, - 'queue.connection' => \Illuminate\Queue\SyncQueue::class, + 'queue.connection' => \Illuminate\Queue\DatabaseQueue::class, 'queue.failer' => \Illuminate\Queue\Failed\DatabaseFailedJobProvider::class, 'queue.listener' => \Illuminate\Queue\Listener::class, 'queue.worker' => \Illuminate\Queue\Worker::class, @@ -144,7 +143,6 @@ namespace PHPSTORM_META { 'filesystem.disk' => \Illuminate\Filesystem\LocalFilesystemAdapter::class, 'flare.logger' => \Monolog\Logger::class, 'flash' => \Laracasts\Flash\FlashNotifier::class, - 'form' => \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter::class, 'hash' => \Illuminate\Hashing\HashManager::class, 'hash.driver' => \Illuminate\Hashing\BcryptHasher::class, 'log' => \Illuminate\Log\LogManager::class, @@ -156,7 +154,7 @@ namespace PHPSTORM_META { 'migrator' => \Illuminate\Database\Migrations\Migrator::class, 'pipeline' => \Illuminate\Pipeline\Pipeline::class, 'queue' => \Illuminate\Queue\QueueManager::class, - 'queue.connection' => \Illuminate\Queue\SyncQueue::class, + 'queue.connection' => \Illuminate\Queue\DatabaseQueue::class, 'queue.failer' => \Illuminate\Queue\Failed\DatabaseFailedJobProvider::class, 'queue.listener' => \Illuminate\Queue\Listener::class, 'queue.worker' => \Illuminate\Queue\Worker::class, @@ -225,7 +223,6 @@ namespace PHPSTORM_META { 'filesystem.disk' => \Illuminate\Filesystem\LocalFilesystemAdapter::class, 'flare.logger' => \Monolog\Logger::class, 'flash' => \Laracasts\Flash\FlashNotifier::class, - 'form' => \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter::class, 'hash' => \Illuminate\Hashing\HashManager::class, 'hash.driver' => \Illuminate\Hashing\BcryptHasher::class, 'log' => \Illuminate\Log\LogManager::class, @@ -237,7 +234,7 @@ namespace PHPSTORM_META { 'migrator' => \Illuminate\Database\Migrations\Migrator::class, 'pipeline' => \Illuminate\Pipeline\Pipeline::class, 'queue' => \Illuminate\Queue\QueueManager::class, - 'queue.connection' => \Illuminate\Queue\SyncQueue::class, + 'queue.connection' => \Illuminate\Queue\DatabaseQueue::class, 'queue.failer' => \Illuminate\Queue\Failed\DatabaseFailedJobProvider::class, 'queue.listener' => \Illuminate\Queue\Listener::class, 'queue.worker' => \Illuminate\Queue\Worker::class, @@ -306,7 +303,6 @@ namespace PHPSTORM_META { 'filesystem.disk' => \Illuminate\Filesystem\LocalFilesystemAdapter::class, 'flare.logger' => \Monolog\Logger::class, 'flash' => \Laracasts\Flash\FlashNotifier::class, - 'form' => \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter::class, 'hash' => \Illuminate\Hashing\HashManager::class, 'hash.driver' => \Illuminate\Hashing\BcryptHasher::class, 'log' => \Illuminate\Log\LogManager::class, @@ -318,7 +314,7 @@ namespace PHPSTORM_META { 'migrator' => \Illuminate\Database\Migrations\Migrator::class, 'pipeline' => \Illuminate\Pipeline\Pipeline::class, 'queue' => \Illuminate\Queue\QueueManager::class, - 'queue.connection' => \Illuminate\Queue\SyncQueue::class, + 'queue.connection' => \Illuminate\Queue\DatabaseQueue::class, 'queue.failer' => \Illuminate\Queue\Failed\DatabaseFailedJobProvider::class, 'queue.listener' => \Illuminate\Queue\Listener::class, 'queue.worker' => \Illuminate\Queue\Worker::class, @@ -387,7 +383,6 @@ namespace PHPSTORM_META { 'filesystem.disk' => \Illuminate\Filesystem\LocalFilesystemAdapter::class, 'flare.logger' => \Monolog\Logger::class, 'flash' => \Laracasts\Flash\FlashNotifier::class, - 'form' => \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter::class, 'hash' => \Illuminate\Hashing\HashManager::class, 'hash.driver' => \Illuminate\Hashing\BcryptHasher::class, 'log' => \Illuminate\Log\LogManager::class, @@ -399,7 +394,7 @@ namespace PHPSTORM_META { 'migrator' => \Illuminate\Database\Migrations\Migrator::class, 'pipeline' => \Illuminate\Pipeline\Pipeline::class, 'queue' => \Illuminate\Queue\QueueManager::class, - 'queue.connection' => \Illuminate\Queue\SyncQueue::class, + 'queue.connection' => \Illuminate\Queue\DatabaseQueue::class, 'queue.failer' => \Illuminate\Queue\Failed\DatabaseFailedJobProvider::class, 'queue.listener' => \Illuminate\Queue\Listener::class, 'queue.worker' => \Illuminate\Queue\Worker::class, @@ -468,7 +463,6 @@ namespace PHPSTORM_META { 'filesystem.disk' => \Illuminate\Filesystem\LocalFilesystemAdapter::class, 'flare.logger' => \Monolog\Logger::class, 'flash' => \Laracasts\Flash\FlashNotifier::class, - 'form' => \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter::class, 'hash' => \Illuminate\Hashing\HashManager::class, 'hash.driver' => \Illuminate\Hashing\BcryptHasher::class, 'log' => \Illuminate\Log\LogManager::class, @@ -480,7 +474,7 @@ namespace PHPSTORM_META { 'migrator' => \Illuminate\Database\Migrations\Migrator::class, 'pipeline' => \Illuminate\Pipeline\Pipeline::class, 'queue' => \Illuminate\Queue\QueueManager::class, - 'queue.connection' => \Illuminate\Queue\SyncQueue::class, + 'queue.connection' => \Illuminate\Queue\DatabaseQueue::class, 'queue.failer' => \Illuminate\Queue\Failed\DatabaseFailedJobProvider::class, 'queue.listener' => \Illuminate\Queue\Listener::class, 'queue.worker' => \Illuminate\Queue\Worker::class, @@ -549,7 +543,6 @@ namespace PHPSTORM_META { 'filesystem.disk' => \Illuminate\Filesystem\LocalFilesystemAdapter::class, 'flare.logger' => \Monolog\Logger::class, 'flash' => \Laracasts\Flash\FlashNotifier::class, - 'form' => \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter::class, 'hash' => \Illuminate\Hashing\HashManager::class, 'hash.driver' => \Illuminate\Hashing\BcryptHasher::class, 'log' => \Illuminate\Log\LogManager::class, @@ -561,7 +554,7 @@ namespace PHPSTORM_META { 'migrator' => \Illuminate\Database\Migrations\Migrator::class, 'pipeline' => \Illuminate\Pipeline\Pipeline::class, 'queue' => \Illuminate\Queue\QueueManager::class, - 'queue.connection' => \Illuminate\Queue\SyncQueue::class, + 'queue.connection' => \Illuminate\Queue\DatabaseQueue::class, 'queue.failer' => \Illuminate\Queue\Failed\DatabaseFailedJobProvider::class, 'queue.listener' => \Illuminate\Queue\Listener::class, 'queue.worker' => \Illuminate\Queue\Worker::class, @@ -630,7 +623,6 @@ namespace PHPSTORM_META { 'filesystem.disk' => \Illuminate\Filesystem\LocalFilesystemAdapter::class, 'flare.logger' => \Monolog\Logger::class, 'flash' => \Laracasts\Flash\FlashNotifier::class, - 'form' => \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter::class, 'hash' => \Illuminate\Hashing\HashManager::class, 'hash.driver' => \Illuminate\Hashing\BcryptHasher::class, 'log' => \Illuminate\Log\LogManager::class, @@ -642,7 +634,7 @@ namespace PHPSTORM_META { 'migrator' => \Illuminate\Database\Migrations\Migrator::class, 'pipeline' => \Illuminate\Pipeline\Pipeline::class, 'queue' => \Illuminate\Queue\QueueManager::class, - 'queue.connection' => \Illuminate\Queue\SyncQueue::class, + 'queue.connection' => \Illuminate\Queue\DatabaseQueue::class, 'queue.failer' => \Illuminate\Queue\Failed\DatabaseFailedJobProvider::class, 'queue.listener' => \Illuminate\Queue\Listener::class, 'queue.worker' => \Illuminate\Queue\Worker::class, @@ -711,7 +703,6 @@ namespace PHPSTORM_META { 'filesystem.disk' => \Illuminate\Filesystem\LocalFilesystemAdapter::class, 'flare.logger' => \Monolog\Logger::class, 'flash' => \Laracasts\Flash\FlashNotifier::class, - 'form' => \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter::class, 'hash' => \Illuminate\Hashing\HashManager::class, 'hash.driver' => \Illuminate\Hashing\BcryptHasher::class, 'log' => \Illuminate\Log\LogManager::class, @@ -723,7 +714,7 @@ namespace PHPSTORM_META { 'migrator' => \Illuminate\Database\Migrations\Migrator::class, 'pipeline' => \Illuminate\Pipeline\Pipeline::class, 'queue' => \Illuminate\Queue\QueueManager::class, - 'queue.connection' => \Illuminate\Queue\SyncQueue::class, + 'queue.connection' => \Illuminate\Queue\DatabaseQueue::class, 'queue.failer' => \Illuminate\Queue\Failed\DatabaseFailedJobProvider::class, 'queue.listener' => \Illuminate\Queue\Listener::class, 'queue.worker' => \Illuminate\Queue\Worker::class, @@ -792,7 +783,6 @@ namespace PHPSTORM_META { 'filesystem.disk' => \Illuminate\Filesystem\LocalFilesystemAdapter::class, 'flare.logger' => \Monolog\Logger::class, 'flash' => \Laracasts\Flash\FlashNotifier::class, - 'form' => \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter::class, 'hash' => \Illuminate\Hashing\HashManager::class, 'hash.driver' => \Illuminate\Hashing\BcryptHasher::class, 'log' => \Illuminate\Log\LogManager::class, @@ -804,7 +794,7 @@ namespace PHPSTORM_META { 'migrator' => \Illuminate\Database\Migrations\Migrator::class, 'pipeline' => \Illuminate\Pipeline\Pipeline::class, 'queue' => \Illuminate\Queue\QueueManager::class, - 'queue.connection' => \Illuminate\Queue\SyncQueue::class, + 'queue.connection' => \Illuminate\Queue\DatabaseQueue::class, 'queue.failer' => \Illuminate\Queue\Failed\DatabaseFailedJobProvider::class, 'queue.listener' => \Illuminate\Queue\Listener::class, 'queue.worker' => \Illuminate\Queue\Worker::class, @@ -873,7 +863,6 @@ namespace PHPSTORM_META { 'filesystem.disk' => \Illuminate\Filesystem\LocalFilesystemAdapter::class, 'flare.logger' => \Monolog\Logger::class, 'flash' => \Laracasts\Flash\FlashNotifier::class, - 'form' => \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter::class, 'hash' => \Illuminate\Hashing\HashManager::class, 'hash.driver' => \Illuminate\Hashing\BcryptHasher::class, 'log' => \Illuminate\Log\LogManager::class, @@ -885,7 +874,7 @@ namespace PHPSTORM_META { 'migrator' => \Illuminate\Database\Migrations\Migrator::class, 'pipeline' => \Illuminate\Pipeline\Pipeline::class, 'queue' => \Illuminate\Queue\QueueManager::class, - 'queue.connection' => \Illuminate\Queue\SyncQueue::class, + 'queue.connection' => \Illuminate\Queue\DatabaseQueue::class, 'queue.failer' => \Illuminate\Queue\Failed\DatabaseFailedJobProvider::class, 'queue.listener' => \Illuminate\Queue\Listener::class, 'queue.worker' => \Illuminate\Queue\Worker::class, @@ -972,7 +961,6 @@ namespace PHPSTORM_META { 'app.aliases.URL' => 'string', 'app.aliases.Validator' => 'string', 'app.aliases.View' => 'string', - 'app.aliases.Image' => 'string', 'app.aliases.Carbon' => 'string', 'app.aliases.Date' => 'string', 'app.aliases.HTMLHelper' => 'string', @@ -980,7 +968,9 @@ namespace PHPSTORM_META { 'app.aliases.Excel' => 'string', 'app.aliases.DataTables' => 'string', 'app.aliases.Yard' => 'string', + 'app.aliases.DHL' => 'string', 'app.mode' => 'string', + 'app.url_crm' => 'string', 'app.domain' => 'string', 'app.tld_care' => 'string', 'app.tld_shop' => 'string', @@ -1074,6 +1064,14 @@ namespace PHPSTORM_META { 'cart.format.decimals' => 'integer', 'cart.format.decimal_point' => 'string', 'cart.format.thousand_seperator' => 'string', + 'cors.paths' => 'array', + 'cors.allowed_methods' => 'array', + 'cors.allowed_origins' => 'array', + 'cors.allowed_origins_patterns' => 'array', + 'cors.allowed_headers' => 'array', + 'cors.exposed_headers' => 'array', + 'cors.max_age' => 'integer', + 'cors.supports_credentials' => 'boolean', 'database.default' => 'string', 'database.connections.sqlite.driver' => 'string', 'database.connections.sqlite.database' => 'string', @@ -1149,6 +1147,116 @@ namespace PHPSTORM_META { 'datatables.json.header' => 'array', 'datatables.json.options' => 'integer', 'datatables.callback' => 'array', + 'dhl.base_url' => 'string', + 'dhl.api_key' => 'string', + 'dhl.username' => 'NULL', + 'dhl.password' => 'NULL', + 'dhl.billing_number' => 'NULL', + 'dhl.default_product' => 'string', + 'dhl.label_format' => 'string', + 'dhl.profile' => 'string', + 'dhl.webhook.enabled' => 'boolean', + 'dhl.webhook.secret' => 'NULL', + 'dhl.webhook.route' => 'string', + 'dhl.use_queue' => 'boolean', + 'dhl.api.api_key' => 'string', + 'dhl.api.api_secret' => 'string', + 'dhl.api.username' => 'string', + 'dhl.api.password' => 'string', + 'dhl.api.account_number_default' => 'string', + 'dhl.api.product_accounts.V01PAK' => 'string', + 'dhl.api.product_accounts.V62WP' => 'string', + 'dhl.api.product_accounts.V53PAK' => 'string', + 'dhl.api.product_accounts.V07PAK' => 'string', + 'dhl.api.sandbox' => 'boolean', + 'dhl.api.test_mode' => 'boolean', + 'dhl.api.api_type' => 'string', + 'dhl.api.endpoints.developer.sandbox' => 'string', + 'dhl.api.endpoints.developer.production' => 'string', + 'dhl.api.endpoints.business_customer.sandbox' => 'string', + 'dhl.api.endpoints.business_customer.production' => 'string', + 'dhl.sender.company' => 'string', + 'dhl.sender.name' => 'string', + 'dhl.sender.street' => 'string', + 'dhl.sender.street_number' => 'string', + 'dhl.sender.postal_code' => 'string', + 'dhl.sender.city' => 'string', + 'dhl.sender.state' => 'string', + 'dhl.sender.country' => 'string', + 'dhl.sender.email' => 'string', + 'dhl.sender.phone' => 'string', + 'dhl.defaults.product' => 'string', + 'dhl.defaults.product_international' => 'string', + 'dhl.defaults.weight_unit' => 'string', + 'dhl.defaults.dimension_unit' => 'string', + 'dhl.defaults.currency' => 'string', + 'dhl.defaults.dimensions.length' => 'integer', + 'dhl.defaults.dimensions.width' => 'integer', + 'dhl.defaults.dimensions.height' => 'integer', + 'dhl.defaults.weight' => 'double', + 'dhl.defaults.services.premium' => 'boolean', + 'dhl.defaults.services.endorsement' => 'string', + 'dhl.defaults.services.visual_check_of_age' => 'boolean', + 'dhl.defaults.services.named_person_only' => 'boolean', + 'dhl.defaults.services.return_receipt' => 'boolean', + 'dhl.defaults.services.bulky_goods' => 'boolean', + 'dhl.labels.format' => 'string', + 'dhl.labels.size' => 'string', + 'dhl.labels.storage_path' => 'string', + 'dhl.labels.public_path' => 'string', + 'dhl.labels.combine_on_page' => 'boolean', + 'dhl.returns.enabled' => 'boolean', + 'dhl.returns.product' => 'string', + 'dhl.returns.receiver_id' => 'string', + 'dhl.tracking.enabled' => 'boolean', + 'dhl.tracking.username' => 'NULL', + 'dhl.tracking.password' => 'NULL', + 'dhl.tracking.auto_update' => 'boolean', + 'dhl.tracking.update_interval' => 'string', + 'dhl.tracking.max_retries' => 'integer', + 'dhl.logging.enabled' => 'boolean', + 'dhl.logging.level' => 'string', + 'dhl.logging.channel' => 'string', + 'dhl.logging.log_requests' => 'boolean', + 'dhl.logging.log_responses' => 'boolean', + 'dhl.queue.connection' => 'string', + 'dhl.queue.queue' => 'string', + 'dhl.queue.timeout' => 'string', + 'dhl.queue.retry_after' => 'integer', + 'dhl.queue.max_tries' => 'string', + 'dhl.error_handling.throw_exceptions' => 'boolean', + 'dhl.error_handling.notification_email' => 'string', + 'dhl.error_handling.retry_failed_jobs' => 'boolean', + 'dhl.cache.enabled' => 'boolean', + 'dhl.cache.ttl' => 'string', + 'dhl.cache.prefix' => 'string', + 'dhl.development.fake_api_calls' => 'boolean', + 'dhl.development.mock_responses' => 'boolean', + 'dhl.development.debug_mode' => 'boolean', + 'dhl.supported_countries.domestic' => 'array', + 'dhl.supported_countries.eu' => 'array', + 'dhl.supported_countries.international' => 'array', + 'dhl.status_mapping.pre-transit' => 'string', + 'dhl.status_mapping.transit' => 'string', + 'dhl.status_mapping.delivered' => 'string', + 'dhl.status_mapping.returned' => 'string', + 'dhl.status_mapping.failure' => 'string', + 'dhl.status_mapping.unknown' => 'string', + 'domains.protocol' => 'string', + 'domains.domains.main.host' => 'string', + 'domains.domains.main.type' => 'string', + 'domains.domains.shop.host' => 'string', + 'domains.domains.shop.type' => 'string', + 'domains.domains.shop.default_user_shop' => 'string', + 'domains.domains.crm.host' => 'string', + 'domains.domains.crm.type' => 'string', + 'domains.domains.portal.host' => 'string', + 'domains.domains.portal.type' => 'string', + 'domains.domains.checkout.host' => 'string', + 'domains.domains.checkout.type' => 'string', + 'domains.domains.user-shop.host' => 'string', + 'domains.domains.user-shop.type' => 'string', + 'domains.reserved_subdomains' => 'array', 'filesystems.default' => 'string', 'filesystems.disks.local.driver' => 'string', 'filesystems.disks.local.root' => 'string', @@ -1461,14 +1569,6 @@ namespace PHPSTORM_META { 'view.paths' => 'array', 'view.compiled' => 'string', 'concurrency.default' => 'string', - 'cors.paths' => 'array', - 'cors.allowed_methods' => 'array', - 'cors.allowed_origins' => 'array', - 'cors.allowed_origins_patterns' => 'array', - 'cors.allowed_headers' => 'array', - 'cors.exposed_headers' => 'array', - 'cors.max_age' => 'integer', - 'cors.supports_credentials' => 'boolean', 'debugbar.enabled' => 'NULL', 'debugbar.hide_empty_tabs' => 'boolean', 'debugbar.except' => 'array', @@ -1727,7 +1827,6 @@ namespace PHPSTORM_META { 'app.aliases.URL' => 'string', 'app.aliases.Validator' => 'string', 'app.aliases.View' => 'string', - 'app.aliases.Image' => 'string', 'app.aliases.Carbon' => 'string', 'app.aliases.Date' => 'string', 'app.aliases.HTMLHelper' => 'string', @@ -1735,7 +1834,9 @@ namespace PHPSTORM_META { 'app.aliases.Excel' => 'string', 'app.aliases.DataTables' => 'string', 'app.aliases.Yard' => 'string', + 'app.aliases.DHL' => 'string', 'app.mode' => 'string', + 'app.url_crm' => 'string', 'app.domain' => 'string', 'app.tld_care' => 'string', 'app.tld_shop' => 'string', @@ -1829,6 +1930,14 @@ namespace PHPSTORM_META { 'cart.format.decimals' => 'integer', 'cart.format.decimal_point' => 'string', 'cart.format.thousand_seperator' => 'string', + 'cors.paths' => 'array', + 'cors.allowed_methods' => 'array', + 'cors.allowed_origins' => 'array', + 'cors.allowed_origins_patterns' => 'array', + 'cors.allowed_headers' => 'array', + 'cors.exposed_headers' => 'array', + 'cors.max_age' => 'integer', + 'cors.supports_credentials' => 'boolean', 'database.default' => 'string', 'database.connections.sqlite.driver' => 'string', 'database.connections.sqlite.database' => 'string', @@ -1904,6 +2013,116 @@ namespace PHPSTORM_META { 'datatables.json.header' => 'array', 'datatables.json.options' => 'integer', 'datatables.callback' => 'array', + 'dhl.base_url' => 'string', + 'dhl.api_key' => 'string', + 'dhl.username' => 'NULL', + 'dhl.password' => 'NULL', + 'dhl.billing_number' => 'NULL', + 'dhl.default_product' => 'string', + 'dhl.label_format' => 'string', + 'dhl.profile' => 'string', + 'dhl.webhook.enabled' => 'boolean', + 'dhl.webhook.secret' => 'NULL', + 'dhl.webhook.route' => 'string', + 'dhl.use_queue' => 'boolean', + 'dhl.api.api_key' => 'string', + 'dhl.api.api_secret' => 'string', + 'dhl.api.username' => 'string', + 'dhl.api.password' => 'string', + 'dhl.api.account_number_default' => 'string', + 'dhl.api.product_accounts.V01PAK' => 'string', + 'dhl.api.product_accounts.V62WP' => 'string', + 'dhl.api.product_accounts.V53PAK' => 'string', + 'dhl.api.product_accounts.V07PAK' => 'string', + 'dhl.api.sandbox' => 'boolean', + 'dhl.api.test_mode' => 'boolean', + 'dhl.api.api_type' => 'string', + 'dhl.api.endpoints.developer.sandbox' => 'string', + 'dhl.api.endpoints.developer.production' => 'string', + 'dhl.api.endpoints.business_customer.sandbox' => 'string', + 'dhl.api.endpoints.business_customer.production' => 'string', + 'dhl.sender.company' => 'string', + 'dhl.sender.name' => 'string', + 'dhl.sender.street' => 'string', + 'dhl.sender.street_number' => 'string', + 'dhl.sender.postal_code' => 'string', + 'dhl.sender.city' => 'string', + 'dhl.sender.state' => 'string', + 'dhl.sender.country' => 'string', + 'dhl.sender.email' => 'string', + 'dhl.sender.phone' => 'string', + 'dhl.defaults.product' => 'string', + 'dhl.defaults.product_international' => 'string', + 'dhl.defaults.weight_unit' => 'string', + 'dhl.defaults.dimension_unit' => 'string', + 'dhl.defaults.currency' => 'string', + 'dhl.defaults.dimensions.length' => 'integer', + 'dhl.defaults.dimensions.width' => 'integer', + 'dhl.defaults.dimensions.height' => 'integer', + 'dhl.defaults.weight' => 'double', + 'dhl.defaults.services.premium' => 'boolean', + 'dhl.defaults.services.endorsement' => 'string', + 'dhl.defaults.services.visual_check_of_age' => 'boolean', + 'dhl.defaults.services.named_person_only' => 'boolean', + 'dhl.defaults.services.return_receipt' => 'boolean', + 'dhl.defaults.services.bulky_goods' => 'boolean', + 'dhl.labels.format' => 'string', + 'dhl.labels.size' => 'string', + 'dhl.labels.storage_path' => 'string', + 'dhl.labels.public_path' => 'string', + 'dhl.labels.combine_on_page' => 'boolean', + 'dhl.returns.enabled' => 'boolean', + 'dhl.returns.product' => 'string', + 'dhl.returns.receiver_id' => 'string', + 'dhl.tracking.enabled' => 'boolean', + 'dhl.tracking.username' => 'NULL', + 'dhl.tracking.password' => 'NULL', + 'dhl.tracking.auto_update' => 'boolean', + 'dhl.tracking.update_interval' => 'string', + 'dhl.tracking.max_retries' => 'integer', + 'dhl.logging.enabled' => 'boolean', + 'dhl.logging.level' => 'string', + 'dhl.logging.channel' => 'string', + 'dhl.logging.log_requests' => 'boolean', + 'dhl.logging.log_responses' => 'boolean', + 'dhl.queue.connection' => 'string', + 'dhl.queue.queue' => 'string', + 'dhl.queue.timeout' => 'string', + 'dhl.queue.retry_after' => 'integer', + 'dhl.queue.max_tries' => 'string', + 'dhl.error_handling.throw_exceptions' => 'boolean', + 'dhl.error_handling.notification_email' => 'string', + 'dhl.error_handling.retry_failed_jobs' => 'boolean', + 'dhl.cache.enabled' => 'boolean', + 'dhl.cache.ttl' => 'string', + 'dhl.cache.prefix' => 'string', + 'dhl.development.fake_api_calls' => 'boolean', + 'dhl.development.mock_responses' => 'boolean', + 'dhl.development.debug_mode' => 'boolean', + 'dhl.supported_countries.domestic' => 'array', + 'dhl.supported_countries.eu' => 'array', + 'dhl.supported_countries.international' => 'array', + 'dhl.status_mapping.pre-transit' => 'string', + 'dhl.status_mapping.transit' => 'string', + 'dhl.status_mapping.delivered' => 'string', + 'dhl.status_mapping.returned' => 'string', + 'dhl.status_mapping.failure' => 'string', + 'dhl.status_mapping.unknown' => 'string', + 'domains.protocol' => 'string', + 'domains.domains.main.host' => 'string', + 'domains.domains.main.type' => 'string', + 'domains.domains.shop.host' => 'string', + 'domains.domains.shop.type' => 'string', + 'domains.domains.shop.default_user_shop' => 'string', + 'domains.domains.crm.host' => 'string', + 'domains.domains.crm.type' => 'string', + 'domains.domains.portal.host' => 'string', + 'domains.domains.portal.type' => 'string', + 'domains.domains.checkout.host' => 'string', + 'domains.domains.checkout.type' => 'string', + 'domains.domains.user-shop.host' => 'string', + 'domains.domains.user-shop.type' => 'string', + 'domains.reserved_subdomains' => 'array', 'filesystems.default' => 'string', 'filesystems.disks.local.driver' => 'string', 'filesystems.disks.local.root' => 'string', @@ -2216,14 +2435,6 @@ namespace PHPSTORM_META { 'view.paths' => 'array', 'view.compiled' => 'string', 'concurrency.default' => 'string', - 'cors.paths' => 'array', - 'cors.allowed_methods' => 'array', - 'cors.allowed_origins' => 'array', - 'cors.allowed_origins_patterns' => 'array', - 'cors.allowed_headers' => 'array', - 'cors.exposed_headers' => 'array', - 'cors.max_age' => 'integer', - 'cors.supports_credentials' => 'boolean', 'debugbar.enabled' => 'NULL', 'debugbar.hide_empty_tabs' => 'boolean', 'debugbar.except' => 'array', @@ -2482,7 +2693,6 @@ namespace PHPSTORM_META { 'app.aliases.URL' => 'string', 'app.aliases.Validator' => 'string', 'app.aliases.View' => 'string', - 'app.aliases.Image' => 'string', 'app.aliases.Carbon' => 'string', 'app.aliases.Date' => 'string', 'app.aliases.HTMLHelper' => 'string', @@ -2490,7 +2700,9 @@ namespace PHPSTORM_META { 'app.aliases.Excel' => 'string', 'app.aliases.DataTables' => 'string', 'app.aliases.Yard' => 'string', + 'app.aliases.DHL' => 'string', 'app.mode' => 'string', + 'app.url_crm' => 'string', 'app.domain' => 'string', 'app.tld_care' => 'string', 'app.tld_shop' => 'string', @@ -2584,6 +2796,14 @@ namespace PHPSTORM_META { 'cart.format.decimals' => 'integer', 'cart.format.decimal_point' => 'string', 'cart.format.thousand_seperator' => 'string', + 'cors.paths' => 'array', + 'cors.allowed_methods' => 'array', + 'cors.allowed_origins' => 'array', + 'cors.allowed_origins_patterns' => 'array', + 'cors.allowed_headers' => 'array', + 'cors.exposed_headers' => 'array', + 'cors.max_age' => 'integer', + 'cors.supports_credentials' => 'boolean', 'database.default' => 'string', 'database.connections.sqlite.driver' => 'string', 'database.connections.sqlite.database' => 'string', @@ -2659,6 +2879,116 @@ namespace PHPSTORM_META { 'datatables.json.header' => 'array', 'datatables.json.options' => 'integer', 'datatables.callback' => 'array', + 'dhl.base_url' => 'string', + 'dhl.api_key' => 'string', + 'dhl.username' => 'NULL', + 'dhl.password' => 'NULL', + 'dhl.billing_number' => 'NULL', + 'dhl.default_product' => 'string', + 'dhl.label_format' => 'string', + 'dhl.profile' => 'string', + 'dhl.webhook.enabled' => 'boolean', + 'dhl.webhook.secret' => 'NULL', + 'dhl.webhook.route' => 'string', + 'dhl.use_queue' => 'boolean', + 'dhl.api.api_key' => 'string', + 'dhl.api.api_secret' => 'string', + 'dhl.api.username' => 'string', + 'dhl.api.password' => 'string', + 'dhl.api.account_number_default' => 'string', + 'dhl.api.product_accounts.V01PAK' => 'string', + 'dhl.api.product_accounts.V62WP' => 'string', + 'dhl.api.product_accounts.V53PAK' => 'string', + 'dhl.api.product_accounts.V07PAK' => 'string', + 'dhl.api.sandbox' => 'boolean', + 'dhl.api.test_mode' => 'boolean', + 'dhl.api.api_type' => 'string', + 'dhl.api.endpoints.developer.sandbox' => 'string', + 'dhl.api.endpoints.developer.production' => 'string', + 'dhl.api.endpoints.business_customer.sandbox' => 'string', + 'dhl.api.endpoints.business_customer.production' => 'string', + 'dhl.sender.company' => 'string', + 'dhl.sender.name' => 'string', + 'dhl.sender.street' => 'string', + 'dhl.sender.street_number' => 'string', + 'dhl.sender.postal_code' => 'string', + 'dhl.sender.city' => 'string', + 'dhl.sender.state' => 'string', + 'dhl.sender.country' => 'string', + 'dhl.sender.email' => 'string', + 'dhl.sender.phone' => 'string', + 'dhl.defaults.product' => 'string', + 'dhl.defaults.product_international' => 'string', + 'dhl.defaults.weight_unit' => 'string', + 'dhl.defaults.dimension_unit' => 'string', + 'dhl.defaults.currency' => 'string', + 'dhl.defaults.dimensions.length' => 'integer', + 'dhl.defaults.dimensions.width' => 'integer', + 'dhl.defaults.dimensions.height' => 'integer', + 'dhl.defaults.weight' => 'double', + 'dhl.defaults.services.premium' => 'boolean', + 'dhl.defaults.services.endorsement' => 'string', + 'dhl.defaults.services.visual_check_of_age' => 'boolean', + 'dhl.defaults.services.named_person_only' => 'boolean', + 'dhl.defaults.services.return_receipt' => 'boolean', + 'dhl.defaults.services.bulky_goods' => 'boolean', + 'dhl.labels.format' => 'string', + 'dhl.labels.size' => 'string', + 'dhl.labels.storage_path' => 'string', + 'dhl.labels.public_path' => 'string', + 'dhl.labels.combine_on_page' => 'boolean', + 'dhl.returns.enabled' => 'boolean', + 'dhl.returns.product' => 'string', + 'dhl.returns.receiver_id' => 'string', + 'dhl.tracking.enabled' => 'boolean', + 'dhl.tracking.username' => 'NULL', + 'dhl.tracking.password' => 'NULL', + 'dhl.tracking.auto_update' => 'boolean', + 'dhl.tracking.update_interval' => 'string', + 'dhl.tracking.max_retries' => 'integer', + 'dhl.logging.enabled' => 'boolean', + 'dhl.logging.level' => 'string', + 'dhl.logging.channel' => 'string', + 'dhl.logging.log_requests' => 'boolean', + 'dhl.logging.log_responses' => 'boolean', + 'dhl.queue.connection' => 'string', + 'dhl.queue.queue' => 'string', + 'dhl.queue.timeout' => 'string', + 'dhl.queue.retry_after' => 'integer', + 'dhl.queue.max_tries' => 'string', + 'dhl.error_handling.throw_exceptions' => 'boolean', + 'dhl.error_handling.notification_email' => 'string', + 'dhl.error_handling.retry_failed_jobs' => 'boolean', + 'dhl.cache.enabled' => 'boolean', + 'dhl.cache.ttl' => 'string', + 'dhl.cache.prefix' => 'string', + 'dhl.development.fake_api_calls' => 'boolean', + 'dhl.development.mock_responses' => 'boolean', + 'dhl.development.debug_mode' => 'boolean', + 'dhl.supported_countries.domestic' => 'array', + 'dhl.supported_countries.eu' => 'array', + 'dhl.supported_countries.international' => 'array', + 'dhl.status_mapping.pre-transit' => 'string', + 'dhl.status_mapping.transit' => 'string', + 'dhl.status_mapping.delivered' => 'string', + 'dhl.status_mapping.returned' => 'string', + 'dhl.status_mapping.failure' => 'string', + 'dhl.status_mapping.unknown' => 'string', + 'domains.protocol' => 'string', + 'domains.domains.main.host' => 'string', + 'domains.domains.main.type' => 'string', + 'domains.domains.shop.host' => 'string', + 'domains.domains.shop.type' => 'string', + 'domains.domains.shop.default_user_shop' => 'string', + 'domains.domains.crm.host' => 'string', + 'domains.domains.crm.type' => 'string', + 'domains.domains.portal.host' => 'string', + 'domains.domains.portal.type' => 'string', + 'domains.domains.checkout.host' => 'string', + 'domains.domains.checkout.type' => 'string', + 'domains.domains.user-shop.host' => 'string', + 'domains.domains.user-shop.type' => 'string', + 'domains.reserved_subdomains' => 'array', 'filesystems.default' => 'string', 'filesystems.disks.local.driver' => 'string', 'filesystems.disks.local.root' => 'string', @@ -2971,14 +3301,6 @@ namespace PHPSTORM_META { 'view.paths' => 'array', 'view.compiled' => 'string', 'concurrency.default' => 'string', - 'cors.paths' => 'array', - 'cors.allowed_methods' => 'array', - 'cors.allowed_origins' => 'array', - 'cors.allowed_origins_patterns' => 'array', - 'cors.allowed_headers' => 'array', - 'cors.exposed_headers' => 'array', - 'cors.max_age' => 'integer', - 'cors.supports_credentials' => 'boolean', 'debugbar.enabled' => 'NULL', 'debugbar.hide_empty_tabs' => 'boolean', 'debugbar.except' => 'array', @@ -3237,153 +3559,175 @@ namespace PHPSTORM_META { 'app.aliases.Gate','app.aliases.Hash','app.aliases.Lang','app.aliases.Log','app.aliases.Mail', 'app.aliases.Notification','app.aliases.Password','app.aliases.Queue','app.aliases.Redirect','app.aliases.Redis', 'app.aliases.Request','app.aliases.Response','app.aliases.Route','app.aliases.Schema','app.aliases.Session', -'app.aliases.Storage','app.aliases.URL','app.aliases.Validator','app.aliases.View','app.aliases.Image', -'app.aliases.Carbon','app.aliases.Date','app.aliases.HTMLHelper','app.aliases.Util','app.aliases.Excel', -'app.aliases.DataTables','app.aliases.Yard','app.mode','app.domain','app.tld_care', -'app.tld_shop','app.protocol','app.pre_url_main','app.pre_url_crm','app.pre_url_portal', -'app.checkout_url','app.contact_mail','app.checkout_mail','app.checkout_test_mail','app.info_mail', -'app.priority_mail','app.default_mail','app.info_test_mail','app.exception_mail','app.ipinfo', -'app.main_tax','app.main_tax_rate','app.shipping_tax','app.php_version','auth.defaults.guard', -'auth.defaults.passwords','auth.guards.web.driver','auth.guards.web.provider','auth.guards.user.driver','auth.guards.user.provider', -'auth.guards.api.driver','auth.guards.api.provider','auth.guards.customers.driver','auth.guards.customers.provider','auth.providers.users.driver', -'auth.providers.users.model','auth.providers.customers.driver','auth.providers.customers.model','auth.passwords.users.provider','auth.passwords.users.table', -'auth.passwords.users.expire','auth.password_timeout','broadcasting.default','broadcasting.connections.reverb.driver','broadcasting.connections.reverb.key', -'broadcasting.connections.reverb.secret','broadcasting.connections.reverb.app_id','broadcasting.connections.reverb.options.host','broadcasting.connections.reverb.options.port','broadcasting.connections.reverb.options.scheme', -'broadcasting.connections.reverb.options.useTLS','broadcasting.connections.reverb.client_options','broadcasting.connections.pusher.driver','broadcasting.connections.pusher.key','broadcasting.connections.pusher.secret', -'broadcasting.connections.pusher.app_id','broadcasting.connections.pusher.options.cluster','broadcasting.connections.pusher.options.encrypted','broadcasting.connections.ably.driver','broadcasting.connections.ably.key', -'broadcasting.connections.log.driver','broadcasting.connections.null.driver','broadcasting.connections.redis.driver','broadcasting.connections.redis.connection','cache.default', -'cache.stores.array.driver','cache.stores.database.driver','cache.stores.database.table','cache.stores.database.connection','cache.stores.file.driver', -'cache.stores.file.path','cache.stores.memcached.driver','cache.stores.memcached.persistent_id','cache.stores.memcached.sasl','cache.stores.memcached.options', -'cache.stores.memcached.servers.0.host','cache.stores.memcached.servers.0.port','cache.stores.memcached.servers.0.weight','cache.stores.redis.driver','cache.stores.redis.connection', -'cache.stores.dynamodb.driver','cache.stores.dynamodb.key','cache.stores.dynamodb.secret','cache.stores.dynamodb.region','cache.stores.dynamodb.table', -'cache.stores.dynamodb.endpoint','cache.stores.octane.driver','cache.stores.apc.driver','cache.prefix','cart.tax', -'cart.database.connection','cart.database.table','cart.destroy_on_logout','cart.format.decimals','cart.format.decimal_point', -'cart.format.thousand_seperator','database.default','database.connections.sqlite.driver','database.connections.sqlite.database','database.connections.sqlite.prefix', -'database.connections.mysql.driver','database.connections.mysql.host','database.connections.mysql.port','database.connections.mysql.database','database.connections.mysql.username', -'database.connections.mysql.password','database.connections.mysql.unix_socket','database.connections.mysql.charset','database.connections.mysql.collation','database.connections.mysql.prefix', -'database.connections.mysql.strict','database.connections.mysql.engine','database.connections.mariadb.driver','database.connections.mariadb.url','database.connections.mariadb.host', -'database.connections.mariadb.port','database.connections.mariadb.database','database.connections.mariadb.username','database.connections.mariadb.password','database.connections.mariadb.unix_socket', -'database.connections.mariadb.charset','database.connections.mariadb.collation','database.connections.mariadb.prefix','database.connections.mariadb.prefix_indexes','database.connections.mariadb.strict', -'database.connections.mariadb.engine','database.connections.mariadb.options','database.connections.pgsql.driver','database.connections.pgsql.host','database.connections.pgsql.port', -'database.connections.pgsql.database','database.connections.pgsql.username','database.connections.pgsql.password','database.connections.pgsql.charset','database.connections.pgsql.prefix', -'database.connections.pgsql.schema','database.connections.pgsql.sslmode','database.connections.sqlsrv.driver','database.connections.sqlsrv.host','database.connections.sqlsrv.port', -'database.connections.sqlsrv.database','database.connections.sqlsrv.username','database.connections.sqlsrv.password','database.connections.sqlsrv.charset','database.connections.sqlsrv.prefix', -'database.migrations','database.redis.client','database.redis.default.host','database.redis.default.password','database.redis.default.port', -'database.redis.default.database','datatables.search.smart','datatables.search.multi_term','datatables.search.case_insensitive','datatables.search.use_wildcards', -'datatables.index_column','datatables.engines.eloquent','datatables.engines.query','datatables.engines.collection','datatables.engines.resource', -'datatables.builders','datatables.nulls_last_sql','datatables.error','datatables.columns.excess','datatables.columns.escape', -'datatables.columns.raw','datatables.columns.blacklist','datatables.columns.whitelist','datatables.json.header','datatables.json.options', -'datatables.callback','filesystems.default','filesystems.disks.local.driver','filesystems.disks.local.root','filesystems.disks.public.driver', -'filesystems.disks.public.root','filesystems.disks.public.url','filesystems.disks.public.visibility','filesystems.disks.s3.driver','filesystems.disks.s3.key', -'filesystems.disks.s3.secret','filesystems.disks.s3.region','filesystems.disks.s3.bucket','filesystems.disks.s3.url','filesystems.disks.user.driver', -'filesystems.disks.user.root','filesystems.disks.user.url','filesystems.disks.user.visibility','filesystems.disks.import.driver','filesystems.disks.import.root', -'filesystems.disks.import.url','filesystems.links./Users/kadmin/Websites/mivita.care/public/storage','filesystems.cloud','hashing.driver','hashing.bcrypt.rounds', -'hashing.argon.memory','hashing.argon.threads','hashing.argon.time','hashing.rehash_on_login','ide-helper.filename', -'ide-helper.models_filename','ide-helper.meta_filename','ide-helper.include_fluent','ide-helper.include_factory_builders','ide-helper.write_model_magic_where', -'ide-helper.write_model_external_builder_methods','ide-helper.write_model_relation_count_properties','ide-helper.write_eloquent_model_mixins','ide-helper.include_helpers','ide-helper.helper_files', -'ide-helper.model_locations','ide-helper.ignored_models','ide-helper.model_hooks','ide-helper.extra.Eloquent','ide-helper.extra.Session', -'ide-helper.extra.Yard','ide-helper.magic.Log.debug','ide-helper.magic.Log.info','ide-helper.magic.Log.notice','ide-helper.magic.Log.warning', -'ide-helper.magic.Log.error','ide-helper.magic.Log.critical','ide-helper.magic.Log.alert','ide-helper.magic.Log.emergency','ide-helper.interfaces', -'ide-helper.model_camel_case_properties','ide-helper.type_overrides.integer','ide-helper.type_overrides.boolean','ide-helper.include_class_docblocks','ide-helper.force_fqn', -'ide-helper.use_generics_annotations','ide-helper.additional_relation_types','ide-helper.additional_relation_return_types','ide-helper.enforce_nullable_relationships','ide-helper.post_migrate', -'ide-helper.macroable_traits','ide-helper.format','ide-helper.custom_db_types','localization.supportedLocales.de.name','localization.supportedLocales.de.script', -'localization.supportedLocales.de.native','localization.supportedLocales.de.regional','localization.supportedLocales.en.name','localization.supportedLocales.en.script','localization.supportedLocales.en.native', -'localization.supportedLocales.en.regional','localization.supportedLocales.es.name','localization.supportedLocales.es.script','localization.supportedLocales.es.native','localization.supportedLocales.es.regional', -'logging.default','logging.deprecations.channel','logging.deprecations.trace','logging.channels.stack.driver','logging.channels.stack.channels', -'logging.channels.single.driver','logging.channels.single.path','logging.channels.single.level','logging.channels.daily.driver','logging.channels.daily.path', -'logging.channels.daily.level','logging.channels.daily.days','logging.channels.slack.driver','logging.channels.slack.url','logging.channels.slack.username', -'logging.channels.slack.emoji','logging.channels.slack.level','logging.channels.papertrail.driver','logging.channels.papertrail.level','logging.channels.papertrail.handler', -'logging.channels.papertrail.handler_with.host','logging.channels.papertrail.handler_with.port','logging.channels.papertrail.handler_with.connectionString','logging.channels.papertrail.processors','logging.channels.stderr.driver', -'logging.channels.stderr.handler','logging.channels.stderr.with.stream','logging.channels.syslog.driver','logging.channels.syslog.level','logging.channels.errorlog.driver', -'logging.channels.errorlog.level','logging.channels.null.driver','logging.channels.null.handler','logging.channels.emergency.path','logging.channels.order_controller.driver', -'logging.channels.order_controller.path','logging.channels.order_controller.level','logging.channels.payone.driver','logging.channels.payone.path','logging.channels.cleanup.driver', -'logging.channels.cleanup.path','logging.channels.payment.driver','logging.channels.payment.path','logging.channels.cron.driver','logging.channels.cron.path', -'mail.default','mail.mailers.smtp.transport','mail.mailers.smtp.scheme','mail.mailers.smtp.url','mail.mailers.smtp.host', -'mail.mailers.smtp.port','mail.mailers.smtp.username','mail.mailers.smtp.password','mail.mailers.smtp.timeout','mail.mailers.smtp.local_domain', -'mail.mailers.ses.transport','mail.mailers.postmark.transport','mail.mailers.resend.transport','mail.mailers.sendmail.transport','mail.mailers.sendmail.path', -'mail.mailers.log.transport','mail.mailers.log.channel','mail.mailers.array.transport','mail.mailers.failover.transport','mail.mailers.failover.mailers', -'mail.mailers.roundrobin.transport','mail.mailers.roundrobin.mailers','mail.from.address','mail.from.name','mail.markdown.theme', -'mail.markdown.paths','mail.driver','mail.host','mail.port','mail.encryption', -'mail.username','mail.password','mail.sendmail','mivita.renewal_days','mivita.abo_booking_days', -'mivita.remind_first_days','mivita.remind_sec_days','mivita.remind_last_days','mivita.edit_data_pass','mivita.add_number_id', -'models.*.path','models.*.namespace','models.*.parent','models.*.use','models.*.connection', -'models.*.timestamps','models.*.soft_deletes','models.*.date_format','models.*.per_page','models.*.base_files', -'models.*.snake_attributes','models.*.indent_with_space','models.*.qualified_tables','models.*.hidden','models.*.guarded', -'models.*.casts.*_json','models.*.except','models.*.only','models.*.table_prefix','models.*.lower_table_name_first', -'models.*.relation_name_strategy','models.*.with_property_constants','models.*.pluralize','models.*.override_pluralize_for','payone.defaults.aid', -'payone.defaults.mid','payone.defaults.portalid','payone.defaults.key','payone.defaults.mode','payone.defaults.api_version', -'payone.defaults.encoding','profanity.replaceFullWords','profanity.replaceWith','profanity.strReplace.a','profanity.strReplace.b', -'profanity.strReplace.c','profanity.strReplace.d','profanity.strReplace.e','profanity.strReplace.f','profanity.strReplace.g', -'profanity.strReplace.h','profanity.strReplace.i','profanity.strReplace.j','profanity.strReplace.k','profanity.strReplace.l', -'profanity.strReplace.m','profanity.strReplace.n','profanity.strReplace.o','profanity.strReplace.p','profanity.strReplace.q', -'profanity.strReplace.r','profanity.strReplace.s','profanity.strReplace.t','profanity.strReplace.u','profanity.strReplace.v', -'profanity.strReplace.w','profanity.strReplace.x','profanity.strReplace.y','profanity.strReplace.z','profanity.defaults', -'profanity.full_word_check','queue.default','queue.connections.sync.driver','queue.connections.database.driver','queue.connections.database.table', -'queue.connections.database.queue','queue.connections.database.retry_after','queue.connections.beanstalkd.driver','queue.connections.beanstalkd.host','queue.connections.beanstalkd.queue', -'queue.connections.beanstalkd.retry_after','queue.connections.sqs.driver','queue.connections.sqs.key','queue.connections.sqs.secret','queue.connections.sqs.prefix', -'queue.connections.sqs.queue','queue.connections.sqs.region','queue.connections.redis.driver','queue.connections.redis.connection','queue.connections.redis.queue', -'queue.connections.redis.retry_after','queue.connections.redis.block_for','queue.batching.database','queue.batching.table','queue.failed.database', -'queue.failed.table','services.postmark.token','services.ses.key','services.ses.secret','services.ses.region', -'services.resend.key','services.slack.notifications.bot_user_oauth_token','services.slack.notifications.channel','services.mailgun.domain','services.mailgun.secret', -'services.sparkpost.secret','services.stripe.model','services.stripe.key','services.stripe.secret','session.driver', -'session.lifetime','session.expire_on_close','session.encrypt','session.files','session.connection', -'session.table','session.store','session.lottery','session.cookie','session.path', -'session.domain','session.secure','session.http_only','session.same_site','session.partitioned', -'sluggable.source','sluggable.maxLength','sluggable.maxLengthKeepWords','sluggable.method','sluggable.separator', -'sluggable.unique','sluggable.uniqueSuffix','sluggable.firstUniqueSuffix','sluggable.includeTrashed','sluggable.reserved', -'sluggable.onUpdate','sluggable.slugEngineOptions','tinker.commands','tinker.alias','tinker.dont_alias', -'translation.driver','translation.route_group_config.middleware','translation.translation_methods','translation.scan_paths','translation.ui_url', -'translation.database.connection','translation.database.languages_table','translation.database.translations_table','trustedproxy.proxies','trustedproxy.headers.for', -'trustedproxy.headers.proto','trustedproxy.headers.host','trustedproxy.headers.port','trustedproxy.headers.prefix','trustedproxy.headers.aws_cloud_front_for', -'view.paths','view.compiled','concurrency.default','cors.paths','cors.allowed_methods', -'cors.allowed_origins','cors.allowed_origins_patterns','cors.allowed_headers','cors.exposed_headers','cors.max_age', -'cors.supports_credentials','debugbar.enabled','debugbar.hide_empty_tabs','debugbar.except','debugbar.storage.enabled', -'debugbar.storage.open','debugbar.storage.driver','debugbar.storage.path','debugbar.storage.connection','debugbar.storage.provider', -'debugbar.storage.hostname','debugbar.storage.port','debugbar.editor','debugbar.remote_sites_path','debugbar.local_sites_path', -'debugbar.include_vendors','debugbar.capture_ajax','debugbar.add_ajax_timing','debugbar.ajax_handler_auto_show','debugbar.ajax_handler_enable_tab', -'debugbar.defer_datasets','debugbar.error_handler','debugbar.clockwork','debugbar.collectors.phpinfo','debugbar.collectors.messages', -'debugbar.collectors.time','debugbar.collectors.memory','debugbar.collectors.exceptions','debugbar.collectors.log','debugbar.collectors.db', -'debugbar.collectors.views','debugbar.collectors.route','debugbar.collectors.auth','debugbar.collectors.gate','debugbar.collectors.session', -'debugbar.collectors.symfony_request','debugbar.collectors.mail','debugbar.collectors.laravel','debugbar.collectors.events','debugbar.collectors.default_request', -'debugbar.collectors.logs','debugbar.collectors.files','debugbar.collectors.config','debugbar.collectors.cache','debugbar.collectors.models', -'debugbar.collectors.livewire','debugbar.collectors.jobs','debugbar.collectors.pennant','debugbar.options.time.memory_usage','debugbar.options.messages.trace', -'debugbar.options.messages.capture_dumps','debugbar.options.memory.reset_peak','debugbar.options.memory.with_baseline','debugbar.options.memory.precision','debugbar.options.auth.show_name', -'debugbar.options.auth.show_guards','debugbar.options.db.with_params','debugbar.options.db.exclude_paths','debugbar.options.db.backtrace','debugbar.options.db.backtrace_exclude_paths', -'debugbar.options.db.timeline','debugbar.options.db.duration_background','debugbar.options.db.explain.enabled','debugbar.options.db.hints','debugbar.options.db.show_copy', -'debugbar.options.db.slow_threshold','debugbar.options.db.memory_usage','debugbar.options.db.soft_limit','debugbar.options.db.hard_limit','debugbar.options.mail.timeline', -'debugbar.options.mail.show_body','debugbar.options.views.timeline','debugbar.options.views.data','debugbar.options.views.group','debugbar.options.views.exclude_paths', -'debugbar.options.route.label','debugbar.options.session.hiddens','debugbar.options.symfony_request.label','debugbar.options.symfony_request.hiddens','debugbar.options.events.data', -'debugbar.options.logs.file','debugbar.options.cache.values','debugbar.inject','debugbar.route_prefix','debugbar.route_middleware', -'debugbar.route_domain','debugbar.theme','debugbar.debug_backtrace_limit','dompdf.show_warnings','dompdf.public_path', -'dompdf.convert_entities','dompdf.options.font_dir','dompdf.options.font_cache','dompdf.options.temp_dir','dompdf.options.chroot', -'dompdf.options.allowed_protocols.file://.rules','dompdf.options.allowed_protocols.http://.rules','dompdf.options.allowed_protocols.https://.rules','dompdf.options.log_output_file','dompdf.options.enable_font_subsetting', -'dompdf.options.pdf_backend','dompdf.options.default_media_type','dompdf.options.default_paper_size','dompdf.options.default_paper_orientation','dompdf.options.default_font', -'dompdf.options.dpi','dompdf.options.enable_php','dompdf.options.enable_javascript','dompdf.options.enable_remote','dompdf.options.font_height_ratio', -'dompdf.options.enable_html5_parser','passport.guard','passport.private_key','passport.public_key','passport.connection', -'passport.client_uuids','passport.personal_access_client.id','passport.personal_access_client.secret','excel.exports.chunk_size','excel.exports.pre_calculate_formulas', -'excel.exports.strict_null_comparison','excel.exports.csv.delimiter','excel.exports.csv.enclosure','excel.exports.csv.line_ending','excel.exports.csv.use_bom', -'excel.exports.csv.include_separator_line','excel.exports.csv.excel_compatibility','excel.exports.csv.output_encoding','excel.exports.csv.test_auto_detect','excel.exports.properties.creator', -'excel.exports.properties.lastModifiedBy','excel.exports.properties.title','excel.exports.properties.description','excel.exports.properties.subject','excel.exports.properties.keywords', -'excel.exports.properties.category','excel.exports.properties.manager','excel.exports.properties.company','excel.imports.read_only','excel.imports.ignore_empty', -'excel.imports.heading_row.formatter','excel.imports.csv.delimiter','excel.imports.csv.enclosure','excel.imports.csv.escape_character','excel.imports.csv.contiguous', -'excel.imports.csv.input_encoding','excel.imports.properties.creator','excel.imports.properties.lastModifiedBy','excel.imports.properties.title','excel.imports.properties.description', -'excel.imports.properties.subject','excel.imports.properties.keywords','excel.imports.properties.category','excel.imports.properties.manager','excel.imports.properties.company', -'excel.imports.cells.middleware','excel.extension_detector.xlsx','excel.extension_detector.xlsm','excel.extension_detector.xltx','excel.extension_detector.xltm', -'excel.extension_detector.xls','excel.extension_detector.xlt','excel.extension_detector.ods','excel.extension_detector.ots','excel.extension_detector.slk', -'excel.extension_detector.xml','excel.extension_detector.gnumeric','excel.extension_detector.htm','excel.extension_detector.html','excel.extension_detector.csv', -'excel.extension_detector.tsv','excel.extension_detector.pdf','excel.value_binder.default','excel.cache.driver','excel.cache.batch.memory_limit', -'excel.cache.illuminate.store','excel.cache.default_ttl','excel.transactions.handler','excel.transactions.db.connection','excel.temporary_files.local_path', -'excel.temporary_files.local_permissions','excel.temporary_files.remote_disk','excel.temporary_files.remote_prefix','excel.temporary_files.force_resync_remote','flare.key', -'flare.flare_middleware','flare.flare_middleware.Spatie\\LaravelIgnition\\FlareMiddleware\\AddLogs.maximum_number_of_collected_logs','flare.flare_middleware.Spatie\\LaravelIgnition\\FlareMiddleware\\AddQueries.maximum_number_of_collected_queries','flare.flare_middleware.Spatie\\LaravelIgnition\\FlareMiddleware\\AddQueries.report_query_bindings','flare.flare_middleware.Spatie\\LaravelIgnition\\FlareMiddleware\\AddJobs.max_chained_job_reporting_depth', -'flare.flare_middleware.Spatie\\FlareClient\\FlareMiddleware\\CensorRequestBodyFields.censor_fields','flare.flare_middleware.Spatie\\FlareClient\\FlareMiddleware\\CensorRequestHeaders.headers','flare.send_logs_as_events','ignition.editor','ignition.theme', -'ignition.enable_share_button','ignition.register_commands','ignition.solution_providers','ignition.ignored_solution_providers','ignition.enable_runnable_solutions', -'ignition.remote_sites_path','ignition.local_sites_path','ignition.housekeeping_endpoint_prefix','ignition.settings_file_path','ignition.recorders', -'ignition.open_ai_key','ignition.with_stack_frame_arguments','ignition.argument_reducers',); +'app.aliases.Storage','app.aliases.URL','app.aliases.Validator','app.aliases.View','app.aliases.Carbon', +'app.aliases.Date','app.aliases.HTMLHelper','app.aliases.Util','app.aliases.Excel','app.aliases.DataTables', +'app.aliases.Yard','app.aliases.DHL','app.mode','app.url_crm','app.domain', +'app.tld_care','app.tld_shop','app.protocol','app.pre_url_main','app.pre_url_crm', +'app.pre_url_portal','app.checkout_url','app.contact_mail','app.checkout_mail','app.checkout_test_mail', +'app.info_mail','app.priority_mail','app.default_mail','app.info_test_mail','app.exception_mail', +'app.ipinfo','app.main_tax','app.main_tax_rate','app.shipping_tax','app.php_version', +'auth.defaults.guard','auth.defaults.passwords','auth.guards.web.driver','auth.guards.web.provider','auth.guards.user.driver', +'auth.guards.user.provider','auth.guards.api.driver','auth.guards.api.provider','auth.guards.customers.driver','auth.guards.customers.provider', +'auth.providers.users.driver','auth.providers.users.model','auth.providers.customers.driver','auth.providers.customers.model','auth.passwords.users.provider', +'auth.passwords.users.table','auth.passwords.users.expire','auth.password_timeout','broadcasting.default','broadcasting.connections.reverb.driver', +'broadcasting.connections.reverb.key','broadcasting.connections.reverb.secret','broadcasting.connections.reverb.app_id','broadcasting.connections.reverb.options.host','broadcasting.connections.reverb.options.port', +'broadcasting.connections.reverb.options.scheme','broadcasting.connections.reverb.options.useTLS','broadcasting.connections.reverb.client_options','broadcasting.connections.pusher.driver','broadcasting.connections.pusher.key', +'broadcasting.connections.pusher.secret','broadcasting.connections.pusher.app_id','broadcasting.connections.pusher.options.cluster','broadcasting.connections.pusher.options.encrypted','broadcasting.connections.ably.driver', +'broadcasting.connections.ably.key','broadcasting.connections.log.driver','broadcasting.connections.null.driver','broadcasting.connections.redis.driver','broadcasting.connections.redis.connection', +'cache.default','cache.stores.array.driver','cache.stores.database.driver','cache.stores.database.table','cache.stores.database.connection', +'cache.stores.file.driver','cache.stores.file.path','cache.stores.memcached.driver','cache.stores.memcached.persistent_id','cache.stores.memcached.sasl', +'cache.stores.memcached.options','cache.stores.memcached.servers.0.host','cache.stores.memcached.servers.0.port','cache.stores.memcached.servers.0.weight','cache.stores.redis.driver', +'cache.stores.redis.connection','cache.stores.dynamodb.driver','cache.stores.dynamodb.key','cache.stores.dynamodb.secret','cache.stores.dynamodb.region', +'cache.stores.dynamodb.table','cache.stores.dynamodb.endpoint','cache.stores.octane.driver','cache.stores.apc.driver','cache.prefix', +'cart.tax','cart.database.connection','cart.database.table','cart.destroy_on_logout','cart.format.decimals', +'cart.format.decimal_point','cart.format.thousand_seperator','cors.paths','cors.allowed_methods','cors.allowed_origins', +'cors.allowed_origins_patterns','cors.allowed_headers','cors.exposed_headers','cors.max_age','cors.supports_credentials', +'database.default','database.connections.sqlite.driver','database.connections.sqlite.database','database.connections.sqlite.prefix','database.connections.mysql.driver', +'database.connections.mysql.host','database.connections.mysql.port','database.connections.mysql.database','database.connections.mysql.username','database.connections.mysql.password', +'database.connections.mysql.unix_socket','database.connections.mysql.charset','database.connections.mysql.collation','database.connections.mysql.prefix','database.connections.mysql.strict', +'database.connections.mysql.engine','database.connections.mariadb.driver','database.connections.mariadb.url','database.connections.mariadb.host','database.connections.mariadb.port', +'database.connections.mariadb.database','database.connections.mariadb.username','database.connections.mariadb.password','database.connections.mariadb.unix_socket','database.connections.mariadb.charset', +'database.connections.mariadb.collation','database.connections.mariadb.prefix','database.connections.mariadb.prefix_indexes','database.connections.mariadb.strict','database.connections.mariadb.engine', +'database.connections.mariadb.options','database.connections.pgsql.driver','database.connections.pgsql.host','database.connections.pgsql.port','database.connections.pgsql.database', +'database.connections.pgsql.username','database.connections.pgsql.password','database.connections.pgsql.charset','database.connections.pgsql.prefix','database.connections.pgsql.schema', +'database.connections.pgsql.sslmode','database.connections.sqlsrv.driver','database.connections.sqlsrv.host','database.connections.sqlsrv.port','database.connections.sqlsrv.database', +'database.connections.sqlsrv.username','database.connections.sqlsrv.password','database.connections.sqlsrv.charset','database.connections.sqlsrv.prefix','database.migrations', +'database.redis.client','database.redis.default.host','database.redis.default.password','database.redis.default.port','database.redis.default.database', +'datatables.search.smart','datatables.search.multi_term','datatables.search.case_insensitive','datatables.search.use_wildcards','datatables.index_column', +'datatables.engines.eloquent','datatables.engines.query','datatables.engines.collection','datatables.engines.resource','datatables.builders', +'datatables.nulls_last_sql','datatables.error','datatables.columns.excess','datatables.columns.escape','datatables.columns.raw', +'datatables.columns.blacklist','datatables.columns.whitelist','datatables.json.header','datatables.json.options','datatables.callback', +'dhl.base_url','dhl.api_key','dhl.username','dhl.password','dhl.billing_number', +'dhl.default_product','dhl.label_format','dhl.profile','dhl.webhook.enabled','dhl.webhook.secret', +'dhl.webhook.route','dhl.use_queue','dhl.api.api_key','dhl.api.api_secret','dhl.api.username', +'dhl.api.password','dhl.api.account_number_default','dhl.api.product_accounts.V01PAK','dhl.api.product_accounts.V62WP','dhl.api.product_accounts.V53PAK', +'dhl.api.product_accounts.V07PAK','dhl.api.sandbox','dhl.api.test_mode','dhl.api.api_type','dhl.api.endpoints.developer.sandbox', +'dhl.api.endpoints.developer.production','dhl.api.endpoints.business_customer.sandbox','dhl.api.endpoints.business_customer.production','dhl.sender.company','dhl.sender.name', +'dhl.sender.street','dhl.sender.street_number','dhl.sender.postal_code','dhl.sender.city','dhl.sender.state', +'dhl.sender.country','dhl.sender.email','dhl.sender.phone','dhl.defaults.product','dhl.defaults.product_international', +'dhl.defaults.weight_unit','dhl.defaults.dimension_unit','dhl.defaults.currency','dhl.defaults.dimensions.length','dhl.defaults.dimensions.width', +'dhl.defaults.dimensions.height','dhl.defaults.weight','dhl.defaults.services.premium','dhl.defaults.services.endorsement','dhl.defaults.services.visual_check_of_age', +'dhl.defaults.services.named_person_only','dhl.defaults.services.return_receipt','dhl.defaults.services.bulky_goods','dhl.labels.format','dhl.labels.size', +'dhl.labels.storage_path','dhl.labels.public_path','dhl.labels.combine_on_page','dhl.returns.enabled','dhl.returns.product', +'dhl.returns.receiver_id','dhl.tracking.enabled','dhl.tracking.username','dhl.tracking.password','dhl.tracking.auto_update', +'dhl.tracking.update_interval','dhl.tracking.max_retries','dhl.logging.enabled','dhl.logging.level','dhl.logging.channel', +'dhl.logging.log_requests','dhl.logging.log_responses','dhl.queue.connection','dhl.queue.queue','dhl.queue.timeout', +'dhl.queue.retry_after','dhl.queue.max_tries','dhl.error_handling.throw_exceptions','dhl.error_handling.notification_email','dhl.error_handling.retry_failed_jobs', +'dhl.cache.enabled','dhl.cache.ttl','dhl.cache.prefix','dhl.development.fake_api_calls','dhl.development.mock_responses', +'dhl.development.debug_mode','dhl.supported_countries.domestic','dhl.supported_countries.eu','dhl.supported_countries.international','dhl.status_mapping.pre-transit', +'dhl.status_mapping.transit','dhl.status_mapping.delivered','dhl.status_mapping.returned','dhl.status_mapping.failure','dhl.status_mapping.unknown', +'domains.protocol','domains.domains.main.host','domains.domains.main.type','domains.domains.shop.host','domains.domains.shop.type', +'domains.domains.shop.default_user_shop','domains.domains.crm.host','domains.domains.crm.type','domains.domains.portal.host','domains.domains.portal.type', +'domains.domains.checkout.host','domains.domains.checkout.type','domains.domains.user-shop.host','domains.domains.user-shop.type','domains.reserved_subdomains', +'filesystems.default','filesystems.disks.local.driver','filesystems.disks.local.root','filesystems.disks.public.driver','filesystems.disks.public.root', +'filesystems.disks.public.url','filesystems.disks.public.visibility','filesystems.disks.s3.driver','filesystems.disks.s3.key','filesystems.disks.s3.secret', +'filesystems.disks.s3.region','filesystems.disks.s3.bucket','filesystems.disks.s3.url','filesystems.disks.user.driver','filesystems.disks.user.root', +'filesystems.disks.user.url','filesystems.disks.user.visibility','filesystems.disks.import.driver','filesystems.disks.import.root','filesystems.disks.import.url', +'filesystems.links./Users/kadmin/Websites/mivita.care/public/storage','filesystems.cloud','hashing.driver','hashing.bcrypt.rounds','hashing.argon.memory', +'hashing.argon.threads','hashing.argon.time','hashing.rehash_on_login','ide-helper.filename','ide-helper.models_filename', +'ide-helper.meta_filename','ide-helper.include_fluent','ide-helper.include_factory_builders','ide-helper.write_model_magic_where','ide-helper.write_model_external_builder_methods', +'ide-helper.write_model_relation_count_properties','ide-helper.write_eloquent_model_mixins','ide-helper.include_helpers','ide-helper.helper_files','ide-helper.model_locations', +'ide-helper.ignored_models','ide-helper.model_hooks','ide-helper.extra.Eloquent','ide-helper.extra.Session','ide-helper.extra.Yard', +'ide-helper.magic.Log.debug','ide-helper.magic.Log.info','ide-helper.magic.Log.notice','ide-helper.magic.Log.warning','ide-helper.magic.Log.error', +'ide-helper.magic.Log.critical','ide-helper.magic.Log.alert','ide-helper.magic.Log.emergency','ide-helper.interfaces','ide-helper.model_camel_case_properties', +'ide-helper.type_overrides.integer','ide-helper.type_overrides.boolean','ide-helper.include_class_docblocks','ide-helper.force_fqn','ide-helper.use_generics_annotations', +'ide-helper.additional_relation_types','ide-helper.additional_relation_return_types','ide-helper.enforce_nullable_relationships','ide-helper.post_migrate','ide-helper.macroable_traits', +'ide-helper.format','ide-helper.custom_db_types','localization.supportedLocales.de.name','localization.supportedLocales.de.script','localization.supportedLocales.de.native', +'localization.supportedLocales.de.regional','localization.supportedLocales.en.name','localization.supportedLocales.en.script','localization.supportedLocales.en.native','localization.supportedLocales.en.regional', +'localization.supportedLocales.es.name','localization.supportedLocales.es.script','localization.supportedLocales.es.native','localization.supportedLocales.es.regional','logging.default', +'logging.deprecations.channel','logging.deprecations.trace','logging.channels.stack.driver','logging.channels.stack.channels','logging.channels.single.driver', +'logging.channels.single.path','logging.channels.single.level','logging.channels.daily.driver','logging.channels.daily.path','logging.channels.daily.level', +'logging.channels.daily.days','logging.channels.slack.driver','logging.channels.slack.url','logging.channels.slack.username','logging.channels.slack.emoji', +'logging.channels.slack.level','logging.channels.papertrail.driver','logging.channels.papertrail.level','logging.channels.papertrail.handler','logging.channels.papertrail.handler_with.host', +'logging.channels.papertrail.handler_with.port','logging.channels.papertrail.handler_with.connectionString','logging.channels.papertrail.processors','logging.channels.stderr.driver','logging.channels.stderr.handler', +'logging.channels.stderr.with.stream','logging.channels.syslog.driver','logging.channels.syslog.level','logging.channels.errorlog.driver','logging.channels.errorlog.level', +'logging.channels.null.driver','logging.channels.null.handler','logging.channels.emergency.path','logging.channels.order_controller.driver','logging.channels.order_controller.path', +'logging.channels.order_controller.level','logging.channels.payone.driver','logging.channels.payone.path','logging.channels.cleanup.driver','logging.channels.cleanup.path', +'logging.channels.payment.driver','logging.channels.payment.path','logging.channels.cron.driver','logging.channels.cron.path','mail.default', +'mail.mailers.smtp.transport','mail.mailers.smtp.scheme','mail.mailers.smtp.url','mail.mailers.smtp.host','mail.mailers.smtp.port', +'mail.mailers.smtp.username','mail.mailers.smtp.password','mail.mailers.smtp.timeout','mail.mailers.smtp.local_domain','mail.mailers.ses.transport', +'mail.mailers.postmark.transport','mail.mailers.resend.transport','mail.mailers.sendmail.transport','mail.mailers.sendmail.path','mail.mailers.log.transport', +'mail.mailers.log.channel','mail.mailers.array.transport','mail.mailers.failover.transport','mail.mailers.failover.mailers','mail.mailers.roundrobin.transport', +'mail.mailers.roundrobin.mailers','mail.from.address','mail.from.name','mail.markdown.theme','mail.markdown.paths', +'mail.driver','mail.host','mail.port','mail.encryption','mail.username', +'mail.password','mail.sendmail','mivita.renewal_days','mivita.abo_booking_days','mivita.remind_first_days', +'mivita.remind_sec_days','mivita.remind_last_days','mivita.edit_data_pass','mivita.add_number_id','models.*.path', +'models.*.namespace','models.*.parent','models.*.use','models.*.connection','models.*.timestamps', +'models.*.soft_deletes','models.*.date_format','models.*.per_page','models.*.base_files','models.*.snake_attributes', +'models.*.indent_with_space','models.*.qualified_tables','models.*.hidden','models.*.guarded','models.*.casts.*_json', +'models.*.except','models.*.only','models.*.table_prefix','models.*.lower_table_name_first','models.*.relation_name_strategy', +'models.*.with_property_constants','models.*.pluralize','models.*.override_pluralize_for','payone.defaults.aid','payone.defaults.mid', +'payone.defaults.portalid','payone.defaults.key','payone.defaults.mode','payone.defaults.api_version','payone.defaults.encoding', +'profanity.replaceFullWords','profanity.replaceWith','profanity.strReplace.a','profanity.strReplace.b','profanity.strReplace.c', +'profanity.strReplace.d','profanity.strReplace.e','profanity.strReplace.f','profanity.strReplace.g','profanity.strReplace.h', +'profanity.strReplace.i','profanity.strReplace.j','profanity.strReplace.k','profanity.strReplace.l','profanity.strReplace.m', +'profanity.strReplace.n','profanity.strReplace.o','profanity.strReplace.p','profanity.strReplace.q','profanity.strReplace.r', +'profanity.strReplace.s','profanity.strReplace.t','profanity.strReplace.u','profanity.strReplace.v','profanity.strReplace.w', +'profanity.strReplace.x','profanity.strReplace.y','profanity.strReplace.z','profanity.defaults','profanity.full_word_check', +'queue.default','queue.connections.sync.driver','queue.connections.database.driver','queue.connections.database.table','queue.connections.database.queue', +'queue.connections.database.retry_after','queue.connections.beanstalkd.driver','queue.connections.beanstalkd.host','queue.connections.beanstalkd.queue','queue.connections.beanstalkd.retry_after', +'queue.connections.sqs.driver','queue.connections.sqs.key','queue.connections.sqs.secret','queue.connections.sqs.prefix','queue.connections.sqs.queue', +'queue.connections.sqs.region','queue.connections.redis.driver','queue.connections.redis.connection','queue.connections.redis.queue','queue.connections.redis.retry_after', +'queue.connections.redis.block_for','queue.batching.database','queue.batching.table','queue.failed.database','queue.failed.table', +'services.postmark.token','services.ses.key','services.ses.secret','services.ses.region','services.resend.key', +'services.slack.notifications.bot_user_oauth_token','services.slack.notifications.channel','services.mailgun.domain','services.mailgun.secret','services.sparkpost.secret', +'services.stripe.model','services.stripe.key','services.stripe.secret','session.driver','session.lifetime', +'session.expire_on_close','session.encrypt','session.files','session.connection','session.table', +'session.store','session.lottery','session.cookie','session.path','session.domain', +'session.secure','session.http_only','session.same_site','session.partitioned','sluggable.source', +'sluggable.maxLength','sluggable.maxLengthKeepWords','sluggable.method','sluggable.separator','sluggable.unique', +'sluggable.uniqueSuffix','sluggable.firstUniqueSuffix','sluggable.includeTrashed','sluggable.reserved','sluggable.onUpdate', +'sluggable.slugEngineOptions','tinker.commands','tinker.alias','tinker.dont_alias','translation.driver', +'translation.route_group_config.middleware','translation.translation_methods','translation.scan_paths','translation.ui_url','translation.database.connection', +'translation.database.languages_table','translation.database.translations_table','trustedproxy.proxies','trustedproxy.headers.for','trustedproxy.headers.proto', +'trustedproxy.headers.host','trustedproxy.headers.port','trustedproxy.headers.prefix','trustedproxy.headers.aws_cloud_front_for','view.paths', +'view.compiled','concurrency.default','debugbar.enabled','debugbar.hide_empty_tabs','debugbar.except', +'debugbar.storage.enabled','debugbar.storage.open','debugbar.storage.driver','debugbar.storage.path','debugbar.storage.connection', +'debugbar.storage.provider','debugbar.storage.hostname','debugbar.storage.port','debugbar.editor','debugbar.remote_sites_path', +'debugbar.local_sites_path','debugbar.include_vendors','debugbar.capture_ajax','debugbar.add_ajax_timing','debugbar.ajax_handler_auto_show', +'debugbar.ajax_handler_enable_tab','debugbar.defer_datasets','debugbar.error_handler','debugbar.clockwork','debugbar.collectors.phpinfo', +'debugbar.collectors.messages','debugbar.collectors.time','debugbar.collectors.memory','debugbar.collectors.exceptions','debugbar.collectors.log', +'debugbar.collectors.db','debugbar.collectors.views','debugbar.collectors.route','debugbar.collectors.auth','debugbar.collectors.gate', +'debugbar.collectors.session','debugbar.collectors.symfony_request','debugbar.collectors.mail','debugbar.collectors.laravel','debugbar.collectors.events', +'debugbar.collectors.default_request','debugbar.collectors.logs','debugbar.collectors.files','debugbar.collectors.config','debugbar.collectors.cache', +'debugbar.collectors.models','debugbar.collectors.livewire','debugbar.collectors.jobs','debugbar.collectors.pennant','debugbar.options.time.memory_usage', +'debugbar.options.messages.trace','debugbar.options.messages.capture_dumps','debugbar.options.memory.reset_peak','debugbar.options.memory.with_baseline','debugbar.options.memory.precision', +'debugbar.options.auth.show_name','debugbar.options.auth.show_guards','debugbar.options.db.with_params','debugbar.options.db.exclude_paths','debugbar.options.db.backtrace', +'debugbar.options.db.backtrace_exclude_paths','debugbar.options.db.timeline','debugbar.options.db.duration_background','debugbar.options.db.explain.enabled','debugbar.options.db.hints', +'debugbar.options.db.show_copy','debugbar.options.db.slow_threshold','debugbar.options.db.memory_usage','debugbar.options.db.soft_limit','debugbar.options.db.hard_limit', +'debugbar.options.mail.timeline','debugbar.options.mail.show_body','debugbar.options.views.timeline','debugbar.options.views.data','debugbar.options.views.group', +'debugbar.options.views.exclude_paths','debugbar.options.route.label','debugbar.options.session.hiddens','debugbar.options.symfony_request.label','debugbar.options.symfony_request.hiddens', +'debugbar.options.events.data','debugbar.options.logs.file','debugbar.options.cache.values','debugbar.inject','debugbar.route_prefix', +'debugbar.route_middleware','debugbar.route_domain','debugbar.theme','debugbar.debug_backtrace_limit','dompdf.show_warnings', +'dompdf.public_path','dompdf.convert_entities','dompdf.options.font_dir','dompdf.options.font_cache','dompdf.options.temp_dir', +'dompdf.options.chroot','dompdf.options.allowed_protocols.file://.rules','dompdf.options.allowed_protocols.http://.rules','dompdf.options.allowed_protocols.https://.rules','dompdf.options.log_output_file', +'dompdf.options.enable_font_subsetting','dompdf.options.pdf_backend','dompdf.options.default_media_type','dompdf.options.default_paper_size','dompdf.options.default_paper_orientation', +'dompdf.options.default_font','dompdf.options.dpi','dompdf.options.enable_php','dompdf.options.enable_javascript','dompdf.options.enable_remote', +'dompdf.options.font_height_ratio','dompdf.options.enable_html5_parser','passport.guard','passport.private_key','passport.public_key', +'passport.connection','passport.client_uuids','passport.personal_access_client.id','passport.personal_access_client.secret','excel.exports.chunk_size', +'excel.exports.pre_calculate_formulas','excel.exports.strict_null_comparison','excel.exports.csv.delimiter','excel.exports.csv.enclosure','excel.exports.csv.line_ending', +'excel.exports.csv.use_bom','excel.exports.csv.include_separator_line','excel.exports.csv.excel_compatibility','excel.exports.csv.output_encoding','excel.exports.csv.test_auto_detect', +'excel.exports.properties.creator','excel.exports.properties.lastModifiedBy','excel.exports.properties.title','excel.exports.properties.description','excel.exports.properties.subject', +'excel.exports.properties.keywords','excel.exports.properties.category','excel.exports.properties.manager','excel.exports.properties.company','excel.imports.read_only', +'excel.imports.ignore_empty','excel.imports.heading_row.formatter','excel.imports.csv.delimiter','excel.imports.csv.enclosure','excel.imports.csv.escape_character', +'excel.imports.csv.contiguous','excel.imports.csv.input_encoding','excel.imports.properties.creator','excel.imports.properties.lastModifiedBy','excel.imports.properties.title', +'excel.imports.properties.description','excel.imports.properties.subject','excel.imports.properties.keywords','excel.imports.properties.category','excel.imports.properties.manager', +'excel.imports.properties.company','excel.imports.cells.middleware','excel.extension_detector.xlsx','excel.extension_detector.xlsm','excel.extension_detector.xltx', +'excel.extension_detector.xltm','excel.extension_detector.xls','excel.extension_detector.xlt','excel.extension_detector.ods','excel.extension_detector.ots', +'excel.extension_detector.slk','excel.extension_detector.xml','excel.extension_detector.gnumeric','excel.extension_detector.htm','excel.extension_detector.html', +'excel.extension_detector.csv','excel.extension_detector.tsv','excel.extension_detector.pdf','excel.value_binder.default','excel.cache.driver', +'excel.cache.batch.memory_limit','excel.cache.illuminate.store','excel.cache.default_ttl','excel.transactions.handler','excel.transactions.db.connection', +'excel.temporary_files.local_path','excel.temporary_files.local_permissions','excel.temporary_files.remote_disk','excel.temporary_files.remote_prefix','excel.temporary_files.force_resync_remote', +'flare.key','flare.flare_middleware','flare.flare_middleware.Spatie\\LaravelIgnition\\FlareMiddleware\\AddLogs.maximum_number_of_collected_logs','flare.flare_middleware.Spatie\\LaravelIgnition\\FlareMiddleware\\AddQueries.maximum_number_of_collected_queries','flare.flare_middleware.Spatie\\LaravelIgnition\\FlareMiddleware\\AddQueries.report_query_bindings', +'flare.flare_middleware.Spatie\\LaravelIgnition\\FlareMiddleware\\AddJobs.max_chained_job_reporting_depth','flare.flare_middleware.Spatie\\FlareClient\\FlareMiddleware\\CensorRequestBodyFields.censor_fields','flare.flare_middleware.Spatie\\FlareClient\\FlareMiddleware\\CensorRequestHeaders.headers','flare.send_logs_as_events','ignition.editor', +'ignition.theme','ignition.enable_share_button','ignition.register_commands','ignition.solution_providers','ignition.ignored_solution_providers', +'ignition.enable_runnable_solutions','ignition.remote_sites_path','ignition.local_sites_path','ignition.housekeeping_endpoint_prefix','ignition.settings_file_path', +'ignition.recorders','ignition.open_ai_key','ignition.with_stack_frame_arguments','ignition.argument_reducers',); registerArgumentsSet('middleware', -'web','api','admin','auth','auth.basic', -'superadmin','sysadmin','active.account','active.shop','subdomain', -'checkout','bindings','cache.headers','can','guest', -'signed','throttle',); +'web','api','admin','superadmin','sysadmin', +'auth','auth.basic','active.account','active.shop','checkout', +'bindings','cache.headers','can','guest','signed', +'throttle',); registerArgumentsSet('routes', 'debugbar.openhandler','debugbar.clockwork','debugbar.assets.css','debugbar.assets.js','debugbar.cache.delete', 'debugbar.queries.explain','languages.index','languages.create','languages.store','languages.translations.index', @@ -3391,172 +3735,116 @@ namespace PHPSTORM_META { 'passport.token.refresh','passport.authorizations.approve','passport.authorizations.deny','passport.tokens.index','passport.tokens.destroy', 'passport.clients.index','passport.clients.store','passport.clients.update','passport.clients.destroy','passport.scopes.index', 'passport.personal.tokens.index','passport.personal.tokens.store','passport.personal.tokens.destroy','ignition.healthCheck','ignition.executeSolution', -'ignition.updateConfig','api.payment_status','api.google_merchant_feed','storage_images', -'product_image','iq_image','user_shop_image','shop_product_image','translation', +'ignition.updateConfig','api.payment_status','api.google_merchant_feed','legal.data-protected', +'legal.imprint','legal.agb','contact.create','contact.store','zahlungsarten', +'versandkosten','storage_file','language.change','translation','product_image', +'storage_images','iq_image','user_shop_image','shop_product_image','checkout.checkout_card', +'checkout.checkout_card_final','checkout.transaction_status','checkout.transaction_status_post','checkout.transaction_approved','register_verify', 'data_protected','imprint','agb','contact_create','contact_store', -'register_user','register_user_member','register_user','register_user_finish','/', -'base.site','cron_jobs_action','cron_jobs_run','login', -'logout','register','password.request','password.email', -'password.reset','password.update','logout','change_login','change_login_confirm', -'data_protected','imprint','agb','loading_modal','home', -'user_update_email_confirm','user_check_mail','register_verify','status_register','status_verify', -'status_error','not_found','my.card_show','my.back_to_shop','homeparty', -'homeparty','user_blocked','wizard_create','wizard_register','wizard_store_create', -'wizard_store_register','wizard_payment','wizard_store_payment','wizard_delete_file','storage_file', -'storage_file','storage','home','modal_load','user_edit', -'user_edit','user_data_store','user_update_password','user_update_password','user_update_password_first', -'user_update_password_first','user_update_email','user_update_email','user_delete_account','user_delete_account', -'user_data_accepted_form','user_data_free','user_data_free_form','user_shop','user_shop_store', -'user_shop_register_form','user_shop_name_check','user_shop_translate','user_shop_tanslate_store','user_shop_upload_image', -'user_shop_delete_image','user_shop_orders','user_shop_order_detail','user_shop_orders_datatable','user_shop_api_orders', -'user_shop_api_orders_action','user_shop_api_orders_checkout','user_shop_api_orders_datatable','user_team_members','user_team_structure', -'user_team_structure','user_team_export','user_team_export_download','user_team_points','user_team_datatable_points', -'user_team_load','user_abos','user_abos_detail','user_abos_update','user_abo_datatable', -'user_customers','user_customer_detail','user_customer_edit','user_customer_add','user_customer_edit', -'user_customer_datatable','user_orders','user_orders_datatable','user_order_detail','user_order_my_delivery', -'user_order_my_delivery','user_order_my_list','user_order_my_list','user_order_my_payment','user_order_my_datatable', -'user_order_my_perform_request','user_order_my_custom_payment','user_order_payment_links','user_order_payment_links_datatable','user_order_payment_links_detail', -'user_order_payment_links_delete','user_homepartys','user_homeparty_detail','user_homeparty_detail','user_homeparty_guests', -'user_homeparty_guest_detail','user_homeparty_guest_detail','user_homeparty_order','user_homeparty_order','user_homeparty_delete', -'user_homeparty_datatable','user_shop_on_site_upload_image','user_shop_on_site_delete_image','user_membership','user_membership_store', -'user_documents','user_documents_store','user_documents_delete','user_payment_credit','user_payment_credit_datatable', -'user_payment_credit_item_datatable','user_downloadcenter','user_downloadcenter_search','admin_translate','admin_translate_update', -'admin_translate_file','admin_translate_file_edit','admin_translate_file_update','admin_sites','admin_sites_store', -'admin_sites_image_upload','admin_sites_image_delete','admin_sites_image_attribute','admin_product_show','admin_product_store', -'admin_product_edit','admin_product_copy','admin_product_delete','admin_product_image_upload','admin_product_image_delete', -'admin_product_image_attribute','admin_product_categories','admin_product_category_edit','admin_product_category_store','admin_product_category_delete', -'admin_product_ingredients','admin_product_ingredient_edit','admin_product_ingredient_store','admin_product_ingredient_delete','admin_product_category_image_upload', -'admin_product_category_image_delete','admin_product_category_image_attribute','admin_product_attributes','admin_product_attribute_store','admin_product_attribute_delete', -'admin_leads','admin_lead_edit','admin_lead_edit','admin_customers','admin_customer_detail', -'admin_customer_edit','admin_customer_edit','admin_customer_datatable','admin_lead_change_mail','admin_lead_change_mail', -'admin_lead_new_mail_verified','admin_lead_released','admin_lead_released','admin_lead_delete_file','admin_lead_store', -'admin_leads_datatable','admin_sales_users','admin_sales_users_detail','admin_sales_users_detail','admin_sales_users_datatable', -'admin_sales_customers','admin_sales_customers_detail','admin_sales_customers_detail','admin_sales_customers_datatable','admin_sales_store', -'admin_sales_invoice','admin_abos','admin_abos_detail','admin_abos_update','admin_abos_datatable', -'admin_payments_invoice','admin_payments_invoice','admin_payments_invoice_datatable','admin_payments_credit','admin_payments_credit', -'admin_payments_credit_datatable','admin_payments_credit_create','admin_payments_credit_delete','admin_payments_sales_volumes','admin_payments_sales_volumes_download', -'admin_payments_sales_volumes_datatable','admin_payments_taxadvisor','admin_payments_taxadvisor_download','admin_payments_taxadvisor_datatable','admin_business_show', -'admin_business_structure','admin_business_structure','admin_business_user_detail','admin_business_user_detail','admin_business_user_datatable', -'admin_business_points','admin_business_points_store','admin_business_points_datatable','admin_business_commissions','admin_business_commissions', -'admin_business_commissions_store','admin_business_commissions_datatable','admin_downloadcenter_files','admin_downloadcenter_upload','admin_downloadcenter_upload', -'admin_downloadcenter_file_edit','admin_downloadcenter_file','admin_downloadcenter_file','admin_downloadcenter_tags','admin_downloadcenter_item_store', -'admin_downloadcenter_item_delete','admin_downloadcenter_datatable','admin_users','admin_user_edit','admin_user_store', -'admin_user_delete','admin_user_login_as','admin_user_datatable','admin_shippings','admin_shipping_edit', -'admin_shipping_store','admin_shipping_delete','admin_shipping_price_delete','admin_shipping_country_delete','admin_payment_methods', -'admin_payment_method_store','admin_countries','admin_country_edit','admin_country_store','admin_levels', -'admin_level_store','admin_settings','admin_setting_store','sysadmin_tools','sysadmin_tool', -'sysadmin_tool_store','sysadmin_settings','sysadmin_setting_store','data_protected','imprint', -'agb','checkout.checkout_card','checkout.checkout_card_final','checkout.transaction_status','checkout.transaction_status', -'checkout.transaction_approved','/portal.home','portal.loading_modal','portal.change_login','portal.login.form', -'portal.login.send-otp','portal.login.otp.form','portal.login.verify-otp','portal.logout','portal.logout_change', -'portal.dashboard','portal.my_data.edit','portal.my_data.store','portal.my_orders','portal.my_orders.show', -'portal.my_orders.create','portal.my_subscriptions','portal.my_subscriptions.create','portal.settings','data_protected', -'imprint','agb','contact_create','contact_store','register_user', -'register_user_member','register_user','register_user_finish','/home','user.card_add_get', -'user.card_add_post','user.card_show','user.card_checkout_server','user.card_update','user.card_remove', -'user.card_delete','user.back_to_shop','user.domain_check','user.site','change_website_lang', -'data_protected','imprint','agb','contact_create','contact_store', -'register_user','register_user_member','register_user','register_user_finish','/home', -'user.card_add_get','user.card_add_post','user.card_show','user.card_checkout_server','user.card_update', -'user.card_remove','user.card_delete','user.back_to_shop','user.domain_check','user.site', -'change_website_lang',); +'public.tracking','public.tracking.check','main.register_user','main.register_user_member','main.register_user_post', +'main.register_user_finish','/','base.site',); registerArgumentsSet('views', 'admin.abo._detail','admin.abo._executions','admin.abo._order_abo','admin.abo._order_abo_show','admin.abo.detail', 'admin.abo.index','admin.abo.modal_abo_update','admin.attribute.index','admin.business._user_detail_in','admin.business.commissions', 'admin.business.index','admin.business.modal_add_points','admin.business.modal_edit_points','admin.business.points','admin.business.show', -'admin.business.structure','admin.business.user_detail','admin.category.edit','admin.category.form','admin.category.images', +'admin.business.structure','admin.business.user_detail','admin.business_optimized._user_detail_in','admin.business_optimized.error','admin.business_optimized.show', +'admin.business_optimized.structure','admin.business_optimized.user_detail','admin.category.edit','admin.category.form','admin.category.images', 'admin.category.index','admin.category.products','admin.change_email','admin.country.edit','admin.country.form', 'admin.country.index','admin.customer._customer_detail','admin.customer._detail','admin.customer._edit','admin.customer.detail', -'admin.customer.edit','admin.customer.index','admin.downloadcenter.file_edit','admin.downloadcenter.file_upload','admin.downloadcenter.files', -'admin.downloadcenter.tags','admin.index','admin.ingredient.edit','admin.ingredient.form','admin.ingredient.index', -'admin.lead.edit','admin.lead.index','admin.lead.m_data_form','admin.lead.m_data_form_edit','admin.lead.m_register_data', -'admin.level.index','admin.modal.business_user_detail','admin.modal.business_user_notfound','admin.modal.business_user_show','admin.modal.change_points', -'admin.modal.is_like_member','admin.modal.member','admin.modal.show_product','admin.modal.show_user_customers','admin.modal.user_level_edit', -'admin.payment.credit','admin.payment.credit_detail','admin.payment.credit_detail_long','admin.payment.invoice','admin.payment.modal_add_credit', -'admin.payment.modal_credit_status','admin.payment.salesvolume','admin.payment.taxadvisor','admin.payment_method.index','admin.product._form', -'admin.product.edit','admin.product.form','admin.product.images','admin.product.index','admin.sales._detail', +'admin.customer.edit','admin.customer.index','admin.dhl.cockpit','admin.dhl.create','admin.dhl.modal_create_shipment', +'admin.dhl.show','admin.downloadcenter.file_edit','admin.downloadcenter.file_upload','admin.downloadcenter.files','admin.downloadcenter.tags', +'admin.index','admin.ingredient.edit','admin.ingredient.form','admin.ingredient.index','admin.lead.edit', +'admin.lead.index','admin.lead.m_data_form','admin.lead.m_data_form_edit','admin.lead.m_register_data','admin.level.index', +'admin.modal.business_user_detail','admin.modal.business_user_notfound','admin.modal.business_user_show','admin.modal.change_points','admin.modal.is_like_member', +'admin.modal.member','admin.modal.show_product','admin.modal.show_user_customers','admin.modal.user_level_edit','admin.payment.credit', +'admin.payment.credit_detail','admin.payment.credit_detail_long','admin.payment.invoice','admin.payment.modal_add_credit','admin.payment.modal_credit_status', +'admin.payment.salesvolume','admin.payment.taxadvisor','admin.payment_method.index','admin.product._form','admin.product.edit', +'admin.product.form','admin.product.images','admin.product.index','admin.revenue.index','admin.sales._detail', 'admin.sales._detail_collection','admin.sales._detail_homparty','admin.sales._detail_homparty_total','admin.sales._detail_homparty_user','admin.sales._detail_shopping_order', 'admin.sales.customer_detail','admin.sales.customers','admin.sales.user_detail','admin.sales.users','admin.settings.index', 'admin.shipping.edit','admin.shipping.index','admin.site.edit','admin.site.form','admin.site.images', 'admin.user.edit','admin.user.index','auth.change','auth.login','auth.passwords.email', -'auth.passwords.reset','auth.register','dashboard._activities','dashboard._membership','dashboard._points', -'dashboard._reminder','dashboard._settings','dashboard._shop','dashboard.reminder','emails._auth', -'emails._blank','emails.auth','emails.blank','emails.checkout','emails.checkout_status', -'emails.collection_detail','emails.contact','emails.custom','emails.custom_payment','emails.custom_payment_detail', -'emails.customer_otp','emails.exception','emails.homeparty_detail','emails.homeparty_detail_total','emails.homeparty_detail_user', -'emails.info','emails.shopping_order_detail','emails.sys','errors.401','errors.403', -'errors.404','errors.419','errors.429','errors.500','errors.503', -'errors._403','errors.illustrated-layout','errors.layout','errors.minimal','flash::message', -'flash::modal','home','layouts.app','layouts.application','layouts.auth', -'layouts.includes.header-language','layouts.includes.layout-footer','layouts.includes.layout-navbar','layouts.includes.layout-navbar-without','layouts.includes.layout-sidenav', -'layouts.layout-1','layouts.layout-1-flex','layouts.layout-2','layouts.layout-2-flex','layouts.layout-2-without', -'layouts.layout-blank','layouts.layout-horizontal-sidenav','layouts.layout-without-navbar','layouts.layout-without-navbar-flex','layouts.layout-without-sidenav', -'legal._agb','legal._data_protected','legal._imprint','legal.agb','legal.agb_de', -'legal.data_protect_de','legal.data_protected','legal.imprint','legal.imprint_de','legal.shop_term_of_use', -'legal.shop_term_of_use_de','notifications::email','pagination::bootstrap-4','pagination::default','pagination::semantic-ui', -'pagination::simple-bootstrap-4','pagination::simple-default','pdf.credit','pdf.credit_details','pdf.credit_details_long', -'pdf.delivery','pdf.delivery-detail','pdf.delivery-homeparty','pdf.delivery-homeparty-detail','pdf.invoice', -'pdf.invoice-collection','pdf.invoice-detail','pdf.invoice-homeparty','pdf.invoice-homeparty-detail','pdf.invoice-journal-collection', -'portal.abo._create_basis_product','portal.abo._create_check','portal.abo._create_data','portal.abo._create_upgrade_products','portal.abo.my_abo', -'portal.abo.my_abo_create','portal.auth.change','portal.auth.login','portal.auth.verify-otp','portal.customer._edit', -'portal.customer._edit_form','portal.customer.edit','portal.dashboard','portal.layout.application','portal.layouts.application', -'portal.layouts.auth','portal.layouts.includes.header-language','portal.layouts.includes.layout-footer','portal.layouts.includes.layout-navbar','portal.layouts.includes.layout-navbar-without', -'portal.layouts.includes.layout-sidenav','portal.layouts.layout-2','portal.orde1r.my_orders','portal.order._detail','portal.order._detail_shopping_order', -'portal.order.my_order_show','portal.order.my_orders','status.not_found','status.status_error','status.status_register', -'status.status_verify','status.user_blocked','status.verify','sys.admin.cronjobs','sys.admin.customers', -'sys.admin.domain-ssl','sys.admin.import','sys.admin.import-show','sys.admin.shopping-orders','sys.index', -'sys.sales.index','sys.settings.index','sys.tools.business_structur','sys.tools.buyings_products','sys.tools.cronjobs', -'sys.tools.customers','sys.tools.domain-ssl','sys.tools.import','sys.tools.import-show','sys.tools.sales', -'sys.tools.shopping-orders','templates.index','translation._index','translation.eloquent_index','translation.index', -'translation.index_file','translation.translation_row','translation::forms._select','translation::forms.search','translation::forms.select', -'translation::forms.text','translation::icons.globe','translation::icons.translate','translation::languages.create','translation::languages.index', -'translation::languages.translations.create','translation::languages.translations.custom','translation::languages.translations.index','translation::layout','translation::nav', -'translation::notifications','user.abo._bak_index','user.abo.detail','user.abo.index','user.abo.modal_abo_show_products', -'user.abo.vat_info','user.components.user_shop_edit','user.components.user_shop_image','user.components.user_shop_on_site','user.components.user_shop_register', -'user.customer.add','user.customer.detail','user.customer.edit','user.customer.index','user.data_confirm', -'user.data_verify','user.delete_account','user.documents.index','user.downloadcenter.content-files','user.downloadcenter.index', -'user.edit','user.form','user.homeparty._address','user.homeparty._edit','user.homeparty.api_order_list', -'user.homeparty.detail','user.homeparty.guest_detail','user.homeparty.guests','user.homeparty.index','user.homeparty.modal_hp_show_products', -'user.homeparty.modal_show_products','user.homeparty.order','user.homeparty.self_guest_detail','user.homeparty.show_bonus','user.homeparty.show_calc_bonus_host', -'user.homeparty.show_products_order','user.homeparty.show_total_order','user.membership._abo_options','user.membership._change','user.membership._payment', -'user.membership._payment_order','user.membership._upgrade','user.membership.index','user.order._bak_shipping_me','user.order._bak_shipping_ot', -'user.order._list_delivery_vat_info','user.order.api_order_list','user.order.comp_product','user.order.custom_payment','user.order.delivery', -'user.order.detail','user.order.index','user.order.list','user.order.list_form','user.order.list_me', -'user.order.list_ot-customer','user.order.list_ot-member','user.order.payment.custom_payment','user.order.payment.index','user.order.shipping_me', -'user.order.shipping_ot','user.order.yard_view_form','user.payment.credit','user.shop','user.shop.detail', -'user.shop.sales.api_order_detail','user.shop.sales.api_order_list','user.shop.sales.api_order_list_total','user.shop.sales.api_orders','user.shop.sales.modal_api_order_detail', -'user.shop.sales.modal_api_order_shipping_detail','user.shop.sales.order_detail','user.shop.sales.orders','user.shop.translate','user.team._points_detail', -'user.team._points_sum','user.team.export','user.team.members','user.team.points','user.team.structure', -'user.update_email','user.update_password','user.update_password_first','user.update_password_first_form','user.user_form', -'user.user_new_form','user.wizard._payment','user.wizard.create','user.wizard.create_release','user.wizard.register', -'user.wizard.register_payment','user.wizard.register_release','vendor.flash.message','vendor.flash.modal','vendor.mail.html.button', -'vendor.mail.html.footer','vendor.mail.html.header','vendor.mail.html.layout','vendor.mail.html.message','vendor.mail.html.panel', -'vendor.mail.html.promotion','vendor.mail.html.promotion.button','vendor.mail.html.subcopy','vendor.mail.html.table','vendor.mail.markdown.button', -'vendor.mail.markdown.footer','vendor.mail.markdown.header','vendor.mail.markdown.layout','vendor.mail.markdown.message','vendor.mail.markdown.panel', -'vendor.mail.markdown.promotion','vendor.mail.markdown.promotion.button','vendor.mail.markdown.subcopy','vendor.mail.markdown.table','vendor.notifications.email', -'vendor.pagination.bootstrap-4','vendor.pagination.default','vendor.pagination.semantic-ui','vendor.pagination.simple-bootstrap-4','vendor.pagination.simple-default', -'vendor.translation.forms._select','vendor.translation.forms.search','vendor.translation.forms.select','vendor.translation.forms.text','vendor.translation.icons.globe', -'vendor.translation.icons.translate','vendor.translation.languages.create','vendor.translation.languages.index','vendor.translation.languages.translations.create','vendor.translation.languages.translations.custom', -'vendor.translation.languages.translations.index','vendor.translation.layout','vendor.translation.nav','vendor.translation.notifications','web.index', -'web.layouts.application','web.layouts.includes.footer','web.layouts.includes.header','web.layouts.layout','web.start', -'web.templates._bcategories','web.templates._card','web.templates._categories','web.templates._content_contact','web.templates._existenzgruendung', -'web.templates._side_contact','web.templates.agb','web.templates.aloevera','web.templates.anforderungsprofil','web.templates.card', -'web.templates.checkout','web.templates.checkout-final','web.templates.checkout-is-final','web.templates.contact-final','web.templates.datenschutz', -'web.templates.erreichbarkeit','web.templates.impressum','web.templates.karrierechancen','web.templates.kontakt','web.templates.partner', -'web.templates.produkte','web.templates.produkte-item','web.templates.produkte-show','web.templates.registrierung','web.templates.registrierung_finish', -'web.templates.ueber-uns','web.templates.vereinbarkeit','web.templates.versandkosten','web.templates.vorteile','web.templates.zahlungsarten', -'web.user.layouts.application','web.user.layouts.includes.footer','web.user.layouts.includes.footer-checkout','web.user.layouts.includes.footer-web','web.user.layouts.includes.header', -'web.user.layouts.includes.header-checkout','web.user.layouts.includes.header-web','web.user.layouts.layout','web.user.start','flash::message', -'flash::modal','laravel-exceptions-renderer::components.card','laravel-exceptions-renderer::components.context','laravel-exceptions-renderer::components.editor','laravel-exceptions-renderer::components.header', -'laravel-exceptions-renderer::components.icons.chevron-down','laravel-exceptions-renderer::components.icons.chevron-up','laravel-exceptions-renderer::components.icons.computer-desktop','laravel-exceptions-renderer::components.icons.moon','laravel-exceptions-renderer::components.icons.sun', -'laravel-exceptions-renderer::components.layout','laravel-exceptions-renderer::components.navigation','laravel-exceptions-renderer::components.theme-switcher','laravel-exceptions-renderer::components.trace','laravel-exceptions-renderer::components.trace-and-editor', -'laravel-exceptions-renderer::show','laravel-exceptions::401','laravel-exceptions::402','laravel-exceptions::403','laravel-exceptions::404', -'laravel-exceptions::419','laravel-exceptions::429','laravel-exceptions::500','laravel-exceptions::503','laravel-exceptions::layout', -'laravel-exceptions::minimal','notifications::email','pagination::bootstrap-4','pagination::bootstrap-5','pagination::default', -'pagination::semantic-ui','pagination::simple-bootstrap-4','pagination::simple-bootstrap-5','pagination::simple-default','pagination::simple-tailwind', -'pagination::tailwind','passport::authorize','translation::forms.search','translation::forms.select','translation::forms.text', -'translation::icons.globe','translation::icons.translate','translation::languages.create','translation::languages.index','translation::languages.translations.create', -'translation::languages.translations.index','translation::layout','translation::nav','translation::notifications',); +'auth.passwords.reset','auth.register','components.abo.product-table','components.abo.product-table-styles','dashboard._activities', +'dashboard._membership','dashboard._points','dashboard._reminder','dashboard._settings','dashboard._shop', +'dashboard.reminder','emails._auth','emails._blank','emails.auth','emails.blank', +'emails.checkout','emails.checkout_status','emails.collection_detail','emails.contact','emails.custom', +'emails.custom_payment','emails.custom_payment_detail','emails.customer_otp','emails.exception','emails.homeparty_detail', +'emails.homeparty_detail_total','emails.homeparty_detail_user','emails.info','emails.shopping_order_detail','emails.sys', +'errors.401','errors.403','errors.404','errors.419','errors.429', +'errors.500','errors.503','errors._403','errors.illustrated-layout','errors.layout', +'errors.minimal','flash::message','flash::modal','home','layouts.app', +'layouts.application','layouts.auth','layouts.includes.header-language','layouts.includes.layout-footer','layouts.includes.layout-navbar', +'layouts.includes.layout-navbar-without','layouts.includes.layout-sidenav','layouts.layout-1','layouts.layout-1-flex','layouts.layout-2', +'layouts.layout-2-flex','layouts.layout-2-without','layouts.layout-blank','layouts.layout-horizontal-sidenav','layouts.layout-without-navbar', +'layouts.layout-without-navbar-flex','layouts.layout-without-sidenav','legal._agb','legal._data_protected','legal._imprint', +'legal.agb','legal.agb_de','legal.data_protect_de','legal.data_protected','legal.imprint', +'legal.imprint_de','legal.shop_term_of_use','legal.shop_term_of_use_de','notifications::email','pagination::bootstrap-4', +'pagination::default','pagination::semantic-ui','pagination::simple-bootstrap-4','pagination::simple-default','pdf.credit', +'pdf.credit_details','pdf.credit_details_long','pdf.delivery','pdf.delivery-detail','pdf.delivery-homeparty', +'pdf.delivery-homeparty-detail','pdf.invoice','pdf.invoice-collection','pdf.invoice-detail','pdf.invoice-homeparty', +'pdf.invoice-homeparty-detail','pdf.invoice-journal-collection','portal.abo._create_basis_product','portal.abo._create_check','portal.abo._create_data', +'portal.abo._create_info','portal.abo._create_upgrade_products','portal.abo.componenten.product-table','portal.abo.componenten.product-table-styles','portal.abo.componenten.vat-info', +'portal.abo.my_abo','portal.abo.my_abo_create','portal.abo.no_shopping_user','portal.auth.change','portal.auth.login', +'portal.auth.verify-otp','portal.customer._edit','portal.customer._edit_form','portal.customer.edit','portal.dashboard', +'portal.dashboard.settings','portal.layout.application','portal.layouts.application','portal.layouts.auth','portal.layouts.includes.header-language', +'portal.layouts.includes.layout-footer','portal.layouts.includes.layout-navbar','portal.layouts.includes.layout-navbar-without','portal.layouts.includes.layout-sidenav','portal.layouts.layout-2', +'portal.my_orders','portal.order._detail','portal.order._detail_shopping_order','portal.order.my_order_show','portal.order.my_orders', +'status.not_found','status.status_error','status.status_register','status.status_verify','status.user_blocked', +'status.verify','sys.admin.cronjobs','sys.admin.customers','sys.admin.domain-ssl','sys.admin.import', +'sys.admin.import-show','sys.admin.shopping-orders','sys.index','sys.sales.index','sys.settings.index', +'sys.tools.business_structur','sys.tools.buyings_products','sys.tools.cronjobs','sys.tools.customers','sys.tools.domain-ssl', +'sys.tools.import','sys.tools.import-show','sys.tools.sales','sys.tools.shopping-orders','templates.index', +'translation._index','translation.eloquent_index','translation.index','translation.index_file','translation.translation_row', +'translation::forms._select','translation::forms.search','translation::forms.select','translation::forms.text','translation::icons.globe', +'translation::icons.translate','translation::languages.create','translation::languages.index','translation::languages.translations.create','translation::languages.translations.custom', +'translation::languages.translations.index','translation::layout','translation::nav','translation::notifications','user.abo._bak_index', +'user.abo.detail','user.abo.index','user.abo.modal_abo_show_products','user.abo.vat_info','user.components.user_shop_edit', +'user.components.user_shop_edit_name','user.components.user_shop_image','user.components.user_shop_on_site','user.components.user_shop_register','user.customer.add', +'user.customer.detail','user.customer.edit','user.customer.index','user.data_confirm','user.data_verify', +'user.delete_account','user.documents.index','user.downloadcenter.content-files','user.downloadcenter.index','user.edit', +'user.form','user.homeparty._address','user.homeparty._edit','user.homeparty.api_order_list','user.homeparty.detail', +'user.homeparty.guest_detail','user.homeparty.guests','user.homeparty.index','user.homeparty.modal_hp_show_products','user.homeparty.modal_show_products', +'user.homeparty.order','user.homeparty.self_guest_detail','user.homeparty.show_bonus','user.homeparty.show_calc_bonus_host','user.homeparty.show_products_order', +'user.homeparty.show_total_order','user.membership._abo_options','user.membership._change','user.membership._payment','user.membership._payment_order', +'user.membership._upgrade','user.membership.index','user.order._bak_shipping_me','user.order._bak_shipping_ot','user.order._list_delivery_vat_info', +'user.order.api_order_list','user.order.comp_product','user.order.custom_payment','user.order.delivery','user.order.detail', +'user.order.index','user.order.list','user.order.list_form','user.order.list_me','user.order.list_ot-customer', +'user.order.list_ot-member','user.order.payment.custom_payment','user.order.payment.index','user.order.shipping_me','user.order.shipping_ot', +'user.order.yard_view_form','user.payment.credit','user.shop','user.shop.detail','user.shop.sales.api_order_detail', +'user.shop.sales.api_order_list','user.shop.sales.api_order_list_total','user.shop.sales.api_orders','user.shop.sales.modal_api_order_detail','user.shop.sales.modal_api_order_shipping_detail', +'user.shop.sales.order_detail','user.shop.sales.orders','user.shop.translate','user.shop_edit_name','user.team._points_detail', +'user.team._points_sum','user.team.export','user.team.marketingplan','user.team.members','user.team.points', +'user.team.show','user.team.structure','user.team.structureOld','user.update_email','user.update_password', +'user.update_password_first','user.update_password_first_form','user.user_form','user.user_new_form','user.wizard._payment', +'user.wizard.create','user.wizard.create_release','user.wizard.register','user.wizard.register_payment','user.wizard.register_release', +'vendor.flash.message','vendor.flash.modal','vendor.mail.html.button','vendor.mail.html.footer','vendor.mail.html.header', +'vendor.mail.html.layout','vendor.mail.html.message','vendor.mail.html.panel','vendor.mail.html.promotion','vendor.mail.html.promotion.button', +'vendor.mail.html.subcopy','vendor.mail.html.table','vendor.mail.markdown.button','vendor.mail.markdown.footer','vendor.mail.markdown.header', +'vendor.mail.markdown.layout','vendor.mail.markdown.message','vendor.mail.markdown.panel','vendor.mail.markdown.promotion','vendor.mail.markdown.promotion.button', +'vendor.mail.markdown.subcopy','vendor.mail.markdown.table','vendor.notifications.email','vendor.pagination.bootstrap-4','vendor.pagination.default', +'vendor.pagination.semantic-ui','vendor.pagination.simple-bootstrap-4','vendor.pagination.simple-default','vendor.translation.forms._select','vendor.translation.forms.search', +'vendor.translation.forms.select','vendor.translation.forms.text','vendor.translation.icons.globe','vendor.translation.icons.translate','vendor.translation.languages.create', +'vendor.translation.languages.index','vendor.translation.languages.translations.create','vendor.translation.languages.translations.custom','vendor.translation.languages.translations.index','vendor.translation.layout', +'vendor.translation.nav','vendor.translation.notifications','web.index','web.layouts.application','web.layouts.includes.footer', +'web.layouts.includes.header','web.layouts.layout','web.start','web.templates._bcategories','web.templates._card', +'web.templates._categories','web.templates._content_contact','web.templates._existenzgruendung','web.templates._side_contact','web.templates.agb', +'web.templates.aloevera','web.templates.anforderungsprofil','web.templates.card','web.templates.checkout','web.templates.checkout-final', +'web.templates.checkout-is-final','web.templates.contact-final','web.templates.datenschutz','web.templates.erreichbarkeit','web.templates.impressum', +'web.templates.karrierechancen','web.templates.kontakt','web.templates.partner','web.templates.produkte','web.templates.produkte-item', +'web.templates.produkte-show','web.templates.registrierung','web.templates.registrierung_finish','web.templates.ueber-uns','web.templates.vereinbarkeit', +'web.templates.versandkosten','web.templates.vorteile','web.templates.zahlungsarten','web.user.layouts.application','web.user.layouts.includes.footer', +'web.user.layouts.includes.footer-checkout','web.user.layouts.includes.footer-web','web.user.layouts.includes.header','web.user.layouts.includes.header-checkout','web.user.layouts.includes.header-web', +'web.user.layouts.layout','web.user.start','flash::message','flash::modal','laravel-exceptions-renderer::components.card', +'laravel-exceptions-renderer::components.context','laravel-exceptions-renderer::components.editor','laravel-exceptions-renderer::components.header','laravel-exceptions-renderer::components.icons.chevron-down','laravel-exceptions-renderer::components.icons.chevron-up', +'laravel-exceptions-renderer::components.icons.computer-desktop','laravel-exceptions-renderer::components.icons.moon','laravel-exceptions-renderer::components.icons.sun','laravel-exceptions-renderer::components.layout','laravel-exceptions-renderer::components.navigation', +'laravel-exceptions-renderer::components.theme-switcher','laravel-exceptions-renderer::components.trace','laravel-exceptions-renderer::components.trace-and-editor','laravel-exceptions-renderer::show','laravel-exceptions::401', +'laravel-exceptions::402','laravel-exceptions::403','laravel-exceptions::404','laravel-exceptions::419','laravel-exceptions::429', +'laravel-exceptions::500','laravel-exceptions::503','laravel-exceptions::layout','laravel-exceptions::minimal','notifications::email', +'pagination::bootstrap-4','pagination::bootstrap-5','pagination::default','pagination::semantic-ui','pagination::simple-bootstrap-4', +'pagination::simple-bootstrap-5','pagination::simple-default','pagination::simple-tailwind','pagination::tailwind','passport::authorize', +'translation::forms.search','translation::forms.select','translation::forms.text','translation::icons.globe','translation::icons.translate', +'translation::languages.create','translation::languages.index','translation::languages.translations.create','translation::languages.translations.index','translation::layout', +'translation::nav','translation::notifications',); registerArgumentsSet('translations', 'auth.failed','auth.password','auth.throttle','pagination.previous','pagination.next', 'passwords.reset','passwords.sent','passwords.throttled','passwords.token','passwords.user', @@ -3609,116 +3897,130 @@ namespace PHPSTORM_META { 'abo.abo_copy_abo_interval','abo.error_abo_interval','abo.error_next_date','abo.checkout_mail_abo_hl','abo.checkout_mail_abo_start', 'abo.checkout_mail_abo_info','abo.abo_new','abo.abo_okay','abo.abo_hold','abo.abo_cancel', 'abo.abo_finish','abo.abo_inactive','abo.abo_grace','abo.pros_hl','abo.pros_list', -'abo.abo_pros','abo.abo_order_hl','abo.add_product','abo.product_prices_career_level_info','abo.product_prices_career_level_cpay_info', -'abo.error_email_has_abo','abo.abo_assigned','abo.base','abo.upgrade','abo.abo_type_info', -'abo.abo_type_info_base','abo.need_basis_product','abo.abo_item_not_found','abo.product_not_found','abo.create_abo', -'abo.data','abo.check','abo.choose','abo.order','abo.basis_product', -'abo.upgrade_products','abo.my_address','abo.my_address_check','abo.my_address_check_info','abo.edit', -'abo.confirm_and_next','account.','account.BIC','account.IBAN','account.VAT_ID_number', -'account.VAT_copy_1','account.VAT_liability','account.account_holder','account.bank_data','account.delivery_address', -'account.firstname_lastname','account.invoice_address','account.my_credit','account.vat_data','account.info_vat_numbers', -'account.new_vat_validate','account.btn_vat_validate','account.phone_need_error','account.phone_need_note','account.required_for_commission_payments', -'account.reverse_charge_action_1','account.reverse_charge_action_2','account.reverse_charge_copy_1','account.reverse_charge_note_1','account.reverse_charge_procedure', -'account.tax_number','account.taxable_sales_1','account.taxable_sales_2','account.validator_creditcard','account.validator_date', -'account.validator_digits','account.validator_email','account.validator_equalTo','account.validator_max','account.validator_maxlength', -'account.validator_min','account.validator_minlength','account.validator_number','account.validator_range','account.validator_rangelength', -'account.validator_required','account.validator_url','actions.','actions.cancel','actions.confirm', -'actions.file_is_too_big','actions.image_too_small ','actions.invalid_file','actions.really_delete_picture','actions.rotate', -'actions.save_image','actions.search_file_or_drag_drop','actions.upload_photo','actions.dictDefaultMessage','actions.dictFallbackMessage', -'actions.dictFallbackText','actions.dictFileTooBig','actions.dictInvalidFileType','actions.dictResponseError','actions.dictCancelUpload', -'actions.dictRemoveFile','actions.dictMaxFilesExceeded','auth.not_found','auth.failed_customer','cal.months.April', -'cal.months.August','cal.months.December','cal.months.February','cal.months.January','cal.months.July', -'cal.months.June','cal.months.March','cal.months.May','cal.months.November','cal.months.October', -'cal.months.September','cal.months_short.Apr','cal.months_short.Aug','cal.months_short.Dec','cal.months_short.Feb', -'cal.months_short.Jan','cal.months_short.Jul','cal.months_short.Jun','cal.months_short.Mar','cal.months_short.May', -'cal.months_short.Nov','cal.months_short.Oct','cal.months_short.Sep','cal.weekdays.Friday','cal.weekdays.Monday', -'cal.weekdays.Saturday','cal.weekdays.Sunday','cal.weekdays.Thursday','cal.weekdays.Tuesday','cal.weekdays.Wednesday', -'cal.weekdays_min.Fr','cal.weekdays_min.Mo','cal.weekdays_min.Sa','cal.weekdays_min.Su','cal.weekdays_min.Th', -'cal.weekdays_min.Tu','cal.weekdays_min.We','cal.weekdays_short.Fri','cal.weekdays_short.Mon','cal.weekdays_short.Sat', -'cal.weekdays_short.Sun','cal.weekdays_short.Thu','cal.weekdays_short.Tue','cal.weekdays_short.Wed','customer.about_shop', -'customer.add_customer_without_email','customer.add_customer_without_email_info1','customer.assigned','customer.assigned_counsellor','customer.check', -'customer.check_and_next','customer.check_and_save','customer.client_sovereignty','customer.counsellor_allocate','customer.created', -'customer.customer_add','customer.customer_billing_address','customer.customer_data','customer.customer_details','customer.customer_has_already_buy', -'customer.customer_has_already_purchased','customer.customer_has_not_yet_purchased','customer.customer_is_not_subscribed_to_newsletter','customer.customer_is_subscribed_to_newsletter','customer.customer_sovereignty_info1', -'customer.date','customer.edit','customer.edit_customer_data','customer.enter','customer.is_counsellor', -'customer.newsletter_subscribed_copy1','customer.next_without_email','customer.under_review','customer.select','dataprotect.data_protect', -'dataprotect.data_protect_copy1','dataprotect.data_protect_copy2','dataprotect.data_protect_copy3','dataprotect.data_protect_copy4','dataprotect.data_protect_copy5', -'dataprotect.data_protect_copy6','dataprotect.data_protect_copy7','dataprotect.data_protect_copy8','dataprotect.data_protect_copy9','dataprotect.data_protect_copy10', -'dataprotect.data_protect_copy11','dataprotect.data_protect_copy12','dataprotect.data_protect_copy13','dataprotect.data_protect_stand_shop','dataprotect.data_protect_stand_site', -'email.account_active','email.account_active_copy1line','email.account_incomplete_copy1line','email.activate_copy','email.active_copy1line', -'email.button_account','email.change_e_mail','email.checkout_copy1line','email.checkout_copy3line','email.checkout_copy3line_extern', -'email.checkout_mail_bank_bic','email.checkout_mail_bank_code','email.checkout_mail_bank_holder','email.checkout_mail_bank_iban','email.checkout_mail_bank_name', -'email.checkout_mail_bank_total','email.checkout_mail_deliver_addess','email.checkout_mail_deliver_customer','email.checkout_mail_hl1','email.checkout_mail_invoice_addess', -'email.checkout_mail_order_for_extern','email.checkout_mail_order_for_me','email.checkout_mail_order_for_membership','email.checkout_mail_order_for_ot','email.checkout_mail_order_for_wizard', -'email.checkout_mail_pay_approved','email.checkout_mail_pay_error','email.checkout_mail_pay_info','email.checkout_mail_pay_invoice_open','email.checkout_mail_pay_pre', -'email.checkout_mail_pay_pre_c1','email.checkout_mail_pay_pre_c2','email.checkout_mail_pay_ref','email.checkout_mail_pay_success','email.checkout_mail_pay_with', -'email.checkout_mail_same_address','email.checkout_mail_shipping','email.checkout_mail_status_info','email.checkout_mail_subtotal_ws','email.checkout_mail_system_status', -'email.checkout_mail_tax','email.checkout_mail_tax_info','email.checkout_mail_total','email.checkout_mail_your_mail','email.checkout_subject', -'email.checkout_subject_extern','email.checkout_subject_paid','email.copy2line','email.copy3line','email.copy_to_browser', -'email.credit_copy1line','email.credit_title','email.dear_mrs','email.dear_sir','email.email', -'email.email_incomplete','email.email_subject','email.email_verify','email.email_verify_copy1line','email.first_name', -'email.footer_copy1','email.footer_copy2','email.footer_copy3','email.greetings','email.hello', -'email.invoice_copy1line','email.invoice_title','email.invoice_subject','email.last_name','email.mail_confirm', -'email.message','email.phone','email.request_from','email.reset_pass_copy1line','email.reset_passwort', -'email.sales_partnership','email.sales_partnership_message','email.salutation','email.sender','email.status_copy1line', -'email.subject','email.subject_activate','email.subject_reset','email.update_level_copy1line','email.update_level_title', -'email.verify_copy1line','email.verify_e_mail','email.your_request_from','email.your_custom_payout','email.your_custom_abo_payout', -'email.subject_custom_payout','email.subject_custom_abo_payout','email.button_custom_payout','email.checkout_custom_payout','gtc.gtc', -'gtc.gtc_copy1','gtc.gtc_copy2','gtc.gtc_copy3','gtc.gtc_copy4','gtc.gtc_copy5', -'gtc.gtc_copy6','gtc.gtc_copy7','gtc.gtc_copy8','gtc.gtc_copy9','gtc.', -'home.','home.MIVITA_Consultancy_agreement','home.active_role','home.activities','home.adjust_data', -'home.adviser_membership_active','home.adviser_onlineshop_active','home.adviser_onlineshop_inactive','home.advisor_account_inactive','home.at', -'home.change_your_email_address','home.change_your_personal_data','home.change_your_personal_password','home.create_your_personal_password.','home.current_points_for', -'home.data','home.data_complete_unlocked','home.declaration_of_consent','home.email_verified','home.expired_on', -'home.log_out_and_see_you_soon','home.login','home.manage_membership','home.manage_membership_now_here','home.membership', -'home.open_since','home.open_your_shop','home.privacy_policy_approved','home.security','home.settings_your_shop', -'home.shop_not_booked','home.today_is','home.until','home.welcome_back','home.your_shop', -'homeparty.','homeparty.acceptect_data_protection','homeparty.add_as_guest','homeparty.add_product','homeparty.completed', -'homeparty.confirm_my_data_is_correct_and_complete','homeparty.copy','homeparty.copy_link','homeparty.copy_link_info','homeparty.country_can_no_longer_be_changed_after_created', -'homeparty.create_delivery_address_host_info','homeparty.create_guest','homeparty.create_guests','homeparty.create_guests_info','homeparty.create_new_homeparty', -'homeparty.credit_bonus','homeparty.credit_homeparty_voucher','homeparty.current_bonus_view','homeparty.data_protection','homeparty.data_protection_reasons_your_personal_data_will_not_be_shown', -'homeparty.deduct_points_by_voucher','homeparty.delivery_address_homeparty','homeparty.delivery_directly_to_the_guest','homeparty.delivery_to_host','homeparty.description_welcome_text', -'homeparty.enter_your_personal_data_for_homeparty','homeparty.event_date','homeparty.event_place','homeparty.from','homeparty.general_overview', -'homeparty.guest','homeparty.guest_delete_really','homeparty.guest_lists','homeparty.guest_order_sent_directly_info','homeparty.guests', -'homeparty.homeparty','homeparty.homeparty_add_host_address_info','homeparty.homeparty_delete_really','homeparty.homeparty_invoice_info','homeparty.homeparty_manage', -'homeparty.host','homeparty.host_address_save','homeparty.host_can_not_delete','homeparty.host_homeparty','homeparty.host_organiser_event', -'homeparty.invitation','homeparty.invitation_link_for_guests','homeparty.invoice_address','homeparty.let_your_guests_fill_in_their_own_details','homeparty.manage', -'homeparty.missing','homeparty.next_bonus','homeparty.order','homeparty.order_can_be_send_delivery_address_info','homeparty.order_create', -'homeparty.order_host','homeparty.order_show','homeparty.please_enter_delivery_address_info','homeparty.revoke_consent_at_any_time','homeparty.shipping_costs_host', -'homeparty.target_turnover','homeparty.voucher','homeparty.voucher_bonus','homeparty.voucher_bonus_cannot_be_applied','homeparty.voucher_total', -'homeparty.your_MIVITA_advice','homeparty.your_data_has_been_successfully_created_have_fun','homeparty.your_host','homeparty.your_hostess','homeparty.welcome_copy', -'membership.','membership.MIVITA_BUSINESS_Paket','membership.abo_copy_1','membership.abo_copy_2','membership.abo_copy_3', -'membership.active','membership.active_package','membership.booked_package','membership.change','membership.change_copy_1', -'membership.change_copy_2','membership.consultant_membership','membership.consultant_online_shop','membership.contract_renewal','membership.deactivate', -'membership.details','membership.downgrade_membership_is_not_possible','membership.end','membership.end_button','membership.end_checkbox', -'membership.end_copy_1','membership.end_copy_2','membership.end_copy_3','membership.expired_on','membership.home_copy_SEPA_32', -'membership.home_copy_SEPA_33','membership.home_copy_SEPA_36','membership.home_copy_alert_31','membership.home_copy_alert_35','membership.home_copy_alert_36', -'membership.home_copy_alert_36_today','membership.home_copy_last_31','membership.home_copy_last_33','membership.home_copy_last_34','membership.home_copy_last_35', -'membership.home_copy_last_36','membership.home_hl','membership.inactive','membership.is_no_longer_possible_to_change_package','membership.membership', -'membership.membership_was_renewed','membership.open_payment_options','membership.payment_copy_1','membership.payment_copy_2','membership.payment_has_been_made', -'membership.renewal_is_active_membership_fee_automatic','membership.status','membership.until','membership.upgrade','membership.upgrade_copy_1', -'membership.upgrade_copy_2','membership.upgrade_package_and_proceed_payment','membership.we_do_not_collect_membership_fee','membership.your_booked_package','membership.info_contract_renewal', -'membership.alert_contract_renewal','msg.shipping_country_was_not_found','msg.shipping_country_was_not_correctly','msg.shopping_cart_was_shipping_free','msg.shipping_cost_cannot_be_0', -'msg.shipping_costs_were_not_calculated_correctly','msg.compensation_products_cannot_be_0','msg.link_for_homeparty_not_found','msg.contact_delete','msg.error_occurred_with_order', -'msg.abo_deaktivert','msg.error_checkbox_not_confirm','msg.no_change_made','msg.booked_package_has_been_changed','msg.cancel_membership_is_requested', -'msg.file_uploaded','msg.file_empty','msg.file_deleted','msg.file_not_found','msg.country_account_has_been_changed__cost_has_been_reset', -'msg.your_shopping_cart_is_empty_please_add_products_first.','msg.homeparty_guest_delete','msg.homeparty_delete','msg.VATID_could_not_be_validated','msg.VATID_successfully_entered', -'msg.reverse_charge_procedure_and_VATID_deleted','msg.no_id_card_deposited_please_upload_first','msg.no_trade_licence_deposited_please_upload_first','msg.please_enter_reason_why_you_not_need_trade_licence','msg.please_select_compensation_product', -'msg.please_select_count_compensation_products','msg.user_not_found','msg.shopping_cart_was_not_user_shop','msg.shopping_instance_not_found','msg.shopping_user_not_found', -'msg.account_released','navigation.documents','navigation.add','navigation.attribute','navigation.business', -'navigation.career_level','navigation.categories','navigation.clients','navigation.commissions','navigation.contents', -'navigation.countries','navigation.credit','navigation.do_order','navigation.edit','navigation.export', -'navigation.general','navigation.home','navigation.ingredients','navigation.invoice','navigation.language', -'navigation.languages','navigation.logout','navigation.manage','navigation.member','navigation.new_member', -'navigation.member_register','navigation.membership','navigation.modules','navigation.my_account','navigation.my_clients', -'navigation.my_data','navigation.my_homeparty','navigation.my_membership','navigation.my_orders','navigation.my_shop', -'navigation.my_team','navigation.order','navigation.orders','navigation.overview','navigation.payment_methods', -'navigation.payments','navigation.points','navigation.products','navigation.sales_volumes','navigation.settings', -'navigation.shipping_costs','navigation.start_site','navigation.structure','navigation.system_settings','navigation.translate', -'navigation.translation','navigation.trigger','navigation.user_roles','navigation.tax_advisor','navigation.downloadcenter', -'navigation.files','navigation.tags','navigation.myabos','navigation.customerabos','navigation.myabo', -'navigation.customerabo','navigation.abo','navigation.abos','navigation.payment_links','navigation.dashboard', -'navigation.shop','navigation.to_shop','order.add_customer','order.advertising_material','order.adviser_collective_invoice', +'abo.abo_pros','abo.abo_order_hl','abo.abo_order_info_2','abo.add_product','abo.product_prices_career_level_info', +'abo.product_prices_career_level_cpay_info','abo.error_email_has_abo','abo.abo_assigned','abo.base','abo.upgrade', +'abo.abo_type_info','abo.abo_type_info_base','abo.need_basis_product','abo.abo_item_not_found','abo.product_not_found', +'abo.create_abo','abo.info','abo.data','abo.check','abo.choose', +'abo.order','abo.basis_product','abo.upgrade_products','abo.base_product','abo.upgrade_product', +'abo.my_address','abo.my_address_check','abo.my_address_check_info','abo.edit','abo.confirm_and_next', +'abo.understood_and_next','abo.change_my_data_empty','abo.abo_error_basis_product','abo.back','account.', +'account.BIC','account.IBAN','account.VAT_ID_number','account.VAT_copy_1','account.VAT_liability', +'account.account_holder','account.bank_data','account.delivery_address','account.firstname_lastname','account.invoice_address', +'account.my_credit','account.vat_data','account.info_vat_numbers','account.new_vat_validate','account.btn_vat_validate', +'account.phone_need_error','account.phone_need_note','account.required_for_commission_payments','account.reverse_charge_action_1','account.reverse_charge_action_2', +'account.reverse_charge_copy_1','account.reverse_charge_note_1','account.reverse_charge_procedure','account.tax_number','account.taxable_sales_1', +'account.taxable_sales_2','account.validator_creditcard','account.validator_date','account.validator_digits','account.validator_email', +'account.validator_equalTo','account.validator_max','account.validator_maxlength','account.validator_min','account.validator_minlength', +'account.validator_number','account.validator_range','account.validator_rangelength','account.validator_required','account.validator_url', +'actions.','actions.cancel','actions.confirm','actions.file_is_too_big','actions.image_too_small ', +'actions.invalid_file','actions.really_delete_picture','actions.rotate','actions.save_image','actions.search_file_or_drag_drop', +'actions.upload_photo','actions.dictDefaultMessage','actions.dictFallbackMessage','actions.dictFallbackText','actions.dictFileTooBig', +'actions.dictInvalidFileType','actions.dictResponseError','actions.dictCancelUpload','actions.dictRemoveFile','actions.dictMaxFilesExceeded', +'auth.not_found','auth.failed_customer','cal.months.April','cal.months.August','cal.months.December', +'cal.months.February','cal.months.January','cal.months.July','cal.months.June','cal.months.March', +'cal.months.May','cal.months.November','cal.months.October','cal.months.September','cal.months_short.Apr', +'cal.months_short.Aug','cal.months_short.Dec','cal.months_short.Feb','cal.months_short.Jan','cal.months_short.Jul', +'cal.months_short.Jun','cal.months_short.Mar','cal.months_short.May','cal.months_short.Nov','cal.months_short.Oct', +'cal.months_short.Sep','cal.weekdays.Friday','cal.weekdays.Monday','cal.weekdays.Saturday','cal.weekdays.Sunday', +'cal.weekdays.Thursday','cal.weekdays.Tuesday','cal.weekdays.Wednesday','cal.weekdays_min.Fr','cal.weekdays_min.Mo', +'cal.weekdays_min.Sa','cal.weekdays_min.Su','cal.weekdays_min.Th','cal.weekdays_min.Tu','cal.weekdays_min.We', +'cal.weekdays_short.Fri','cal.weekdays_short.Mon','cal.weekdays_short.Sat','cal.weekdays_short.Sun','cal.weekdays_short.Thu', +'cal.weekdays_short.Tue','cal.weekdays_short.Wed','customer.about_shop','customer.add_customer_without_email','customer.add_customer_without_email_info1', +'customer.assigned','customer.assigned_counsellor','customer.check','customer.check_and_next','customer.check_and_save', +'customer.client_sovereignty','customer.counsellor_allocate','customer.created','customer.customer_add','customer.customer_billing_address', +'customer.customer_data','customer.customer_details','customer.customer_has_already_buy','customer.customer_has_already_purchased','customer.customer_has_not_yet_purchased', +'customer.customer_is_not_subscribed_to_newsletter','customer.customer_is_subscribed_to_newsletter','customer.customer_sovereignty_info1','customer.date','customer.edit', +'customer.edit_customer_data','customer.enter','customer.is_counsellor','customer.newsletter_subscribed_copy1','customer.next_without_email', +'customer.under_review','customer.select','dataprotect.data_protect','dataprotect.data_protect_copy1','dataprotect.data_protect_copy2', +'dataprotect.data_protect_copy3','dataprotect.data_protect_copy4','dataprotect.data_protect_copy5','dataprotect.data_protect_copy6','dataprotect.data_protect_copy7', +'dataprotect.data_protect_copy8','dataprotect.data_protect_copy9','dataprotect.data_protect_copy10','dataprotect.data_protect_copy11','dataprotect.data_protect_copy12', +'dataprotect.data_protect_copy13','dataprotect.data_protect_stand_shop','dataprotect.data_protect_stand_site','email.account_active','email.account_active_copy1line', +'email.account_incomplete_copy1line','email.activate_copy','email.active_copy1line','email.button_account','email.change_e_mail', +'email.checkout_copy1line','email.checkout_copy3line','email.checkout_copy3line_extern','email.checkout_mail_bank_bic','email.checkout_mail_bank_code', +'email.checkout_mail_bank_holder','email.checkout_mail_bank_iban','email.checkout_mail_bank_name','email.checkout_mail_bank_total','email.checkout_mail_deliver_addess', +'email.checkout_mail_deliver_customer','email.checkout_mail_hl1','email.checkout_mail_invoice_addess','email.checkout_mail_order_for_extern','email.checkout_mail_order_for_me', +'email.checkout_mail_order_for_membership','email.checkout_mail_order_for_ot','email.checkout_mail_order_for_wizard','email.checkout_mail_pay_approved','email.checkout_mail_pay_error', +'email.checkout_mail_pay_info','email.checkout_mail_pay_invoice_open','email.checkout_mail_pay_pre','email.checkout_mail_pay_pre_c1','email.checkout_mail_pay_pre_c2', +'email.checkout_mail_pay_ref','email.checkout_mail_pay_success','email.checkout_mail_pay_with','email.checkout_mail_same_address','email.checkout_mail_shipping', +'email.checkout_mail_status_info','email.checkout_mail_subtotal_ws','email.checkout_mail_system_status','email.checkout_mail_tax','email.checkout_mail_tax_info', +'email.checkout_mail_total','email.checkout_mail_your_mail','email.checkout_subject','email.checkout_subject_extern','email.checkout_subject_paid', +'email.copy2line','email.copy3line','email.copy_to_browser','email.credit_copy1line','email.credit_title', +'email.dear_mrs','email.dear_sir','email.email','email.email_incomplete','email.email_subject', +'email.email_verify','email.email_verify_copy1line','email.first_name','email.footer_copy1','email.footer_copy2', +'email.footer_copy3','email.greetings','email.hello','email.invoice_copy1line','email.invoice_title', +'email.invoice_subject','email.last_name','email.mail_confirm','email.message','email.phone', +'email.request_from','email.reset_pass_copy1line','email.reset_passwort','email.sales_partnership','email.sales_partnership_message', +'email.salutation','email.sender','email.status_copy1line','email.subject','email.subject_activate', +'email.subject_reset','email.update_level_copy1line','email.update_level_title','email.verify_copy1line','email.verify_e_mail', +'email.your_request_from','email.your_custom_payout','email.your_custom_abo_payout','email.subject_custom_payout','email.subject_custom_abo_payout', +'email.button_custom_payout','email.checkout_custom_payout','gtc.gtc','gtc.gtc_copy1','gtc.gtc_copy2', +'gtc.gtc_copy3','gtc.gtc_copy4','gtc.gtc_copy5','gtc.gtc_copy6','gtc.gtc_copy7', +'gtc.gtc_copy8','gtc.gtc_copy9','gtc.','home.','home.MIVITA_Consultancy_agreement', +'home.active_role','home.activities','home.adjust_data','home.adviser_membership_active','home.adviser_onlineshop_active', +'home.adviser_onlineshop_inactive','home.advisor_account_inactive','home.at','home.change_your_email_address','home.change_your_personal_data', +'home.change_your_personal_password','home.create_your_personal_password.','home.current_points_for','home.data','home.data_complete_unlocked', +'home.declaration_of_consent','home.email_verified','home.expired_on','home.log_out_and_see_you_soon','home.login', +'home.manage_membership','home.manage_membership_now_here','home.membership','home.open_since','home.open_your_shop', +'home.privacy_policy_approved','home.security','home.settings_your_shop','home.shop_not_booked','home.today_is', +'home.until','home.welcome_back','home.your_shop','homeparty.','homeparty.acceptect_data_protection', +'homeparty.add_as_guest','homeparty.add_product','homeparty.completed','homeparty.confirm_my_data_is_correct_and_complete','homeparty.copy', +'homeparty.copy_link','homeparty.copy_link_info','homeparty.country_can_no_longer_be_changed_after_created','homeparty.create_delivery_address_host_info','homeparty.create_guest', +'homeparty.create_guests','homeparty.create_guests_info','homeparty.create_new_homeparty','homeparty.credit_bonus','homeparty.credit_homeparty_voucher', +'homeparty.current_bonus_view','homeparty.data_protection','homeparty.data_protection_reasons_your_personal_data_will_not_be_shown','homeparty.deduct_points_by_voucher','homeparty.delivery_address_homeparty', +'homeparty.delivery_directly_to_the_guest','homeparty.delivery_to_host','homeparty.description_welcome_text','homeparty.enter_your_personal_data_for_homeparty','homeparty.event_date', +'homeparty.event_place','homeparty.from','homeparty.general_overview','homeparty.guest','homeparty.guest_delete_really', +'homeparty.guest_lists','homeparty.guest_order_sent_directly_info','homeparty.guests','homeparty.homeparty','homeparty.homeparty_add_host_address_info', +'homeparty.homeparty_delete_really','homeparty.homeparty_invoice_info','homeparty.homeparty_manage','homeparty.host','homeparty.host_address_save', +'homeparty.host_can_not_delete','homeparty.host_homeparty','homeparty.host_organiser_event','homeparty.invitation','homeparty.invitation_link_for_guests', +'homeparty.invoice_address','homeparty.let_your_guests_fill_in_their_own_details','homeparty.manage','homeparty.missing','homeparty.next_bonus', +'homeparty.order','homeparty.order_can_be_send_delivery_address_info','homeparty.order_create','homeparty.order_host','homeparty.order_show', +'homeparty.please_enter_delivery_address_info','homeparty.revoke_consent_at_any_time','homeparty.shipping_costs_host','homeparty.target_turnover','homeparty.voucher', +'homeparty.voucher_bonus','homeparty.voucher_bonus_cannot_be_applied','homeparty.voucher_total','homeparty.your_MIVITA_advice','homeparty.your_data_has_been_successfully_created_have_fun', +'homeparty.your_host','homeparty.your_hostess','homeparty.welcome_copy','marketingplan.title','marketingplan.subtitle', +'marketingplan.current_level','marketingplan.qualification','marketingplan.kp_points','marketingplan.tp_points','marketingplan.customer_points_full', +'marketingplan.team_points_full','marketingplan.provisions','marketingplan.standard','marketingplan.shop','marketingplan.provision_rates', +'marketingplan.team_provisions','marketingplan.team_provisions_by_lines','marketingplan.growth_bonus','marketingplan.next_level','marketingplan.your_current_level', +'marketingplan.your_next_goal','marketingplan.legend','marketingplan.legend_ku_description','marketingplan.legend_tp_description','marketingplan.legend_lines_description', +'marketingplan.legend_percentage_description','marketingplan.no_levels_available','marketingplan.no_levels_configured','marketingplan.loading_error','marketingplan.loading_time', +'marketingplan.show_details','marketingplan.back_to_overview','marketingplan.level_position','marketingplan.level_name','marketingplan.level_requirements', +'marketingplan.level_benefits','marketingplan.paylines','marketingplan.line_1','marketingplan.line_2','marketingplan.line_3', +'marketingplan.line_4','marketingplan.line_5','marketingplan.line_6','marketingplan.line_7','marketingplan.line_8', +'marketingplan.points','marketingplan.percent','marketingplan.euro','marketingplan.active','marketingplan.inactive', +'marketingplan.achieved','marketingplan.not_achieved','marketingplan.in_progress','marketingplan.congratulations','marketingplan.keep_going', +'marketingplan.almost_there','marketingplan.next_step','marketingplan.goal_reached','marketingplan.help_kp_points','marketingplan.help_tp_points', +'marketingplan.help_provisions','marketingplan.help_growth_bonus','membership.','membership.MIVITA_BUSINESS_Paket','membership.abo_copy_1', +'membership.abo_copy_2','membership.abo_copy_3','membership.active','membership.active_package','membership.booked_package', +'membership.change','membership.change_copy_1','membership.change_copy_2','membership.consultant_membership','membership.consultant_online_shop', +'membership.contract_renewal','membership.deactivate','membership.details','membership.downgrade_membership_is_not_possible','membership.end', +'membership.end_button','membership.end_checkbox','membership.end_copy_1','membership.end_copy_2','membership.end_copy_3', +'membership.expired_on','membership.home_copy_SEPA_32','membership.home_copy_SEPA_33','membership.home_copy_SEPA_36','membership.home_copy_alert_31', +'membership.home_copy_alert_35','membership.home_copy_alert_36','membership.home_copy_alert_36_today','membership.home_copy_last_31','membership.home_copy_last_33', +'membership.home_copy_last_34','membership.home_copy_last_35','membership.home_copy_last_36','membership.home_hl','membership.inactive', +'membership.is_no_longer_possible_to_change_package','membership.membership','membership.membership_was_renewed','membership.open_payment_options','membership.payment_copy_1', +'membership.payment_copy_2','membership.payment_has_been_made','membership.renewal_is_active_membership_fee_automatic','membership.status','membership.until', +'membership.upgrade','membership.upgrade_copy_1','membership.upgrade_copy_2','membership.upgrade_package_and_proceed_payment','membership.we_do_not_collect_membership_fee', +'membership.your_booked_package','membership.info_contract_renewal','membership.alert_contract_renewal','msg.shipping_country_was_not_found','msg.shipping_country_was_not_correctly', +'msg.shopping_cart_was_shipping_free','msg.shipping_cost_cannot_be_0','msg.shipping_costs_were_not_calculated_correctly','msg.compensation_products_cannot_be_0','msg.link_for_homeparty_not_found', +'msg.contact_delete','msg.error_occurred_with_order','msg.abo_deaktivert','msg.error_checkbox_not_confirm','msg.no_change_made', +'msg.booked_package_has_been_changed','msg.cancel_membership_is_requested','msg.file_uploaded','msg.file_empty','msg.file_deleted', +'msg.file_not_found','msg.country_account_has_been_changed__cost_has_been_reset','msg.your_shopping_cart_is_empty_please_add_products_first.','msg.homeparty_guest_delete','msg.homeparty_delete', +'msg.VATID_could_not_be_validated','msg.VATID_successfully_entered','msg.reverse_charge_procedure_and_VATID_deleted','msg.no_id_card_deposited_please_upload_first','msg.no_trade_licence_deposited_please_upload_first', +'msg.please_enter_reason_why_you_not_need_trade_licence','msg.please_select_compensation_product','msg.please_select_count_compensation_products','msg.user_not_found','msg.shopping_cart_was_not_user_shop', +'msg.shopping_instance_not_found','msg.shopping_user_not_found','msg.account_released','navigation.documents','navigation.add', +'navigation.attribute','navigation.business','navigation.career_level','navigation.categories','navigation.clients', +'navigation.commissions','navigation.contents','navigation.countries','navigation.credit','navigation.do_order', +'navigation.edit','navigation.export','navigation.general','navigation.home','navigation.ingredients', +'navigation.invoice','navigation.language','navigation.languages','navigation.logout','navigation.manage', +'navigation.member','navigation.new_member','navigation.member_register','navigation.membership','navigation.modules', +'navigation.my_account','navigation.my_clients','navigation.my_data','navigation.my_homeparty','navigation.my_membership', +'navigation.my_orders','navigation.my_shop','navigation.my_team','navigation.order','navigation.orders', +'navigation.overview','navigation.payment_methods','navigation.payments','navigation.points','navigation.products', +'navigation.sales_volumes','navigation.settings','navigation.shipping_costs','navigation.start_site','navigation.structure', +'navigation.system_settings','navigation.translate','navigation.translation','navigation.trigger','navigation.user_roles', +'navigation.tax_advisor','navigation.downloadcenter','navigation.files','navigation.tags','navigation.myabos', +'navigation.customerabos','navigation.myabo','navigation.customerabo','navigation.abo','navigation.abos', +'navigation.payment_links','navigation.dashboard','navigation.shop','navigation.to_shop','navigation.marketingplan', +'navigation.dhl_cockpit','navigation.revenue','order.add_customer','order.advertising_material','order.adviser_collective_invoice', 'order.adviser_order_for_membership','order.adviser_order_for_registration','order.art_no','order.article','order.article_remove', 'order.assigned_advisor','order.assigned_counsellor','order.billing_address_of_client','order.billing_address_of_the_advisor','order.client_order_via_shop', 'order.collective_invoice','order.collective_invoice_contains_orders','order.compensation_product','order.confirm_and_proceed_to_checkout','order.confirm_and_proceed_to_order', @@ -3782,34 +4084,36 @@ namespace PHPSTORM_META { 'portal.mail_otp','portal.mail_otp_valid','portal.mail_otp_not_requested','portal.mail_greetings','portal.mail_sender', 'portal.verify_otp_title','portal.verify_otp_description','portal.verify_otp_resend','portal.verify_otp_resend_link','portal.login_send_otp_description', 'portal.login_send_otp_description2','portal.back_to_shop','portal.guest','portal.change_login_title','portal.change_login_title2', -'portal.logout_button','portal.change_login_description','portal.change_login_description2','portal.change_login_description3','register.accept-contract', -'register.account_deleted','register.adjust','register.advisor_invite','register.agree','register.agree_and_continue', -'register.assigned','register.badge','register.business_license_later','register.business_license_non','register.business_license_non_text', -'register.business_license_now','register.business_license_release','register.business_license_deposited','register.change_documents','register.change_email', -'register.change_email_address_verify_it_info','register.change_email_contact_address_verify_it_info','register.complete','register.complete_registration','register.confirm_your_identity', -'register.contract','register.contract_data_protection','register.create_advisor_send_email_to_invite','register.create_and_next','register.data', -'register.data_protection','register.declaration-of-consent','register.delete_account','register.i_have_read_and_accept_it','register.next', -'register.open_payment_options','register.package','register.password','register.password_has_already_been_created','register.really_delete_the_file', -'register.reg_checked','register.reg_finisch_hl','register.reg_finisch_line_1','register.reg_finisch_line_2','register.reg_hl', -'register.reg_line_1','register.registration','register.registration_completed','register.required_fields','register.save_and_continue', -'register.select','register.sender','register.trade_licence','register.upload','register.wizard_business_license_hl', -'register.wizard_business_license_line_1','register.wizard_create_release_hl','register.wizard_create_release_line_1','register.wizard_finish_hl','register.wizard_finish_line_1', -'register.wizard_reg_release_hl','register.wizard_reg_release_line_1','register.wizard_verification_hl','register.wizard_verification_line_1','reminder.button_31', -'reminder.button_32','reminder.button_33','reminder.button_34','reminder.button_35','reminder.button_36', -'reminder.button_37','reminder.copy_first_31','reminder.copy_first_32','reminder.copy_first_33','reminder.copy_first_34', -'reminder.copy_first_35','reminder.copy_first_36','reminder.copy_first_37','reminder.copy_last_31','reminder.copy_last_32', -'reminder.copy_last_33','reminder.copy_last_34','reminder.copy_last_35','reminder.copy_last_36','reminder.copy_last_37', -'reminder.subject','shop.','shop.Choose Your Shop Name','shop.Shop details','shop.Terms of Use', -'shop.Your Shop','shop.Your Shop Name','shop.accept_and_next','shop.active_since','shop.available', -'shop.check','shop.declaration_of_shop','shop.domain','shop.name','shop.not_available', -'shop.not_available_copy','shop.open_copy_1','shop.open_copy_2','shop.open_note_1','shop.open_note_hl', -'shop.open_your_shop','shop.order_customer','shop.orders_customers','shop.preview_shop_internet_address','shop.save and continue', -'shop.shop_about','shop.shop_about_help','shop.shop_accessibility','shop.shop_accessibility_help','shop.shop_contact', -'shop.shop_contact_help','shop.shop_image','shop.shop_image_copy','shop.shop_name_description','shop.shop_name_error_1', -'shop.shop_name_error_2','shop.shop_on_site','shop.shop_on_site_copy','shop.shop_title','shop.shop_title_help', -'shop.your_shop_name','shop.shop_contact_text','shop.shop_accessibility_text','shop.phone','shop.mobil', -'shop.your_street_number','shop.your_zip_code','shop.your_city','shop.your_phone_number','shop.your_mobile_number', -'shop.error_subdomain_exists','tables.','tables.VAT','tables.account','tables.account_to', +'portal.logout_button','portal.change_login_description','portal.change_login_description2','portal.change_login_description3','portal.change_my_data', +'portal.change_my_data_empty','portal.my_orders_empty','portal.my_orders_info','portal.change_my_data_empty_button','portal.my_subscriptions_empty', +'portal.my_subscriptions_info','register.accept-contract','register.account_deleted','register.adjust','register.advisor_invite', +'register.agree','register.agree_and_continue','register.assigned','register.badge','register.business_license_later', +'register.business_license_non','register.business_license_non_text','register.business_license_now','register.business_license_release','register.business_license_deposited', +'register.change_documents','register.change_email','register.change_email_address_verify_it_info','register.change_email_contact_address_verify_it_info','register.complete', +'register.complete_registration','register.confirm_your_identity','register.contract','register.contract_data_protection','register.create_advisor_send_email_to_invite', +'register.create_and_next','register.data','register.data_protection','register.declaration-of-consent','register.delete_account', +'register.i_have_read_and_accept_it','register.next','register.open_payment_options','register.package','register.password', +'register.password_has_already_been_created','register.really_delete_the_file','register.reg_checked','register.reg_finisch_hl','register.reg_finisch_line_1', +'register.reg_finisch_line_2','register.reg_hl','register.reg_line_1','register.registration','register.registration_completed', +'register.required_fields','register.save_and_continue','register.select','register.sender','register.trade_licence', +'register.upload','register.wizard_business_license_hl','register.wizard_business_license_line_1','register.wizard_create_release_hl','register.wizard_create_release_line_1', +'register.wizard_finish_hl','register.wizard_finish_line_1','register.wizard_reg_release_hl','register.wizard_reg_release_line_1','register.wizard_verification_hl', +'register.wizard_verification_line_1','reminder.button_31','reminder.button_32','reminder.button_33','reminder.button_34', +'reminder.button_35','reminder.button_36','reminder.button_37','reminder.copy_first_31','reminder.copy_first_32', +'reminder.copy_first_33','reminder.copy_first_34','reminder.copy_first_35','reminder.copy_first_36','reminder.copy_first_37', +'reminder.copy_last_31','reminder.copy_last_32','reminder.copy_last_33','reminder.copy_last_34','reminder.copy_last_35', +'reminder.copy_last_36','reminder.copy_last_37','reminder.subject','shop.','shop.Choose Your Shop Name', +'shop.Shop details','shop.Terms of Use','shop.Your Shop','shop.Your Shop Name','shop.accept_and_next', +'shop.accept_and_change','shop.active_since','shop.available','shop.check','shop.declaration_of_shop', +'shop.domain','shop.name','shop.not_available','shop.not_available_copy','shop.open_copy_1', +'shop.open_copy_2','shop.open_note_1','shop.open_note_hl','shop.open_your_shop','shop.edit_your_shop', +'shop.order_customer','shop.orders_customers','shop.preview_shop_internet_address','shop.save and continue','shop.shop_about', +'shop.shop_about_help','shop.shop_accessibility','shop.shop_accessibility_help','shop.shop_contact','shop.shop_contact_help', +'shop.shop_image','shop.shop_image_copy','shop.shop_name_description','shop.shop_name_error_1','shop.shop_name_error_2', +'shop.shop_on_site','shop.shop_on_site_copy','shop.shop_title','shop.shop_title_help','shop.your_shop_name', +'shop.shop_contact_text','shop.shop_accessibility_text','shop.phone','shop.mobil','shop.your_street_number', +'shop.your_zip_code','shop.your_city','shop.your_phone_number','shop.your_mobile_number','shop.error_subdomain_exists', +'shop.your_current_shop_name','tables.','tables.VAT','tables.account','tables.account_to', 'tables.activ','tables.addition','tables.address','tables.adviser_no','tables.amount', 'tables.art','tables.article_no','tables.assigned_advisor','tables.birthday','tables.c_no', 'tables.city','tables.commission','tables.contents','tables.country','tables.created', @@ -3838,7 +4142,16 @@ namespace PHPSTORM_META { 'team.qualification_commission','team.qualification_points','team.register_new_consultant','team.s','team.shop', 'team.shop_commission','team.sponsor','team.structure','team.total_points','team.total_turnover', 'team.until','team.volume_KU','team.your_sponsor','team.your_team','team.sales_store_net', -'team.filename_export','team.payout_details','webcontent.career_opportunities_at_mivita','webcontent.requirement_profile','webcontent.advantages_of_direct_sales', +'team.filename_export','team.payout_details','team.filter_active','team.filter_not_active','team.filter_all', +'team.all_status','team.qualified_green','team.in_progress_yellow','team.no_level_red','team.optimized', +'team.standard_monitoring','team.fallback','team.loading_time','team.memory','team.memory_usage', +'team.team_size','team.team_members','team.execution_time','team.user_id','team.performance_details', +'team.team_structure','team.new_member','team.optimized_action','team.standard_action','team.close', +'team.show_details','team.test_optimized','team.standard_version','team.performance_metrics_team_overview','team.performance_metrics_my_team', +'team.performance_monitoring','team.fallback_support','team.optimized_with_cache','team.datatable_mode_switched','team.error_loading_optimized_overview', +'team.live_not_supported_fallback','team.optimized_performance_features','team.next_level','team.calculation_type','team.version', +'team.cache','team.live','team.ID','team.Ebene','team.Level', +'team.KD','team.bis','webcontent.career_opportunities_at_mivita','webcontent.requirement_profile','webcontent.advantages_of_direct_sales', 'webcontent.compatibility_with_family_private_life','webcontent.start_up','webcontent.that_sounds_exciting_to_you','webcontent.then_get_in_touch_with_us_today','webcontent.to_the_registration', 'webcontent.to_the_contact_form','webcontent.aloe_vera_word_copy','webcontent.origin_aloe_hl','webcontent.origin_aloe_subl','webcontent.origin_aloe_copy', 'webcontent.bio_quality_hl','webcontent.bio_quality_subl','webcontent.bio_quality_copy','webcontent.shop_at_the_aloe_vera_farm','webcontent.aloe_high_quality_hl', @@ -3896,18 +4209,28 @@ namespace PHPSTORM_META { 'translation::translation.prompt_language_if_any','translation::translation.prompt_to_driver',); registerArgumentsSet('env', 'APP_NAME','APP_ENV','APP_DEBUG','APP_KEY','APP_URL', -'APP_DOMAIN','APP_TLD_CARE','APP_TLD_SHOP','APP_PROTOCOL','APP_URL_MAIN', -'APP_URL_CHECKOUT','APP_URL_CRM','APP_URL_PORTAL','APP_CONTACT_MAIL','APP_CHECKOUT_MAIL', -'APP_INFO_MAIL','APP_PROORITY_MAIL','APP_DEFAULT_MAIL','APP_CHECKOUT_TEST_MAIL','APP_INFO_TEST_MAIL', -'EXCEPTION_MAIL','SESSION_DOMAIN','APP_MODE','APP_IPINFO','APP_MAIN_TAX', -'APP_MAIN_TAX_RATE','APP_SHIPPING_TAX','APP_PHP_VERSION','LOG_CHANNEL','DB_CONNECTION', -'DB_HOST','DB_PORT','DB_DATABASE','DB_USERNAME','DB_PASSWORD', -'PAYONE_URL','PAYONE_TS','PAYONE_KEY','BROADCAST_DRIVER','CACHE_DRIVER', -'SESSION_DRIVER','SESSION_LIFETIME','QUEUE_DRIVER','REDIS_HOST','REDIS_PASSWORD', -'REDIS_PORT','MAIL_DRIVER','MAIL_HOST','MAIL_PORT','MAIL_USERNAME', -'MAIL_PASSWORD','MAIL_ENCRYPTION','PUSHER_APP_ID','PUSHER_APP_KEY','PUSHER_APP_SECRET', -'PUSHER_APP_CLUSTER','MIX_PUSHER_APP_KEY','MIX_PUSHER_APP_CLUSTER','MIVITA_RENEWAL_DAYS','MIVITA_REMIND_FIRST_DAYS', -'MIVITA_REMIND_SEC_DAYS','MIVITA_ABO_BOOKING_DAYS','MIVITA_REMIND_LAST_DAYS','MIVITA_EDIT_DATA_PASS','MIVITA_ADD_NUMBER_ID',); +'APP_URL_CRM','APP_DOMAIN','APP_TLD_CARE','APP_TLD_SHOP','APP_PROTOCOL', +'APP_URL_MAIN','APP_URL_CHECKOUT','APP_URL_CRM','APP_URL_PORTAL','APP_CONTACT_MAIL', +'APP_CHECKOUT_MAIL','APP_INFO_MAIL','APP_PROORITY_MAIL','APP_DEFAULT_MAIL','APP_CHECKOUT_TEST_MAIL', +'APP_INFO_TEST_MAIL','EXCEPTION_MAIL','SESSION_DOMAIN','APP_MODE','APP_IPINFO', +'APP_MAIN_TAX','APP_MAIN_TAX_RATE','APP_SHIPPING_TAX','APP_PHP_VERSION','LOG_CHANNEL', +'DB_CONNECTION','DB_HOST','DB_PORT','DB_DATABASE','DB_USERNAME', +'DB_PASSWORD','PAYONE_URL','PAYONE_TS','PAYONE_KEY','BROADCAST_DRIVER', +'CACHE_DRIVER','SESSION_DRIVER','SESSION_LIFETIME','QUEUE_DRIVER','REDIS_HOST', +'REDIS_PASSWORD','REDIS_PORT','MAIL_DRIVER','MAIL_HOST','MAIL_PORT', +'MAIL_USERNAME','MAIL_PASSWORD','MAIL_ENCRYPTION','PUSHER_APP_ID','PUSHER_APP_KEY', +'PUSHER_APP_SECRET','PUSHER_APP_CLUSTER','MIX_PUSHER_APP_KEY','MIX_PUSHER_APP_CLUSTER','MIVITA_RENEWAL_DAYS', +'MIVITA_REMIND_FIRST_DAYS','MIVITA_REMIND_SEC_DAYS','MIVITA_ABO_BOOKING_DAYS','MIVITA_REMIND_LAST_DAYS','MIVITA_EDIT_DATA_PASS', +'MIVITA_ADD_NUMBER_ID','DHL_API_TYPE','DHL_API_KEY','DHL_API_SECRET','DHL_API_USERNAME', +'DHL_API_PASSWORD','DHL_SANDBOX','DHL_TEST_MODE','DHL_ACCOUNT_NUMBER_DEFAULT','DHL_ACCOUNT_NUMBER_V01PAK', +'DHL_ACCOUNT_NUMBER_V62WP','DHL_ACCOUNT_NUMBER_V53PAK','DHL_ACCOUNT_NUMBER_V07PAK','DHL_SENDER_COMPANY','DHL_SENDER_NAME', +'DHL_SENDER_STREET','DHL_SENDER_STREET_NUMBER','DHL_SENDER_POSTAL_CODE','DHL_SENDER_CITY','DHL_SENDER_STATE', +'DHL_SENDER_COUNTRY','DHL_SENDER_EMAIL','DHL_SENDER_PHONE','DHL_DEFAULT_PRODUCT','DHL_LABEL_FORMAT', +'DHL_LABEL_SIZE','DHL_LABEL_STORAGE_PATH','DHL_LABEL_PUBLIC_PATH','DHL_RETURNS_ENABLED','DHL_RETURN_PRODUCT', +'DHL_RETURN_RECEIVER_ID','DHL_TRACKING_ENABLED','DHL_TRACKING_AUTO_UPDATE','DHL_TRACKING_UPDATE_INTERVAL','DHL_LOGGING_ENABLED', +'DHL_LOGGING_LEVEL','DHL_LOG_REQUESTS','DHL_LOG_RESPONSES','DHL_QUEUE_CONNECTION','DHL_QUEUE_NAME', +'DHL_QUEUE_TIMEOUT','DHL_QUEUE_MAX_TRIES','DHL_THROW_EXCEPTIONS','DHL_ERROR_NOTIFICATION_EMAIL','DHL_CACHE_ENABLED', +'DHL_CACHE_TTL','DHL_FAKE_API_CALLS','DHL_MOCK_RESPONSES','DHL_DEBUG_MODE',); expectedArguments(\Illuminate\Support\Facades\Gate::has(), 0, argumentsSet('auth')); expectedArguments(\Illuminate\Support\Facades\Gate::allows(), 0, argumentsSet('auth')); diff --git a/_ide_helper.php b/_ide_helper.php index 43d6879..fb37930 100644 --- a/_ide_helper.php +++ b/_ide_helper.php @@ -11313,6 +11313,89 @@ namespace Illuminate\Support\Facades { return $instance->setConnectionName($name); } + /** + * Release a reserved job back onto the queue after (n) seconds. + * + * @param string $queue + * @param \Illuminate\Queue\Jobs\DatabaseJobRecord $job + * @param int $delay + * @return mixed + * @static + */ + public static function release($queue, $job, $delay) + { + /** @var \Illuminate\Queue\DatabaseQueue $instance */ + return $instance->release($queue, $job, $delay); + } + + /** + * Delete a reserved job from the queue. + * + * @param string $queue + * @param string $id + * @return void + * @throws \Throwable + * @static + */ + public static function deleteReserved($queue, $id) + { + /** @var \Illuminate\Queue\DatabaseQueue $instance */ + $instance->deleteReserved($queue, $id); + } + + /** + * Delete a reserved job from the reserved queue and release it. + * + * @param string $queue + * @param \Illuminate\Queue\Jobs\DatabaseJob $job + * @param int $delay + * @return void + * @static + */ + public static function deleteAndRelease($queue, $job, $delay) + { + /** @var \Illuminate\Queue\DatabaseQueue $instance */ + $instance->deleteAndRelease($queue, $job, $delay); + } + + /** + * Delete all of the jobs from the queue. + * + * @param string $queue + * @return int + * @static + */ + public static function clear($queue) + { + /** @var \Illuminate\Queue\DatabaseQueue $instance */ + return $instance->clear($queue); + } + + /** + * Get the queue or return the default. + * + * @param string|null $queue + * @return string + * @static + */ + public static function getQueue($queue) + { + /** @var \Illuminate\Queue\DatabaseQueue $instance */ + return $instance->getQueue($queue); + } + + /** + * Get the underlying database instance. + * + * @return \Illuminate\Database\Connection + * @static + */ + public static function getDatabase() + { + /** @var \Illuminate\Queue\DatabaseQueue $instance */ + return $instance->getDatabase(); + } + /** * Get the maximum number of attempts for an object-based queue handler. * @@ -11323,7 +11406,7 @@ namespace Illuminate\Support\Facades { public static function getJobTries($job) { //Method inherited from \Illuminate\Queue\Queue - /** @var \Illuminate\Queue\SyncQueue $instance */ + /** @var \Illuminate\Queue\DatabaseQueue $instance */ return $instance->getJobTries($job); } @@ -11337,7 +11420,7 @@ namespace Illuminate\Support\Facades { public static function getJobBackoff($job) { //Method inherited from \Illuminate\Queue\Queue - /** @var \Illuminate\Queue\SyncQueue $instance */ + /** @var \Illuminate\Queue\DatabaseQueue $instance */ return $instance->getJobBackoff($job); } @@ -11351,7 +11434,7 @@ namespace Illuminate\Support\Facades { public static function getJobExpiration($job) { //Method inherited from \Illuminate\Queue\Queue - /** @var \Illuminate\Queue\SyncQueue $instance */ + /** @var \Illuminate\Queue\DatabaseQueue $instance */ return $instance->getJobExpiration($job); } @@ -11365,7 +11448,7 @@ namespace Illuminate\Support\Facades { public static function createPayloadUsing($callback) { //Method inherited from \Illuminate\Queue\Queue - \Illuminate\Queue\SyncQueue::createPayloadUsing($callback); + \Illuminate\Queue\DatabaseQueue::createPayloadUsing($callback); } /** @@ -11377,7 +11460,7 @@ namespace Illuminate\Support\Facades { public static function getContainer() { //Method inherited from \Illuminate\Queue\Queue - /** @var \Illuminate\Queue\SyncQueue $instance */ + /** @var \Illuminate\Queue\DatabaseQueue $instance */ return $instance->getContainer(); } @@ -11391,7 +11474,7 @@ namespace Illuminate\Support\Facades { public static function setContainer($container) { //Method inherited from \Illuminate\Queue\Queue - /** @var \Illuminate\Queue\SyncQueue $instance */ + /** @var \Illuminate\Queue\DatabaseQueue $instance */ $instance->setContainer($container); } @@ -21324,319 +21407,62 @@ namespace App\Services\Facade { } } -namespace Alban\LaravelCollectiveSpatieHtmlParser { +namespace Acme\Dhl\Facades { /** * * - * @see \Collective\Html\HtmlBuilder */ - class FormFacade { + class DHL { /** * * + * @param array $orderData + * @return array * @static */ - public static function checkbox($name, $value = 1, $checked = null, $options = []) + public static function createLabel($orderData) { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->checkbox($name, $value, $checked, $options); + /** @var \Acme\Dhl\DhlManager $instance */ + return $instance->createLabel($orderData); } /** * * + * @param string $shipmentNumber + * @return bool * @static */ - public static function open($options = []) + public static function cancelLabel($shipmentNumber) { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->open($options); + /** @var \Acme\Dhl\DhlManager $instance */ + return $instance->cancelLabel($shipmentNumber); } /** * * + * @param string $trackingNumber + * @return array * @static */ - public static function label($name, $value = null, $options = [], $escape_html = true) + public static function track($trackingNumber) { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->label($name, $value, $options, $escape_html); + /** @var \Acme\Dhl\DhlManager $instance */ + return $instance->track($trackingNumber); } /** * * + * @param array $returnData + * @return array * @static */ - public static function text($name, $value = null, $options = []) + public static function createReturn($returnData) { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->text($name, $value, $options); - } - - /** - * - * - * @static - */ - public static function password($name, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->password($name, $options); - } - - /** - * - * - * @static - */ - public static function select($name, $list = [], $selected = null, $selectAttributes = [], $optionsAttributes = [], $optgroupsAttributes = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->select($name, $list, $selected, $selectAttributes, $optionsAttributes, $optgroupsAttributes); - } - - /** - * - * - * @static - */ - public static function radio($name, $value = null, $checked = null, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->radio($name, $value, $checked, $options); - } - - /** - * - * - * @static - */ - public static function submit($value = null, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->submit($value, $options); - } - - /** - * - * - * @static - */ - public static function close() - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->close(); - } - - /** - * - * - * @static - */ - public static function input($type, $name, $value = null, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->input($type, $name, $value, $options); - } - - /** - * - * - * @static - */ - public static function search($name, $value = null, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->search($name, $value, $options); - } - - /** - * - * - * @static - */ - public static function model($model, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->model($model, $options); - } - - /** - * - * - * @static - */ - public static function hidden($name, $value = null, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->hidden($name, $value, $options); - } - - /** - * - * - * @static - */ - public static function email($name, $value = null, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->email($name, $value, $options); - } - - /** - * - * - * @static - */ - public static function tel($name, $value = null, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->tel($name, $value, $options); - } - - /** - * - * - * @static - */ - public static function number($name, $value = null, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->number($name, $value, $options); - } - - /** - * - * - * @static - */ - public static function date($name, $value = null, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->date($name, $value, $options); - } - - /** - * - * - * @static - */ - public static function datetime($name, $value = null, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->datetime($name, $value, $options); - } - - /** - * - * - * @static - */ - public static function datetimeLocal($name, $value = null, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->datetimeLocal($name, $value, $options); - } - - /** - * - * - * @static - */ - public static function time($name, $value = null, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->time($name, $value, $options); - } - - /** - * - * - * @static - */ - public static function url($name, $value = null, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->url($name, $value, $options); - } - - /** - * - * - * @static - */ - public static function file($name, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->file($name, $options); - } - - /** - * - * - * @static - */ - public static function textarea($name, $value = null, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->textarea($name, $value, $options); - } - - /** - * - * - * @static - */ - public static function reset($value, $attributes = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->reset($value, $attributes); - } - - /** - * - * - * @static - */ - public static function image($url, $name = null, $attributes = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->image($url, $name, $attributes); - } - - /** - * - * - * @static - */ - public static function color($name, $value = null, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->color($name, $value, $options); - } - - /** - * - * - * @static - */ - public static function button($value = null, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->button($value, $options); - } - - /** - * - * - * @static - */ - public static function mergeOptions($element, $options = []) - { - /** @var \Alban\LaravelCollectiveSpatieHtmlParser\FormAdapter $instance */ - return $instance->mergeOptions($element, $options); + /** @var \Acme\Dhl\DhlManager $instance */ + return $instance->createReturn($returnData); } } @@ -22790,639 +22616,6 @@ namespace Laracasts\Flash { } } -namespace Spatie\Html\Facades { - /** - * - * - */ - class Html { - /** - * - * - * @param string|null $href - * @param string|null $text - * @return \Spatie\Html\Elements\A - * @static - */ - public static function a($href = null, $contents = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->a($href, $contents); - } - - /** - * - * - * @param string|null $href - * @param string|null $text - * @return \Spatie\Html\Elements\I - * @static - */ - public static function i($contents = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->i($contents); - } - - /** - * - * - * @param \Spatie\Html\HtmlElement|string|null $contents - * @return \Spatie\Html\Elements\P - * @static - */ - public static function p($contents = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->p($contents); - } - - /** - * - * - * @param string|null $type - * @param string|null $text - * @return \Spatie\Html\Elements\Button - * @static - */ - public static function button($contents = null, $type = null, $name = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->button($contents, $type, $name); - } - - /** - * - * - * @param \Illuminate\Support\Collection|iterable|string $classes - * @return \Illuminate\Contracts\Support\Htmlable - * @static - */ - public static function class($classes) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->class($classes); - } - - /** - * - * - * @param string|null $name - * @param bool $checked - * @param string|null $value - * @return \Spatie\Html\Elements\Input - * @static - */ - public static function checkbox($name = null, $checked = null, $value = '1') - { - /** @var \Spatie\Html\Html $instance */ - return $instance->checkbox($name, $checked, $value); - } - - /** - * - * - * @param \Spatie\Html\HtmlElement|string|iterable|int|float|null $contents - * @return \Spatie\Html\Elements\Div - * @static - */ - public static function div($contents = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->div($contents); - } - - /** - * - * - * @param string|null $name - * @param string|null $value - * @return \Spatie\Html\Elements\Input - * @static - */ - public static function email($name = null, $value = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->email($name, $value); - } - - /** - * - * - * @param string|null $name - * @param string|null $value - * @return \Spatie\Html\Elements\Input - * @static - */ - public static function search($name = null, $value = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->search($name, $value); - } - - /** - * - * - * @param string|null $name - * @param string|null $value - * @param bool $format - * @return \Spatie\Html\Elements\Input - * @static - */ - public static function date($name = '', $value = null, $format = true) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->date($name, $value, $format); - } - - /** - * - * - * @param string|null $name - * @param string|null $value - * @param bool $format - * @return \Spatie\Html\Elements\Input - * @static - */ - public static function datetime($name = '', $value = null, $format = true) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->datetime($name, $value, $format); - } - - /** - * - * - * @param string|null $name - * @param string|null $value - * @param string|null $min - * @param string|null $max - * @param string|null $step - * @return \Spatie\Html\Elements\Input - * @static - */ - public static function range($name = '', $value = null, $min = null, $max = null, $step = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->range($name, $value, $min, $max, $step); - } - - /** - * - * - * @param string|null $name - * @param string|null $value - * @param bool $format - * @return \Spatie\Html\Elements\Input - * @static - */ - public static function time($name = '', $value = null, $format = true) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->time($name, $value, $format); - } - - /** - * - * - * @param string $tag - * @return \Spatie\Html\Elements\Element - * @static - */ - public static function element($tag) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->element($tag); - } - - /** - * - * - * @param string|null $type - * @param string|null $name - * @param string|null $value - * @return \Spatie\Html\Elements\Input - * @static - */ - public static function input($type = null, $name = null, $value = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->input($type, $name, $value); - } - - /** - * - * - * @param \Spatie\Html\HtmlElement|string|null $legend - * @return \Spatie\Html\Elements\Fieldset - * @static - */ - public static function fieldset($legend = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->fieldset($legend); - } - - /** - * - * - * @param string $method - * @param string|null $action - * @return \Spatie\Html\Elements\Form - * @static - */ - public static function form($method = 'POST', $action = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->form($method, $action); - } - - /** - * - * - * @param string|null $name - * @param string|null $value - * @return \Spatie\Html\Elements\Input - * @static - */ - public static function hidden($name = null, $value = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->hidden($name, $value); - } - - /** - * - * - * @param string|null $src - * @param string|null $alt - * @return \Spatie\Html\Elements\Img - * @static - */ - public static function img($src = null, $alt = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->img($src, $alt); - } - - /** - * - * - * @param \Spatie\Html\HtmlElement|iterable|string|null $contents - * @param string|null $for - * @return \Spatie\Html\Elements\Label - * @static - */ - public static function label($contents = null, $for = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->label($contents, $for); - } - - /** - * - * - * @param \Spatie\Html\HtmlElement|string|null $contents - * @return \Spatie\Html\Elements\Legend - * @static - */ - public static function legend($contents = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->legend($contents); - } - - /** - * - * - * @param string $email - * @param string|null $text - * @return \Spatie\Html\Elements\A - * @static - */ - public static function mailto($email, $text = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->mailto($email, $text); - } - - /** - * - * - * @param string|null $name - * @param iterable $options - * @param string|iterable|null $value - * @return \Spatie\Html\Elements\Select - * @static - */ - public static function multiselect($name = null, $options = [], $value = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->multiselect($name, $options, $value); - } - - /** - * - * - * @param string|null $name - * @param string|null $value - * @param string|null $min - * @param string|null $max - * @param string|null $step - * @return \Spatie\Html\Elements\Input - * @static - */ - public static function number($name = null, $value = null, $min = null, $max = null, $step = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->number($name, $value, $min, $max, $step); - } - - /** - * - * - * @param string|null $text - * @param string|null $value - * @param bool $selected - * @return \Spatie\Html\Elements\Option - * @static - */ - public static function option($text = null, $value = null, $selected = false) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->option($text, $value, $selected); - } - - /** - * - * - * @param string|null $value - * @return \Spatie\Html\Elements\Input - * @static - */ - public static function password($name = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->password($name); - } - - /** - * - * - * @param string|null $name - * @param bool $checked - * @param string|null $value - * @return \Spatie\Html\Elements\Input - * @static - */ - public static function radio($name = null, $checked = null, $value = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->radio($name, $checked, $value); - } - - /** - * - * - * @param string|null $name - * @param iterable $options - * @param string|iterable|null $value - * @return \Spatie\Html\Elements\Select - * @static - */ - public static function select($name = null, $options = [], $value = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->select($name, $options, $value); - } - - /** - * - * - * @param \Spatie\Html\HtmlElement|string|null $contents - * @return \Spatie\Html\Elements\Span - * @static - */ - public static function span($contents = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->span($contents); - } - - /** - * - * - * @param string|null $text - * @return \Spatie\Html\Elements\Button - * @static - */ - public static function submit($text = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->submit($text); - } - - /** - * - * - * @param string|null $text - * @return \Spatie\Html\Elements\Button - * @static - */ - public static function reset($text = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->reset($text); - } - - /** - * - * - * @param string $number - * @param string|null $text - * @return \Spatie\Html\Elements\A - * @static - */ - public static function tel($number, $text = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->tel($number, $text); - } - - /** - * - * - * @param string|null $name - * @param string|null $value - * @return \Spatie\Html\Elements\Input - * @static - */ - public static function text($name = null, $value = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->text($name, $value); - } - - /** - * - * - * @param string|null $name - * @return \Spatie\Html\Elements\File - * @static - */ - public static function file($name = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->file($name); - } - - /** - * - * - * @param string|null $name - * @param string|null $value - * @return \Spatie\Html\Elements\Textarea - * @static - */ - public static function textarea($name = null, $value = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->textarea($name, $value); - } - - /** - * - * - * @return \Spatie\Html\Elements\Input - * @static - */ - public static function token() - { - /** @var \Spatie\Html\Html $instance */ - return $instance->token(); - } - - /** - * - * - * @param \ArrayAccess|array $model - * @return \Spatie\Html\Html - * @static - */ - public static function model($model) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->model($model); - } - - /** - * - * - * @param \ArrayAccess|array $model - * @param string|null $method - * @param string|null $action - * @return \Spatie\Html\Elements\Form - * @static - */ - public static function modelForm($model, $method = 'POST', $action = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->modelForm($model, $method, $action); - } - - /** - * - * - * @return \Spatie\Html\Html - * @static - */ - public static function endModel() - { - /** @var \Spatie\Html\Html $instance */ - return $instance->endModel(); - } - - /** - * - * - * @return \Illuminate\Contracts\Support\Htmlable - * @static - */ - public static function closeModelForm() - { - /** @var \Spatie\Html\Html $instance */ - return $instance->closeModelForm(); - } - - /** - * Retrieve the value from the current session or assigned model. This is - * a public alias for `old`. - * - * @param string $name - * @param mixed $value - * @return mixed - * @static - */ - public static function value($name, $default = null) - { - /** @var \Spatie\Html\Html $instance */ - return $instance->value($name, $default); - } - - /** - * Register a custom macro. - * - * @param string $name - * @param object|callable $macro - * @param-closure-this static $macro - * @return void - * @static - */ - public static function macro($name, $macro) - { - \Spatie\Html\Html::macro($name, $macro); - } - - /** - * Mix another object into the class. - * - * @param object $mixin - * @param bool $replace - * @return void - * @throws \ReflectionException - * @static - */ - public static function mixin($mixin, $replace = true) - { - \Spatie\Html\Html::mixin($mixin, $replace); - } - - /** - * Checks if macro is registered. - * - * @param string $name - * @return bool - * @static - */ - public static function hasMacro($name) - { - return \Spatie\Html\Html::hasMacro($name); - } - - /** - * Flush the existing macros. - * - * @return void - * @static - */ - public static function flushMacros() - { - \Spatie\Html\Html::flushMacros(); - } - - } - } - namespace Spatie\LaravelIgnition\Facades { /** * @@ -28795,12 +27988,11 @@ namespace { class Excel extends \Maatwebsite\Excel\Facades\Excel {} class DataTables extends \Yajra\DataTables\Facades\DataTables {} class Yard extends \App\Services\Facade\Yard {} - class Form extends \Alban\LaravelCollectiveSpatieHtmlParser\FormFacade {} + class DHL extends \Acme\Dhl\Facades\DHL {} class Debugbar extends \Barryvdh\Debugbar\Facades\Debugbar {} class PDF extends \Barryvdh\DomPDF\Facade\Pdf {} class Pdf extends \Barryvdh\DomPDF\Facade\Pdf {} class Flash extends \Laracasts\Flash\Flash {} - class Html extends \Spatie\Html\Facades\Html {} class Flare extends \Spatie\LaravelIgnition\Facades\Flare {} } diff --git a/app/Console/Commands/CheckPaymentsAccount.php b/app/Console/Commands/CheckPaymentsAccount.php new file mode 100644 index 0000000..64ed689 --- /dev/null +++ b/app/Console/Commands/CheckPaymentsAccount.php @@ -0,0 +1,213 @@ +info('COMMAND [payments:check-accounts] started.'); + $this->info('COMMAND [payments:check-accounts] started.'); + + // Die Logik wurde 1:1 aus der checkPaymentsAccounts-Methode übernommen + $renewalDate = Carbon::now()->modify('+'.(config('mivita.remind_first_days')+1).' days'); + Log::channel('cron')->info('Erneuerungsdatum für Zahlungen: ' . $renewalDate->format('Y-m-d H:i:s')); + + $users = User::where('payment_account', '!=', NULL) + ->where('active', '=', 1) + ->where('blocked', '!=', 1) + ->where('payment_account', '<', $renewalDate) + ->get(); + + Log::channel('cron')->info('Found ' . $users->count() . ' users for payment reminders.'); + $this->info('Found ' . $users->count() . ' users for payment reminders.'); + + foreach ($users as $user){ + Log::channel('cron')->info('Prüfe Zahlungserinnerungen für Benutzer: ' . $user->email); + $this->checkReminderPayments($user); + } + + Log::channel('cron')->info('COMMAND [payments:check-accounts] finished.'); + $this->info('COMMAND [payments:check-accounts] finished.'); + return 0; // Success + } + + /** + * Überprüft und sendet Zahlungserinnerungen basierend auf Benutzerkontostand + * + * RULES: + * > 21 remind_first_days = 31 reminder_first + * > 21 remind_first_days + sepa = 32 reminder_first_sepa + * > 14 remind_sec_days = 33 reminder_sec + * > 2 remind_last_days = 34 reminder_last + * > 0 deaktiv = 35 reminder_deaktiv + * > 0 deaktiv + sepa = 36 reminder_deaktiv_sepa + * == 7 abo_booking_days + sepa + cron = 37 reminder_collect_sepa + * + * @param User $user Benutzer + * @return void + */ + + private function checkReminderPayments(User $user) + { + //35 reminder_deaktiv, 36 reminder_deaktiv_sepa + if(!$user->isActiveAccount()){ + Log::channel('cron')->info('Inaktives Konto für Benutzer: ' . $user->email); + $this->checkIsReminderSend($user, 35); + return; + } + + //34 reminder_last + if($user->daysActiveAccount() <= config('mivita.remind_last_days')){ + Log::channel('cron')->info('Letzte Erinnerung für Benutzer: ' . $user->email . ' (Tage aktiv: ' . $user->daysActiveAccount() . ')'); + $this->checkIsReminderSend($user, 34); + return; + } + + //33 reminder_sec + if($user->daysActiveAccount() <= config('mivita.remind_sec_days')){ + Log::channel('cron')->info('Zweite Erinnerung für Benutzer: ' . $user->email . ' (Tage aktiv: ' . $user->daysActiveAccount() . ')'); + $this->checkIsReminderSend($user, 33); + return; + } + + //31 reminder_first + if($user->daysActiveAccount() > config('mivita.remind_sec_days')){ + Log::channel('cron')->info('Erste Erinnerung für Benutzer: ' . $user->email . ' (Tage aktiv: ' . $user->daysActiveAccount() . ')'); + $this->checkIsReminderSend($user, 31); + return; + } + } + + /** + * Überprüft, ob eine Erinnerung bereits gesendet wurde + * + * @param User $user Benutzer + * @param int $status Status-Code der Erinnerung + * @return bool + */ + private function checkIsReminderSend(User $user, $status) + { + $isSend = UserHistory::whereUserId($user->id) + ->whereAction('reminder_payments') + ->whereIdentifier($user->payment_account) + ->whereStatus($status) + ->latest() + ->first(); + + if($isSend){ + Log::channel('cron')->info('Erinnerung bereits gesendet für Benutzer: ' . $user->email . ' (Status: ' . $status . ')'); + return true; + } + + Log::channel('cron')->info('Sende neue Erinnerung für Benutzer: ' . $user->email . ' (Status: ' . $status . ')'); + $referenz = $this->sendReminderMail($user, $status); + + UserHistory::create([ + 'user_id' => $user->id, + 'action' => 'reminder_payments', + 'referenz' => $referenz, + 'identifier' => $user->payment_account, + 'status' => $status + ]); + + return false; + } + + /** + * Sendet eine Erinnerungs-E-Mail an den Benutzer + * + * @param User $user Benutzer + * @param int $status Status-Code der Erinnerung + * @return int + */ + private function sendReminderMail(User $user, $status) + { + $days = abs($user->daysActiveAccount()); + $pay_date = Carbon::parse($user->payment_account)->modify('- ' . config('mivita.abo_booking_days') . ' days')->format('d.m.Y'); + $datetime = $user->getPaymentAccountDateFormat(); + $price = ""; + + if($user->payment_order_id && isset($user->payment_order_product->price)){ + $price = 'von ' . $user->payment_order_product->getFormattedPrice() . ' EUR'; + } + + $message = __('reminder.copy_first_' . $status, ['days' => $days, 'datetime' => $datetime, 'price' => $price, 'pay_date' => $pay_date]); + $message_last = __('reminder.copy_last_' . $status, ['days' => $days, 'datetime' => $datetime, 'price' => $price, 'pay_date' => $pay_date]); + $button = __('reminder.button_' . $status); + + $message = preg_replace("/[\n\r]/", "", $message); + $message_last = preg_replace("/[\n\r]/", "", $message_last); + + $data = [ + 'subject' => __('reminder.subject') . " | ID: " . $status, + 'message' => $message, + 'message_last' => $message_last, + 'url' => route('user_membership'), + 'button' => $button, + ]; + + $sender = User::find(1); + $customer_mail = UserMessage::create(['user_id' => $user->id, 'send_user_id' => $sender->id, 'email' => $user->email, 'subject' => $data['subject'], 'message' => $data['message'] . " " . $data['message_last']]); + + try { + if(!Util::isTestSystem()){ + if($status >= 34){ + Log::channel('cron')->info('Sende kritische Erinnerung mit BCC an: ' . $user->email); + Mail::to($user->email) + ->locale($user->getLocale()) + ->bcc(config('app.default_mail')) + ->send(new MailCustomMessage($user, $data, $sender, false)); + } else { + Log::channel('cron')->info('Sende normale Erinnerung an: ' . $user->email); + Mail::to($user->email) + ->locale($user->getLocale()) + ->send(new MailCustomMessage($user, $data, $sender, false)); + } + } else { + Log::channel('cron')->info('Testsystem: E-Mail-Versand simuliert für: ' . $user->email); + } + } catch(\Exception $e) { + Log::channel('cron')->error('Mail-Fehler für Benutzer ' . $user->email . ': ' . $e->getMessage()); + $customer_mail->fail = true; + $customer_mail->error = $e->getMessage(); + $customer_mail->save(); + return 0; + } + + $customer_mail->send = true; + $customer_mail->sent_at = now(); + $customer_mail->save(); + + Log::channel('cron')->info('Erinnerungsmail erfolgreich gesendet an: ' . $user->email); + return 1; + } +} \ No newline at end of file diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index ce5cf2c..2275555 100755 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -3,6 +3,9 @@ namespace App\Console; use App\Console\Commands\BusinessStore; +use App\Console\Commands\CheckPaymentsAccount; +use App\Console\Commands\UserMakeAboOrder; +use App\Console\Commands\UserCleanup; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; @@ -15,6 +18,9 @@ class Kernel extends ConsoleKernel */ protected $commands = [ BusinessStore::class, + CheckPaymentsAccount::class, + UserMakeAboOrder::class, + UserCleanup::class, ]; /** @@ -25,8 +31,15 @@ class Kernel extends ConsoleKernel */ protected function schedule(Schedule $schedule) { - // $schedule->command('inspire') - // ->hourly(); + // Job 1: Überprüft täglich um 02:00 Uhr die Zahlungskonten. + $schedule->command('payments:check-accounts')->dailyAt('02:00'); + + // Jobs 2, 3, 4: Die Befehle aus deinem alten Shell-Skript. + // Werden nacheinander täglich zu unterschiedlichen Zeiten ausgeführt, + // um die Serverlast zu verteilen. + $schedule->command('business:store 0 0')->dailyAt('03:00'); + $schedule->command('user:cleanup')->dailyAt('03:30'); + $schedule->command('user:make_abo_order')->dailyAt('04:00'); } /** diff --git a/app/Http/Controllers/DhlShipmentController.php b/app/Http/Controllers/DhlShipmentController.php new file mode 100644 index 0000000..0bcb159 --- /dev/null +++ b/app/Http/Controllers/DhlShipmentController.php @@ -0,0 +1,662 @@ +middleware('auth'); + $this->middleware('admin')->except(['show', 'track']); + } + + /** + * Test the DHL API login credentials and return a JSON response. + * + * @return \Illuminate\Http\JsonResponse + */ + public function testLogin() + { + try { + // Get DHL configuration with admin settings + $settingController = new \App\Http\Controllers\SettingController(); + $dhlConfig = $settingController->getDhlConfig(); + + // Create DhlClient with merged configuration + $dhlClient = new \Acme\Dhl\Support\DhlClient( + $dhlConfig['base_url'], + $dhlConfig['api_key'], + $dhlConfig['username'], + $dhlConfig['password'] + ); + + // Test the connection + $connectionTest = $dhlClient->testConnection(); + + if ($connectionTest) { + $result = [ + 'success' => true, + 'message' => 'DHL API Verbindung erfolgreich getestet!', + 'details' => [ + 'base_url' => $dhlConfig['base_url'], + 'using_admin_config' => !empty($dhlConfig['api_key']) + ] + ]; + } else { + $result = [ + 'success' => false, + 'message' => 'DHL API Verbindung fehlgeschlagen. Prüfen Sie Ihre Zugangsdaten.' + ]; + } + + return response()->json($result); + } catch (Exception $e) { + Log::error('[DHL Controller] Test login failed', [ + 'error' => $e->getMessage() + ]); + + return response()->json([ + 'success' => false, + 'message' => 'DHL API Test fehlgeschlagen: ' . $e->getMessage() + ], 500); + } + } + + /** + * Display the DHL Cockpit (main overview) + * + * @param Request $request + * @return View + */ + public function index(Request $request): View + { + // Statistics for dashboard widgets + $stats = [ + 'total_shipments' => DhlShipment::count(), + 'pending_shipments' => DhlShipment::where('status', 'pending')->count(), + 'shipped_today' => DhlShipment::whereDate('created_at', today())->count(), + 'returns_count' => DhlShipment::where('type', 'return')->count(), + ]; + + return view('admin.dhl.cockpit', compact('stats')); + } + + /** + * Provides data for the DHL Cockpit DataTable. + * + * @param Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function datatable(Request $request): JsonResponse + { + $query = DhlShipment::with(['shoppingOrder.shopping_user']) + ->select('dhl_package_shipments.*') // Explicitly select to avoid conflicts + ->orderBy('created_at', 'desc'); + + // Apply filters from the request + if ($request->filled('type')) { + $query->where('type', $request->get('type')); + } + if ($request->filled('status')) { + $query->where('status', $request->get('status')); + } + if ($request->filled('date_from')) { + $query->whereDate('created_at', '>=', $request->get('date_from')); + } + if ($request->filled('date_to')) { + $query->whereDate('created_at', '<=', $request->get('date_to')); + } + if ($request->filled('search')) { + $search = $request->get('search'); + $query->where(function ($q) use ($search) { + $q->where('dhl_shipment_no', 'LIKE', "%{$search}%") + ->orWhere('id', 'LIKE', "%{$search}%") + ->orWhereHas('shoppingOrder', function ($orderQuery) use ($search) { + $orderQuery->where('id', $search); + }); + }); + } + + return DataTables::eloquent($query) + ->addColumn('checkbox', function ($shipment) { + return ''; + }) + ->editColumn('id', function ($shipment) { + return '#' . $shipment->id . ''; + }) + ->addColumn('type', function ($shipment) { + if ($shipment->type == 'outbound') { + return ' Ausgehend'; + } else { + return ' Retoure'; + } + }) + ->addColumn('order', function ($shipment) { + if ($shipment->order_id) { + return '#' . $shipment->order_id . ''; + } + return 'N/A'; + }) + ->addColumn('customer', function ($shipment) { + if ($shipment->shoppingOrder && $shipment->shoppingOrder->shopping_user) { + return e($shipment->shoppingOrder->shopping_user->billing_firstname) . ' ' . e($shipment->shoppingOrder->shopping_user->billing_lastname) . + '
' . e($shipment->shoppingOrder->shopping_user->billing_email) . ''; + } + return 'Unbekannt'; + }) + ->editColumn('dhl_shipment_no', function ($shipment) { + return $shipment->dhl_shipment_no ? '' . e($shipment->dhl_shipment_no) . '' : '-'; + }) + ->addColumn('status', function ($shipment) { + $statusMap = [ + 'pending' => ['class' => 'warning', 'text' => 'Wartend'], + 'created' => ['class' => 'success', 'text' => 'Erstellt'], + 'shipped' => ['class' => 'primary', 'text' => 'Versendet'], + 'delivered' => ['class' => 'info', 'text' => 'Zugestellt'], + 'cancelled' => ['class' => 'secondary', 'text' => 'Storniert'], + 'failed' => ['class' => 'danger', 'text' => 'Fehler'], + ]; + $statusInfo = $statusMap[$shipment->status] ?? ['class' => 'light', 'text' => e($shipment->status)]; + return '' . $statusInfo['text'] . ''; + }) + ->addColumn('tracking_status', function ($shipment) { + if ($shipment->tracking_status) { + return '' . e($shipment->tracking_status) . '' . + ($shipment->last_tracked_at ? '
' . $shipment->last_tracked_at->format('d.m.Y H:i') . '' : ''); + } + return '-'; + }) + ->editColumn('weight_kg', function ($shipment) { + return number_format($shipment->weight_kg, 2) . ' kg'; + }) + ->editColumn('created_at', function ($shipment) { + return $shipment->created_at->format('d.m.Y H:i'); + }) + ->addColumn('actions', function ($shipment) { + $buttons = '
'; + $buttons .= ''; + if ($shipment->label_path) { + $buttons .= ''; + } + if ($shipment->canCancel()) { + $buttons .= ''; + } + if ($shipment->type == 'outbound' && !$shipment->returns()->count()) { + $buttons .= ''; + } + $buttons .= '
'; + return $buttons; + }) + ->rawColumns(['checkbox', 'id', 'type', 'order', 'customer', 'dhl_shipment_no', 'status', 'tracking_status', 'actions']) + ->make(true); + } + + /** + * Show the form for creating a new shipment + * + * @param ShoppingOrder $order + * @return View + */ + public function create(ShoppingOrder $order): View|\Illuminate\Http\RedirectResponse + { + // Check if order already has a shipment + $existingShipment = DhlShipment::where('shopping_order_id', $order->id) + ->where('type', 'outbound') + ->first(); + + if ($existingShipment) { + return redirect()->route('admin.dhl.show', $existingShipment) + ->with('warning', 'Für diese Bestellung existiert bereits eine Sendung.'); + } + + return view('admin.dhl.create', compact('order')); + } + + /** + * Store a new shipment (async via queue) + * + * @param Request $request + * @return JsonResponse + */ + public function store(Request $request): JsonResponse + { + try { + // Use DhlModalService for validation + $dhlModalService = new DhlModalService(); + $validationResult = $dhlModalService->validateShipmentData($request->all()); + + if (!$validationResult['valid']) { + return response()->json([ + 'success' => false, + 'message' => 'Validierungsfehler: ' . implode(', ', $validationResult['errors']) + ], 422); + } + + // Basic Laravel validation as fallback + $request->validate([ + 'order_id' => 'required|exists:shopping_orders,id', + 'weight' => 'required|numeric|min:0.1|max:31.5', + 'product_code' => 'sometimes|string', + 'priority' => 'sometimes|string|in:normal,high', + 'auto_track' => 'sometimes|boolean', + // Shipping address validation + 'shipping_firstname' => 'required|string|max:50', + 'shipping_lastname' => 'required|string|max:50', + 'shipping_company' => 'nullable|string|max:100', + 'shipping_address' => 'required|string|max:100', + 'shipping_houseNumber' => 'required|string|max:50', + 'shipping_zipcode' => 'required|string|max:10', + 'shipping_city' => 'required|string|max:50', + 'shipping_country_id' => 'required|exists:countries,id', + 'shipping_phone' => 'nullable|string|max:20', + ]); + + $order = ShoppingOrder::findOrFail($request->order_id); + + // Check if shipment already exists + /* $existingShipment = DhlShipment::where('shopping_order_id', $order->id) + ->where('type', 'outbound') + ->first(); + + if ($existingShipment) { + return response()->json([ + 'success' => false, + 'message' => 'Für diese Bestellung existiert bereits eine Sendung.' + ], 422); + } + */ + + // Use service to prepare address data + $shippingAddress = $dhlModalService->prepareAddressForApi($request->all()); + + // Prepare options for shipment creation + $options = [ + 'product_code' => $request->get('product_code', 'V01PAK'), + 'priority' => $request->get('priority', 'normal'), + 'auto_track' => $request->get('auto_track', true), + 'shipping_address' => $shippingAddress, + 'services' => $request->get('services', []), + 'dimensions' => $request->only(['length', 'width', 'height']) + ]; + + // Use DhlShipmentService (handles queue/sync automatically based on config) + $dhlShipmentService = new DhlShipmentService(); + $result = $dhlShipmentService->createShipment($order, (float) $request->weight, $options); + + Log::info('[DHL Controller] Shipment creation processed', [ + 'order_id' => $order->id, + 'weight' => $request->weight, + 'queued' => $result['queued'] ?? false, + 'success' => $result['success'] ?? false, + ]); + + return response()->json($result); + } catch (Exception $e) { + Log::error('[DHL Controller] Failed to dispatch shipment creation', [ + 'error' => $e->getMessage(), + 'order_id' => $request->order_id ?? 'unknown', + ]); + + return response()->json([ + 'success' => false, + 'message' => 'Fehler beim Erstellen der Sendung: ' . $e->getMessage() + ], 500); + } + } + + /** + * Display the specified shipment + * + * @param DhlShipment $shipment + * @return View + */ + public function show(DhlShipment $shipment): View + { + $shipment->load(['shoppingOrder.shopping_user', 'relatedShipment']); + + return view('admin.dhl.show', compact('shipment')); + } + + /** + * Cancel the specified shipment + * + * @param Request $request + * @param DhlShipment $shipment + * @return JsonResponse + */ + public function cancel(Request $request, DhlShipment $shipment): JsonResponse + { + try { + // Validate cancellation is possible + if (!$shipment->canCancel()) { + return response()->json([ + 'success' => false, + 'message' => 'Diese Sendung kann nicht mehr storniert werden.' + ], 422); + } + + // Dispatch cancellation job + $options = [ + 'priority' => $request->get('priority', 'normal') + ]; + + CancelShipmentJob::dispatch($shipment, $options); + + Log::info('[DHL Controller] Shipment cancellation job dispatched', [ + 'shipment_id' => $shipment->id, + 'shipment_number' => $shipment->shipment_number, + ]); + + return response()->json([ + 'success' => true, + 'message' => 'Sendung wird storniert...' + ]); + } catch (Exception $e) { + Log::error('[DHL Controller] Failed to dispatch shipment cancellation', [ + 'error' => $e->getMessage(), + 'shipment_id' => $shipment->id, + ]); + + return response()->json([ + 'success' => false, + 'message' => 'Fehler beim Stornieren der Sendung: ' . $e->getMessage() + ], 500); + } + } + + /** + * Create return label for the specified shipment + * + * @param Request $request + * @param DhlShipment $shipment + * @return JsonResponse + */ + public function createReturnLabel(Request $request, DhlShipment $shipment): JsonResponse + { + try { + // Validate return label creation is possible + if ($shipment->type !== 'outbound') { + return response()->json([ + 'success' => false, + 'message' => 'Retourenlabels können nur für ausgehende Sendungen erstellt werden.' + ], 422); + } + + // Check if return label already exists + $existingReturn = DhlShipment::where('related_shipment_id', $shipment->id) + ->where('type', 'return') + ->first(); + + if ($existingReturn) { + return response()->json([ + 'success' => false, + 'message' => 'Für diese Sendung existiert bereits ein Retourenlabel.' + ], 422); + } + + // Dispatch return label creation job + $options = [ + 'auto_track' => $request->get('auto_track', false), + 'priority' => $request->get('priority', 'normal') + ]; + + CreateReturnLabelJob::dispatch($shipment, $options); + + Log::info('[DHL Controller] Return label creation job dispatched', [ + 'original_shipment_id' => $shipment->id, + 'shipment_number' => $shipment->shipment_number, + ]); + + return response()->json([ + 'success' => true, + 'message' => 'Retourenlabel wird erstellt...' + ]); + } catch (Exception $e) { + Log::error('[DHL Controller] Failed to dispatch return label creation', [ + 'error' => $e->getMessage(), + 'shipment_id' => $shipment->id, + ]); + + return response()->json([ + 'success' => false, + 'message' => 'Fehler beim Erstellen des Retourenlabels: ' . $e->getMessage() + ], 500); + } + } + + /** + * Update tracking status for the specified shipment + * + * @param DhlShipment $shipment + * @return JsonResponse + */ + public function updateTracking(DhlShipment $shipment): JsonResponse + { + try { + if (!$shipment->tracking_number) { + return response()->json([ + 'success' => false, + 'message' => 'Keine Tracking-Nummer verfügbar.' + ], 422); + } + + // Dispatch tracking update job + TrackShipmentJob::dispatch($shipment, ['auto_retrack' => false]); + + Log::info('[DHL Controller] Tracking update job dispatched', [ + 'shipment_id' => $shipment->id, + 'tracking_number' => $shipment->tracking_number, + ]); + + return response()->json([ + 'success' => true, + 'message' => 'Tracking-Informationen werden aktualisiert...' + ]); + } catch (Exception $e) { + Log::error('[DHL Controller] Failed to dispatch tracking update', [ + 'error' => $e->getMessage(), + 'shipment_id' => $shipment->id, + ]); + + return response()->json([ + 'success' => false, + 'message' => 'Fehler beim Aktualisieren der Tracking-Informationen: ' . $e->getMessage() + ], 500); + } + } + + /** + * Download shipping label + * + * @param DhlShipment $shipment + * @return Response + */ + public function downloadLabel(DhlShipment $shipment): Response + { + try { + if (!$shipment->label_path || !Storage::exists($shipment->label_path)) { + abort(404, 'Versandlabel nicht gefunden.'); + } + + $labelContent = Storage::get($shipment->label_path); + $filename = sprintf( + 'dhl-label-%s-%s.pdf', + $shipment->type, + $shipment->shipment_number ?: $shipment->id + ); + + return response($labelContent, 200) + ->header('Content-Type', 'application/pdf') + ->header('Content-Disposition', "attachment; filename=\"{$filename}\""); + } catch (Exception $e) { + Log::error('[DHL Controller] Failed to download label', [ + 'error' => $e->getMessage(), + 'shipment_id' => $shipment->id, + 'label_path' => $shipment->label_path, + ]); + + abort(500, 'Fehler beim Download des Versandlabels.'); + } + } + + /** + * Batch operations (multiple shipments) + * + * @param Request $request + * @return JsonResponse + */ + public function batchAction(Request $request): JsonResponse + { + try { + $request->validate([ + 'action' => 'required|in:cancel,download_labels,update_tracking', + 'shipment_ids' => 'required|array|min:1', + 'shipment_ids.*' => 'exists:dhl_package_shipments,id', + ]); + + $shipmentIds = $request->shipment_ids; + $action = $request->action; + $processed = 0; + $errors = []; + + foreach ($shipmentIds as $shipmentId) { + try { + $shipment = DhlShipment::findOrFail($shipmentId); + + switch ($action) { + case 'cancel': + if ($shipment->canCancel()) { + CancelShipmentJob::dispatch($shipment); + $processed++; + } else { + $errors[] = "Sendung {$shipment->shipment_number} kann nicht storniert werden."; + } + break; + + case 'update_tracking': + if ($shipment->tracking_number) { + TrackShipmentJob::dispatch($shipment, ['auto_retrack' => false]); + $processed++; + } else { + $errors[] = "Sendung {$shipment->shipment_number} hat keine Tracking-Nummer."; + } + break; + + case 'download_labels': + // This would require ZIP creation - implement if needed + $errors[] = "Stapel-Download noch nicht implementiert."; + break; + } + } catch (Exception $e) { + $errors[] = "Fehler bei Sendung {$shipmentId}: " . $e->getMessage(); + } + } + + Log::info('[DHL Controller] Batch action executed', [ + 'action' => $action, + 'processed' => $processed, + 'errors_count' => count($errors), + ]); + + return response()->json([ + 'success' => $processed > 0, + 'message' => sprintf('%d Sendungen verarbeitet.', $processed), + 'processed' => $processed, + 'errors' => $errors, + ]); + } catch (Exception $e) { + Log::error('[DHL Controller] Batch action failed', [ + 'error' => $e->getMessage(), + 'action' => $request->action ?? 'unknown', + ]); + + return response()->json([ + 'success' => false, + 'message' => 'Fehler bei der Stapelverarbeitung: ' . $e->getMessage() + ], 500); + } + } + + /** + * Public tracking page (for customers) + * + * @param Request $request + * @return View|JsonResponse + */ + public function track(Request $request): View|JsonResponse + { + if ($request->expectsJson()) { + $request->validate([ + 'tracking_number' => 'required|string|min:10', + ]); + + try { + $shipment = DhlShipment::where('tracking_number', $request->tracking_number)->first(); + + if (!$shipment) { + return response()->json([ + 'success' => false, + 'message' => 'Sendung nicht gefunden.' + ], 404); + } + + // Dispatch tracking update + TrackShipmentJob::dispatch($shipment, ['auto_retrack' => false]); + + return response()->json([ + 'success' => true, + 'data' => [ + 'tracking_number' => $shipment->tracking_number, + 'status' => $shipment->status, + 'tracking_status' => $shipment->tracking_status, + 'last_tracked_at' => $shipment->last_tracked_at?->format('d.m.Y H:i'), + ] + ]); + } catch (Exception $e) { + Log::error('[DHL Controller] Public tracking failed', [ + 'error' => $e->getMessage(), + 'tracking_number' => $request->tracking_number ?? 'unknown', + ]); + + return response()->json([ + 'success' => false, + 'message' => 'Fehler beim Abrufen der Tracking-Informationen.' + ], 500); + } + } + + return view('public.tracking'); + } +} diff --git a/app/Http/Controllers/FileController.php b/app/Http/Controllers/FileController.php index d0908d7..db061d3 100644 --- a/app/Http/Controllers/FileController.php +++ b/app/Http/Controllers/FileController.php @@ -130,24 +130,23 @@ class FileController extends Controller if(!Storage::disk($disk)->exists($path)){ return Response::make('Datei nicht gefunden.', 404); } + + if ($do === 'download') { + return Storage::disk($disk)->download($path, $filename); + } + $file = Storage::disk($disk)->get($path); $mime = Storage::disk($disk)->mimeType($path); if(isset($file)){ - if($do === 'download'){ - return Response::make($file, 200) - ->header("Content-Type", $mime) - ->header('Content-disposition', 'attachment; filename="'.$filename.'"'); - } if($do === 'stream'){ - return Response::make($file, 200) - ->header("Content-Type", $mime) - ->header('Content-disposition','inline; filename="'.$filename.'"'); + return Storage::disk($disk)->response($path, $filename); } if($do === 'file'){ return Response::make($file, 200) ->header("Content-Type", $mime) + ->header("Content-Length", strlen($file)) ->header('Content-disposition', 'filename="'.$filename.'"'); } if($do === 'image'){ diff --git a/app/Http/Controllers/ModalController.php b/app/Http/Controllers/ModalController.php index 53639a8..d638607 100644 --- a/app/Http/Controllers/ModalController.php +++ b/app/Http/Controllers/ModalController.php @@ -17,6 +17,7 @@ use App\Models\ShoppingOrder; use App\Models\UserSalesVolume; use App\Services\BusinessPlan\TreeCalcBot; use App\Services\BusinessPlan\TreeCalcBotOptimized; +use App\Services\DhlModalService; class ModalController extends Controller { @@ -170,6 +171,11 @@ class ModalController extends Controller $user_abo = UserAbo::find($data['id']); $ret = view("user.abo.modal_abo_show_products", compact( 'data', 'user_abo'))->render(); } + + if($data['action'] === 'create-dhl-shipment') { + $id = $data['id'] ?? null; + $ret = $this->handleDhlShipmentModal($id, $data); + } } return response()->json(['response' => $data, 'html'=>$ret, 'status'=>$status]); @@ -195,6 +201,55 @@ class ModalController extends Controller } + /** + * Handle DHL shipment modal preparation + * + * @param mixed $id Order ID or 'new' + * @param array $data Request data + * @return string Rendered view + */ + private function handleDhlShipmentModal($id, array $data): string + { + try { + $dhlModalService = new DhlModalService(); + $modalData = $dhlModalService->prepareModalData($id, $data); + + // Merge the prepared data with the original request data + $viewData = array_merge($data, $modalData, [ + 'id' => $id, + 'data' => $data + ]); + + return view("admin.dhl.modal_create_shipment", $viewData)->render(); + + } catch (\Exception $e) { + \Log::error('[ModalController] Error in DHL shipment modal', [ + 'order_id' => $id, + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + + // Return error view or fallback + $errorData = [ + 'id' => $id, + 'data' => $data, + 'order' => null, + 'orderWeight' => 1.0, + 'shippingAddress' => null, + 'availableCountries' => \App\Models\Country::where('active', 1)->get(), + 'productCodes' => [ + 'V01PAK' => 'DHL Paket (National)', + 'V53WPAK' => 'DHL Paket International', + 'V54EPAK' => 'DHL Express' + ], + 'errors' => ['Fehler beim Laden der Daten: ' . $e->getMessage()], + 'warnings' => [] + ]; + + return view("admin.dhl.modal_create_shipment", $errorData)->render(); + } + } + } diff --git a/app/Http/Controllers/Portal/InController.php b/app/Http/Controllers/Portal/InController.php index df553eb..6093ef1 100755 --- a/app/Http/Controllers/Portal/InController.php +++ b/app/Http/Controllers/Portal/InController.php @@ -65,16 +65,16 @@ class InController extends Controller $data = Request::all(); - $response = ""; $status = false; - - if(isset($data['action']) && $data['action'] === 'user-order-show-product'){ - $product = Product::find($data['id']); //current user form order - $ret = view("admin.modal.show_product", compact('product', 'data'))->render(); - return response()->json(['response' => $data, 'html'=>$ret, 'status'=>$status]); - + if(isset($data['action'])){ + if($data['action'] === 'user-order-show-product'){ + $product = Product::find($data['id']); //current user form order + $ret = view("admin.modal.show_product", compact('product', 'data'))->render(); + return response()->json(['response' => $data, 'html'=>$ret, 'status'=>$status]); + } } + $data = Request::get('data'); $target = Request::get('target'); if($data === "data_protection"){ diff --git a/app/Http/Controllers/SettingController.php b/app/Http/Controllers/SettingController.php index 52ed1d7..ea09007 100644 --- a/app/Http/Controllers/SettingController.php +++ b/app/Http/Controllers/SettingController.php @@ -27,19 +27,92 @@ class SettingController extends Controller public function store() { - $data = Request::all(); - if(isset($data['action'])){ - if(isset($data['settings'])){ - foreach ($data['settings'] as $key=>$value){ + if (isset($data['action'])) { + if (isset($data['settings'])) { + foreach ($data['settings'] as $key => $value) { $value['val'] = isset($value['val']) ? $value['val'] : false; Setting::setContentBySlug($key, $value['val'], $value['type']); } } + + // DHL-spezifische Behandlung + if ($data['action'] === 'save_dhl') { + $this->updateDhlConfigCache(); + \Session()->flash('alert-save-dhl', 'DHL Konfiguration erfolgreich gespeichert!'); + } else { + \Session()->flash('alert-save', '1'); + } } - \Session()->flash('alert-save', '1'); + return redirect(route('admin_settings')); - - } -} \ No newline at end of file + + /** + * Get DHL configuration merged from database settings and .env values + * Database settings override .env values + */ + public function getDhlConfig() + { + return [ + // API Settings + 'base_url' => Setting::getContentBySlug('dhl_base_url') ?: config('dhl.base_url'), + 'api_key' => Setting::getContentBySlug('dhl_api_key') ?: config('dhl.api_key'), + 'username' => Setting::getContentBySlug('dhl_username') ?: config('dhl.username'), + 'password' => Setting::getContentBySlug('dhl_password') ?: config('dhl.password'), + 'billing_number' => Setting::getContentBySlug('dhl_billing_number') ?: config('dhl.billing_number'), + + // Product Settings + 'default_product' => Setting::getContentBySlug('dhl_product') ?: config('dhl.default_product'), + 'label_format' => Setting::getContentBySlug('dhl_label_format') ?: config('dhl.label_format'), + 'print_format' => Setting::getContentBySlug('dhl_print_format') ?: config('dhl.print_format'), + 'retoure_print_format' => Setting::getContentBySlug('dhl_retoure_print_format') ?: config('dhl.retoure_print_format'), + 'use_queue' => Setting::getContentBySlug('dhl_use_queue') ?: config('dhl.use_queue'), + + // Sender Address + 'sender' => [ + 'company' => Setting::getContentBySlug('dhl_sender_company') ?: config('dhl.sender.company'), + 'name' => Setting::getContentBySlug('dhl_sender_name') ?: config('dhl.sender.name'), + 'street' => Setting::getContentBySlug('dhl_sender_street') ?: config('dhl.sender.street'), + 'houseNumber' => Setting::getContentBySlug('dhl_sender_house_number') ?: config('dhl.sender.houseNumber'), + 'postalCode' => Setting::getContentBySlug('dhl_sender_postal_code') ?: config('dhl.sender.postalCode'), + 'city' => Setting::getContentBySlug('dhl_sender_city') ?: config('dhl.sender.city'), + 'country' => Setting::getContentBySlug('dhl_sender_country') ?: config('dhl.sender.country'), + 'email' => Setting::getContentBySlug('dhl_sender_email') ?: config('dhl.sender.email'), + 'phone' => Setting::getContentBySlug('dhl_sender_phone') ?: config('dhl.sender.phone'), + ], + + // Account Numbers + 'account_numbers' => [ + 'V01PAK' => Setting::getContentBySlug('dhl_account_v01pak') ?: config('dhl.account_numbers.V01PAK'), + 'V62WP' => Setting::getContentBySlug('dhl_account_v62wp') ?: config('dhl.account_numbers.V62WP'), + 'V53PAK' => Setting::getContentBySlug('dhl_account_v53pak') ?: config('dhl.account_numbers.V53PAK'), + 'V07PAK' => Setting::getContentBySlug('dhl_account_v07pak') ?: config('dhl.account_numbers.V07PAK'), + 'default' => config('dhl.account_numbers.default'), + ], + + // Static config values (webhook, profile, legacy) + 'profile' => config('dhl.profile'), + 'webhook' => config('dhl.webhook'), + 'legacy' => config('dhl.legacy'), + ]; + } + + /** + * Update DHL configuration cache after saving settings + */ + private function updateDhlConfigCache() + { + // Clear config cache to force reload from database + \Artisan::call('config:clear'); + + // Optional: Test DHL connection with new settings + try { + $dhlManager = app('Acme\Dhl\DhlManager'); + // You could add a connection test here if needed + \Log::info('DHL configuration updated successfully'); + } catch (\Exception $e) { + \Log::error('DHL configuration update failed: ' . $e->getMessage()); + } + } +} diff --git a/app/Http/Controllers/UserShopController.php b/app/Http/Controllers/UserShopController.php index a38ead5..60a3a36 100755 --- a/app/Http/Controllers/UserShopController.php +++ b/app/Http/Controllers/UserShopController.php @@ -322,6 +322,9 @@ class UserShopController extends Controller return redirect()->back()->withErrors($validator)->withInput(Request::all()); } \Session()->flash('shop-name-error', 'check'); + if(Request::get('user_shop_id')){ + return back()->withInput(Request::all()); + } return redirect(route('user_shop'))->withInput(Request::all()); } @@ -329,6 +332,8 @@ class UserShopController extends Controller $rules = array( 'user_shop_name' => ' required|alpha_dash|unique:user_shops,name|min:4|max:20|full_word_check', + 'user_shop_active' => 'accepted', + ); Validator::extend('full_word_check', function ($attribute, $value, $parameters, $validator) { if(in_array($value, config('profanity.full_word_check'))){ @@ -337,41 +342,43 @@ class UserShopController extends Controller return true; }); $validator = Validator::make(Request::all(), $rules); - - if ($validator->fails()) { - \Session()->flash('shop-name-error', 'error'); - }else{ - \Session()->flash('shop-name-error', 'check'); - - } - - $rules = array( - 'user_shop_active' => 'accepted', - ); - $validator = Validator::make(Request::all(), $rules); if ($validator->fails()) { + \Session()->flash('shop-name-error', 'error'); return redirect()->back()->withErrors($validator)->withInput(Request::all()); } + \Session()->flash('shop-name-error', 'check'); + //all is right - save $user = Auth::user(); $data = Request::all(); $slug = SlugService::createSlug(UserShop::class, 'slug', $data['user_shop_name']); - $user_shop = UserShop::create([ - 'user_id' => $user->id, - 'name' => $slug, - 'active' => true, - 'active_date' => now(), - ] - ); - - $ret = $this->userShopRegisterSubDomain($user_shop->slug); + if(isset($data['user_shop_id'])){ + $user_shop = UserShop::find($data['user_shop_id']); + if($user_shop->user_id != $user->id){ + abort(404); + } + $user_shop->name = $slug; + $user_shop->slug = $slug; + $user_shop->save(); + }else{ + $user_shop = UserShop::create([ + 'user_id' => $user->id, + 'name' => $slug, + 'active' => true, + 'active_date' => now(), + ] + ); + } + \Session()->flash('alert-save', true); + return redirect(route('user_shop')); + /*$ret = $this->userShopRegisterSubDomain($user_shop->slug); if($ret['success'] === true){ \Session()->flash('alert-save', true); }else{ $user_shop->forceDelete(); \Session()->flash('alert-error', $ret['error']); } - return redirect(route('user_shop')); + return redirect(route('user_shop'));*/ } } @@ -444,4 +451,19 @@ class UserShopController extends Controller )); } + public function editName(){ + $user = Auth::user(); + $user_shop = $user->shop; + if(!$user_shop){ + abort(404); + } + $user_shop_domain = $user_shop->getSubdomain(false); + $data = [ + 'user' => $user, + 'user_shop_id' => $user_shop->id, + 'user_shop_domain' => $user_shop_domain, + ]; + return view('user.shop_edit_name', $data); + } + } \ No newline at end of file diff --git a/app/Http/Middleware/DomainResolver.php b/app/Http/Middleware/DomainResolver.php index 668e007..d94810a 100644 --- a/app/Http/Middleware/DomainResolver.php +++ b/app/Http/Middleware/DomainResolver.php @@ -37,24 +37,26 @@ class DomainResolver // leiten wir sicher auf die Hauptdomain um. if ($context->isUnknown()) { // Detailliertes Logging für spätere Analyse - \Log::warning('Unknown domain accessed', [ - 'host' => $request->getHost(), - 'subdomain' => $context->subdomain, - 'user_agent' => $request->userAgent(), - 'ip' => $request->ip(), - 'referer' => $request->header('referer'), - 'path' => $request->getPathInfo() - ]); - + if(config('app.debug')){ + \Log::warning('Unknown domain accessed', [ + 'host' => $request->getHost(), + 'subdomain' => $context->subdomain, + 'user_agent' => $request->userAgent(), + 'ip' => $request->ip(), + 'referer' => $request->header('referer'), + 'path' => $request->getPathInfo() + ]); + } // Holt die URL der Hauptdomain vom DomainService und leitet um. $mainUrl = app(\App\Services\DomainService::class)->buildUrl('main'); return redirect()->away($mainUrl, 301); } - \Log::debug('DomainResolver: context', [ - 'context' => $context, - 'subdomain' => $context->subdomain - ]); - + if(config('app.debug')){ + \Log::debug('DomainResolver: context', [ + 'context' => $context, + 'subdomain' => $context->subdomain + ]); + } // Für User-Shop-Domains: Validierung und Route-Parameter-Bereinigung if ($context->isUserShop()) { // Validiere UserShop-Berechtigung (bereits im DomainServiceProvider geprüft, diff --git a/app/Jobs/CancelShipmentJob.php b/app/Jobs/CancelShipmentJob.php new file mode 100644 index 0000000..9b54e6c --- /dev/null +++ b/app/Jobs/CancelShipmentJob.php @@ -0,0 +1,144 @@ +dhlShipment = $dhlShipment; + $this->options = $options; + + // Set queue name based on priority + if (isset($options['priority']) && $options['priority'] === 'high') { + $this->onQueue('high-priority'); + } else { + $this->onQueue('dhl-cancellations'); + } + } + + /** + * Execute the job. + */ + public function handle(): void + { + try { + Log::info('[DHL Queue] Starting shipment cancellation job', [ + 'shipment_id' => $this->dhlShipment->id, + 'shipment_number' => $this->dhlShipment->shipment_number, + 'attempt' => $this->attempts(), + ]); + + $dhlService = new DhlApiService(); + + // Cancel the shipment + $success = $dhlService->cancelShipment($this->dhlShipment); + + if ($success) { + Log::info('[DHL Queue] Shipment cancelled successfully', [ + 'shipment_id' => $this->dhlShipment->id, + 'shipment_number' => $this->dhlShipment->shipment_number, + ]); + } else { + throw new Exception('Cancellation returned false'); + } + + } catch (Exception $e) { + Log::error('[DHL Queue] Shipment cancellation failed', [ + 'shipment_id' => $this->dhlShipment->id, + 'shipment_number' => $this->dhlShipment->shipment_number, + 'error' => $e->getMessage(), + 'attempt' => $this->attempts(), + 'max_tries' => $this->tries, + ]); + + // If this is the final attempt, mark as permanently failed + if ($this->attempts() >= $this->tries) { + Log::error('[DHL Queue] Shipment cancellation permanently failed', [ + 'shipment_id' => $this->dhlShipment->id, + 'error' => $e->getMessage(), + ]); + } + + throw $e; // Re-throw to trigger retry mechanism + } + } + + /** + * Handle a job failure. + * + * @param Exception $exception + */ + public function failed(Exception $exception): void + { + Log::error('[DHL Queue] CancelShipmentJob permanently failed', [ + 'shipment_id' => $this->dhlShipment->id, + 'shipment_number' => $this->dhlShipment->shipment_number, + 'error' => $exception->getMessage(), + 'trace' => $exception->getTraceAsString(), + ]); + + // You could implement additional failure handling here: + // - Send notification to admin + // - Create manual task for staff to handle cancellation + // - Update shipment status to indicate cancellation failed + } + + /** + * Determine the time at which the job should timeout. + * + * @return \DateTime + */ + public function retryUntil() + { + return now()->addHour(); // Shorter timeout for cancellations + } +} diff --git a/app/Jobs/CreateReturnLabelJob.php b/app/Jobs/CreateReturnLabelJob.php new file mode 100644 index 0000000..7f27637 --- /dev/null +++ b/app/Jobs/CreateReturnLabelJob.php @@ -0,0 +1,148 @@ +originalShipment = $originalShipment; + $this->options = $options; + + // Set queue name based on priority + if (isset($options['priority']) && $options['priority'] === 'high') { + $this->onQueue('high-priority'); + } else { + $this->onQueue('dhl-returns'); + } + } + + /** + * Execute the job. + */ + public function handle(): void + { + try { + Log::info('[DHL Queue] Starting return label creation job', [ + 'original_shipment_id' => $this->originalShipment->id, + 'original_shipment_number' => $this->originalShipment->shipment_number, + 'attempt' => $this->attempts(), + ]); + + $dhlService = new DhlApiService(); + + // Create the return label + $returnShipment = $dhlService->createReturnLabel( + $this->originalShipment, + $this->options + ); + + Log::info('[DHL Queue] Return label created successfully', [ + 'original_shipment_id' => $this->originalShipment->id, + 'return_shipment_id' => $returnShipment->id, + 'return_shipment_number' => $returnShipment->shipment_number, + ]); + + // Trigger follow-up actions if specified + if (isset($this->options['auto_track']) && $this->options['auto_track']) { + \App\Jobs\TrackShipmentJob::dispatch($returnShipment)->delay(now()->addMinutes(5)); + } + + } catch (Exception $e) { + Log::error('[DHL Queue] Return label creation failed', [ + 'original_shipment_id' => $this->originalShipment->id, + 'error' => $e->getMessage(), + 'attempt' => $this->attempts(), + 'max_tries' => $this->tries, + ]); + + // If this is the final attempt, mark as permanently failed + if ($this->attempts() >= $this->tries) { + Log::error('[DHL Queue] Return label creation permanently failed', [ + 'original_shipment_id' => $this->originalShipment->id, + 'error' => $e->getMessage(), + ]); + } + + throw $e; // Re-throw to trigger retry mechanism + } + } + + /** + * Handle a job failure. + * + * @param Exception $exception + */ + public function failed(Exception $exception): void + { + Log::error('[DHL Queue] CreateReturnLabelJob permanently failed', [ + 'original_shipment_id' => $this->originalShipment->id, + 'original_shipment_number' => $this->originalShipment->shipment_number, + 'error' => $exception->getMessage(), + 'trace' => $exception->getTraceAsString(), + ]); + + // You could implement additional failure handling here: + // - Send notification to admin + // - Create manual task for staff to handle return label creation + // - Update original shipment to mark return label creation failed + } + + /** + * Determine the time at which the job should timeout. + * + * @return \DateTime + */ + public function retryUntil() + { + return now()->addHours(2); + } +} diff --git a/app/Jobs/CreateShipmentJob.php b/app/Jobs/CreateShipmentJob.php new file mode 100644 index 0000000..7894fe2 --- /dev/null +++ b/app/Jobs/CreateShipmentJob.php @@ -0,0 +1,179 @@ +shoppingOrder = $shoppingOrder; + $this->weight = $weight; + $this->options = $options; + + // Load DHL config once when creating the job + if (empty($dhlConfig)) { + $settingController = new \App\Http\Controllers\SettingController(); + $this->dhlConfig = $settingController->getDhlConfig(); + } else { + $this->dhlConfig = $dhlConfig; + } + + // Set queue name based on priority + if (isset($options['priority']) && $options['priority'] === 'high') { + $this->onQueue('high-priority'); + } else { + $this->onQueue('dhl-shipments'); + } + } + + /** + * Execute the job. + */ + public function handle(): void + { + try { + Log::info('[DHL Queue] Starting shipment creation job', [ + 'order_id' => $this->shoppingOrder->id, + 'weight' => $this->weight, + 'attempt' => $this->attempts(), + ]); + + // Use DHL configuration loaded in constructor + $dhlClient = new \Acme\Dhl\Support\DhlClient( + $this->dhlConfig['base_url'], + $this->dhlConfig['api_key'], + $this->dhlConfig['username'], + $this->dhlConfig['password'] + ); + + $shippingService = new \Acme\Dhl\Services\ShippingService($dhlClient); + + // Prepare order data using helper + $orderData = DhlDataHelper::prepareOrderData($this->shoppingOrder, $this->weight, $this->options, $this->dhlConfig); + + // Create the shipment using new package + $result = $shippingService->createLabel($orderData); + + Log::info('[DHL Queue] Shipment created successfully', [ + 'order_id' => $this->shoppingOrder->id, + 'shipment_number' => $result['shipmentNumber'] ?? 'N/A', + 'label_path' => $result['labelPath'] ?? 'N/A', + ]); + + // Trigger follow-up actions if specified (if tracking number available) + if (isset($this->options['auto_track']) && $this->options['auto_track'] && !empty($result['trackingNumber'])) { + Log::info('[DHL Queue] Scheduling tracking update', [ + 'tracking_number' => $result['trackingNumber'] + ]); + // Note: TrackShipmentJob would need to be updated to work with tracking numbers + } + } catch (Exception $e) { + Log::error('[DHL Queue] Shipment creation failed', [ + 'order_id' => $this->shoppingOrder->id, + 'error' => $e->getMessage(), + 'attempt' => $this->attempts(), + 'max_tries' => $this->tries, + ]); + + // If this is the final attempt, mark as permanently failed + if ($this->attempts() >= $this->tries) { + Log::error('[DHL Queue] Shipment creation permanently failed', [ + 'order_id' => $this->shoppingOrder->id, + 'error' => $e->getMessage(), + ]); + } + + throw $e; // Re-throw to trigger retry mechanism + } + } + + /** + * Handle a job failure. + * + * @param Exception $exception + */ + public function failed(Exception $exception): void + { + Log::error('[DHL Queue] CreateShipmentJob permanently failed', [ + 'order_id' => $this->shoppingOrder->id, + 'error' => $exception->getMessage(), + 'trace' => $exception->getTraceAsString(), + ]); + + // You could implement additional failure handling here: + // - Send notification to admin + // - Update order status + // - Create manual task for staff + } + + + /** + * Determine the time at which the job should timeout. + * + * @return \DateTime + */ + public function retryUntil() + { + return now()->addHours(2); + } +} diff --git a/app/Jobs/TrackShipmentJob.php b/app/Jobs/TrackShipmentJob.php new file mode 100644 index 0000000..44c56b7 --- /dev/null +++ b/app/Jobs/TrackShipmentJob.php @@ -0,0 +1,194 @@ +dhlShipment = $dhlShipment; + $this->options = $options; + + // Set queue name - tracking is usually lower priority + $this->onQueue('dhl-tracking'); + } + + /** + * Execute the job. + */ + public function handle(): void + { + try { + Log::info('[DHL Queue] Starting shipment tracking job', [ + 'shipment_id' => $this->dhlShipment->id, + 'tracking_number' => $this->dhlShipment->tracking_number, + 'attempt' => $this->attempts(), + ]); + + $dhlService = new DhlApiService(); + + // Get tracking details + $trackingDetails = $dhlService->getTrackingDetails($this->dhlShipment); + + Log::info('[DHL Queue] Shipment tracking updated successfully', [ + 'shipment_id' => $this->dhlShipment->id, + 'tracking_status' => $trackingDetails['status'] ?? 'unknown', + 'events_count' => isset($trackingDetails['events']) ? count($trackingDetails['events']) : 0, + ]); + + // Schedule next tracking update if shipment is still in transit + if (isset($this->options['auto_retrack']) && $this->options['auto_retrack']) { + $status = $trackingDetails['status'] ?? ''; + if ($this->shouldContinueTracking($status)) { + // Schedule next tracking in 2-6 hours based on current status + $nextTrackingDelay = $this->getNextTrackingDelay($status); + TrackShipmentJob::dispatch($this->dhlShipment, $this->options) + ->delay(now()->addMinutes($nextTrackingDelay)); + + Log::info('[DHL Queue] Next tracking job scheduled', [ + 'shipment_id' => $this->dhlShipment->id, + 'delay_minutes' => $nextTrackingDelay, + ]); + } + } + + } catch (Exception $e) { + Log::warning('[DHL Queue] Shipment tracking failed', [ + 'shipment_id' => $this->dhlShipment->id, + 'tracking_number' => $this->dhlShipment->tracking_number, + 'error' => $e->getMessage(), + 'attempt' => $this->attempts(), + 'max_tries' => $this->tries, + ]); + + // For tracking, we don't necessarily need to fail hard + if ($this->attempts() >= $this->tries) { + Log::warning('[DHL Queue] Shipment tracking permanently failed', [ + 'shipment_id' => $this->dhlShipment->id, + 'error' => $e->getMessage(), + ]); + + // Don't re-throw for final attempt - just log and continue + return; + } + + throw $e; // Re-throw to trigger retry mechanism + } + } + + /** + * Handle a job failure. + * + * @param Exception $exception + */ + public function failed(Exception $exception): void + { + Log::warning('[DHL Queue] TrackShipmentJob permanently failed', [ + 'shipment_id' => $this->dhlShipment->id, + 'tracking_number' => $this->dhlShipment->tracking_number, + 'error' => $exception->getMessage(), + ]); + + // Tracking failures are less critical - just log them + } + + /** + * Determine if we should continue tracking this shipment + * + * @param string $status + * @return bool + */ + private function shouldContinueTracking(string $status): bool + { + $finalStates = [ + 'delivered', + 'delivered_to_recipient', + 'delivered_to_pickup_location', + 'returned_to_sender', + 'cancelled', + 'lost', + ]; + + return !in_array(strtolower($status), $finalStates); + } + + /** + * Get delay for next tracking update based on current status + * + * @param string $status + * @return int Minutes until next tracking + */ + private function getNextTrackingDelay(string $status): int + { + switch (strtolower($status)) { + case 'picked_up': + case 'in_transit': + return 120; // 2 hours for active shipments + case 'out_for_delivery': + return 60; // 1 hour when out for delivery + case 'exception': + case 'failed_attempt': + return 240; // 4 hours for problem shipments + default: + return 180; // 3 hours default + } + } + + /** + * Determine the time at which the job should timeout. + * + * @return \DateTime + */ + public function retryUntil() + { + return now()->addMinutes(30); // Short timeout for tracking + } +} diff --git a/app/Mail/MailAutoReleaseAccount.php b/app/Mail/MailAutoReleaseAccount.php index 2363137..03ae254 100644 --- a/app/Mail/MailAutoReleaseAccount.php +++ b/app/Mail/MailAutoReleaseAccount.php @@ -51,7 +51,7 @@ class MailAutoReleaseAccount extends Mailable $copy1line .= 'Sollte Daten nicht vollständig sein, bitte Kontakt zum Berater aufnehmen.'."\n"; return $this->view('emails.info')->with([ - 'url' => route('admin_lead_edit', $this->user->id).'?show=check_lead', + 'url' => config('app.url_crm') . '/admin/lead/edit/' . $this->user->id . '?show=check_lead', 'title' => 'Berater Registrierung prüfen', 'button' => 'zur Berater Prüfung', 'copy1line' => $copy1line, diff --git a/app/Mail/MailInfo.php b/app/Mail/MailInfo.php index ed1d238..3a24f0a 100644 --- a/app/Mail/MailInfo.php +++ b/app/Mail/MailInfo.php @@ -61,14 +61,14 @@ class MailInfo extends Mailable $copy1line = "Infos zum Berater:"."\n"; $button = "zum Berater"; $title = "Ein Berater möchte seine Mitgliedschaft beenden."; - $url = route('admin_lead_edit', $this->user->id).'?show=check_lead'; + $url = config('app.url_crm') . '/admin/lead/edit/' . $this->user->id . '?show=check_lead'; } if($this->action === "check_is_like_customer") { $copy1line = "Hier geht es zum Kunden:"."\n"; $button = "zum Kunden"; $title = "Ein Kunden muss überprüft werden und einem Berater zugeordnet werden, da die Adresse nicht eindeutig ist."; - $url = route('admin_customer_detail', $this->user->id); + $url = config('app.url_crm') . '/admin/customer/detail/' . $this->user->id; $content .= $this->user ? 'Firma: '.$this->user->billing_company."\n" : ''; $content .= \App\Services\HTMLHelper::getSalutationLang($this->user->billing_salutation)." "; $content .= $this->user->billing_firstname." "; @@ -86,7 +86,7 @@ class MailInfo extends Mailable $copy1line = "Hier geht es zum Kunden:"."\n"; $button = "zum Kunden"; $title = "Ein Kunden muss erneut überprüft werden, da bei einer Änderung eine bestehende Kundenhoheit gefunden wurde."; - $url = route('admin_customer_detail', $this->user->id); + $url = config('app.url_crm') . '/admin/customer/detail/' . $this->user->id; $content .= "Folgende Daten für die Kundenhoheit wurden geändert:"."\n"; foreach ($this->data as $key=>$value){ $content .= $this->user->{$key}." => ".$value."\n"; diff --git a/app/Mail/MailReleaseAccount.php b/app/Mail/MailReleaseAccount.php index e064716..8328440 100644 --- a/app/Mail/MailReleaseAccount.php +++ b/app/Mail/MailReleaseAccount.php @@ -52,7 +52,7 @@ class MailReleaseAccount extends Mailable $copy1line .= 'Der Berater erhält eine Mail, dass sein Account freigeschaltet wurde. Der Vertrag wird automatisch mit den Daten des Vertriebspartners erstellt.'."\n"; return $this->view('emails.info')->with([ - 'url' => route('admin_lead_edit', $this->user->id).'?show=check_lead', + 'url' => config('app.url_crm') . '/admin/lead/edit/' . $this->user->id . '?show=check_lead', 'title' => 'Berater Registrierung prüfen', 'button' => 'zur Berater Prüfung', 'copy1line' => $copy1line, diff --git a/app/Mail/MailReleaseDocument.php b/app/Mail/MailReleaseDocument.php index 565ebaa..764ff1c 100644 --- a/app/Mail/MailReleaseDocument.php +++ b/app/Mail/MailReleaseDocument.php @@ -48,7 +48,7 @@ class MailReleaseDocument extends Mailable $copy1line .= 'Bei fehlerhafter Angabe nimm bitte kontakt mit dem Berater auf.'."\n"; return $this->view('emails.info')->with([ - 'url' => route('admin_lead_edit', $this->user->id).'?show=check_lead', + 'url' => config('app.url_crm') . '/admin/lead/edit/' . $this->user->id . '?show=check_lead', 'title' => 'Berater Unterlagen prüfen', 'button' => 'zur Berater Prüfung', 'copy1line' => $copy1line, diff --git a/app/Models/DhlShipment.php b/app/Models/DhlShipment.php new file mode 100644 index 0000000..a5a7ce9 --- /dev/null +++ b/app/Models/DhlShipment.php @@ -0,0 +1,407 @@ +|DhlShipment active() + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment outbound() + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment query() + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment returns() + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment trackable() + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereApiErrors($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereApiRequestData($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereApiResponseData($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereCurrency($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereDeliveredAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereHeight($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereLabelFormat($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereLabelPath($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereLabelPrinted($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereLastTrackedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereLength($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereMetadata($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereNotes($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereProductCode($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientCity($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientCompany($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientCountry($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientEmail($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientName($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientPhone($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientPostalCode($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientState($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientStreet($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRecipientStreetNumber($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereRelatedShipmentId($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereServices($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereShipmentNumber($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereShippedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereShippingCost($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereShoppingOrderId($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereTrackingDetails($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereTrackingNumber($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereTrackingStatus($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereType($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereWeight($value) + * @method static \Illuminate\Database\Eloquent\Builder|DhlShipment whereWidth($value) + * @mixin \Eloquent + */ +class DhlShipment extends Model +{ + use HasFactory; + + /** + * The table associated with the model. + */ + protected $table = 'dhl_shipments'; + + /** + * The attributes that are mass assignable. + */ + protected $fillable = [ + 'shopping_order_id', + 'shipment_number', + 'tracking_number', + 'type', + 'related_shipment_id', + 'weight', + 'length', + 'width', + 'height', + 'product_code', + 'services', + 'label_path', + 'label_format', + 'label_printed', + 'status', + 'tracking_status', + 'tracking_details', + 'last_tracked_at', + 'recipient_name', + 'recipient_company', + 'recipient_street', + 'recipient_street_number', + 'recipient_postal_code', + 'recipient_city', + 'recipient_state', + 'recipient_country', + 'recipient_email', + 'recipient_phone', + 'api_request_data', + 'api_response_data', + 'api_errors', + 'shipping_cost', + 'currency', + 'notes', + 'metadata', + 'shipped_at', + 'delivered_at', + ]; + + /** + * The attributes that should be cast. + */ + protected $casts = [ + 'weight' => 'float', + 'length' => 'integer', + 'width' => 'integer', + 'height' => 'integer', + 'services' => 'array', + 'label_printed' => 'boolean', + 'tracking_details' => 'array', + 'last_tracked_at' => 'datetime', + 'api_request_data' => 'array', + 'api_response_data' => 'array', + 'shipping_cost' => 'decimal:2', + 'metadata' => 'array', + 'shipped_at' => 'datetime', + 'delivered_at' => 'datetime', + ]; + + /** + * Shipment types + */ + public const TYPE_OUTBOUND = 'outbound'; + public const TYPE_RETURN = 'return'; + + /** + * Shipment statuses + */ + public const STATUS_CREATED = 'created'; + public const STATUS_SUBMITTED = 'submitted'; + public const STATUS_IN_TRANSIT = 'in_transit'; + public const STATUS_DELIVERED = 'delivered'; + public const STATUS_RETURNED = 'returned'; + public const STATUS_CANCELLED = 'cancelled'; + public const STATUS_FAILED = 'failed'; + + /** + * Get the shopping order that owns the shipment + */ + public function shoppingOrder(): BelongsTo + { + return $this->belongsTo(ShoppingOrder::class, 'shopping_order_id'); + } + + /** + * Get the related shipment (for returns) + */ + public function relatedShipment(): BelongsTo + { + return $this->belongsTo(DhlShipment::class, 'related_shipment_id'); + } + + /** + * Get the return shipment (for outbound shipments) + */ + public function returnShipment(): HasOne + { + return $this->hasOne(DhlShipment::class, 'related_shipment_id'); + } + + /** + * Scope for outbound shipments + */ + public function scopeOutbound($query) + { + return $query->where('type', self::TYPE_OUTBOUND); + } + + /** + * Scope for return shipments + */ + public function scopeReturns($query) + { + return $query->where('type', self::TYPE_RETURN); + } + + /** + * Scope for active shipments (not cancelled or failed) + */ + public function scopeActive($query) + { + return $query->whereNotIn('status', [self::STATUS_CANCELLED, self::STATUS_FAILED]); + } + + /** + * Scope for trackable shipments (have tracking number) + */ + public function scopeTrackable($query) + { + return $query->whereNotNull('tracking_number'); + } + + /** + * Check if shipment is outbound + */ + public function isOutbound(): bool + { + return $this->type === self::TYPE_OUTBOUND; + } + + /** + * Check if shipment is return + */ + public function isReturn(): bool + { + return $this->type === self::TYPE_RETURN; + } + + /** + * Check if shipment can be cancelled + */ + public function canBeCancelled(): bool + { + return in_array($this->status, [ + self::STATUS_CREATED, + self::STATUS_SUBMITTED, + ]); + } + + /** + * Check if shipment is delivered + */ + public function isDelivered(): bool + { + return $this->status === self::STATUS_DELIVERED; + } + + /** + * Check if shipment has tracking information + */ + public function hasTracking(): bool + { + return !empty($this->tracking_number); + } + + /** + * Check if label is available + */ + public function hasLabel(): bool + { + return !empty($this->label_path) && file_exists(storage_path('app/' . $this->label_path)); + } + + /** + * Get full recipient address as formatted string + */ + public function getRecipientAddressAttribute(): string + { + $address = $this->recipient_name; + + if ($this->recipient_company) { + $address .= "\n" . $this->recipient_company; + } + + $address .= "\n" . $this->recipient_street . ' ' . $this->recipient_street_number; + $address .= "\n" . $this->recipient_postal_code . ' ' . $this->recipient_city; + + if ($this->recipient_state) { + $address .= "\n" . $this->recipient_state; + } + + $address .= "\n" . $this->recipient_country; + + return $address; + } + + /** + * Get package dimensions as formatted string + */ + public function getDimensionsAttribute(): ?string + { + if (!$this->length || !$this->width || !$this->height) { + return null; + } + + return $this->length . ' x ' . $this->width . ' x ' . $this->height . ' cm'; + } + + /** + * Get human-readable status + */ + public function getStatusLabelAttribute(): string + { + return match($this->status) { + self::STATUS_CREATED => 'Erstellt', + self::STATUS_SUBMITTED => 'Übertragen', + self::STATUS_IN_TRANSIT => 'Unterwegs', + self::STATUS_DELIVERED => 'Zugestellt', + self::STATUS_RETURNED => 'Zurückgeschickt', + self::STATUS_CANCELLED => 'Storniert', + self::STATUS_FAILED => 'Fehler', + default => 'Unbekannt', + }; + } + + /** + * Get human-readable type + */ + public function getTypeLabelAttribute(): string + { + return match($this->type) { + self::TYPE_OUTBOUND => 'Versand', + self::TYPE_RETURN => 'Retoure', + default => 'Unbekannt', + }; + } + + /** + * Get label file URL for download + */ + public function getLabelUrlAttribute(): ?string + { + if (!$this->hasLabel()) { + return null; + } + + return route('admin.dhl.shipments.label', $this->id); + } + + /** + * Boot the model + */ + protected static function boot() + { + parent::boot(); + + static::creating(function ($shipment) { + // Set default values + if (empty($shipment->currency)) { + $shipment->currency = config('dhl.defaults.currency', 'EUR'); + } + + if (empty($shipment->product_code)) { + $shipment->product_code = config('dhl.defaults.product', 'V01PAK'); + } + + if (empty($shipment->label_format)) { + $shipment->label_format = config('dhl.labels.format', 'PDF'); + } + }); + } +} \ No newline at end of file diff --git a/app/Models/ShoppingOrder.php b/app/Models/ShoppingOrder.php index 861b777..e152fc6 100644 --- a/app/Models/ShoppingOrder.php +++ b/app/Models/ShoppingOrder.php @@ -101,6 +101,12 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property int|null $abo_interval * @method static \Illuminate\Database\Eloquent\Builder|ShoppingOrder whereAboInterval($value) * @method static \Illuminate\Database\Eloquent\Builder|ShoppingOrder whereIsAbo($value) + * @property-read \Illuminate\Database\Eloquent\Collection $dhlOutboundShipments + * @property-read int|null $dhl_outbound_shipments_count + * @property-read \Illuminate\Database\Eloquent\Collection $dhlReturnShipments + * @property-read int|null $dhl_return_shipments_count + * @property-read \Illuminate\Database\Eloquent\Collection $dhlShipments + * @property-read int|null $dhl_shipments_count * @mixin \Eloquent */ class ShoppingOrder extends Model @@ -623,4 +629,44 @@ class ShoppingOrder extends Model return $ret; } + /** + * Get DHL shipments for this order + */ + public function dhlShipments() + { + return $this->hasMany('App\Models\DhlShipment', 'shopping_order_id'); + } + + /** + * Get outbound DHL shipments only + */ + public function dhlOutboundShipments() + { + return $this->hasMany('App\Models\DhlShipment', 'shopping_order_id')->where('type', 'outbound'); + } + + /** + * Get return DHL shipments only + */ + public function dhlReturnShipments() + { + return $this->hasMany('App\Models\DhlShipment', 'shopping_order_id')->where('type', 'return'); + } + + /** + * Check if order has DHL shipments + */ + public function hasDhlShipments(): bool + { + return $this->dhlShipments()->exists(); + } + + /** + * Get latest DHL shipment + */ + public function getLatestDhlShipment() + { + return $this->dhlShipments()->latest()->first(); + } + } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php old mode 100755 new mode 100644 index 92ac31a..4281359 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -16,7 +16,7 @@ class AppServiceProvider extends ServiceProvider public function boot() { Schema::defaultStringLength(191); - + if ($this->app->environment('production')) { URL::forceScheme('https'); } @@ -25,7 +25,7 @@ class AppServiceProvider extends ServiceProvider \View::composer('*', function ($view) { try { $context = app(\App\Domain\DomainContext::class); - + // Für die Main-Domain: user_shop immer auf null setzen if ($context->type === 'main') { $view->with('user_shop', null); @@ -49,9 +49,8 @@ class AppServiceProvider extends ServiceProvider public function register() { - if ($this->app->environment() !== 'production') { + if ($this->app->environment() !== 'production' && class_exists(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class)) { $this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class); } - // ... } } diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php old mode 100755 new mode 100644 diff --git a/app/Providers/BroadcastServiceProvider.php b/app/Providers/BroadcastServiceProvider.php old mode 100755 new mode 100644 diff --git a/app/Providers/DomainServiceProvider.php b/app/Providers/DomainServiceProvider.php index 4c12e35..eea1069 100644 --- a/app/Providers/DomainServiceProvider.php +++ b/app/Providers/DomainServiceProvider.php @@ -44,10 +44,12 @@ class DomainServiceProvider extends ServiceProvider // Analysiere den Host der aktuellen Anfrage $domainInfo = $domainService->parseDomain($request->getHost()); - \Log::debug('DomainServiceProvider: domainInfo', [ - 'domainInfo' => $domainInfo, - 'host' => $request->getHost() - ]); + if (config('app.debug')) { + \Log::debug('DomainServiceProvider: domainInfo', [ + 'domainInfo' => $domainInfo, + 'host' => $request->getHost() + ]); + } $userShop = null; // Wenn es sich um eine User-Shop-Domain handelt, versuche das Shop-Objekt zu finden. diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php old mode 100755 new mode 100644 diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php old mode 100755 new mode 100644 diff --git a/app/Providers/YardServiceProvider.php b/app/Providers/YardServiceProvider.php old mode 100755 new mode 100644 diff --git a/app/Services/DhlApiService.php b/app/Services/DhlApiService.php new file mode 100644 index 0000000..8173c5b --- /dev/null +++ b/app/Services/DhlApiService.php @@ -0,0 +1,1312 @@ +config = config('dhl'); + $this->isSandbox = $this->config['api']['sandbox'] ?? true; + + $this->initializeClients(); + } + + /** + * Initialize DHL API clients + * + * @throws Exception + */ + private function initializeClients(): void + { + try { + // Get all credentials from the config. + $apiKey = $this->config['api']['api_key'] ?? null; + $apiSecret = $this->config['api']['api_secret'] ?? null; + $username = $this->config['api']['username'] ?? null; + $password = $this->config['api']['password'] ?? null; + + // The ChristophSchaeffer library for the "Geschäftskundenversand API" (SOAP) + // always requires both sets of credentials: + // 1. API Key/Secret for the Application's identity. + // 2. Username/Password for the Business Customer Portal user's identity. + + // We must validate that all four are present. + if (empty($apiKey) || empty($apiSecret)) { + throw new Exception('DHL API Key (DHL_API_KEY) and API Secret (DHL_API_SECRET) are missing. Please get them from the DHL Developer Portal.'); + } + if (empty($username) || empty($password)) { + throw new Exception('DHL API Username (DHL_API_USERNAME) and Password (DHL_API_PASSWORD) are missing. Please get them from your customer\'s DHL Business Customer Portal.'); + } + + $shippingCredentials = new ShippingClientCredentials( + $apiKey, + $apiSecret, + $username, + $password + ); + + $this->shippingClient = new ShippingClient( + $shippingCredentials, + $this->isSandbox, + MultiClient::LANGUAGE_LOCALE_GERMAN_DE + ); + + // Initialize Tracking Client (if enabled) + if ($this->config['tracking']['enabled']) { + // For tracking, the ZT-Token is often used as the "user" context + $trackingUser = $this->config['tracking']['username'] ?? 'zt12345'; // Sandbox default + $trackingPass = $this->config['tracking']['password'] ?? 'geheim'; // Sandbox default + + $trackingCredentials = new TrackingClientCredentials( + $apiKey, + $apiSecret, + $trackingUser, + $trackingPass + ); + + $this->trackingClient = new TrackingClient( + $trackingCredentials, + $this->isSandbox + ); + } + + } catch (Exception $e) { + $this->logError('Failed to initialize DHL clients', $e); + throw new Exception('DHL API initialization failed: ' . $e->getMessage()); + } + } + + /** + * Create a shipment for an order + * + * @param ShoppingOrder $order The shopping order + * @param float $weight Package weight in kg + * @param array $options Additional options + * @return DhlShipment The created shipment + * @throws Exception + */ + public function createShipment(ShoppingOrder $order, float $weight = 1.0, array $options = []): DhlShipment + { + try { + $this->logInfo('Creating DHL shipment', [ + 'order_id' => $order->id, + 'weight' => $weight, + 'options' => $options + ]); + + // Build shipment data from order, including address parsing + $shipmentData = $this->buildShipmentData($order, $weight, $options); + \Log::info('shipmentData', $shipmentData); + // Validate the built data before proceeding + $this->validateShipmentData($shipmentData, $order->id); + + // Create DHL shipment record first + $dhlShipment = $this->createDhlShipmentRecord($order, $shipmentData); + + // Create shipment order for DHL API + $shipmentOrder = $this->buildShipmentOrder($shipmentData, $dhlShipment); + + // Call DHL API + $request = new createShipmentOrder([$shipmentOrder]); // Constructor expects array of ShipmentOrder objects + + try { + $response = $this->shippingClient->createShipmentOrder($request); + + // Enhanced debug logging for troubleshooting + $this->logInfo('DHL API Response received', [ + 'response_class' => get_class($response), + 'has_errors' => !$response->hasNoErrors(), + 'creation_states_count' => count($response->CreationStates ?? []) + ]); + + // Log detailed response structure for debugging + if (!$response->hasNoErrors()) { + $this->logResponseStructure($response); + } + } catch (\ErrorException $e) { + if (str_contains($e->getMessage(), 'Attempt to read property "Version" on null')) { + throw new \Exception( + 'The DHL API returned an unexpected response. This often indicates an authentication problem with the Business Customer API. ' . + 'Please double-check your DHL_API_USERNAME and DHL_API_PASSWORD in the .env file. ' . + 'Original error: ' . $e->getMessage(), 0, $e + ); + } + throw $e; + } + + // Process response + $this->processShipmentResponse($dhlShipment, $response, $request); + + $this->logInfo('DHL shipment created successfully', [ + 'shipment_id' => $dhlShipment->id, + 'shipment_number' => $dhlShipment->shipment_number + ]); + + return $dhlShipment->fresh(); + + } catch (DhlException $e) { + $this->logError('DHL API error during shipment creation', $e, ['order_id' => $order->id]); + throw new Exception('DHL shipment creation failed: ' . $e->getMessage()); + } catch (Exception $e) { + $this->logError('General error during shipment creation', $e, ['order_id' => $order->id]); + throw $e; + } + } + + /** + * Test DHL API Login Credentials by creating a dummy shipment. + * + * @return array + */ + public function testLogin(): array + { + try { + $this->logInfo('Starting DHL API connection test.'); + + // Check basic configuration first + if (!$this->isConfigured()) { + return [ + 'success' => false, + 'message' => 'DHL API ist nicht vollständig konfiguriert. Prüfen Sie die .env Einstellungen.', + ]; + } + + // 1. Find a test order (preferably with complete shipping data) + $testOrder = ShoppingOrder::with(['shopping_user', 'shopping_order_items']) + ->whereHas('shopping_user') + ->where('id', 35511) // Use specific test order + ->first(); + + if (!$testOrder) { + return [ + 'success' => false, + 'message' => 'Test-Bestellung (ID: 35511) nicht gefunden. Bitte verwenden Sie eine existierende Bestellung für den Test.', + ]; + } + + $this->logInfo('Using test order for API test', [ + 'order_id' => $testOrder->id, + 'has_user' => !is_null($testOrder->shopping_user), + 'has_items' => $testOrder->shopping_order_items->count() > 0 + ]); + + // 2. Define test options with realistic data + $weight = 1.0; + $options = [ + 'product_code' => 'V01PAK', + 'is_test' => true // Mark as test to avoid any side effects + ]; + + // 3. Wrap in transaction to avoid database pollution + DB::beginTransaction(); + + try { + $dhlShipment = $this->createShipment($testOrder, $weight, $options); + + // If we get here, the API call was successful + $success = !empty($dhlShipment->shipment_number); + + if ($success) { + $this->logInfo('DHL API test successful', [ + 'shipment_number' => $dhlShipment->shipment_number, + 'status' => $dhlShipment->status + ]); + + $result = [ + 'success' => true, + 'message' => 'DHL API Test erfolgreich! Verbindung und Authentifizierung funktionieren.', + 'details' => [ + 'shipment_number' => $dhlShipment->shipment_number, + 'status' => $dhlShipment->status, + 'api_type' => $this->config['api']['api_type'] ?? 'unknown', + 'sandbox' => $this->isSandbox + ] + ]; + } else { + $result = [ + 'success' => false, + 'message' => 'DHL API Test unvollständig: Sendung erstellt aber keine Sendungsnummer erhalten.', + 'details' => [ + 'status' => $dhlShipment->status ?? 'unknown', + 'errors' => $dhlShipment->api_errors ?? 'No specific errors' + ] + ]; + } + + } catch (Exception $apiException) { + $this->logError('DHL API test failed during createShipment', $apiException, [ + 'order_id' => $testOrder->id + ]); + + $result = [ + 'success' => false, + 'message' => 'DHL API Test fehlgeschlagen: ' . $apiException->getMessage(), + 'details' => [ + 'error_type' => get_class($apiException), + 'api_type' => $this->config['api']['api_type'] ?? 'unknown', + 'sandbox' => $this->isSandbox + ] + ]; + } + + // Always rollback transaction for tests + DB::rollBack(); + + return $result; + + } catch (Exception $e) { + DB::rollBack(); // Ensure rollback on any failure + + $this->logError('DHL API test failed with general error', $e); + + return [ + 'success' => false, + 'message' => 'DHL API Test fehlgeschlagen: ' . $e->getMessage(), + 'details' => [ + 'error_type' => get_class($e), + 'configured' => $this->isConfigured(), + 'sandbox' => $this->isSandbox + ] + ]; + } + } + + /** + * Validates required shipment data fields to prevent API errors. + * + * @param array $shipmentData + * @param int $orderId + * @throws \Exception + */ + private function validateShipmentData(array $shipmentData, int $orderId): void + { + $requiredFields = [ + 'recipient_street' => 'streetName', + 'recipient_street_number' => 'streetNumber', + 'recipient_postal_code' => 'postal code', + 'recipient_city' => 'city', + 'recipient_country' => 'countryISOCode', + ]; + + $errors = []; + foreach ($requiredFields as $field => $errorName) { + if (empty(trim($shipmentData[$field]))) { + $errors[] = $errorName; + } + } + + if (empty(trim($shipmentData['recipient_name'])) && empty(trim($shipmentData['recipient_company']))) { + $errors[] = 'recipient name or company'; + } + + if (!empty($errors)) { + $errorMessage = 'Shipment data is missing required fields for Order ID ' . $orderId . ': ' . implode(', ', $errors); + throw new \Exception($errorMessage); + } + } + + /** + * Cancel a shipment + * + * @param DhlShipment $shipment + * @return bool Success status + * @throws Exception + */ + public function cancelShipment(DhlShipment $shipment): bool + { + try { + if (!$shipment->canBeCancelled()) { + throw new Exception('Shipment cannot be cancelled in current status: ' . $shipment->status); + } + + $this->logInfo('Cancelling DHL shipment', [ + 'shipment_id' => $shipment->id, + 'shipment_number' => $shipment->shipment_number + ]); + + $request = new deleteShipmentOrder([$shipment->shipment_number]); // Constructor expects array of shipment numbers + + $response = $this->shippingClient->deleteShipmentOrder($request); + + if ($this->isResponseSuccessful($response)) { + $shipment->update([ + 'status' => DhlShipment::STATUS_CANCELLED, + 'api_response_data' => $this->extractResponseData($response), + ]); + + $this->logInfo('DHL shipment cancelled successfully', [ + 'shipment_id' => $shipment->id + ]); + + return true; + } else { + $errorMessage = $this->extractErrorMessage($response); + $shipment->update([ + 'api_errors' => $errorMessage, + ]); + + throw new Exception('DHL cancellation failed: ' . $errorMessage); + } + + } catch (DhlException $e) { + $this->logError('DHL API error during cancellation', $e, ['shipment_id' => $shipment->id]); + throw new Exception('DHL shipment cancellation failed: ' . $e->getMessage()); + } catch (Exception $e) { + $this->logError('General error during cancellation', $e, ['shipment_id' => $shipment->id]); + throw $e; + } + } + + /** + * Create a return label + * + * @param DhlShipment $originalShipment The original outbound shipment + * @param array $options Additional options + * @return DhlShipment The return shipment + * @throws Exception + */ + public function createReturnLabel(DhlShipment $originalShipment, array $options = []): DhlShipment + { + try { + if (!$this->config['returns']['enabled']) { + throw new Exception('Return labels are disabled in configuration'); + } + + $this->logInfo('Creating DHL return label', [ + 'original_shipment_id' => $originalShipment->id, + 'options' => $options + ]); + + // Create return shipment using regular createShipment but with return data + $returnShipment = $this->createShipment( + $originalShipment->shoppingOrder, + $originalShipment->weight, + array_merge($options, ['is_return' => true, 'original_shipment' => $originalShipment]) + ); + + $this->logInfo('DHL return label created successfully', [ + 'return_shipment_id' => $returnShipment->id, + 'original_shipment_id' => $originalShipment->id + ]); + + return $returnShipment; + + } catch (Exception $e) { + $this->logError('Error creating return label', $e, [ + 'original_shipment_id' => $originalShipment->id + ]); + throw $e; + } + } + + /** + * Get tracking details for a shipment + * + * @param DhlShipment $shipment + * @return array Tracking details + * @throws Exception + */ + public function getTrackingDetails(DhlShipment $shipment): array + { + try { + if (!$this->config['tracking']['enabled']) { + throw new Exception('Tracking is disabled in configuration'); + } + + if (!$shipment->hasTracking()) { + throw new Exception('Shipment has no tracking number'); + } + + if (!$this->trackingClient) { + throw new Exception('Tracking client not initialized'); + } + + $this->logInfo('Getting tracking details', [ + 'shipment_id' => $shipment->id, + 'tracking_number' => $shipment->tracking_number + ]); + + $request = new getPieceDetail(); + $request->pieceCode = $shipment->tracking_number; + + $response = $this->trackingClient->getPieceDetail($request); + + if ($response && is_array($response) && count($response) > 0) { + $trackingData = method_exists($response[0], 'toArray') ? $response[0]->toArray() : (array)$response[0]; + + // Update shipment with latest tracking info + $shipment->update([ + 'tracking_details' => $trackingData, + 'last_tracked_at' => now(), + 'tracking_status' => $trackingData['status'] ?? null, + ]); + + $this->logInfo('Tracking details updated', [ + 'shipment_id' => $shipment->id, + 'status' => $trackingData['status'] ?? 'unknown' + ]); + + return $trackingData; + } else { + throw new Exception('No tracking data available'); + } + + } catch (DhlException $e) { + $this->logError('DHL API error during tracking', $e, ['shipment_id' => $shipment->id]); + throw new Exception('DHL tracking failed: ' . $e->getMessage()); + } catch (Exception $e) { + $this->logError('General error during tracking', $e, ['shipment_id' => $shipment->id]); + throw $e; + } + } + + /** + * Build shipment data from order + * + * @param ShoppingOrder $order + * @param float $weight + * @param array $options + * @return array + */ + private function buildShipmentData(ShoppingOrder $order, float $weight, array $options): array + { + $isReturn = $options['is_return'] ?? false; + $originalShipment = $options['original_shipment'] ?? null; + $shoppingUser = $order->shopping_user; + + // Data placeholders + $recipientName = ''; + $recipientCompany = null; + $streetName = ''; + $streetNumber = ''; + $recipientPostalCode = ''; + $recipientCity = ''; + $recipientCountryCode = ''; + $recipientEmail = $shoppingUser->email ?? null; // Fallback to user's main email + $recipientPhone = null; + + $customAddress = $options['shipping_address'] ?? null; + + if ($customAddress) { + // Use custom address from options (e.g., modal) + $recipientName = trim(($customAddress['firstname'] ?? '') . ' ' . ($customAddress['lastname'] ?? '')); + $recipientCompany = $customAddress['company'] ?? null; + $streetName = $customAddress['address'] ?? ''; + $streetNumber = $customAddress['address_2'] ?? ''; + $recipientPostalCode = $customAddress['zipcode'] ?? ''; + $recipientCity = $customAddress['city'] ?? ''; + $recipientPhone = $customAddress['phone'] ?? null; + + if (!empty($customAddress['country_id'])) { + $country = \App\Models\Country::find($customAddress['country_id']); + if ($country) { + $recipientCountryCode = $country->code; + } + } + } else { + // Fallback to shopping_user data + $useShipping = !($shoppingUser->same_as_billing ?? true); + $firstname = $useShipping ? ($shoppingUser->shipping_firstname ?? '') : ($shoppingUser->billing_firstname ?? ''); + $lastname = $useShipping ? ($shoppingUser->shipping_lastname ?? '') : ($shoppingUser->billing_lastname ?? ''); + $company = $useShipping ? ($shoppingUser->shipping_company ?? '') : ($shoppingUser->billing_company ?? ''); + $address = $useShipping ? ($shoppingUser->shipping_address ?? '') : ($shoppingUser->billing_address ?? ''); + $address_2 = $useShipping ? ($shoppingUser->shipping_address_2 ?? '') : ($shoppingUser->billing_address_2 ?? ''); + $zipcode = $useShipping ? ($shoppingUser->shipping_zipcode ?? '') : ($shoppingUser->billing_zipcode ?? ''); + $city = $useShipping ? ($shoppingUser->shipping_city ?? '') : ($shoppingUser->billing_city ?? ''); + $country = $useShipping ? ($shoppingUser->shipping_country ?? null) : ($shoppingUser->billing_country ?? null); + $phone = $useShipping ? ($shoppingUser->shipping_phone ?? '') : ($shoppingUser->billing_phone ?? ''); + $email = $shoppingUser->billing_email ?? ''; + + $recipientName = trim($firstname . ' ' . $lastname); + $recipientCompany = $company; + $streetName = $address; + //$streetNumber = $address_2; + $recipientPostalCode = $zipcode; + $recipientCity = $city; + $recipientCountryCode = $country->code ?? ''; + $recipientPhone = $phone; + $recipientEmail = $email; + } + + // Universal address parsing for combined street/number fields + if (empty($streetNumber) && !empty($streetName)) { + if (preg_match('/^([^\d]*[^\d\s])\s*(\d.*)$/', $streetName, $matches) && count($matches) === 3) { + $streetName = trim($matches[1]); + $streetNumber = trim($matches[2]); + } + } + + // --- START FIX: Automatically determine product code based on destination country --- + $isDomestic = strtoupper($recipientCountryCode) === 'DE'; + $productCode = $options['product_code'] ?? ($isDomestic + ? $this->config['defaults']['product'] + : $this->config['defaults']['product_international']); + // --- END FIX --- + + return [ + 'order_id' => $order->id, + 'type' => $isReturn ? DhlShipment::TYPE_RETURN : DhlShipment::TYPE_OUTBOUND, + 'related_shipment_id' => $originalShipment?->id, + 'weight' => $weight, + 'length' => $options['length'] ?? $this->config['defaults']['dimensions']['length'], + 'width' => $options['width'] ?? $this->config['defaults']['dimensions']['width'], + 'height' => $options['height'] ?? $this->config['defaults']['dimensions']['height'], + 'product_code' => $productCode, + 'services' => $options['services'] ?? [], + + // Recipient address + 'recipient_name' => $recipientName, + 'recipient_company' => $recipientCompany, + 'recipient_street' => $streetName, + 'recipient_street_number' => $streetNumber, + 'recipient_postal_code' => $recipientPostalCode, + 'recipient_city' => $recipientCity, + 'recipient_state' => null, // Not used + 'recipient_country' => $recipientCountryCode, + 'recipient_email' => $recipientEmail, + 'recipient_phone' => $recipientPhone, + ]; + } + + /** + * Create DHL shipment record in database + * + * @param ShoppingOrder $order + * @param array $shipmentData + * @return DhlShipment + */ + private function createDhlShipmentRecord(ShoppingOrder $order, array $shipmentData): DhlShipment + { + return DhlShipment::create([ + 'shopping_order_id' => $order->id, + 'type' => $shipmentData['type'], + 'related_shipment_id' => $shipmentData['related_shipment_id'], + 'weight' => $shipmentData['weight'], + 'length' => $shipmentData['length'], + 'width' => $shipmentData['width'], + 'height' => $shipmentData['height'], + 'product_code' => $shipmentData['product_code'], + 'services' => $shipmentData['services'], + 'status' => DhlShipment::STATUS_CREATED, + + // Recipient data + 'recipient_name' => $shipmentData['recipient_name'], + 'recipient_company' => $shipmentData['recipient_company'], + 'recipient_street' => $shipmentData['recipient_street'], + 'recipient_street_number' => $shipmentData['recipient_street_number'], + 'recipient_postal_code' => $shipmentData['recipient_postal_code'], + 'recipient_city' => $shipmentData['recipient_city'], + 'recipient_state' => $shipmentData['recipient_state'], + 'recipient_country' => $shipmentData['recipient_country'], + 'recipient_email' => $shipmentData['recipient_email'], + 'recipient_phone' => $shipmentData['recipient_phone'], + ]); + } + + /** + * Build ShipmentOrder object for DHL API + * + * @param array $shipmentData + * @param DhlShipment $dhlShipment + * @return ShipmentOrder + */ + private function buildShipmentOrder(array $shipmentData, DhlShipment $dhlShipment): ShipmentOrder + { + $shipmentOrder = new ShipmentOrder(); + $isReturn = $shipmentData['type'] === DhlShipment::TYPE_RETURN; + $productCode = $shipmentData['product_code']; + + // --- DYNAMIC ACCOUNT NUMBER SELECTION --- + $accountNumber = $this->getAccountNumberForProduct($productCode); + + // --- VALIDATE COUNTRY CODES --- + // Ensure sender country code is valid + $senderCountry = 'DE'; + if (!empty($this->config['sender']['country']) && strlen(trim($this->config['sender']['country'])) === 2) { + $senderCountry = strtoupper(trim($this->config['sender']['country'])); + } + + // Ensure recipient country code is valid + $recipientCountry = 'DE'; + if (!empty($shipmentData['recipient_country']) && strlen(trim($shipmentData['recipient_country'])) === 2) { + $recipientCountry = strtoupper(trim($shipmentData['recipient_country'])); + } + // --- END VALIDATION --- + + // Set basic shipment details + $shipmentOrder->sequenceNumber = $dhlShipment->id; + + // Set shipment details + $shipmentOrder->Shipment->ShipmentDetails->product = $productCode; + $shipmentOrder->Shipment->ShipmentDetails->accountNumber = $accountNumber; + $shipmentOrder->Shipment->ShipmentDetails->customerReference = ($isReturn ? 'Return-' : 'Order-') . $shipmentData['order_id']; + $shipmentOrder->Shipment->ShipmentDetails->shipmentDate = date('Y-m-d'); + + // --- ROBUST NAME & ADDRESS HANDLING --- + if ($isReturn) { + // RETURN LABEL: Customer ships TO warehouse + // Shipper = Customer (original recipient) + $this->setAddressBlock( + $shipmentOrder->Shipment->Shipper, + $shipmentData['recipient_name'], + $shipmentData['recipient_company'], + $shipmentData['recipient_street'], + $shipmentData['recipient_street_number'], + $shipmentData['recipient_postal_code'], + $shipmentData['recipient_city'], + $recipientCountry + ); + + // Receiver = Warehouse (original sender) + $this->setAddressBlock( + $shipmentOrder->Shipment->Receiver, + $this->config['sender']['name'] ?? '', + $this->config['sender']['company'] ?? '', + $this->config['sender']['street'] ?? '', + $this->config['sender']['street_number'] ?? '', + $this->config['sender']['postal_code'] ?? '', + $this->config['sender']['city'] ?? '', + $senderCountry, + true // isSender=true to throw exception on config error + ); + + } else { + // OUTBOUND LABEL: Warehouse ships TO customer (normal flow) + // Shipper = Warehouse + $this->setAddressBlock( + $shipmentOrder->Shipment->Shipper, + $this->config['sender']['name'] ?? '', + $this->config['sender']['company'] ?? '', + $this->config['sender']['street'] ?? '', + $this->config['sender']['street_number'] ?? '', + $this->config['sender']['postal_code'] ?? '', + $this->config['sender']['city'] ?? '', + $senderCountry, + true // isSender=true to throw exception on config error + ); + + // Receiver = Customer + $this->setAddressBlock( + $shipmentOrder->Shipment->Receiver, + $shipmentData['recipient_name'], + $shipmentData['recipient_company'], + $shipmentData['recipient_street'], + $shipmentData['recipient_street_number'], + $shipmentData['recipient_postal_code'], + $shipmentData['recipient_city'], + $recipientCountry + ); + } + + // Set package details + $shipmentOrder->Shipment->ShipmentDetails->ShipmentItem->weightInKG = $shipmentData['weight']; + $shipmentOrder->Shipment->ShipmentDetails->ShipmentItem->lengthInCM = $shipmentData['length']; + $shipmentOrder->Shipment->ShipmentDetails->ShipmentItem->widthInCM = $shipmentData['width']; + $shipmentOrder->Shipment->ShipmentDetails->ShipmentItem->heightInCM = $shipmentData['height']; + + // Configure minimal services to avoid SDK issues with dynamic properties + $this->configureShipmentServices($shipmentOrder, $shipmentData); + + return $shipmentOrder; + } + + /** + * Sets the address and name details for a shipper or receiver block. + * + * @param Shipper|Receiver $addressBlock The Shipper or Receiver object from the SDK + * @param string $name + * @param string|null $company + * @param string $street + * @param string $streetNumber + * @param string $postalCode + * @param string $city + * @param string $countryCode + * @param bool $isSender + * @throws \Exception + */ + private function setAddressBlock(Shipper|Receiver &$addressBlock, string $name, ?string $company, string $street, string $streetNumber, string $postalCode, string $city, string $countryCode, bool $isSender = false): void + { + $name = trim($name); + $company = trim($company ?? ''); + + $name1 = $name; + $name2 = $company; + + if (empty($name1)) { + // If personal name is empty, company MUST be name1 + $name1 = $name2; + $name2 = ''; + } + + if (empty($name1)) { + if ($isSender) { + throw new \Exception('DHL Sender Name (name1) is not configured. Please set DHL_SENDER_NAME or DHL_SENDER_COMPANY in your .env file.'); + } + return; + } + + // Handle the structural difference between Shipper (has ->Name object) and Receiver (has ->name1 directly) + if ($addressBlock instanceof Shipper) { + if (is_null($addressBlock->Name)) $addressBlock->Name = new ShipperName(); + $addressBlock->Name->name1 = $name1; + if (!empty($name2)) $addressBlock->Name->name2 = $name2; + + if (is_null($addressBlock->Address)) $addressBlock->Address = new ShipperAddress(); + if (is_null($addressBlock->Address->Origin)) $addressBlock->Address->Origin = new ShipperOrigin(); + } elseif ($addressBlock instanceof Receiver) { + $addressBlock->name1 = $name1; // Assign directly + + if (is_null($addressBlock->Address)) $addressBlock->Address = new ReceiverAddress(); + if (is_null($addressBlock->Address->Origin)) $addressBlock->Address->Origin = new ReceiverOrigin(); + } + + $addressBlock->Address->streetName = $street; + $addressBlock->Address->streetNumber = $streetNumber; + $addressBlock->Address->zip = $postalCode; + $addressBlock->Address->city = $city; + $addressBlock->Address->Origin->countryISOCode = $countryCode; + } + + /** + * Configure shipment services to avoid SDK dynamic property issues + * + * @param ShipmentOrder $shipmentOrder + * @param array $shipmentData + */ + private function configureShipmentServices(ShipmentOrder $shipmentOrder, array $shipmentData): void + { + // Initialize basic services that are commonly used and properly supported + $services = $shipmentData['services'] ?? []; + + // Only configure services that are explicitly requested and known to work + // This prevents the SDK from creating dynamic properties like ShipmentHandling + + if (isset($services['premium']) && $services['premium']) { + $shipmentOrder->Shipment->ShipmentDetails->Service->Premium->active = true; + } + + if (isset($services['endorsement']) && $services['endorsement']) { + $shipmentOrder->Shipment->ShipmentDetails->Service->Endorsement->active = true; + $shipmentOrder->Shipment->ShipmentDetails->Service->Endorsement->type = $services['endorsement']; + } + + if (isset($services['bulky_goods']) && $services['bulky_goods']) { + $shipmentOrder->Shipment->ShipmentDetails->Service->BulkyGoods->active = true; + } + + if (isset($services['return_receipt']) && $services['return_receipt']) { + $shipmentOrder->Shipment->ShipmentDetails->Service->ReturnReceipt->active = true; + } + + // Avoid problematic services that cause dynamic property warnings + // Do NOT set ShipmentHandling or other services that the SDK dynamically creates + + $this->logInfo('Configured DHL services', [ + 'requested_services' => array_keys($services), + 'product_code' => $shipmentData['product_code'] + ]); + } + + /** + * Get the correct DHL account number for a given product code. + * + * @param string $productCode + * @return string + * @throws \Exception + */ + private function getAccountNumberForProduct(string $productCode): string + { + $productAccounts = $this->config['api']['product_accounts'] ?? []; + + if (isset($productAccounts[$productCode]) && !empty($productAccounts[$productCode])) { + return $productAccounts[$productCode]; + } + + $defaultAccount = $this->config['api']['account_number_default'] ?? null; + if (!empty($defaultAccount)) { + return $defaultAccount; + } + + throw new \Exception("DHL Abrechnungsnummer for product '{$productCode}' is not configured, and no default account number is set."); + } + + /** + * Process shipment response from DHL API + * + * @param DhlShipment $dhlShipment + * @param CreateShipmentOrderResponse $response + * @param createShipmentOrder $request + * @throws Exception + */ + private function processShipmentResponse(DhlShipment $dhlShipment, $response, $request): void + { + // Store request and response data + $dhlShipment->update([ + 'api_request_data' => $this->extractRequestData($request), + 'api_response_data' => $this->extractResponseData($response), + ]); + + $isSuccessful = $this->isResponseSuccessful($response); + + $this->logInfo('Processing DHL shipment response', [ + 'shipment_id' => $dhlShipment->id, + 'is_successful' => $isSuccessful, + 'has_creation_states' => !empty($response->CreationStates), + 'creation_states_count' => count($response->CreationStates ?? []) + ]); + + if ($isSuccessful) { + // Extract shipment and tracking information + $shipmentData = $this->extractShipmentData($response); + + $this->logInfo('Extracted shipment data', [ + 'shipment_id' => $dhlShipment->id, + 'shipment_number' => $shipmentData['shipment_number'] ?? 'not_found', + 'has_label_data' => !empty($shipmentData['label_data']) + ]); + + // Validate that we got essential data + if (empty($shipmentData['shipment_number'])) { + $this->logError('No shipment number in successful response', new Exception('Missing shipment number'), [ + 'shipment_id' => $dhlShipment->id, + 'response_data' => $this->extractResponseData($response) + ]); + + $dhlShipment->update([ + 'status' => DhlShipment::STATUS_FAILED, + 'api_errors' => 'DHL API reported success but no shipment number was provided', + ]); + + throw new Exception('DHL API reported success but no shipment number was provided'); + } + + // Save label if provided + $labelPath = null; + if (isset($shipmentData['label_data']) && $shipmentData['label_data']) { + try { + $labelPath = $this->saveLabelFile($dhlShipment, $shipmentData['label_data']); + } catch (Exception $e) { + $this->logError('Failed to save label file', $e, ['shipment_id' => $dhlShipment->id]); + // Don't fail the whole process for label save issues + } + } + + // Update shipment with success data + $dhlShipment->update([ + 'shipment_number' => $shipmentData['shipment_number'], + 'tracking_number' => $shipmentData['tracking_number'] ?? $shipmentData['shipment_number'], + 'label_path' => $labelPath, + 'status' => DhlShipment::STATUS_SUBMITTED, + 'shipped_at' => now(), + ]); + + } else { + // Handle API error + $errorMessage = $this->extractErrorMessage($response); + + $this->logInfo('DHL API returned errors', [ + 'shipment_id' => $dhlShipment->id, + 'error_message' => $errorMessage + ]); + + $dhlShipment->update([ + 'status' => DhlShipment::STATUS_FAILED, + 'api_errors' => $errorMessage, + ]); + + throw new Exception('DHL API error: ' . $errorMessage); + } + } + + /** + * Extract shipment data from response + * + * @param CreateShipmentOrderResponse $response + * @return array + */ + private function extractShipmentData($response): array + { + $data = []; + + // Use the SDK's proper structure with CreationStates + if ($response instanceof CreateShipmentOrderResponse && !empty($response->CreationStates)) { + foreach ($response->CreationStates as $creationState) { + if (isset($creationState->shipmentNumber)) { + $data['shipment_number'] = $creationState->shipmentNumber; + } + if (isset($creationState->LabelData->labelData)) { + $data['label_data'] = $creationState->LabelData->labelData; + } + // Only process the first creation state for now (single shipment) + break; + } + } + + return $data; + } + + /** + * Check if response indicates success + * + * @param CreateShipmentOrderResponse|DeleteShipmentOrderResponse $response + * @return bool + */ + private function isResponseSuccessful($response): bool + { + // Use the SDK's built-in success check methods + if ($response instanceof CreateShipmentOrderResponse) { + return $response->hasNoErrors(); + } elseif ($response instanceof DeleteShipmentOrderResponse) { + return $response->hasNoErrors(); + } + + return false; + } + + /** + * Extract error message from response + * + * @param CreateShipmentOrderResponse|DeleteShipmentOrderResponse $response + * @return string + */ + private function extractErrorMessage($response): string + { + $messages = []; + + // Check for top-level status messages + if (is_object($response) && property_exists($response, 'Status') && !empty($response->Status)) { + $statusArray = is_array($response->Status) ? $response->Status : [$response->Status]; + + foreach ($statusArray as $status) { + if (is_object($status)) { + // Try different status property names + $statusProps = ['statusText', 'statusMessage', 'statusCode']; + foreach ($statusProps as $prop) { + if (property_exists($status, $prop) && !empty($status->{$prop})) { + $value = $status->{$prop}; + $messages[] = is_array($value) ? implode(', ', $value) : (string)$value; + break; + } + } + } + } + } + + // Check for messages within CreationStates + if ($response instanceof CreateShipmentOrderResponse && !empty($response->CreationStates)) { + foreach ($response->CreationStates as $creationState) { + // Check LabelData Status + if (isset($creationState->LabelData->Status)) { + $statusArray = is_array($creationState->LabelData->Status) + ? $creationState->LabelData->Status + : [$creationState->LabelData->Status]; + + foreach ($statusArray as $status) { + if (is_object($status)) { + $statusProps = ['statusText', 'statusMessage', 'statusCode']; + foreach ($statusProps as $prop) { + if (property_exists($status, $prop) && !empty($status->{$prop})) { + $value = $status->{$prop}; + $messages[] = is_array($value) ? implode('; ', $value) : (string)$value; + break; + } + } + } + } + } + + // Check direct status on CreationState + if (isset($creationState->sequenceNumber) && isset($creationState->LabelData)) { + // This might be a successful creation state, not an error + continue; + } + } + } + + // If we still have no messages, check if this might actually be a success + if (empty($messages) && $response instanceof CreateShipmentOrderResponse) { + if ($response->hasNoErrors()) { + return 'No errors found - response appears successful'; + } else { + // Try to extract any available information from the response + $responseData = $this->extractResponseData($response); + $this->logInfo('Could not extract error message, full response data:', $responseData ?? []); + return 'DHL API returned errors but no specific error message could be extracted. Check logs for full response.'; + } + } + + return !empty($messages) ? implode('; ', array_unique($messages)) : 'Unknown DHL API error'; + } + + /** + * Extract request data for logging + * + * @param createShipmentOrder|deleteShipmentOrder $request + * @return array|null + */ + private function extractRequestData($request): ?array + { + // Safely convert complex SDK objects to arrays for logging + return json_decode(json_encode($request), true); + } + + /** + * Extract response data for logging + * + * @param CreateShipmentOrderResponse|DeleteShipmentOrderResponse $response + * @return array|null + */ + private function extractResponseData($response): ?array + { + // Safely convert complex SDK objects to arrays for logging + return json_decode(json_encode($response), true); + } + + /** + * Save label file to storage + * + * @param DhlShipment $dhlShipment + * @param string $labelData Base64 encoded label data + * @return string The saved file path + */ + private function saveLabelFile(DhlShipment $dhlShipment, string $labelData): string + { + $filename = 'dhl_label_' . $dhlShipment->id . '_' . time() . '.pdf'; + $path = 'dhl/labels/' . $filename; + + Storage::put($path, base64_decode($labelData)); + + return $path; + } + + /** + * Log info message + * + * @param string $message + * @param array $context + */ + private function logInfo(string $message, array $context = []): void + { + if ($this->config['logging']['enabled']) { + Log::info('[DHL API] ' . $message, $context); + } + } + + /** + * Log error message + * + * @param string $message + * @param Exception $exception + * @param array $context + */ + private function logError(string $message, Exception $exception, array $context = []): void + { + if ($this->config['logging']['enabled']) { + Log::error('[DHL API] ' . $message, array_merge($context, [ + 'exception' => $exception->getMessage(), + 'trace' => $exception->getTraceAsString(), + ])); + } + } + + /** + * Log detailed response structure for debugging + * + * @param CreateShipmentOrderResponse $response + */ + private function logResponseStructure($response): void + { + if (!$this->config['logging']['enabled']) { + return; + } + + $structure = [ + 'class' => get_class($response), + 'hasNoErrors' => $response->hasNoErrors(), + 'properties' => [] + ]; + + // Get all public properties + $reflection = new \ReflectionClass($response); + foreach ($reflection->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { + $name = $property->getName(); + try { + $value = $property->getValue($response); + + if (is_object($value)) { + $structure['properties'][$name] = [ + 'type' => 'object', + 'class' => get_class($value), + 'properties' => $this->extractObjectProperties($value, 2) // Limit depth + ]; + } elseif (is_array($value)) { + $structure['properties'][$name] = [ + 'type' => 'array', + 'count' => count($value), + 'sample' => count($value) > 0 ? $this->extractObjectProperties($value[0], 1) : null + ]; + } else { + $structure['properties'][$name] = [ + 'type' => gettype($value), + 'value' => $value + ]; + } + } catch (\Exception $e) { + $structure['properties'][$name] = [ + 'type' => 'error', + 'error' => $e->getMessage() + ]; + } + } + + Log::info('[DHL API] Response structure analysis:', $structure); + } + + /** + * Extract object properties for debugging (with depth limit) + * + * @param mixed $object + * @param int $maxDepth + * @return array|mixed + */ + private function extractObjectProperties($object, int $maxDepth = 1) + { + if ($maxDepth <= 0 || !is_object($object)) { + return is_object($object) ? get_class($object) : $object; + } + + $properties = []; + $reflection = new \ReflectionClass($object); + + foreach ($reflection->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { + $name = $property->getName(); + try { + $value = $property->getValue($object); + + if (is_object($value)) { + $properties[$name] = $this->extractObjectProperties($value, $maxDepth - 1); + } elseif (is_array($value)) { + $properties[$name] = [ + 'type' => 'array', + 'count' => count($value) + ]; + } else { + $properties[$name] = $value; + } + } catch (\Exception $e) { + $properties[$name] = 'Error: ' . $e->getMessage(); + } + } + + return $properties; + } + + /** + * Check if service is properly configured + * + * @return bool + */ + public function isConfigured(): bool + { + $apiType = $this->config['api']['api_type']; + + if ($apiType === 'developer') { + return !empty($this->config['api']['api_key']) && + !empty($this->config['api']['api_secret']); + } else { + return !empty($this->config['api']['username']) && + !empty($this->config['api']['password']) && + !empty($this->config['api']['account_number']); + } + } + + /** + * Test API connection + * + * @return array Test results + */ + public function testConnection(): array + { + try { + // Simple test - check if clients are initialized + if ($this->shippingClient) { + return [ + 'success' => true, + 'message' => 'DHL API clients initialized successfully', + 'sandbox' => $this->isSandbox, + 'configured' => $this->isConfigured(), + 'api_type' => $this->config['api']['api_type'], + ]; + } else { + throw new Exception('Shipping client not initialized'); + } + + } catch (Exception $e) { + return [ + 'success' => false, + 'message' => 'DHL API connection failed: ' . $e->getMessage(), + 'sandbox' => $this->isSandbox, + 'configured' => $this->isConfigured(), + ]; + } + } +} \ No newline at end of file diff --git a/app/Services/DhlDataHelper.php b/app/Services/DhlDataHelper.php new file mode 100644 index 0000000..b3b0880 --- /dev/null +++ b/app/Services/DhlDataHelper.php @@ -0,0 +1,89 @@ +getDhlConfig(); + } + return [ + 'order_id' => $order->id, + 'weight_kg' => $weight, + 'product_code' => $options['product_code'] ?? 'V01PAK', + 'label_format' => $options['label_format'] ?? $dhlConfig['label_format'] ?? 'PDF', + 'print_format' => $options['print_format'] ?? $dhlConfig['print_format'] ?? null, + 'retoure_print_format' => $options['retoure_print_format'] ?? $dhlConfig['retoure_print_format'] ?? null, + + // Shipper data (sender) - from admin settings + 'shipper' => [ + 'name' => $dhlConfig['sender']['company'] ?? 'mivita care gmbh', + 'name2' => $dhlConfig['sender']['name'] ?? '', + 'street' => $dhlConfig['sender']['street'] ?? 'Leinfeld', + 'houseNumber' => $dhlConfig['sender']['house_number'] ?? '2', + 'postalCode' => $dhlConfig['sender']['postalCode'] ?? '87755', + 'city' => $dhlConfig['sender']['city'] ?? 'Kirchhaslach', + 'country' => $dhlConfig['sender']['country'] ?? 'DE', + 'email' => $dhlConfig['sender']['email'] ?? 'versand@mivita.care', + 'phone' => $dhlConfig['sender']['phone'] ?? '+49 123 456789', + ], + + // Consignee data (recipient) - from order + 'consignee' => [ + 'name' => $shippingAddress['firstname'] ?? '' . ' ' . $shippingAddress['lastname'] ?? '', + 'name2' => $shippingAddress['company'] ?? '', + 'street' => $shippingAddress['address'] ?? '', + 'houseNumber' => $shippingAddress['houseNumber'] ?? '', + 'postalCode' => $shippingAddress['zipcode'] ?? '', + 'city' => $shippingAddress['city'] ?? '', + 'country' => $shippingAddress['country']?->code ?? 'DE', + 'email' => $shippingAddress['email'] ?? '', + 'phone' => $shippingAddress['phone'] ?? '', + ], + // Package dimensions from options or defaults + 'dimensions' => [ + 'length' => $options['length'] ?? 30, + 'width' => $options['width'] ?? 25, + 'height' => $options['height'] ?? 10, + ], + + // Additional services + 'services' => $options['services'] ?? [], + + // Custom reference for tracking + 'reference' => 'Order-' . $order->id, + ]; + } +} diff --git a/app/Services/DhlModalService.php b/app/Services/DhlModalService.php new file mode 100644 index 0000000..31f0ba4 --- /dev/null +++ b/app/Services/DhlModalService.php @@ -0,0 +1,439 @@ +config = config('dhl'); + } + + /** + * Prepare modal data for DHL shipment creation + * + * @param mixed $id Order ID or 'new' + * @param array $data Additional data from the request + * @return array Prepared data for the view + * @throws Exception + */ + public function prepareModalData($id, array $data): array + { + $result = [ + 'order' => null, + 'orderWeight' => 1.0, + 'shippingAddress' => null, + 'availableCountries' => $this->getAvailableCountries(), + 'productCodes' => $this->getAvailableProductCodes(), + 'errors' => [], + 'warnings' => [] + ]; + + // If no order ID or 'new', return empty data for order selection + if (!$id || $id === 'new') { + return $result; + } + + try { + // Load and validate order + $order = $this->loadOrder($id); + if (!$order) { + $result['errors'][] = "Bestellung #{$id} wurde nicht gefunden."; + return $result; + } + + $result['order'] = $order; + + // Calculate order weight + $result['orderWeight'] = $this->calculateOrderWeight($order); + + // Process and validate shipping address + $result['shippingAddress'] = $this->processShippingAddress($order); + + // Validate address completeness + $addressValidation = $this->validateAddress($result['shippingAddress']); + if (!$addressValidation['valid']) { + $result['errors'] = array_merge($result['errors'], $addressValidation['errors']); + } + if (!empty($addressValidation['warnings'])) { + $result['warnings'] = array_merge($result['warnings'], $addressValidation['warnings']); + } + + Log::info('[DHL Modal] Prepared modal data successfully', [ + 'order_id' => $order->id, + 'weight' => $result['orderWeight'], + 'address_valid' => empty($result['errors']) + ]); + } catch (Exception $e) { + Log::error('[DHL Modal] Error preparing modal data', [ + 'order_id' => $id, + 'error' => $e->getMessage() + ]); + + $result['errors'][] = 'Fehler beim Laden der Bestelldaten: ' . $e->getMessage(); + } + + return $result; + } + + /** + * Load order with required relationships + * + * @param mixed $id + * @return ShoppingOrder|null + */ + private function loadOrder($id): ?ShoppingOrder + { + return ShoppingOrder::with([ + 'shopping_order_items', + 'shopping_user', + ])->find($id); + } + + /** + * Calculate order weight in kg + * + * @param ShoppingOrder $order + * @return float + */ + private function calculateOrderWeight(ShoppingOrder $order): float + { + return $order->weight / 100; + /* + // Default fallback weight + $defaultWeight = 1.0; + + if (!$order->shopping_order_items || $order->shopping_order_items->isEmpty()) { + return $defaultWeight; + } + + // If order has a weight field (in grams), convert to kg + if ($order->weight && $order->weight > 0) { + return round($order->weight / 100, 1); // Convert grams to kg + } + + // Calculate from items if available + $totalWeight = 0; + foreach ($order->shopping_order_items as $item) { + if ($item->weight && $item->weight > 0) { + $totalWeight += ($item->weight * $item->quantity); + } + } + + if ($totalWeight > 0) { + return round($totalWeight / 100, 1); // Convert grams to kg + } + + // Estimate based on item count if no weight data + $itemCount = $order->shopping_order_items->sum('quantity'); + $estimatedWeight = max($itemCount * 0.5, $defaultWeight); // Estimate 0.5kg per item + + return round($estimatedWeight, 1); + */ + } + + /** + * Process and parse shipping address from order + * + * @param ShoppingOrder $order + * @return array + */ + private function processShippingAddress(ShoppingOrder $order): array + { + $shoppingUser = $order->shopping_user; + + if (!$shoppingUser) { + return $this->getEmptyAddress(); + } + + // Determine if shipping address is different from billing + $useShipping = !($shoppingUser->same_as_billing ?? true); + + // Extract address data + $addressData = [ + 'firstname' => $useShipping ? ($shoppingUser->shipping_firstname ?? '') : ($shoppingUser->billing_firstname ?? ''), + 'lastname' => $useShipping ? ($shoppingUser->shipping_lastname ?? '') : ($shoppingUser->billing_lastname ?? ''), + 'company' => $useShipping ? ($shoppingUser->shipping_company ?? '') : ($shoppingUser->billing_company ?? ''), + 'address' => $useShipping ? ($shoppingUser->shipping_address ?? '') : ($shoppingUser->billing_address ?? ''), + 'address_2' => $useShipping ? ($shoppingUser->shipping_address_2 ?? '') : ($shoppingUser->billing_address_2 ?? ''), + 'zipcode' => $useShipping ? ($shoppingUser->shipping_zipcode ?? '') : ($shoppingUser->billing_zipcode ?? ''), + 'city' => $useShipping ? ($shoppingUser->shipping_city ?? '') : ($shoppingUser->billing_city ?? ''), + 'country' => $useShipping ? ($shoppingUser->shipping_country ?? null) : ($shoppingUser->billing_country ?? null), + 'phone' => $useShipping ? ($shoppingUser->shipping_phone ?? '') : ($shoppingUser->billing_phone ?? ''), + 'email' => $shoppingUser->billing_email ?? '', + 'houseNumber' => '', + ]; + + // Parse and separate street name and number + $this->parseStreetAddress($addressData); + + return $addressData; + } + + /** + * Parse street address and separate street name from house number + * + * @param array &$addressData + */ + private function parseStreetAddress(array &$addressData): void + { + $address = trim($addressData['address']); + // If address_2 is empty and address contains both street and number + if (!empty($address)) { + // Try to separate street name and house number + $patterns = [ + // Pattern 1: "Musterstraße 123" or "Musterstraße 123a" + '/^(.+?)\s+(\d+[a-zA-Z]?)$/u', + // Pattern 2: "Musterstraße 123-125" or "Musterstraße 123/125" + '/^(.+?)\s+(\d+[-\/]\d+[a-zA-Z]?)$/u', + // Pattern 3: "123 Musterstraße" (number first) + '/^(\d+[a-zA-Z]?)\s+(.+)$/u' + ]; + + foreach ($patterns as $index => $pattern) { + if (preg_match($pattern, $address, $matches)) { + if ($index === 2) { + // Number first pattern + $addressData['address'] = trim($matches[2]); + $addressData['houseNumber'] = trim($matches[1]); + } else { + // Street first patterns + $addressData['address'] = trim($matches[1]); + $addressData['houseNumber'] = trim($matches[2]); + } + break; + } + } + } + + // Clean up the address data + $addressData['address'] = trim($addressData['address']); + $addressData['houseNumber'] = trim($addressData['houseNumber']); + } + + /** + * Validate address completeness and format + * + * @param array $address + * @return array Validation result with 'valid', 'errors', and 'warnings' keys + */ + private function validateAddress(array $address): array + { + $errors = []; + $warnings = []; + + // Required fields + $requiredFields = [ + 'firstname' => 'Vorname', + 'lastname' => 'Nachname', + 'address' => 'Straße', + 'zipcode' => 'Postleitzahl', + 'city' => 'Stadt' + ]; + + foreach ($requiredFields as $field => $label) { + if (empty(trim($address[$field]))) { + $errors[] = "{$label} ist erforderlich."; + } + } + + // Name validation + if (empty(trim($address['firstname'])) && empty(trim($address['lastname'])) && empty(trim($address['company']))) { + $errors[] = 'Entweder Name oder Firmenname muss angegeben werden.'; + } + + // Street number validation + if (!empty($address['address']) && empty($address['houseNumber'])) { + $warnings[] = 'Hausnummer konnte nicht automatisch erkannt werden. Bitte prüfen Sie die Adressangaben.'; + } + + // Postal code format validation for Germany + if (!empty($address['zipcode']) && $address['country'] && $address['country']->code === 'DE') { + if (!preg_match('/^\d{5}$/', $address['zipcode'])) { + $warnings[] = 'Deutsche Postleitzahl sollte 5 Ziffern haben.'; + } + } + + // Country validation + if (!$address['country']) { + $errors[] = 'Land konnte nicht ermittelt werden.'; + } + + return [ + 'valid' => empty($errors), + 'errors' => $errors, + 'warnings' => $warnings + ]; + } + + /** + * Get empty address template + * + * @return array + */ + private function getEmptyAddress(): array + { + return [ + 'firstname' => '', + 'lastname' => '', + 'company' => '', + 'address' => '', + 'address_2' => '', + 'zipcode' => '', + 'city' => '', + 'country' => null, + 'phone' => '', + 'email' => '', + ]; + } + + /** + * Get available countries for shipping + * + * @return \Illuminate\Database\Eloquent\Collection + */ + private function getAvailableCountries() + { + return Country::where('active', 1)->get(); + } + + /** + * Get available DHL product codes from settings + * + * @return array + */ + private function getAvailableProductCodes(): array + { + // Get DHL configuration with merged settings + $settingController = new \App\Http\Controllers\SettingController(); + $dhlConfig = $settingController->getDhlConfig(); + + $productCodes = []; + + // Add products based on configured account numbers + $accountNumbers = $dhlConfig['account_numbers'] ?? []; + + if (!empty($accountNumbers['V01PAK'])) { + $productCodes['V01PAK'] = 'DHL Paket National'; + } + + if (!empty($accountNumbers['V53PAK'])) { + $productCodes['V53PAK'] = 'DHL Paket International'; + } + + if (!empty($accountNumbers['V62WP'])) { + $productCodes['V62WP'] = 'DHL Warenpost National'; + } + + if (!empty($accountNumbers['V07PAK'])) { + $productCodes['V07PAK'] = 'DHL Retoure Online'; + } + + // Fallback to default if no account numbers configured + if (empty($productCodes)) { + $productCodes = [ + 'V01PAK' => 'DHL Paket National', + 'V53PAK' => 'DHL Paket International', + 'V62WP' => 'DHL Warenpost National' + ]; + } + + return $productCodes; + } + + /** + * Validate shipment parameters before API call + * + * @param array $shipmentData + * @return array Validation result + */ + public function validateShipmentData(array $shipmentData): array + { + $errors = []; + $warnings = []; + + // Weight validation + $weight = floatval($shipmentData['weight'] ?? 0); + if ($weight < 0.1) { + $errors[] = 'Gewicht muss mindestens 0.1 kg betragen.'; + } elseif ($weight > 31.5) { + $errors[] = 'Gewicht darf maximal 31.5 kg betragen.'; + } + + // Product code validation + $productCode = $shipmentData['product_code'] ?? ''; + $availableProducts = array_keys($this->getAvailableProductCodes()); + if (!in_array($productCode, $availableProducts)) { + $errors[] = 'Ungültiger Produktcode ausgewählt.'; + } + + // Address validation + $requiredAddressFields = [ + 'shipping_firstname' => 'Vorname', + 'shipping_lastname' => 'Nachname', + 'shipping_address' => 'Straße', + 'shipping_houseNumber' => 'Hausnummer', + 'shipping_zipcode' => 'Postleitzahl', + 'shipping_city' => 'Stadt', + 'shipping_country_id' => 'Land' + ]; + + foreach ($requiredAddressFields as $field => $label) { + if (empty(trim($shipmentData[$field] ?? ''))) { + $errors[] = "{$label} ist erforderlich."; + } + } + + return [ + 'valid' => empty($errors), + 'errors' => $errors, + 'warnings' => $warnings + ]; + } + + /** + * Prepare address data for DHL API + * + * @param array $formData + * @return array + */ + public function prepareAddressForApi(array $formData): array + { + $country = null; + if (!empty($formData['shipping_country_id'])) { + $country = Country::find($formData['shipping_country_id']); + } + + return [ + 'firstname' => trim($formData['shipping_firstname'] ?? ''), + 'lastname' => trim($formData['shipping_lastname'] ?? ''), + 'company' => trim($formData['shipping_company'] ?? ''), + 'address' => trim($formData['shipping_address'] ?? ''), + 'address_2' => trim($formData['shipping_address_2'] ?? ''), + 'houseNumber' => trim($formData['shipping_houseNumber'] ?? ''), + 'zipcode' => trim($formData['shipping_zipcode'] ?? ''), + 'city' => trim($formData['shipping_city'] ?? ''), + 'country_id' => $country?->id, + 'phone' => trim($formData['shipping_phone'] ?? '') + ]; + } +} diff --git a/app/Services/DhlShipmentService.php b/app/Services/DhlShipmentService.php new file mode 100644 index 0000000..28cd017 --- /dev/null +++ b/app/Services/DhlShipmentService.php @@ -0,0 +1,147 @@ +getDhlConfig(); + \Log::info('dhlConfig', $dhlConfig); + // Check if queue should be used + $useQueue = $dhlConfig['use_queue'] ?? false; + + if ($useQueue) { + return $this->createShipmentAsync($order, $weight, $options, $dhlConfig); + } else { + return $this->createShipmentSync($order, $weight, $options, $dhlConfig); + } + } + + /** + * Create shipment asynchronously using queue + * + * @param ShoppingOrder $order + * @param float $weight + * @param array $options + * @param array $dhlConfig + * @return array + */ + private function createShipmentAsync(ShoppingOrder $order, float $weight, array $options, array $dhlConfig): array + { + try { + // Dispatch job with pre-loaded config + CreateShipmentJob::dispatch($order, $weight, $options, $dhlConfig); + + Log::info('[DHL Service] Shipment creation dispatched to queue', [ + 'order_id' => $order->id, + 'weight' => $weight + ]); + + return [ + 'success' => true, + 'message' => 'Sendung wird erstellt. Sie erhalten eine Benachrichtigung, sobald das Versandlabel verfügbar ist.', + 'queued' => true, + 'order_id' => $order->id + ]; + } catch (Exception $e) { + Log::error('[DHL Service] Failed to dispatch shipment creation', [ + 'error' => $e->getMessage(), + 'order_id' => $order->id, + ]); + + return [ + 'success' => false, + 'message' => 'Fehler beim Einreihen der Sendungserstellung: ' . $e->getMessage(), + 'queued' => false + ]; + } + } + + /** + * Create shipment synchronously + * + * @param ShoppingOrder $order + * @param float $weight + * @param array $options + * @param array $dhlConfig + * @return array + */ + private function createShipmentSync(ShoppingOrder $order, float $weight, array $options, array $dhlConfig): array + { + try { + Log::info('[DHL Service] Creating shipment synchronously', [ + 'order_id' => $order->id, + 'weight' => $weight + ]); + + // Create DHL client directly + $dhlClient = new \Acme\Dhl\Support\DhlClient( + $dhlConfig['base_url'], + $dhlConfig['api_key'], + $dhlConfig['username'], + $dhlConfig['password'] + ); + + $shippingService = new \Acme\Dhl\Services\ShippingService($dhlClient); + + // Prepare order data using helper + $orderData = DhlDataHelper::prepareOrderData($order, $weight, $options, $dhlConfig); + Log::info('orderData', $orderData); + // Create the shipment directly + $result = $shippingService->createLabel($orderData); + + Log::info('[DHL Service] Shipment created successfully (sync)', [ + 'order_id' => $order->id, + 'shipment_number' => $result['shipmentNumber'] ?? 'N/A', + 'label_path' => $result['labelPath'] ?? 'N/A', + ]); + + return [ + 'success' => true, + 'message' => 'Versandlabel erfolgreich erstellt!', + 'queued' => false, + 'order_id' => $order->id, + 'shipment_number' => $result['shipmentNumber'] ?? null, + 'tracking_number' => $result['trackingNumber'] ?? null, + 'label_path' => $result['labelPath'] ?? null, + 'label_url' => $result['labelUrl'] ?? null, + ]; + } catch (Exception $e) { + Log::error('[DHL Service] Shipment creation failed (sync)', [ + 'order_id' => $order->id, + 'error' => $e->getMessage() + ]); + + return [ + 'success' => false, + 'message' => 'Fehler beim Erstellen des Versandlabels: ' . $e->getMessage(), + 'queued' => false, + 'order_id' => $order->id + ]; + } + } +} diff --git a/app/Services/DomainService.php b/app/Services/DomainService.php index 1918c13..c216810 100644 --- a/app/Services/DomainService.php +++ b/app/Services/DomainService.php @@ -126,7 +126,9 @@ class DomainService $subdomain = null; if (count($parts) > 2) { $subdomain = $parts[0]; - \Log::debug('DomainService: Using extracted subdomain', ['subdomain' => $subdomain, 'host' => $host]); + if (config('app.debug')) { + \Log::debug('DomainService: Using extracted subdomain', ['subdomain' => $subdomain, 'host' => $host]); + } } // Determine domain type based on subdomain and host diff --git a/app/Services/Payment.php b/app/Services/Payment.php index 4d593ec..e41b6c8 100644 --- a/app/Services/Payment.php +++ b/app/Services/Payment.php @@ -270,6 +270,9 @@ class Payment public static function paymentStatusSendMail(ShoppingOrder $shopping_order, $shopping_payment, $data){ $bcc = []; $billing_email = $shopping_order->shopping_user->billing_email; + + // Überprüfung der Billing-E-Mail-Adresse + if(!$billing_email){ if($data['mode'] === 'test'){ $billing_email = config('app.checkout_test_mail'); @@ -277,6 +280,11 @@ class Payment $billing_email = config('app.checkout_mail'); } } + if(!filter_var($billing_email, FILTER_VALIDATE_EMAIL)){ + \Log::channel('payment')->error("Invalid billing email at shopping_order ".$shopping_order->id, ['billing_email' => $billing_email]); + $billing_email = config('app.checkout_mail'); + } + if($data['mode'] === 'test'){ $bcc[] = config('app.checkout_test_mail'); }else{ diff --git a/app/Services/Util.php b/app/Services/Util.php index c4aa7e4..cecc668 100644 --- a/app/Services/Util.php +++ b/app/Services/Util.php @@ -1,4 +1,5 @@ $length){ + if (strlen($str) > $length) { $str = substr($str, 0, $length); //$str = substr($str, 0, strrpos($str, " ")); - $str = $str." ..."; + $str = $str . " ..."; } return $str; } - - public static function reFormatNumber($value){ + + public static function reFormatNumber($value) + { return (float) str_replace(',', '.', self::_format_number($value)); } - public static function formatNumber($value, $dec=2){ + public static function formatNumber($value, $dec = 2) + { $value = floatval(str_replace(',', '', $value)); return number_format($value, $dec, self::_decimal_separator(), self::_thousands_separator()); - } - public static function cleanIntegerFromString($value) { + public static function cleanIntegerFromString($value) + { // Entferne alle nicht-numerischen Zeichen außer Minus $cleanStr = preg_replace("/[^0-9-]/", "", $value); - + // Konvertiere zu Integer und entferne führende Nullen $number = (int)$cleanStr; - + return $number; } - public static function cleanNumberFormat($num = 0, $dec = 2, $fullzero = false){ - - if($fullzero && $num == 0){ + public static function cleanNumberFormat($num = 0, $dec = 2, $fullzero = false) + { + + if ($fullzero && $num == 0) { return number_format($num, $dec, self::_decimal_separator(), self::_thousands_separator()); } - return rtrim(rtrim(number_format($num, $dec, self::_decimal_separator(), self::_thousands_separator()),'0'), self::_decimal_separator()); + return rtrim(rtrim(number_format($num, $dec, self::_decimal_separator(), self::_thousands_separator()), '0'), self::_decimal_separator()); } - public static function utf8ize( $mixed ) { + public static function utf8ize($mixed) + { if (is_array($mixed)) { foreach ($mixed as $key => $value) { $mixed[$key] = self::utf8ize($value); @@ -115,14 +124,17 @@ class Util } - public static function getPostRoute(){ + public static function getPostRoute() + { return self::$postRoute; } - public static function setPostRoute($postRoute){ + public static function setPostRoute($postRoute) + { self::$postRoute = $postRoute; } - public static function getUserShop(){ + public static function getUserShop() + { $shop = session('user_shop'); if (empty($shop) || !is_object($shop)) { return null; @@ -130,17 +142,19 @@ class Util return $shop; } - public static function getDefaultUserShop(){ + public static function getDefaultUserShop() + { $user = \App\User::find(6); - if($user && $user->shop){ + if ($user && $user->shop) { return $user->shop; } return false; } - public static function getAuthUser(){ - if(\Session::has('auth_user')){ - if($auth_user = \Session::get('auth_user')){ + public static function getAuthUser() + { + if (\Session::has('auth_user')) { + if ($auth_user = \Session::get('auth_user')) { return $auth_user; } } @@ -148,91 +162,102 @@ class Util } - public static function getUserShopIdentifier(){ - if(\Session::has('user_shop_identifier')){ - if($user_shop_identifier = \Session::get('user_shop_identifier')){ + public static function getUserShopIdentifier() + { + if (\Session::has('user_shop_identifier')) { + if ($user_shop_identifier = \Session::get('user_shop_identifier')) { return $user_shop_identifier; } } return false; } - public static function getInstanceStatus(){ + public static function getInstanceStatus() + { $identifier = self::getUserShopIdentifier(); - if($identifier && \Session::has('user_shop_payment') && \Session::get('user_shop_payment') === 6){ + if ($identifier && \Session::has('user_shop_payment') && \Session::get('user_shop_payment') === 6) { return OrderPaymentService::getInstanceStatus($identifier); } return false; } - public static function setInstanceStatus($status, $lower = true){ + public static function setInstanceStatus($status, $lower = true) + { $identifier = self::getUserShopIdentifier(); - if($identifier && \Session::has('user_shop_payment') && \Session::get('user_shop_payment') === 6){ + if ($identifier && \Session::has('user_shop_payment') && \Session::get('user_shop_payment') === 6) { OrderPaymentService::updateInstanceStatus($identifier, $status, $lower); } } - public static function setInstanceStatusByPayment($shopping_payment, $status, $lower = true){ - if($shopping_payment->identifier){ + public static function setInstanceStatusByPayment($shopping_payment, $status, $lower = true) + { + if ($shopping_payment->identifier) { OrderPaymentService::updateInstanceStatus($shopping_payment->identifier, $status, $lower); } } - public static function getShoppingInstance(){ - if(\Session::has('shopping_instance')){ + public static function getShoppingInstance() + { + if (\Session::has('shopping_instance')) { return \Session::get('shopping_instance'); } return false; } - public static function getUserHistory(){ + public static function getUserHistory() + { $auth_user = self::getAuthUser(); $user_shop_identifier = self::getUserShopIdentifier(); - if($user_shop_identifier && $auth_user){ + if ($user_shop_identifier && $auth_user) { return UserHistory::whereUserId($auth_user->id)->whereIdentifier($user_shop_identifier)->get()->last(); } return false; } - public static function setUserHistoryValue($values = []){ - if($user_history = self::getUserHistory()){ - foreach ($values as $key=>$val){ + public static function setUserHistoryValue($values = []) + { + if ($user_history = self::getUserHistory()) { + foreach ($values as $key => $val) { $user_history->{$key} = $val; } $user_history->save(); - } + } } - public static function getUserHistoryValue($key){ - if($user_history = self::getUserHistory()) { + public static function getUserHistoryValue($key) + { + if ($user_history = self::getUserHistory()) { return $user_history->{$key}; } return null; } - public static function getUserShoppingMode(){ - if($auth_user = self::getAuthUser()){ - if($auth_user->isTestMode()){ + public static function getUserShoppingMode() + { + if ($auth_user = self::getAuthUser()) { + if ($auth_user->isTestMode()) { return 'test'; } } return config('app.mode'); } - public static function addRoute($p = []){ + public static function addRoute($p = []) + { $b = []; - if(\Session::has('user_shop')){ - if($user_shop = \Session::get('user_shop')){ + if (\Session::has('user_shop')) { + if ($user_shop = \Session::get('user_shop')) { $b = ['subdomain' => $user_shop->slug]; } } return array_merge($p, $b); } - public static function checkUserLandIsNot($user){ + public static function checkUserLandIsNot($user) + { - if(isset($user->account->country_id)){ + if (isset($user->account->country_id)) { //ch schweiz is out - if($user->account->country_id === 6){ + if ($user->account->country_id === 6) { return false; } return true; @@ -241,9 +266,10 @@ class Util } - public static function getMyMivitaShopUrl($add_url = ""){ - if(\Session::has('user_shop_domain')){ - $url = \Session::get('user_shop_domain').$add_url; + public static function getMyMivitaShopUrl($add_url = "") + { + if (\Session::has('user_shop_domain')) { + $url = \Session::get('user_shop_domain') . $add_url; if (!str_starts_with($url, 'http')) { $url = 'https://' . ltrim($url, '/'); } @@ -251,83 +277,90 @@ class Util } //alois sein shop $user = \App\User::find(6); - if($user && $user->shop){ - return config('app.protocol').$user->shop->slug.".".config('app.domain').config('app.tld_care').$add_url; + if ($user && $user->shop) { + return config('app.protocol') . $user->shop->slug . "." . config('app.domain') . config('app.tld_care') . $add_url; } } - public static function getMyMivitaPortalUrl($protocol = true){ + public static function getMyMivitaPortalUrl($protocol = true) + { $pro = $protocol ? config('app.protocol') : ""; - return $pro.config('app.pre_url_portal').config('app.domain').config('app.tld_care'); + return $pro . config('app.pre_url_portal') . config('app.domain') . config('app.tld_care'); } - public static function getMyMivitaUrl($protocol = true){ + public static function getMyMivitaUrl($protocol = true) + { $pro = $protocol ? config('app.protocol') : ""; - return $pro.config('app.pre_url_crm').config('app.domain').config('app.tld_care'); + return $pro . config('app.pre_url_crm') . config('app.domain') . config('app.tld_care'); } - public static function getUserPaymentFor($instance = 'shopping'){ - if(Yard::instance($instance)->getYardExtra('user_shop_payment')){ + public static function getUserPaymentFor($instance = 'shopping') + { + if (Yard::instance($instance)->getYardExtra('user_shop_payment')) { return Yard::instance($instance)->getYardExtra('user_shop_payment'); } - if(\Session::has('user_shop_payment')){ + if (\Session::has('user_shop_payment')) { return \Session::get('user_shop_payment'); } return null; } - public static function getUserShopBackUrl($reference = ""){ + public static function getUserShopBackUrl($reference = "") + { - if(\Session::has('user_shop')){ - if(\Session::has('user_shop_domain')){ + if (\Session::has('user_shop')) { + if (\Session::has('user_shop_domain')) { return \Session::get('user_shop_domain'); } - if($user_shop = \Session::get('user_shop')){ - return config('app.protocol').$user_shop->slug.".".config('app.domain').config('app.tld_care')."/back/to/shop/".$reference; + if ($user_shop = \Session::get('user_shop')) { + return config('app.protocol') . $user_shop->slug . "." . config('app.domain') . config('app.tld_care') . "/back/to/shop/" . $reference; } } - return config('app.protocol').config('app.domain').config('app.tld_care'); + return config('app.protocol') . config('app.domain') . config('app.tld_care'); } - public static function getUserCardBackUrl($uri, $instance = 'shopping'){ + public static function getUserCardBackUrl($uri, $instance = 'shopping') + { - if(\Session::has('user_shop')){ - if(\Session::has('user_shop_domain')){ - if(\Session::has('back_link')){ + if (\Session::has('user_shop')) { + if (\Session::has('user_shop_domain')) { + if (\Session::has('back_link')) { return \Session::get('back_link'); } - if(self::getUserPaymentFor($instance) === 3){ - return \Session::get('user_shop_domain')."/user/membership"; + if (self::getUserPaymentFor($instance) === 3) { + return \Session::get('user_shop_domain') . "/user/membership"; } - if(self::getUserPaymentFor($instance) === 2){ - return \Session::get('user_shop_domain')."/user/orders"; + if (self::getUserPaymentFor($instance) === 2) { + return \Session::get('user_shop_domain') . "/user/orders"; } return \Session::get('user_shop_domain'); } - if($user_shop = \Session::get('user_shop')){ - return config('app.protocol').$user_shop->slug.".".config('app.domain').config('app.tld_care').$uri; + if ($user_shop = \Session::get('user_shop')) { + return config('app.protocol') . $user_shop->slug . "." . config('app.domain') . config('app.tld_care') . $uri; } } - return config('app.protocol').config('app.domain').config('app.tld_care'); + return config('app.protocol') . config('app.domain') . config('app.tld_care'); } - public static function isMivitaShop(){ - if(Request::getHost() === 'checkout.'.config('app.domain').config('app.tld_care')){ - if($user_shop = \Session::get('user_shop')){ - if($user_shop->slug === 'aloevera' || $user_shop->slug === 'naturcosmetic'){ + public static function isMivitaShop() + { + if (Request::getHost() === 'checkout.' . config('app.domain') . config('app.tld_care')) { + if ($user_shop = \Session::get('user_shop')) { + if ($user_shop->slug === 'aloevera' || $user_shop->slug === 'naturcosmetic') { return true; } - } + } } - if(Request::getHost() === 'naturcosmetic.'.config('app.domain').config('app.tld_care')){ + if (Request::getHost() === 'naturcosmetic.' . config('app.domain') . config('app.tld_care')) { return true; - } - return \Config::get('app.url') === config('app.domain').config('app.tld_shop'); + } + return \Config::get('app.url') === config('app.domain') . config('app.tld_shop'); } - public static function isTestSystem($dev = false){ - if(\Config::get('app.tld_care') === '.test' || \Config::get('app.tld_shop') === '.lshop'){ - if($dev && config('app.debug') !== true){ + public static function isTestSystem($dev = false) + { + if (\Config::get('app.tld_care') === '.test' || \Config::get('app.tld_shop') === '.lshop') { + if ($dev && config('app.debug') !== true) { return false; } return true; @@ -347,25 +380,61 @@ class Util return $size; } } - + public static function sanitize($string, $force_lowercase = true, $anal = false, $substr = false) { - $strip = array("~", "`", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "=", "+", "[", "{", "]", - "}", "\\", "|", ";", ":", "\"", "'", "‘", "’", "“", "”", "–", "—", - "—", "–", ",", "<", ".", ">", "/", "?"); + $strip = array( + "~", + "`", + "!", + "@", + "#", + "$", + "%", + "^", + "&", + "*", + "(", + ")", + "_", + "=", + "+", + "[", + "{", + "]", + "}", + "\\", + "|", + ";", + ":", + "\"", + "'", + "‘", + "’", + "“", + "”", + "–", + "—", + "—", + "–", + ",", + "<", + ".", + ">", + "/", + "?" + ); $clean = trim(str_replace($strip, "", strip_tags($string))); $clean = preg_replace('/\s+/', "_", $clean); - $clean = ($anal) ? preg_replace("/[^a-zA-Z0-9]/", "", $clean) : $clean ; - - if($substr){ - $clean = (strlen($clean) > 20) ? substr($clean,-20) : $clean; + $clean = ($anal) ? preg_replace("/[^a-zA-Z0-9]/", "", $clean) : $clean; + if ($substr) { + $clean = (strlen($clean) > 20) ? substr($clean, -20) : $clean; } return ($force_lowercase) ? (function_exists('mb_strtolower')) ? - mb_strtolower($clean, 'UTF-8') : - strtolower($clean) : + mb_strtolower($clean, 'UTF-8') : + strtolower($clean) : $clean; } - -} \ No newline at end of file +} diff --git a/app/User.php b/app/User.php index b4e6ab9..b27f723 100755 --- a/app/User.php +++ b/app/User.php @@ -120,6 +120,8 @@ use Laravel\Passport\HasApiTokens; * @method static \Illuminate\Database\Eloquent\Builder|User wherePreSponsor($value) * @property \Illuminate\Support\Carbon|null $pre_deleted_at * @method static \Illuminate\Database\Eloquent\Builder|User wherePreDeletedAt($value) + * @property-read \Illuminate\Database\Eloquent\Collection $userBusiness + * @property-read int|null $user_business_count * @mixin \Eloquent */ class User extends Authenticatable diff --git a/app/helpers.php b/app/helpers.php index 4a77953..0eed855 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -129,62 +129,4 @@ if (! function_exists('legal_url')) { return url($path); } } -} - -if (! function_exists('main_asset')) { - /** - * Generate an asset URL using the main domain to avoid CORS issues. - * This ensures all assets are loaded from the main domain regardless of subdomain. - */ - function main_asset($path) - { - // Entferne führende Slashes - $path = ltrim($path, '/'); - - // Baue die Hauptdomain-URL - $protocol = config('app.protocol', 'https://'); - $domain = config('app.domain', 'mivita'); - $tld = config('app.tld_care', '.care'); - - return $protocol . $domain . $tld . '/' . $path; - } -} - -if (! function_exists('cors_asset')) { - /** - * Alias for main_asset for backward compatibility and clarity. - */ - function cors_asset($path) - { - return main_asset($path); - } -} - - -if (!function_exists('route')) { - /** - * Route auf Hauptdomain generieren - */ - function route($name, $parameters = [], $absolute = true) - { - $url = route($name, $parameters, $absolute); - - // Ersetze Subdomain mit Hauptdomain - if (request()->hasHeader('X-Subdomain')) { - $currentHost = request()->getHost(); - $url = str_replace($currentHost, config('app.domain').config('app.tld_care'), $url); - } - - return $url; - } -} - -if (!function_exists('asset_route')) { - /** - * Asset/Storage Route immer auf Hauptdomain - */ - function asset_route($name, $parameters = []) - { - return 'https://' . config('app.domain') . config('app.tld_care') . route($name, $parameters, false); - } } \ No newline at end of file diff --git a/bootstrap/cache/packages.php b/bootstrap/cache/packages.php index d51eaf0..bd81898 100755 --- a/bootstrap/cache/packages.php +++ b/bootstrap/cache/packages.php @@ -1,15 +1,4 @@ - array ( - 'aliases' => - array ( - 'Form' => 'Alban\\LaravelCollectiveSpatieHtmlParser\\FormFacade', - ), - 'providers' => - array ( - 0 => 'Alban\\LaravelCollectiveSpatieHtmlParser\\ServiceProvider', - ), - ), 'barryvdh/laravel-debugbar' => array ( 'aliases' => diff --git a/bootstrap/cache/services.php b/bootstrap/cache/services.php index 3d18020..c49c1e7 100755 --- a/bootstrap/cache/services.php +++ b/bootstrap/cache/services.php @@ -23,39 +23,39 @@ 19 => 'Illuminate\\Translation\\TranslationServiceProvider', 20 => 'Illuminate\\Validation\\ValidationServiceProvider', 21 => 'Illuminate\\View\\ViewServiceProvider', - 22 => 'Alban\\LaravelCollectiveSpatieHtmlParser\\ServiceProvider', - 23 => 'Barryvdh\\Debugbar\\ServiceProvider', - 24 => 'Barryvdh\\DomPDF\\ServiceProvider', - 25 => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider', - 26 => 'Cviebrock\\EloquentSluggable\\ServiceProvider', - 27 => 'Jenssegers\\Date\\DateServiceProvider', - 28 => 'JoeDixon\\Translation\\TranslationServiceProvider', - 29 => 'JoeDixon\\Translation\\TranslationBindingsServiceProvider', - 30 => 'Laracasts\\Flash\\FlashServiceProvider', - 31 => 'Illuminate\\Database\\Eloquent\\LegacyFactoryServiceProvider', - 32 => 'Laravel\\Passport\\PassportServiceProvider', - 33 => 'Laravel\\Sail\\SailServiceProvider', - 34 => 'Laravel\\Tinker\\TinkerServiceProvider', - 35 => 'Laravel\\Ui\\UiServiceProvider', - 36 => 'Maatwebsite\\Excel\\ExcelServiceProvider', - 37 => 'Carbon\\Laravel\\ServiceProvider', - 38 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider', - 39 => 'Termwind\\Laravel\\TermwindServiceProvider', - 40 => 'Pest\\Laravel\\PestServiceProvider', - 41 => 'Spatie\\Html\\HtmlServiceProvider', - 42 => 'Spatie\\LaravelIgnition\\IgnitionServiceProvider', - 43 => 'Yajra\\DataTables\\DataTablesServiceProvider', - 44 => 'Laravel\\Tinker\\TinkerServiceProvider', + 22 => 'Barryvdh\\Debugbar\\ServiceProvider', + 23 => 'Barryvdh\\DomPDF\\ServiceProvider', + 24 => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider', + 25 => 'Cviebrock\\EloquentSluggable\\ServiceProvider', + 26 => 'Jenssegers\\Date\\DateServiceProvider', + 27 => 'JoeDixon\\Translation\\TranslationServiceProvider', + 28 => 'JoeDixon\\Translation\\TranslationBindingsServiceProvider', + 29 => 'Laracasts\\Flash\\FlashServiceProvider', + 30 => 'Illuminate\\Database\\Eloquent\\LegacyFactoryServiceProvider', + 31 => 'Laravel\\Passport\\PassportServiceProvider', + 32 => 'Laravel\\Sail\\SailServiceProvider', + 33 => 'Laravel\\Tinker\\TinkerServiceProvider', + 34 => 'Laravel\\Ui\\UiServiceProvider', + 35 => 'Maatwebsite\\Excel\\ExcelServiceProvider', + 36 => 'Carbon\\Laravel\\ServiceProvider', + 37 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider', + 38 => 'Termwind\\Laravel\\TermwindServiceProvider', + 39 => 'Pest\\Laravel\\PestServiceProvider', + 40 => 'Spatie\\Html\\HtmlServiceProvider', + 41 => 'Spatie\\LaravelIgnition\\IgnitionServiceProvider', + 42 => 'Yajra\\DataTables\\DataTablesServiceProvider', + 43 => 'Laravel\\Tinker\\TinkerServiceProvider', + 44 => 'Acme\\Dhl\\DhlServiceProvider', 45 => 'App\\Providers\\AppServiceProvider', 46 => 'App\\Providers\\AuthServiceProvider', 47 => 'App\\Providers\\EventServiceProvider', 48 => 'App\\Providers\\DomainServiceProvider', 49 => 'App\\Providers\\RouteServiceProvider', - 50 => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider', - 51 => 'Jenssegers\\Date\\DateServiceProvider', - 52 => 'Maatwebsite\\Excel\\ExcelServiceProvider', - 53 => 'Yajra\\DataTables\\DataTablesServiceProvider', - 54 => 'App\\Providers\\YardServiceProvider', + 50 => 'Jenssegers\\Date\\DateServiceProvider', + 51 => 'Maatwebsite\\Excel\\ExcelServiceProvider', + 52 => 'Yajra\\DataTables\\DataTablesServiceProvider', + 53 => 'App\\Providers\\YardServiceProvider', + 54 => 'Alban\\LaravelCollectiveSpatieHtmlParser\\ServiceProvider', ), 'eager' => array ( @@ -69,24 +69,24 @@ 7 => 'Illuminate\\Pagination\\PaginationServiceProvider', 8 => 'Illuminate\\Session\\SessionServiceProvider', 9 => 'Illuminate\\View\\ViewServiceProvider', - 10 => 'Alban\\LaravelCollectiveSpatieHtmlParser\\ServiceProvider', - 11 => 'Barryvdh\\Debugbar\\ServiceProvider', - 12 => 'Barryvdh\\DomPDF\\ServiceProvider', - 13 => 'Cviebrock\\EloquentSluggable\\ServiceProvider', - 14 => 'Jenssegers\\Date\\DateServiceProvider', - 15 => 'JoeDixon\\Translation\\TranslationServiceProvider', - 16 => 'Laracasts\\Flash\\FlashServiceProvider', - 17 => 'Illuminate\\Database\\Eloquent\\LegacyFactoryServiceProvider', - 18 => 'Laravel\\Passport\\PassportServiceProvider', - 19 => 'Laravel\\Ui\\UiServiceProvider', - 20 => 'Maatwebsite\\Excel\\ExcelServiceProvider', - 21 => 'Carbon\\Laravel\\ServiceProvider', - 22 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider', - 23 => 'Termwind\\Laravel\\TermwindServiceProvider', - 24 => 'Pest\\Laravel\\PestServiceProvider', - 25 => 'Spatie\\Html\\HtmlServiceProvider', - 26 => 'Spatie\\LaravelIgnition\\IgnitionServiceProvider', - 27 => 'Yajra\\DataTables\\DataTablesServiceProvider', + 10 => 'Barryvdh\\Debugbar\\ServiceProvider', + 11 => 'Barryvdh\\DomPDF\\ServiceProvider', + 12 => 'Cviebrock\\EloquentSluggable\\ServiceProvider', + 13 => 'Jenssegers\\Date\\DateServiceProvider', + 14 => 'JoeDixon\\Translation\\TranslationServiceProvider', + 15 => 'Laracasts\\Flash\\FlashServiceProvider', + 16 => 'Illuminate\\Database\\Eloquent\\LegacyFactoryServiceProvider', + 17 => 'Laravel\\Passport\\PassportServiceProvider', + 18 => 'Laravel\\Ui\\UiServiceProvider', + 19 => 'Maatwebsite\\Excel\\ExcelServiceProvider', + 20 => 'Carbon\\Laravel\\ServiceProvider', + 21 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider', + 22 => 'Termwind\\Laravel\\TermwindServiceProvider', + 23 => 'Pest\\Laravel\\PestServiceProvider', + 24 => 'Spatie\\Html\\HtmlServiceProvider', + 25 => 'Spatie\\LaravelIgnition\\IgnitionServiceProvider', + 26 => 'Yajra\\DataTables\\DataTablesServiceProvider', + 27 => 'Acme\\Dhl\\DhlServiceProvider', 28 => 'App\\Providers\\AppServiceProvider', 29 => 'App\\Providers\\AuthServiceProvider', 30 => 'App\\Providers\\EventServiceProvider', @@ -96,6 +96,7 @@ 34 => 'Maatwebsite\\Excel\\ExcelServiceProvider', 35 => 'Yajra\\DataTables\\DataTablesServiceProvider', 36 => 'App\\Providers\\YardServiceProvider', + 37 => 'Alban\\LaravelCollectiveSpatieHtmlParser\\ServiceProvider', ), 'deferred' => array ( diff --git a/composer.json b/composer.json index 87073fe..1ccd3c2 100755 --- a/composer.json +++ b/composer.json @@ -4,15 +4,15 @@ "keywords": ["framework", "laravel"], "license": "MIT", "type": "project", - "require": { + + "require": { "php": "^8.2", - "alban/laravel-collective-spatie-html-parser": "^1.1.9", "barryvdh/laravel-dompdf": "^2.2", "cocur/slugify": "^4.5", "cviebrock/eloquent-sluggable": "^11.0", "doctrine/dbal": "^3.6.0|^4.0", "guzzlehttp/guzzle": "^7.4", - "intervention/image": "^3.0", + "intervention/image": "^3", "jenssegers/date": "^4.0", "joedixon/laravel-translation": "2.x-dev", "laracasts/flash": "^3.2", @@ -24,6 +24,7 @@ "maatwebsite/excel": "^3.1", "setasign/fpdf": "^1.8.6", "setasign/fpdi": "^2.6", + "spatie/laravel-html": "^3.7", "wearepixel/laravel-google-shopping-feed": "^4.0", "yajra/laravel-datatables-oracle": "^11.0" }, @@ -54,7 +55,7 @@ "laravel": { "dont-discover": [] } - }, + }, "autoload": { "files": [ "app/helpers.php" @@ -62,6 +63,8 @@ "psr-4": { "App\\": "app/", "Gloudemans\\Shoppingcart\\": "packages/digital-bird/shoppingcart/src", + "Alban\\LaravelCollectiveSpatieHtmlParser\\": "packages/laravel-collective-spatie-html-parser/src", + "Acme\\Dhl\\": "packages/acme-laravel-dhl/src", "Database\\Factories\\": "database/factories/", "Database\\Seeders\\": "database/seeders/" } @@ -78,10 +81,7 @@ "@php artisan vendor:publish --force --tag=livewire:assets --ansi" ], "post-update-cmd": [ - "@php artisan vendor:publish --tag=laravel-assets --ansi --force", - "php artisan ide-helper:generate", - "php artisan ide-helper:meta", - "php artisan ide-helper:models" + "@php artisan vendor:publish --tag=laravel-assets --ansi --force" ], "post-root-package-install": [ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" @@ -95,6 +95,10 @@ ] }, "repositories": [ + { + "type": "path", + "url": "./packages/acme-laravel-dhl" + }, { "type": "vcs", "url": "https://github.com/bjhijmans/laravel-translation.git" @@ -102,4 +106,5 @@ ], "minimum-stability": "dev", "prefer-stable": true + } diff --git a/composer.lock b/composer.lock index bf8932e..3f12675 100644 --- a/composer.lock +++ b/composer.lock @@ -4,60 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f85e5045b24cd27d3bf9f066d3f48713", + "content-hash": "7fc21677cad34a62767eaccbc243e7d6", "packages": [ - { - "name": "alban/laravel-collective-spatie-html-parser", - "version": "v1.1.10", - "source": { - "type": "git", - "url": "https://github.com/christianalban/laravel-collective-spatie-html-parser.git", - "reference": "496fbd38dea78179e9fcd930ee6c42b1c9b1b889" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/christianalban/laravel-collective-spatie-html-parser/zipball/496fbd38dea78179e9fcd930ee6c42b1c9b1b889", - "reference": "496fbd38dea78179e9fcd930ee6c42b1c9b1b889", - "shasum": "" - }, - "require": { - "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0", - "php": "^8.3 | ^8.2 | ^8.0 | ^7.4", - "spatie/laravel-html": "^3.12" - }, - "type": "library", - "extra": { - "laravel": { - "aliases": { - "Form": "Alban\\LaravelCollectiveSpatieHtmlParser\\FormFacade" - }, - "providers": [ - "Alban\\LaravelCollectiveSpatieHtmlParser\\ServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Alban\\LaravelCollectiveSpatieHtmlParser\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Albán", - "email": "christianalbanctdb1d@gmail.com" - } - ], - "description": "Adapter class that allows use spatie/laravel-html for old projects that were using the abanondated laravelcollective/html package, and allow to update to new versions of laravel", - "support": { - "issues": "https://github.com/christianalban/laravel-collective-spatie-html-parser/issues", - "source": "https://github.com/christianalban/laravel-collective-spatie-html-parser/tree/v1.1.10" - }, - "time": "2025-05-04T17:49:43+00:00" - }, { "name": "barryvdh/laravel-dompdf", "version": "v2.2.0", @@ -419,16 +367,16 @@ }, { "name": "composer/semver", - "version": "3.4.3", + "version": "3.4.4", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", "shasum": "" }, "require": { @@ -480,7 +428,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.3" + "source": "https://github.com/composer/semver/tree/3.4.4" }, "funding": [ { @@ -490,13 +438,9 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2024-09-19T14:15:21+00:00" + "time": "2025-08-20T19:15:30+00:00" }, { "name": "cviebrock/eloquent-sluggable", @@ -716,34 +660,34 @@ }, { "name": "doctrine/dbal", - "version": "4.2.3", + "version": "4.3.2", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "33d2d7fe1269b2301640c44cf2896ea607b30e3e" + "reference": "7669f131d43b880de168b2d2df9687d152d6c762" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/33d2d7fe1269b2301640c44cf2896ea607b30e3e", - "reference": "33d2d7fe1269b2301640c44cf2896ea607b30e3e", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/7669f131d43b880de168b2d2df9687d152d6c762", + "reference": "7669f131d43b880de168b2d2df9687d152d6c762", "shasum": "" }, "require": { - "doctrine/deprecations": "^0.5.3|^1", - "php": "^8.1", + "doctrine/deprecations": "^1.1.5", + "php": "^8.2", "psr/cache": "^1|^2|^3", "psr/log": "^1|^2|^3" }, "require-dev": { - "doctrine/coding-standard": "12.0.0", + "doctrine/coding-standard": "13.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.2", - "phpstan/phpstan": "2.1.1", - "phpstan/phpstan-phpunit": "2.0.3", + "phpstan/phpstan": "2.1.17", + "phpstan/phpstan-phpunit": "2.0.6", "phpstan/phpstan-strict-rules": "^2", - "phpunit/phpunit": "10.5.39", - "slevomat/coding-standard": "8.13.1", - "squizlabs/php_codesniffer": "3.10.2", + "phpunit/phpunit": "11.5.23", + "slevomat/coding-standard": "8.16.2", + "squizlabs/php_codesniffer": "3.13.1", "symfony/cache": "^6.3.8|^7.0", "symfony/console": "^5.4|^6.3|^7.0" }, @@ -802,7 +746,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/4.2.3" + "source": "https://github.com/doctrine/dbal/tree/4.3.2" }, "funding": [ { @@ -818,7 +762,7 @@ "type": "tidelift" } ], - "time": "2025-03-07T18:29:05+00:00" + "time": "2025-08-05T13:30:38+00:00" }, { "name": "doctrine/deprecations", @@ -870,33 +814,32 @@ }, { "name": "doctrine/inflector", - "version": "2.0.10", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", - "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^11.0", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.3", - "phpunit/phpunit": "^8.5 || ^9.5", - "vimeo/psalm": "^4.25 || ^5.4" + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + "Doctrine\\Inflector\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -941,7 +884,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.10" + "source": "https://github.com/doctrine/inflector/tree/2.1.0" }, "funding": [ { @@ -957,7 +900,7 @@ "type": "tidelift" } ], - "time": "2024-02-18T20:23:39+00:00" + "time": "2025-08-10T19:31:58+00:00" }, { "name": "doctrine/lexer", @@ -1968,16 +1911,16 @@ }, { "name": "intervention/image", - "version": "3.11.3", + "version": "3.11.4", "source": { "type": "git", "url": "https://github.com/Intervention/image.git", - "reference": "d0f097b8a3fa8fb758efc9440b513aa3833cda17" + "reference": "8c49eb21a6d2572532d1bc425964264f3e496846" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Intervention/image/zipball/d0f097b8a3fa8fb758efc9440b513aa3833cda17", - "reference": "d0f097b8a3fa8fb758efc9440b513aa3833cda17", + "url": "https://api.github.com/repos/Intervention/image/zipball/8c49eb21a6d2572532d1bc425964264f3e496846", + "reference": "8c49eb21a6d2572532d1bc425964264f3e496846", "shasum": "" }, "require": { @@ -2024,7 +1967,7 @@ ], "support": { "issues": "https://github.com/Intervention/image/issues", - "source": "https://github.com/Intervention/image/tree/3.11.3" + "source": "https://github.com/Intervention/image/tree/3.11.4" }, "funding": [ { @@ -2040,7 +1983,7 @@ "type": "ko_fi" } ], - "time": "2025-05-22T17:26:23+00:00" + "time": "2025-07-30T13:13:19+00:00" }, { "name": "jenssegers/date", @@ -2125,23 +2068,23 @@ "source": { "type": "git", "url": "https://github.com/bjhijmans/laravel-translation.git", - "reference": "a490272ba2db86887a71c07c4aa838b9858866d5" + "reference": "d63900e2de938e1274b74fde5f80c60e9a1484e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bjhijmans/laravel-translation/zipball/a490272ba2db86887a71c07c4aa838b9858866d5", - "reference": "a490272ba2db86887a71c07c4aa838b9858866d5", + "url": "https://api.github.com/repos/bjhijmans/laravel-translation/zipball/d63900e2de938e1274b74fde5f80c60e9a1484e5", + "reference": "d63900e2de938e1274b74fde5f80c60e9a1484e5", "shasum": "" }, "require": { - "illuminate/support": "^8.0||^9.0||^10.0||^11.0", + "illuminate/support": "^8.0||^9.0||^10.0||^11.0||^12.0", "laravel/legacy-factories": "^1.3", "php": "^8.0" }, "require-dev": { "mockery/mockery": "^1.0.0", - "orchestra/testbench": "^6.0|^8.0|^9.0", - "phpunit/phpunit": "^9.0|^10.0" + "orchestra/testbench": "^6.0|^8.0|^9.0|^10.0", + "phpunit/phpunit": "^9.0|^10.0|^11.0" }, "default-branch": true, "type": "library", @@ -2184,7 +2127,7 @@ "support": { "source": "https://github.com/bjhijmans/laravel-translation/tree/2.x" }, - "time": "2024-04-03T15:19:33+00:00" + "time": "2025-07-01T07:27:42+00:00" }, { "name": "laracasts/flash", @@ -2245,16 +2188,16 @@ }, { "name": "laravel/framework", - "version": "v11.45.1", + "version": "v11.45.2", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "b09ba32795b8e71df10856a2694706663984a239" + "reference": "d134bf11e2208c0c5bd488cf19e612ca176b820a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/b09ba32795b8e71df10856a2694706663984a239", - "reference": "b09ba32795b8e71df10856a2694706663984a239", + "url": "https://api.github.com/repos/laravel/framework/zipball/d134bf11e2208c0c5bd488cf19e612ca176b820a", + "reference": "d134bf11e2208c0c5bd488cf19e612ca176b820a", "shasum": "" }, "require": { @@ -2362,7 +2305,7 @@ "league/flysystem-read-only": "^3.25.1", "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", - "orchestra/testbench-core": "^9.13.2", + "orchestra/testbench-core": "^9.16.0", "pda/pheanstalk": "^5.0.6", "php-http/discovery": "^1.15", "phpstan/phpstan": "^2.0", @@ -2456,7 +2399,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-06-03T14:01:40+00:00" + "time": "2025-08-13T20:28:00+00:00" }, { "name": "laravel/legacy-factories", @@ -2977,16 +2920,16 @@ }, { "name": "league/commonmark", - "version": "2.7.0", + "version": "2.7.1", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "6fbb36d44824ed4091adbcf4c7d4a3923cdb3405" + "reference": "10732241927d3971d28e7ea7b5712721fa2296ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/6fbb36d44824ed4091adbcf4c7d4a3923cdb3405", - "reference": "6fbb36d44824ed4091adbcf4c7d4a3923cdb3405", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/10732241927d3971d28e7ea7b5712721fa2296ca", + "reference": "10732241927d3971d28e7ea7b5712721fa2296ca", "shasum": "" }, "require": { @@ -3015,7 +2958,7 @@ "symfony/process": "^5.4 | ^6.0 | ^7.0", "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", "unleashedtech/php-coding-standard": "^3.1.1", - "vimeo/psalm": "^4.24.0 || ^5.0.0" + "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" }, "suggest": { "symfony/yaml": "v2.3+ required if using the Front Matter extension" @@ -3080,7 +3023,7 @@ "type": "tidelift" } ], - "time": "2025-05-05T12:20:28+00:00" + "time": "2025-07-20T12:47:49+00:00" }, { "name": "league/config", @@ -3220,16 +3163,16 @@ }, { "name": "league/flysystem", - "version": "3.29.1", + "version": "3.30.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319" + "reference": "2203e3151755d874bb2943649dae1eb8533ac93e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/edc1bb7c86fab0776c3287dbd19b5fa278347319", - "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/2203e3151755d874bb2943649dae1eb8533ac93e", + "reference": "2203e3151755d874bb2943649dae1eb8533ac93e", "shasum": "" }, "require": { @@ -3253,13 +3196,13 @@ "composer/semver": "^3.0", "ext-fileinfo": "*", "ext-ftp": "*", - "ext-mongodb": "^1.3", + "ext-mongodb": "^1.3|^2", "ext-zip": "*", "friendsofphp/php-cs-fixer": "^3.5", "google/cloud-storage": "^1.23", "guzzlehttp/psr7": "^2.6", "microsoft/azure-storage-blob": "^1.1", - "mongodb/mongodb": "^1.2", + "mongodb/mongodb": "^1.2|^2", "phpseclib/phpseclib": "^3.0.36", "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.5.11|^10.0", @@ -3297,22 +3240,22 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.29.1" + "source": "https://github.com/thephpleague/flysystem/tree/3.30.0" }, - "time": "2024-10-08T08:58:34+00:00" + "time": "2025-06-25T13:29:59+00:00" }, { "name": "league/flysystem-local", - "version": "3.29.0", + "version": "3.30.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-local.git", - "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27" + "reference": "6691915f77c7fb69adfb87dcd550052dc184ee10" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/e0e8d52ce4b2ed154148453d321e97c8e931bd27", - "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/6691915f77c7fb69adfb87dcd550052dc184ee10", + "reference": "6691915f77c7fb69adfb87dcd550052dc184ee10", "shasum": "" }, "require": { @@ -3346,9 +3289,9 @@ "local" ], "support": { - "source": "https://github.com/thephpleague/flysystem-local/tree/3.29.0" + "source": "https://github.com/thephpleague/flysystem-local/tree/3.30.0" }, - "time": "2024-08-09T21:24:39+00:00" + "time": "2025-05-21T10:34:19+00:00" }, { "name": "league/mime-type-detection", @@ -3670,16 +3613,16 @@ }, { "name": "maatwebsite/excel", - "version": "3.1.64", + "version": "3.1.66", "source": { "type": "git", "url": "https://github.com/SpartnerNL/Laravel-Excel.git", - "reference": "e25d44a2d91da9179cd2d7fec952313548597a79" + "reference": "3b29c2426a46674f444890c45f742452a396aae8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/e25d44a2d91da9179cd2d7fec952313548597a79", - "reference": "e25d44a2d91da9179cd2d7fec952313548597a79", + "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/3b29c2426a46674f444890c45f742452a396aae8", + "reference": "3b29c2426a46674f444890c45f742452a396aae8", "shasum": "" }, "require": { @@ -3687,7 +3630,7 @@ "ext-json": "*", "illuminate/support": "5.8.*||^6.0||^7.0||^8.0||^9.0||^10.0||^11.0||^12.0", "php": "^7.0||^8.0", - "phpoffice/phpspreadsheet": "^1.29.9", + "phpoffice/phpspreadsheet": "^1.29.12", "psr/simple-cache": "^1.0||^2.0||^3.0" }, "require-dev": { @@ -3735,7 +3678,7 @@ ], "support": { "issues": "https://github.com/SpartnerNL/Laravel-Excel/issues", - "source": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.64" + "source": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.66" }, "funding": [ { @@ -3747,7 +3690,7 @@ "type": "github" } ], - "time": "2025-02-24T11:12:50+00:00" + "time": "2025-08-07T08:31:22+00:00" }, { "name": "maennchen/zipstream-php", @@ -3936,16 +3879,16 @@ }, { "name": "masterminds/html5", - "version": "2.9.0", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/Masterminds/html5-php.git", - "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6" + "reference": "fcf91eb64359852f00d921887b219479b4f21251" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", - "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251", + "reference": "fcf91eb64359852f00d921887b219479b4f21251", "shasum": "" }, "require": { @@ -3997,9 +3940,9 @@ ], "support": { "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.9.0" + "source": "https://github.com/Masterminds/html5-php/tree/2.10.0" }, - "time": "2024-03-31T07:05:07+00:00" + "time": "2025-07-25T09:04:22+00:00" }, { "name": "monolog/monolog", @@ -4275,29 +4218,29 @@ }, { "name": "nette/utils", - "version": "v4.0.7", + "version": "v4.0.8", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2" + "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/e67c4061eb40b9c113b218214e42cb5a0dda28f2", - "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2", + "url": "https://api.github.com/repos/nette/utils/zipball/c930ca4e3cf4f17dcfb03037703679d2396d2ede", + "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede", "shasum": "" }, "require": { - "php": "8.0 - 8.4" + "php": "8.0 - 8.5" }, "conflict": { "nette/finder": "<3", "nette/schema": "<1.2.2" }, "require-dev": { - "jetbrains/phpstorm-attributes": "dev-master", + "jetbrains/phpstorm-attributes": "^1.2", "nette/tester": "^2.5", - "phpstan/phpstan": "^1.0", + "phpstan/phpstan-nette": "^2.0@stable", "tracy/tracy": "^2.9" }, "suggest": { @@ -4315,6 +4258,9 @@ } }, "autoload": { + "psr-4": { + "Nette\\": "src" + }, "classmap": [ "src/" ] @@ -4355,22 +4301,22 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.7" + "source": "https://github.com/nette/utils/tree/v4.0.8" }, - "time": "2025-06-03T04:55:08+00:00" + "time": "2025-08-06T21:43:34+00:00" }, { "name": "nikic/php-parser", - "version": "v5.5.0", + "version": "v5.6.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", "shasum": "" }, "require": { @@ -4389,7 +4335,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -4413,9 +4359,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" }, - "time": "2025-05-31T08:24:38+00:00" + "time": "2025-08-13T20:13:15+00:00" }, { "name": "nunomaduro/termwind", @@ -4791,16 +4737,16 @@ }, { "name": "phpoffice/phpspreadsheet", - "version": "1.29.10", + "version": "1.30.0", "source": { "type": "git", "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", - "reference": "c80041b1628c4f18030407134fe88303661d4e4e" + "reference": "2f39286e0136673778b7a142b3f0d141e43d1714" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/c80041b1628c4f18030407134fe88303661d4e4e", - "reference": "c80041b1628c4f18030407134fe88303661d4e4e", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/2f39286e0136673778b7a142b3f0d141e43d1714", + "reference": "2f39286e0136673778b7a142b3f0d141e43d1714", "shasum": "" }, "require": { @@ -4891,22 +4837,22 @@ ], "support": { "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", - "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.29.10" + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.30.0" }, - "time": "2025-02-08T02:56:14+00:00" + "time": "2025-08-10T06:28:02+00:00" }, { "name": "phpoption/phpoption", - "version": "1.9.3", + "version": "1.9.4", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", "shasum": "" }, "require": { @@ -4914,7 +4860,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" }, "type": "library", "extra": { @@ -4956,7 +4902,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.4" }, "funding": [ { @@ -4968,20 +4914,20 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:41:07+00:00" + "time": "2025-08-21T11:53:16+00:00" }, { "name": "phpseclib/phpseclib", - "version": "3.0.43", + "version": "3.0.46", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "709ec107af3cb2f385b9617be72af8cf62441d02" + "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/709ec107af3cb2f385b9617be72af8cf62441d02", - "reference": "709ec107af3cb2f385b9617be72af8cf62441d02", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", + "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", "shasum": "" }, "require": { @@ -5062,7 +5008,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.43" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.46" }, "funding": [ { @@ -5078,7 +5024,7 @@ "type": "tidelift" } ], - "time": "2024-12-14T21:12:59+00:00" + "time": "2025-06-26T16:29:55+00:00" }, { "name": "psr/cache", @@ -5543,16 +5489,16 @@ }, { "name": "psy/psysh", - "version": "v0.12.8", + "version": "v0.12.10", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "85057ceedee50c49d4f6ecaff73ee96adb3b3625" + "reference": "6e80abe6f2257121f1eb9a4c55bf29d921025b22" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/85057ceedee50c49d4f6ecaff73ee96adb3b3625", - "reference": "85057ceedee50c49d4f6ecaff73ee96adb3b3625", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/6e80abe6f2257121f1eb9a4c55bf29d921025b22", + "reference": "6e80abe6f2257121f1eb9a4c55bf29d921025b22", "shasum": "" }, "require": { @@ -5602,12 +5548,11 @@ "authors": [ { "name": "Justin Hileman", - "email": "justin@justinhileman.info", - "homepage": "http://justinhileman.com" + "email": "justin@justinhileman.info" } ], "description": "An interactive shell for modern PHP.", - "homepage": "http://psysh.org", + "homepage": "https://psysh.org", "keywords": [ "REPL", "console", @@ -5616,9 +5561,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.8" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.10" }, - "time": "2025-03-16T03:05:19+00:00" + "time": "2025-08-04T12:39:37+00:00" }, { "name": "ralouphie/getallheaders", @@ -5742,21 +5687,20 @@ }, { "name": "ramsey/uuid", - "version": "4.8.1", + "version": "4.9.0", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28" + "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28", - "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0", "shasum": "" }, "require": { "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", - "ext-json": "*", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -5815,22 +5759,22 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.8.1" + "source": "https://github.com/ramsey/uuid/tree/4.9.0" }, - "time": "2025-06-01T06:28:46+00:00" + "time": "2025-06-25T14:20:11+00:00" }, { "name": "sabberworm/php-css-parser", - "version": "v8.8.0", + "version": "v8.9.0", "source": { "type": "git", "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git", - "reference": "3de493bdddfd1f051249af725c7e0d2c38fed740" + "reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/3de493bdddfd1f051249af725c7e0d2c38fed740", - "reference": "3de493bdddfd1f051249af725c7e0d2c38fed740", + "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/d8e916507b88e389e26d4ab03c904a082aa66bb9", + "reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9", "shasum": "" }, "require": { @@ -5838,7 +5782,8 @@ "php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" }, "require-dev": { - "phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41" + "phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41", + "rawr/cross-data-providers": "^2.0.0" }, "suggest": { "ext-mbstring": "for parsing UTF-8 CSS" @@ -5880,9 +5825,9 @@ ], "support": { "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues", - "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.8.0" + "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.9.0" }, - "time": "2025-03-23T17:59:05+00:00" + "time": "2025-07-11T13:20:48+00:00" }, { "name": "setasign/fpdf", @@ -5932,16 +5877,16 @@ }, { "name": "setasign/fpdi", - "version": "v2.6.3", + "version": "v2.6.4", "source": { "type": "git", "url": "https://github.com/Setasign/FPDI.git", - "reference": "67c31f5e50c93c20579ca9e23035d8c540b51941" + "reference": "4b53852fde2734ec6a07e458a085db627c60eada" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Setasign/FPDI/zipball/67c31f5e50c93c20579ca9e23035d8c540b51941", - "reference": "67c31f5e50c93c20579ca9e23035d8c540b51941", + "url": "https://api.github.com/repos/Setasign/FPDI/zipball/4b53852fde2734ec6a07e458a085db627c60eada", + "reference": "4b53852fde2734ec6a07e458a085db627c60eada", "shasum": "" }, "require": { @@ -5956,7 +5901,7 @@ "setasign/fpdf": "~1.8.6", "setasign/tfpdf": "~1.33", "squizlabs/php_codesniffer": "^3.5", - "tecnickcom/tcpdf": "^6.2" + "tecnickcom/tcpdf": "^6.8" }, "suggest": { "setasign/fpdf": "FPDI will extend this class but as it is also possible to use TCPDF or tFPDF as an alternative. There's no fixed dependency configured." @@ -5992,7 +5937,7 @@ ], "support": { "issues": "https://github.com/Setasign/FPDI/issues", - "source": "https://github.com/Setasign/FPDI/tree/v2.6.3" + "source": "https://github.com/Setasign/FPDI/tree/v2.6.4" }, "funding": [ { @@ -6000,7 +5945,7 @@ "type": "tidelift" } ], - "time": "2025-02-05T13:22:35+00:00" + "time": "2025-08-05T09:57:14+00:00" }, { "name": "spatie/array-to-xml", @@ -6150,16 +6095,16 @@ }, { "name": "symfony/console", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44" + "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/66c1440edf6f339fd82ed6c7caa76cb006211b44", - "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44", + "url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1", + "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1", "shasum": "" }, "require": { @@ -6224,7 +6169,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.0" + "source": "https://github.com/symfony/console/tree/v7.3.2" }, "funding": [ { @@ -6235,12 +6180,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-24T10:34:04+00:00" + "time": "2025-07-30T17:13:41+00:00" }, { "name": "symfony/css-selector", @@ -6376,16 +6325,16 @@ }, { "name": "symfony/error-handler", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "cf68d225bc43629de4ff54778029aee6dc191b83" + "reference": "0b31a944fcd8759ae294da4d2808cbc53aebd0c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/cf68d225bc43629de4ff54778029aee6dc191b83", - "reference": "cf68d225bc43629de4ff54778029aee6dc191b83", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/0b31a944fcd8759ae294da4d2808cbc53aebd0c3", + "reference": "0b31a944fcd8759ae294da4d2808cbc53aebd0c3", "shasum": "" }, "require": { @@ -6433,7 +6382,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.3.0" + "source": "https://github.com/symfony/error-handler/tree/v7.3.2" }, "funding": [ { @@ -6444,12 +6393,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-29T07:19:49+00:00" + "time": "2025-07-07T08:17:57+00:00" }, { "name": "symfony/event-dispatcher", @@ -6609,16 +6562,16 @@ }, { "name": "symfony/finder", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d" + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/ec2344cf77a48253bbca6939aa3d2477773ea63d", - "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d", + "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe", + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe", "shasum": "" }, "require": { @@ -6653,7 +6606,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.3.0" + "source": "https://github.com/symfony/finder/tree/v7.3.2" }, "funding": [ { @@ -6664,25 +6617,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-30T19:00:26+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "4236baf01609667d53b20371486228231eb135fd" + "reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/4236baf01609667d53b20371486228231eb135fd", - "reference": "4236baf01609667d53b20371486228231eb135fd", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6877c122b3a6cc3695849622720054f6e6fa5fa6", + "reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6", "shasum": "" }, "require": { @@ -6732,7 +6689,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.3.0" + "source": "https://github.com/symfony/http-foundation/tree/v7.3.2" }, "funding": [ { @@ -6743,25 +6700,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-12T14:48:23+00:00" + "time": "2025-07-10T08:47:49+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "ac7b8e163e8c83dce3abcc055a502d4486051a9f" + "reference": "6ecc895559ec0097e221ed2fd5eb44d5fede083c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/ac7b8e163e8c83dce3abcc055a502d4486051a9f", - "reference": "ac7b8e163e8c83dce3abcc055a502d4486051a9f", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/6ecc895559ec0097e221ed2fd5eb44d5fede083c", + "reference": "6ecc895559ec0097e221ed2fd5eb44d5fede083c", "shasum": "" }, "require": { @@ -6846,7 +6807,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.3.0" + "source": "https://github.com/symfony/http-kernel/tree/v7.3.2" }, "funding": [ { @@ -6857,25 +6818,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-29T07:47:32+00:00" + "time": "2025-07-31T10:45:04+00:00" }, { "name": "symfony/mailer", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "0f375bbbde96ae8c78e4aa3e63aabd486e33364c" + "reference": "d43e84d9522345f96ad6283d5dfccc8c1cfc299b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/0f375bbbde96ae8c78e4aa3e63aabd486e33364c", - "reference": "0f375bbbde96ae8c78e4aa3e63aabd486e33364c", + "url": "https://api.github.com/repos/symfony/mailer/zipball/d43e84d9522345f96ad6283d5dfccc8c1cfc299b", + "reference": "d43e84d9522345f96ad6283d5dfccc8c1cfc299b", "shasum": "" }, "require": { @@ -6926,7 +6891,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.3.0" + "source": "https://github.com/symfony/mailer/tree/v7.3.2" }, "funding": [ { @@ -6937,25 +6902,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-04T09:51:09+00:00" + "time": "2025-07-15T11:36:08+00:00" }, { "name": "symfony/mime", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "0e7b19b2f399c31df0cdbe5d8cbf53f02f6cfcd9" + "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/0e7b19b2f399c31df0cdbe5d8cbf53f02f6cfcd9", - "reference": "0e7b19b2f399c31df0cdbe5d8cbf53f02f6cfcd9", + "url": "https://api.github.com/repos/symfony/mime/zipball/e0a0f859148daf1edf6c60b398eb40bfc96697d1", + "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1", "shasum": "" }, "require": { @@ -7010,7 +6979,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.3.0" + "source": "https://github.com/symfony/mime/tree/v7.3.2" }, "funding": [ { @@ -7021,16 +6990,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-02-19T08:51:26+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -7089,7 +7062,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -7100,6 +7073,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -7109,16 +7086,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { @@ -7167,7 +7144,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -7178,16 +7155,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", @@ -7250,7 +7231,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" }, "funding": [ { @@ -7261,6 +7242,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -7270,7 +7255,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -7331,7 +7316,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -7342,6 +7327,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -7351,7 +7340,7 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", @@ -7412,7 +7401,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -7423,6 +7412,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -7432,7 +7425,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -7492,7 +7485,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -7503,6 +7496,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -7512,16 +7509,16 @@ }, { "name": "symfony/polyfill-php83", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", "shasum": "" }, "require": { @@ -7568,7 +7565,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" }, "funding": [ { @@ -7579,16 +7576,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-07-08T02:45:35+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", @@ -7647,7 +7648,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" }, "funding": [ { @@ -7658,6 +7659,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -7811,16 +7816,16 @@ }, { "name": "symfony/routing", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "8e213820c5fea844ecea29203d2a308019007c15" + "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/8e213820c5fea844ecea29203d2a308019007c15", - "reference": "8e213820c5fea844ecea29203d2a308019007c15", + "url": "https://api.github.com/repos/symfony/routing/zipball/7614b8ca5fa89b9cd233e21b627bfc5774f586e4", + "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4", "shasum": "" }, "require": { @@ -7872,7 +7877,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.3.0" + "source": "https://github.com/symfony/routing/tree/v7.3.2" }, "funding": [ { @@ -7883,12 +7888,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-24T20:43:28+00:00" + "time": "2025-07-15T11:36:08+00:00" }, { "name": "symfony/service-contracts", @@ -7975,16 +7984,16 @@ }, { "name": "symfony/string", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125" + "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125", + "url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca", + "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca", "shasum": "" }, "require": { @@ -8042,7 +8051,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.0" + "source": "https://github.com/symfony/string/tree/v7.3.2" }, "funding": [ { @@ -8053,25 +8062,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-20T20:19:01+00:00" + "time": "2025-07-10T08:47:49+00:00" }, { "name": "symfony/translation", - "version": "v6.4.22", + "version": "v6.4.24", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "7e3b3b7146c6fab36ddff304a8041174bf6e17ad" + "reference": "300b72643e89de0734d99a9e3f8494a3ef6936e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/7e3b3b7146c6fab36ddff304a8041174bf6e17ad", - "reference": "7e3b3b7146c6fab36ddff304a8041174bf6e17ad", + "url": "https://api.github.com/repos/symfony/translation/zipball/300b72643e89de0734d99a9e3f8494a3ef6936e1", + "reference": "300b72643e89de0734d99a9e3f8494a3ef6936e1", "shasum": "" }, "require": { @@ -8137,7 +8150,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.4.22" + "source": "https://github.com/symfony/translation/tree/v6.4.24" }, "funding": [ { @@ -8148,12 +8161,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-29T07:06:44+00:00" + "time": "2025-07-30T17:30:48+00:00" }, { "name": "symfony/translation-contracts", @@ -8235,16 +8252,16 @@ }, { "name": "symfony/uid", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "7beeb2b885cd584cd01e126c5777206ae4c3c6a3" + "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/7beeb2b885cd584cd01e126c5777206ae4c3c6a3", - "reference": "7beeb2b885cd584cd01e126c5777206ae4c3c6a3", + "url": "https://api.github.com/repos/symfony/uid/zipball/a69f69f3159b852651a6bf45a9fdd149520525bb", + "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb", "shasum": "" }, "require": { @@ -8289,7 +8306,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.3.0" + "source": "https://github.com/symfony/uid/tree/v7.3.1" }, "funding": [ { @@ -8305,20 +8322,20 @@ "type": "tidelift" } ], - "time": "2025-05-24T14:28:13+00:00" + "time": "2025-06-27T19:55:54+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "548f6760c54197b1084e1e5c71f6d9d523f2f78e" + "reference": "53205bea27450dc5c65377518b3275e126d45e75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/548f6760c54197b1084e1e5c71f6d9d523f2f78e", - "reference": "548f6760c54197b1084e1e5c71f6d9d523f2f78e", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/53205bea27450dc5c65377518b3275e126d45e75", + "reference": "53205bea27450dc5c65377518b3275e126d45e75", "shasum": "" }, "require": { @@ -8330,7 +8347,6 @@ "symfony/console": "<6.4" }, "require-dev": { - "ext-iconv": "*", "symfony/console": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", "symfony/process": "^6.4|^7.0", @@ -8373,7 +8389,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.3.0" + "source": "https://github.com/symfony/var-dumper/tree/v7.3.2" }, "funding": [ { @@ -8384,12 +8400,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-27T18:39:23+00:00" + "time": "2025-07-29T20:02:46+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -8801,16 +8821,16 @@ "packages-dev": [ { "name": "barryvdh/laravel-debugbar", - "version": "v3.15.4", + "version": "v3.16.0", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "c0667ea91f7185f1e074402c5788195e96bf8106" + "reference": "f265cf5e38577d42311f1a90d619bcd3740bea23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/c0667ea91f7185f1e074402c5788195e96bf8106", - "reference": "c0667ea91f7185f1e074402c5788195e96bf8106", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/f265cf5e38577d42311f1a90d619bcd3740bea23", + "reference": "f265cf5e38577d42311f1a90d619bcd3740bea23", "shasum": "" }, "require": { @@ -8818,7 +8838,7 @@ "illuminate/session": "^9|^10|^11|^12", "illuminate/support": "^9|^10|^11|^12", "php": "^8.1", - "php-debugbar/php-debugbar": "~2.1.1", + "php-debugbar/php-debugbar": "~2.2.0", "symfony/finder": "^6|^7" }, "require-dev": { @@ -8838,7 +8858,7 @@ ] }, "branch-alias": { - "dev-master": "3.15-dev" + "dev-master": "3.16-dev" } }, "autoload": { @@ -8870,7 +8890,7 @@ ], "support": { "issues": "https://github.com/barryvdh/laravel-debugbar/issues", - "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.15.4" + "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.16.0" }, "funding": [ { @@ -8882,24 +8902,24 @@ "type": "github" } ], - "time": "2025-04-16T06:32:06+00:00" + "time": "2025-07-14T11:56:43+00:00" }, { "name": "barryvdh/laravel-ide-helper", - "version": "v3.5.5", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-ide-helper.git", - "reference": "8d441ec99f8612b942b55f5183151d91591b618a" + "reference": "8d00250cba25728373e92c1d8dcebcbf64623d29" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/8d441ec99f8612b942b55f5183151d91591b618a", - "reference": "8d441ec99f8612b942b55f5183151d91591b618a", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/8d00250cba25728373e92c1d8dcebcbf64623d29", + "reference": "8d00250cba25728373e92c1d8dcebcbf64623d29", "shasum": "" }, "require": { - "barryvdh/reflection-docblock": "^2.3", + "barryvdh/reflection-docblock": "^2.4", "composer/class-map-generator": "^1.0", "ext-json": "*", "illuminate/console": "^11.15 || ^12", @@ -8964,7 +8984,7 @@ ], "support": { "issues": "https://github.com/barryvdh/laravel-ide-helper/issues", - "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.5.5" + "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.6.0" }, "funding": [ { @@ -8976,20 +8996,20 @@ "type": "github" } ], - "time": "2025-02-11T13:59:46+00:00" + "time": "2025-07-17T20:11:57+00:00" }, { "name": "barryvdh/reflection-docblock", - "version": "v2.3.1", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/barryvdh/ReflectionDocBlock.git", - "reference": "b6ff9f93603561f50e53b64310495d20b8dff5d8" + "reference": "d103774cbe7e94ddee7e4870f97f727b43fe7201" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/b6ff9f93603561f50e53b64310495d20b8dff5d8", - "reference": "b6ff9f93603561f50e53b64310495d20b8dff5d8", + "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/d103774cbe7e94ddee7e4870f97f727b43fe7201", + "reference": "d103774cbe7e94ddee7e4870f97f727b43fe7201", "shasum": "" }, "require": { @@ -9026,9 +9046,9 @@ } ], "support": { - "source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.3.1" + "source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.4.0" }, - "time": "2025-01-18T19:26:32+00:00" + "time": "2025-07-17T06:07:30+00:00" }, { "name": "brianium/paratest", @@ -9125,16 +9145,16 @@ }, { "name": "composer/class-map-generator", - "version": "1.6.1", + "version": "1.6.2", "source": { "type": "git", "url": "https://github.com/composer/class-map-generator.git", - "reference": "134b705ddb0025d397d8318a75825fe3c9d1da34" + "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/class-map-generator/zipball/134b705ddb0025d397d8318a75825fe3c9d1da34", - "reference": "134b705ddb0025d397d8318a75825fe3c9d1da34", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/ba9f089655d4cdd64e762a6044f411ccdaec0076", + "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076", "shasum": "" }, "require": { @@ -9178,7 +9198,7 @@ ], "support": { "issues": "https://github.com/composer/class-map-generator/issues", - "source": "https://github.com/composer/class-map-generator/tree/1.6.1" + "source": "https://github.com/composer/class-map-generator/tree/1.6.2" }, "funding": [ { @@ -9188,13 +9208,9 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2025-03-24T13:50:44+00:00" + "time": "2025-08-20T18:52:43+00:00" }, { "name": "fakerphp/faker", @@ -9261,16 +9277,16 @@ }, { "name": "fidry/cpu-core-counter", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "8520451a140d3f46ac33042715115e290cf5785f" + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", - "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", "shasum": "" }, "require": { @@ -9280,10 +9296,10 @@ "fidry/makefile": "^0.2.0", "fidry/php-cs-fixer-config": "^1.1.2", "phpstan/extension-installer": "^1.2.0", - "phpstan/phpstan": "^1.9.2", - "phpstan/phpstan-deprecation-rules": "^1.0.0", - "phpstan/phpstan-phpunit": "^1.2.2", - "phpstan/phpstan-strict-rules": "^1.4.4", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^8.5.31 || ^9.5.26", "webmozarts/strict-phpunit": "^7.5" }, @@ -9310,7 +9326,7 @@ ], "support": { "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" }, "funding": [ { @@ -9318,20 +9334,20 @@ "type": "github" } ], - "time": "2024-08-06T10:04:20+00:00" + "time": "2025-08-14T07:29:31+00:00" }, { "name": "filp/whoops", - "version": "2.18.2", + "version": "2.18.4", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3" + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/89dabca1490bc77dbcab41c2b20968c7e44bf7c3", - "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", "shasum": "" }, "require": { @@ -9381,7 +9397,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.18.2" + "source": "https://github.com/filp/whoops/tree/2.18.4" }, "funding": [ { @@ -9389,7 +9405,7 @@ "type": "github" } ], - "time": "2025-06-11T20:42:19+00:00" + "time": "2025-08-08T12:00:00+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -9504,16 +9520,16 @@ }, { "name": "laravel/pint", - "version": "v1.22.1", + "version": "v1.24.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "941d1927c5ca420c22710e98420287169c7bcaf7" + "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/941d1927c5ca420c22710e98420287169c7bcaf7", - "reference": "941d1927c5ca420c22710e98420287169c7bcaf7", + "url": "https://api.github.com/repos/laravel/pint/zipball/0345f3b05f136801af8c339f9d16ef29e6b4df8a", + "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a", "shasum": "" }, "require": { @@ -9524,10 +9540,10 @@ "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.75.0", - "illuminate/view": "^11.44.7", - "larastan/larastan": "^3.4.0", - "laravel-zero/framework": "^11.36.1", + "friendsofphp/php-cs-fixer": "^3.82.2", + "illuminate/view": "^11.45.1", + "larastan/larastan": "^3.5.0", + "laravel-zero/framework": "^11.45.0", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^2.3.1", "pestphp/pest": "^2.36.0" @@ -9537,6 +9553,9 @@ ], "type": "project", "autoload": { + "files": [ + "overrides/Runner/Parallel/ProcessFactory.php" + ], "psr-4": { "App\\": "app/", "Database\\Seeders\\": "database/seeders/", @@ -9566,20 +9585,20 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-05-08T08:38:12+00:00" + "time": "2025-07-10T18:09:32+00:00" }, { "name": "laravel/sail", - "version": "v1.43.1", + "version": "v1.44.0", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "3e7d899232a8c5e3ea4fc6dee7525ad583887e72" + "reference": "a09097bd2a8a38e23ac472fa6a6cf5b0d1c1d3fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/3e7d899232a8c5e3ea4fc6dee7525ad583887e72", - "reference": "3e7d899232a8c5e3ea4fc6dee7525ad583887e72", + "url": "https://api.github.com/repos/laravel/sail/zipball/a09097bd2a8a38e23ac472fa6a6cf5b0d1c1d3fe", + "reference": "a09097bd2a8a38e23ac472fa6a6cf5b0d1c1d3fe", "shasum": "" }, "require": { @@ -9629,7 +9648,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2025-05-19T13:19:21+00:00" + "time": "2025-07-04T16:17:06+00:00" }, { "name": "mockery/mockery", @@ -9716,16 +9735,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.13.1", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", - "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -9764,7 +9783,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -9772,7 +9791,7 @@ "type": "tidelift" } ], - "time": "2025-04-29T12:36:36+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nunomaduro/collision", @@ -10315,16 +10334,16 @@ }, { "name": "php-debugbar/php-debugbar", - "version": "v2.1.6", + "version": "v2.2.4", "source": { "type": "git", "url": "https://github.com/php-debugbar/php-debugbar.git", - "reference": "16fa68da5617220594aa5e33fa9de415f94784a0" + "reference": "3146d04671f51f69ffec2a4207ac3bdcf13a9f35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/16fa68da5617220594aa5e33fa9de415f94784a0", - "reference": "16fa68da5617220594aa5e33fa9de415f94784a0", + "url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/3146d04671f51f69ffec2a4207ac3bdcf13a9f35", + "reference": "3146d04671f51f69ffec2a4207ac3bdcf13a9f35", "shasum": "" }, "require": { @@ -10332,6 +10351,9 @@ "psr/log": "^1|^2|^3", "symfony/var-dumper": "^4|^5|^6|^7" }, + "replace": { + "maximebf/debugbar": "self.version" + }, "require-dev": { "dbrekelmans/bdi": "^1", "phpunit/phpunit": "^8|^9", @@ -10346,7 +10368,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -10379,9 +10401,9 @@ ], "support": { "issues": "https://github.com/php-debugbar/php-debugbar/issues", - "source": "https://github.com/php-debugbar/php-debugbar/tree/v2.1.6" + "source": "https://github.com/php-debugbar/php-debugbar/tree/v2.2.4" }, - "time": "2025-02-21T17:47:03+00:00" + "time": "2025-07-22T14:01:30+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -10438,16 +10460,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.6.2", + "version": "5.6.3", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62" + "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/92dde6a5919e34835c506ac8c523ef095a95ed62", - "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94f8051919d1b0369a6bcc7931d679a511c03fe9", + "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9", "shasum": "" }, "require": { @@ -10496,9 +10518,9 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.2" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.3" }, - "time": "2025-04-13T19:20:35+00:00" + "time": "2025-08-01T19:43:32+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -10560,16 +10582,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "2.1.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68" + "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", - "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/b9e61a61e39e02dd90944e9115241c7f7e76bfd8", + "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8", "shasum": "" }, "require": { @@ -10601,9 +10623,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.1.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.2.0" }, - "time": "2025-02-19T13:28:12+00:00" + "time": "2025-07-13T07:04:09+00:00" }, { "name": "phpunit/php-code-coverage", @@ -11773,23 +11795,23 @@ }, { "name": "sebastian/recursion-context", - "version": "5.0.0", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", "shasum": "" }, "require": { "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { @@ -11824,15 +11846,28 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2023-02-03T07:05:40+00:00" + "time": "2025-08-10T07:50:56+00:00" }, { "name": "sebastian/type", @@ -12325,16 +12360,16 @@ }, { "name": "symfony/yaml", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "cea40a48279d58dc3efee8112634cb90141156c2" + "reference": "b8d7d868da9eb0919e99c8830431ea087d6aae30" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/cea40a48279d58dc3efee8112634cb90141156c2", - "reference": "cea40a48279d58dc3efee8112634cb90141156c2", + "url": "https://api.github.com/repos/symfony/yaml/zipball/b8d7d868da9eb0919e99c8830431ea087d6aae30", + "reference": "b8d7d868da9eb0919e99c8830431ea087d6aae30", "shasum": "" }, "require": { @@ -12377,7 +12412,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.3.0" + "source": "https://github.com/symfony/yaml/tree/v7.3.2" }, "funding": [ { @@ -12388,12 +12423,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-04T10:10:33+00:00" + "time": "2025-07-10T08:47:49+00:00" }, { "name": "ta-tikoma/phpunit-architecture-test", diff --git a/config/app.php b/config/app.php index 990e021..f12baf3 100755 --- a/config/app.php +++ b/config/app.php @@ -52,7 +52,8 @@ return [ | */ 'mode' => env('APP_MODE', 'live'), - 'url' => env('APP_URL', 'https://mivita'), + 'url' => env('APP_URL', 'https://mivita.care'), + 'url_crm' => env('APP_URL_CRM', 'https://my.mivita.care'), 'domain' => env('APP_DOMAIN', 'mivita'), 'tld_care' => env('APP_TLD_CARE', '.local'), 'tld_shop' => env('APP_TLD_SHOP', '.lshop'), @@ -60,7 +61,7 @@ return [ 'pre_url_main' => env('APP_URL_MAIN', ''), 'pre_url_crm' => env('APP_URL_CRM', 'my.'), 'pre_url_portal' => env('APP_URL_PORTAL', 'in.'), - + 'checkout_url' => env('APP_URL_CHECKOUT', 'checkout.'), 'contact_mail' => env('APP_CONTACT_MAIL', 'kontakt@mivita.care'), 'checkout_mail' => env('APP_CHECKOUT_MAIL', 'no-reply@mivita.care'), @@ -185,6 +186,10 @@ return [ */ Laravel\Tinker\TinkerServiceProvider::class, + /* + * DHL Package Service Provider... + */ + Acme\Dhl\DhlServiceProvider::class, /* * Application Service Providers... @@ -195,11 +200,11 @@ return [ App\Providers\EventServiceProvider::class, App\Providers\DomainServiceProvider::class, App\Providers\RouteServiceProvider::class, - Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, Jenssegers\Date\DateServiceProvider::class, Maatwebsite\Excel\ExcelServiceProvider::class, Yajra\DataTables\DataTablesServiceProvider::class, App\Providers\YardServiceProvider::class, + Alban\LaravelCollectiveSpatieHtmlParser\ServiceProvider::class, ], /* @@ -257,6 +262,9 @@ return [ 'Excel' => Maatwebsite\Excel\Facades\Excel::class, 'DataTables' => Yajra\DataTables\Facades\DataTables::class, 'Yard' => App\Services\Facade\Yard::class, + 'DHL' => Acme\Dhl\Facades\DHL::class, + 'Form' => Alban\LaravelCollectiveSpatieHtmlParser\FormFacade::class, + 'Html' => Alban\LaravelCollectiveSpatieHtmlParser\HtmlFacade::class, ], ]; diff --git a/config/dhl.php b/config/dhl.php new file mode 100644 index 0000000..89a7a87 --- /dev/null +++ b/config/dhl.php @@ -0,0 +1,108 @@ + env('DHL_BASE_URL', 'https://api-eu.dhl.com'), + 'api_key' => env('DHL_API_KEY'), + 'username' => env('DHL_USERNAME'), + 'password' => env('DHL_PASSWORD'), + 'billing_number' => env('DHL_BILLING_NUMBER'), + + /* + |-------------------------------------------------------------------------- + | Default Product Settings + |-------------------------------------------------------------------------- + */ + 'default_product' => env('DHL_PRODUCT', 'V01PAK'), + 'label_format' => env('DHL_LABEL_FORMAT', 'PDF'), + 'print_format' => env('DHL_PRINT_FORMAT', 'A4'), + 'retoure_print_format' => env('DHL_RETOURE_PRINT_FORMAT', 'A4'), + 'profile' => env('DHL_PROFILE', 'STANDARD_GRUPPENPROFIL'), + + /* + |-------------------------------------------------------------------------- + | Queue Settings + |-------------------------------------------------------------------------- + */ + 'use_queue' => env('DHL_USE_QUEUE', false), + + /* + |-------------------------------------------------------------------------- + | Webhook Configuration + |-------------------------------------------------------------------------- + */ + 'webhook' => [ + 'enabled' => env('DHL_WEBHOOK_ENABLED', false), + 'secret' => env('DHL_WEBHOOK_SECRET'), + 'route' => env('DHL_WEBHOOK_ROUTE', 'dhl/webhooks/tracking') + ], + + /* + |-------------------------------------------------------------------------- + | Sender Address + |-------------------------------------------------------------------------- + */ + 'sender' => [ + 'company' => env('DHL_SENDER_COMPANY', 'mivita care gmbh'), + 'name' => env('DHL_SENDER_NAME', ''), + 'street' => env('DHL_SENDER_STREET', 'Leinfeld'), + 'houseNumber' => env('DHL_SENDER_STREET_NUMBER', '2'), + 'postalCode' => env('DHL_SENDER_POSTAL_CODE', '87755'), + 'city' => env('DHL_SENDER_CITY', 'Kirchhaslach'), + 'country' => env('DHL_SENDER_COUNTRY', 'DE'), + 'email' => env('DHL_SENDER_EMAIL', 'versand@mivita.care'), + 'phone' => env('DHL_SENDER_PHONE', '+49 123 456789'), + ], + + /* + |-------------------------------------------------------------------------- + | Account Numbers + |-------------------------------------------------------------------------- + */ + 'account_numbers' => [ + 'default' => env('DHL_ACCOUNT_NUMBER_DEFAULT', '63144073550101'), + 'V01PAK' => env('DHL_ACCOUNT_NUMBER_V01PAK', '63144073550101'), // DHL Paket National + 'V62WP' => env('DHL_ACCOUNT_NUMBER_V62WP', '63144073556201'), // Warenpost National + 'V53PAK' => env('DHL_ACCOUNT_NUMBER_V53PAK', '63144073555301'), // DHL Paket International + 'V07PAK' => env('DHL_ACCOUNT_NUMBER_V07PAK', '63144073550701'), // DHL Retoure Online + ], + + /* + |-------------------------------------------------------------------------- + | Logging Settings + |-------------------------------------------------------------------------- + */ + 'logging' => [ + 'enabled' => env('DHL_LOGGING_ENABLED', true), + 'level' => env('DHL_LOGGING_LEVEL', 'info'), + 'channel' => env('DHL_LOGGING_CHANNEL', 'single'), + ], + + /* + |-------------------------------------------------------------------------- + | Legacy Settings (for compatibility) + |-------------------------------------------------------------------------- + */ + 'legacy' => [ + 'api_type' => env('DHL_API_TYPE', 'developer'), + 'api_secret' => env('DHL_API_SECRET'), + 'sandbox' => env('DHL_SANDBOX', true), + 'test_mode' => env('DHL_TEST_MODE', true), + ] +]; diff --git a/config/profanity.php b/config/profanity.php index ddeb5df..a924108 100644 --- a/config/profanity.php +++ b/config/profanity.php @@ -133,5 +133,16 @@ return [ 'mivita', 'shop', 'myaloe', + 'my', + 'in', + 'portal', + 'checkout', + 'login', + 'logout', + 'register', + 'forgot', + 'reset', + 'verify', + 'confirm', ] ]; diff --git a/config/queue.php b/config/queue.php index 391304f..cc52b0a 100755 --- a/config/queue.php +++ b/config/queue.php @@ -13,7 +13,7 @@ return [ | */ - 'default' => env('QUEUE_DRIVER', 'sync'), + 'default' => env('QUEUE_DRIVER', 'database'), /* |-------------------------------------------------------------------------- diff --git a/database/migrations/2025_08_19_155158_create_dhl_shipments_table.php b/database/migrations/2025_08_19_155158_create_dhl_shipments_table.php new file mode 100644 index 0000000..3ff17e4 --- /dev/null +++ b/database/migrations/2025_08_19_155158_create_dhl_shipments_table.php @@ -0,0 +1,109 @@ +id(); + + // Relationship zu ShoppingOrder + $table->unsignedInteger('shopping_order_id'); // int(10) unsigned to match shopping_orders.id + $table->foreign('shopping_order_id')->references('id')->on('shopping_orders')->onDelete('cascade'); + + // DHL Shipment Daten + $table->string('shipment_number')->nullable(); // DHL Sendungsnummer + $table->string('tracking_number')->nullable(); // DHL Tracking-Nummer (kann identisch sein) + + // Sendungstyp: 'outbound' (regulär) oder 'return' (Retoure) + $table->enum('type', ['outbound', 'return'])->default('outbound'); + + // Für Retouren: Verweis auf ursprüngliche Sendung + $table->unsignedBigInteger('related_shipment_id')->nullable(); // id() uses bigInteger + $table->foreign('related_shipment_id')->references('id')->on('dhl_shipments')->onDelete('set null'); + + // Paketdaten + $table->decimal('weight', 8, 2)->default(1.0); // in kg + $table->integer('length')->nullable(); // in cm + $table->integer('width')->nullable(); // in cm + $table->integer('height')->nullable(); // in cm + + // DHL Service-Optionen + $table->string('product_code')->default('V01PAK'); // V01PAK = DHL Paket + $table->json('services')->nullable(); // Zusätzliche Services (Premium, etc.) + + // Labels und Dokumente + $table->string('label_path')->nullable(); // Pfad zum generierten Label + $table->string('label_format')->default('PDF'); // PDF oder ZPL + $table->boolean('label_printed')->default(false); + + // Status-Management + $table->enum('status', [ + 'created', // Sendung erstellt, noch nicht bei DHL + 'submitted', // An DHL übertragen + 'in_transit', // Unterwegs + 'delivered', // Zugestellt + 'returned', // Zurückgeschickt + 'cancelled', // Storniert + 'failed' // Fehler bei Erstellung/Übertragung + ])->default('created'); + + // Tracking-Status (von DHL API) + $table->string('tracking_status')->nullable(); + $table->text('tracking_details')->nullable(); // JSON für detaillierte Tracking-Info + $table->timestamp('last_tracked_at')->nullable(); + + // Empfängeradresse (Kopie für Archivierung) + $table->string('recipient_name'); + $table->string('recipient_company')->nullable(); + $table->string('recipient_street'); + $table->string('recipient_street_number'); + $table->string('recipient_postal_code'); + $table->string('recipient_city'); + $table->string('recipient_state')->nullable(); + $table->string('recipient_country', 2)->default('DE'); + $table->string('recipient_email')->nullable(); + $table->string('recipient_phone')->nullable(); + + // API Response Data (für Debugging und Audit) + $table->json('api_request_data')->nullable(); // Gesendete Daten + $table->json('api_response_data')->nullable(); // Empfangene Antwort + $table->text('api_errors')->nullable(); // Fehlermeldungen + + // Kosten und Abrechnung + $table->decimal('shipping_cost', 10, 2)->nullable(); // Versandkosten + $table->string('currency', 3)->default('EUR'); + + // Zusätzliche Metadaten + $table->text('notes')->nullable(); // Interne Notizen + $table->json('metadata')->nullable(); // Flexible zusätzliche Daten + + // Timestamps + $table->timestamp('shipped_at')->nullable(); // Wann wurde versendet + $table->timestamp('delivered_at')->nullable(); // Wann zugestellt + $table->timestamps(); + + // Indizes für Performance + $table->index('shopping_order_id'); + $table->index('shipment_number'); + $table->index('tracking_number'); + $table->index(['type', 'status']); + $table->index('created_at'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('dhl_shipments'); + } +}; diff --git a/database/migrations/2025_08_22_172138_add_routing_code_to_dhl_package_shipments_table.php b/database/migrations/2025_08_22_172138_add_routing_code_to_dhl_package_shipments_table.php new file mode 100644 index 0000000..c1ee8bf --- /dev/null +++ b/database/migrations/2025_08_22_172138_add_routing_code_to_dhl_package_shipments_table.php @@ -0,0 +1,28 @@ +string('routing_code')->nullable()->after('dhl_shipment_no'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('dhl_package_shipments', function (Blueprint $table) { + $table->dropColumn('routing_code'); + }); + } +}; diff --git a/dev/dhl-modul/AKTUALISIERUNG-PAKET-ANSATZ.md b/dev/dhl-modul/AKTUALISIERUNG-PAKET-ANSATZ.md new file mode 100644 index 0000000..6f08c4a --- /dev/null +++ b/dev/dhl-modul/AKTUALISIERUNG-PAKET-ANSATZ.md @@ -0,0 +1,151 @@ +# DHL Modul - Aktualisierung auf Paket-Ansatz + +## Überarbeitung: Von SDK zu eigenständigem Laravel-Paket + +**Datum**: $(date '+%Y-%m-%d') +**Grund**: Das christoph-schaeffer/dhl-business-shipping SDK ist veraltet und macht Probleme mit DHL-Login + +## Neue Architektur: packages/acme-laravel-dhl + +### ✅ Durchgeführte Überarbeitungen + +#### 1. Code Refactoring für bessere Lesbarkeit +- **Services vollständig überarbeitet**: + - `ShippingService` - Versandlabel-Erstellung mit klarer Struktur + - `TrackingService` - Tracking-Status mit Bulk-Updates + - `ReturnsService` - Retourenlabel-Management + - `DhlClient` - HTTP-Client mit umfassendem Error-Handling + +- **Verbesserungen**: + - Vollständige PHPDoc-Dokumentation + - Typisierung aller Parameter und Rückgabewerte + - Private Methoden für bessere Code-Organisation + - Validierung aller Eingabedaten + - Exception-Handling mit aussagekräftigen Fehlermeldungen + +#### 2. Datenbankschema an optimierten Plan angepasst +- **Vereinfachtes Schema** entsprechend PLAN-OPTIMIERT.md: + - Eine zentrale `dhl_shipments` Tabelle + - `type` Spalte für Outbound/Return-Unterscheidung + - `related_shipment_id` für Retour-Verknüpfung + - Tracking-Status direkt in Haupttabelle + +- **Migration erstellt**: + - `/database/migrations/2025_01_01_000000_create_dhl_shipments_table.php` + - `/database/migrations/2025_01_01_000200_create_dhl_tracking_events_table.php` + +- **Neue Models**: + - `DhlShipment` - Hauptmodel mit Relationships und Scopes + - `DhlTrackingEvent` - Tracking-Events für Phase 2 + +#### 3. Error Handling und Validation implementiert +- **DhlClient mit robustem Error-Handling**: + - HTTP-Status-Code spezifische Exceptions + - Retry-Mechanismus (3 Versuche) + - Timeout-Handling (30 Sekunden) + - User-Agent für API-Identifikation + +- **Service-Validierung**: + - Eingabedaten-Validierung vor API-Aufrufen + - Konfigurationsprüfung (Billing Number, etc.) + - Aussagekräftige Exception-Messages + +### 🗂️ Neue Paket-Struktur + +``` +packages/acme-laravel-dhl/ +├── composer.json # Laravel-Paket Konfiguration +├── config/dhl.php # DHL API Konfiguration +├── database/migrations/ # Datenbank-Struktur +│ ├── 2025_01_01_000000_create_dhl_shipments_table.php +│ └── 2025_01_01_000200_create_dhl_tracking_events_table.php +├── src/ +│ ├── DhlServiceProvider.php # Laravel Service Provider +│ ├── DhlManager.php # Hauptmanager-Klasse +│ ├── Facades/DHL.php # Laravel Facade +│ ├── Models/ +│ │ ├── DhlShipment.php # Zentrales Shipment-Model +│ │ └── DhlTrackingEvent.php # Tracking-Events +│ ├── Services/ +│ │ ├── ShippingService.php # Versandlabel-Service +│ │ ├── TrackingService.php # Tracking-Service +│ │ └── ReturnsService.php # Retour-Service +│ ├── Support/ +│ │ └── DhlClient.php # HTTP-API-Client +│ └── Http/Controllers/ # Für spätere Web-Integration +└── routes/api.php # API-Routen für Webhooks +``` + +### 📋 Aktualisierter Implementierungsplan + +#### Phase 1: Paket-Integration (2 Schritte) +1. **Paket-Registrierung im Hauptprojekt** + - composer.json Repositories-Eintrag + - Service Provider Registration + - Konfiguration publizieren + +2. **API-Credentials Setup** + - .env Variablen konfigurieren + - Test-Verbindung zur DHL API + +#### Phase 2: Admin-Integration (4 Schritte) +3. **Admin-Controller erstellen** + - DhlShipmentController für Backend + - Integration in bestehende Order-Verwaltung + +4. **Blade-Views für DHL Cockpit** + - Sendungsübersicht mit DataTables + - Label-Download und -Druck + +5. **Queue-Jobs für Async-Processing** + - CreateShipmentJob + - CancelShipmentJob + - CreateReturnLabelJob + +6. **Tracking-Automation** + - UpdateTrackingStatusJob + - Artisan Command für Scheduler + +#### Phase 3: Testing & Finalisierung (2 Schritte) +7. **Unit/Feature Tests** + - Service-Tests mit Mocked API + - Controller-Tests + - Database-Tests + +8. **Documentation & Polish** + - Benutzerhandbuch + - API-Dokumentation + - Performance-Optimierung + +### ⚠️ Breaking Changes zum alten Ansatz + +1. **Namespace geändert**: `App\Services\DhlApiService` → `Acme\Dhl\Services\*` +2. **Datenbank**: `dhl_shipments` statt mehrerer separater Tabellen +3. **Model**: `DhlShipment` statt `DhlShipment` (ähnlich, aber neue Struktur) +4. **API-Integration**: Direkter HTTP-Client statt SDK-Wrapper + +### 🔧 Migration vom alten System + +Falls bereits bestehende DHL-Integration vorhanden: +1. Bestehende Daten nach `dhl_shipments` migrieren +2. Controller-Aufrufe auf neue Services umstellen +3. View-Integrationen aktualisieren + +### 📦 Vorteile des Paket-Ansatzes + +- **Unabhängigkeit**: Keine Abhängigkeit von veralteten SDKs +- **Wartbarkeit**: Sauberer, dokumentierter Code +- **Flexibilität**: Direkter API-Zugang ermöglicht alle DHL-Features +- **Testbarkeit**: Vollständig mockbare Services +- **Laravel-Integration**: Native Laravel-Patterns und Features +- **Wiederverwendbarkeit**: Als eigenständiges Paket nutzbar + +### 🎯 Nächste Schritte + +1. **Paket im Hauptprojekt registrieren** +2. **DHL API-Credentials konfigurieren** +3. **Erste Testlabel erstellen** +4. **Admin-Interface implementieren** +5. **Bestehende Order-Integration anpassen** + +Das überarbeitete System ist nun deutlich stabiler, wartbarer und zukunftssicher. \ No newline at end of file diff --git a/dev/dhl-modul/OPTIMIERUNGEN.md b/dev/dhl-modul/OPTIMIERUNGEN.md new file mode 100644 index 0000000..22a56f8 --- /dev/null +++ b/dev/dhl-modul/OPTIMIERUNGEN.md @@ -0,0 +1,33 @@ +# Optimierungen am DHL-Modul + +## Code-Verbesserungen + +- Erweiterte Logging in allen Services und DhlClient. +- Robuste Payload-Extraktion mit data_get(). +- Exponential Backoff für Rate-Limits in DhlClient. + +## Behebene Fehler + +- Namenskonflikt: Shipment zu DhlShipment geändert. +- Erweiterte Status-Updates in Webhook und TrackingService (mehr Codes gemappt). +- Validierung in cancelLabel: Prüft Existenz und canCancel(). + +## Optionale Queues + +- Neu in config: 'use_queue' => false (Standard: synchron). +- In Services: Wenn true, dispatch Job (z.B. SyncTrackingJob); sonst synchron. +- Für Entwicklung/Erstrollout: Synchron. Bei höherer Last: Aktivieren für Asynchronität. + +## Weitere Dokumentation + +- Aktualisierte README.md und PAKET-INSTALLATION.md mit Queue-Infos. + +## Weitere Fixes + +- DhlClient: Sleep zu usleep geändert mit Cap auf 10 Sekunden. +- ShippingService: HouseNumber required for DHL!, Retry für Storage::put. +- TrackingService: Transaction um firstOrCreate. +- Migration: Kommentar zur anonymen Klasse. +- Status-Mappings: Zentral in DhlShipment, referenziert in TrackingService. +- DhlManager: Descriptive Variablen, Typ-Hints. +- Storage-Retry: 3 Versuche mit Logging in ShippingService. diff --git a/dev/dhl-modul/PAKET-INSTALLATION.md b/dev/dhl-modul/PAKET-INSTALLATION.md new file mode 100644 index 0000000..dcad577 --- /dev/null +++ b/dev/dhl-modul/PAKET-INSTALLATION.md @@ -0,0 +1,257 @@ +# DHL Paket Installation & Setup + +## 1. Paket im Hauptprojekt registrieren + +### composer.json erweitern + +```json +{ + "repositories": [ + { + "type": "path", + "url": "./packages/acme-laravel-dhl" + } + ], + "require": { + "acme/laravel-dhl": "*" + } +} +``` + +### Installation + +```bash +composer update acme/laravel-dhl +``` + +## 2. Service Provider registrieren + +### config/app.php (falls Auto-Discovery nicht funktioniert) + +```php +'providers' => [ + // ... + Acme\Dhl\DhlServiceProvider::class, +], + +'aliases' => [ + // ... + 'DHL' => Acme\Dhl\Facades\DHL::class, +], +``` + +## 3. Konfiguration publizieren + +```bash +# Konfigurationsdatei publizieren +php artisan vendor:publish --provider="Acme\Dhl\DhlServiceProvider" --tag="config" + +# Migrations ausführen +php artisan migrate +``` + +## 4. Umgebungsvariablen konfigurieren + +### .env erweitern + +```env +# DHL API Configuration +DHL_BASE_URL=https://api-eu.dhl.com +DHL_API_KEY=your_api_key_here +DHL_USERNAME=your_username +DHL_PASSWORD=your_password +DHL_BILLING_NUMBER=your_billing_number + +# DHL Default Settings +DHL_PRODUCT=V01PAK +DHL_LABEL_FORMAT=PDF +DHL_PRINT_FORMAT=A4 +DHL_RETOURE_PRINT_FORMAT=A4 +DHL_PROFILE=STANDARD_GRUPPENPROFIL + +# DHL Webhook (optional) +DHL_WEBHOOK_ENABLED=false +DHL_WEBHOOK_SECRET=your_webhook_secret +DHL_WEBHOOK_ROUTE=dhl/webhooks/tracking + +# DHL Queue Settings (optional) +DHL_USE_QUEUE=false +``` + +## 5. Test-Verbindung prüfen + +### Artisan Command (zu erstellen) + +```bash +php artisan dhl:test-connection +``` + +### Oder via Tinker + +```php +php artisan tinker + +// Service-Container Test +app(Acme\Dhl\Services\ShippingService::class); + +// Model Test +use Acme\Dhl\Models\DhlShipment; +DhlShipment::query()->count(); + +// API-Test (vereinfacht) +// $client = app(Acme\Dhl\Support\DhlClient::class); +// $client->testConnection(); +``` + +## 6. Integration in bestehende Models + +### ShoppingOrder Model erweitern + +```php +use Acme\Dhl\Models\DhlShipment; + +class ShoppingOrder extends Model +{ + /** + * DHL Sendungen für diese Bestellung + */ + public function dhlShipments(): HasMany + { + return $this->hasMany(DhlShipment::class, 'order_id'); + } + + /** + * Ausgehende DHL Sendungen + */ + public function dhlOutboundShipments(): HasMany + { + return $this->dhlShipments()->outbound(); + } + + /** + * DHL Retouren + */ + public function dhlReturns(): HasMany + { + return $this->dhlShipments()->returns(); + } + + /** + * Hat DHL Sendungen + */ + public function hasDhlShipments(): bool + { + return $this->dhlShipments()->exists(); + } + + /** + * Neueste DHL Sendung + */ + public function latestDhlShipment(): ?DhlShipment + { + return $this->dhlShipments() + ->latest() + ->first(); + } +} +``` + +## 7. Verwendung in Controllern + +### Beispiel Controller-Integration + +```php +use Acme\Dhl\Services\ShippingService; +use Acme\Dhl\Services\TrackingService; +use Acme\Dhl\Services\ReturnsService; + +class OrderController extends Controller +{ + public function createDhlLabel( + ShoppingOrder $order, + ShippingService $shippingService + ) { + try { + $result = $shippingService->createLabel([ + 'order_id' => $order->id, + 'shipper' => [ + 'name' => 'Ihre Firma', + 'street' => 'Ihre Straße 123', + 'postalCode' => '12345', + 'city' => 'Ihre Stadt', + 'country' => 'DE' + ], + 'consignee' => [ + 'name' => $order->shipping_name, + 'street' => $order->shipping_street, + 'postalCode' => $order->shipping_zip, + 'city' => $order->shipping_city, + 'country' => $order->shipping_country + ], + 'weight_kg' => $order->total_weight ?? 1.0 + ]); + + return response()->json([ + 'success' => true, + 'shipment_number' => $result['shipmentNumber'], + 'label_path' => $result['label_path'] + ]); + + } catch (Exception $e) { + return response()->json([ + 'success' => false, + 'error' => $e->getMessage() + ], 422); + } + } +} +``` + +## 8. Facade-Verwendung + +```php +use DHL; + +// Label erstellen +$result = DHL::createLabel($orderData); + +// Tracking-Status abrufen +$status = DHL::getTrackingStatus('1234567890'); + +// Retourenlabel erstellen +$return = DHL::createReturn($returnData); +``` + +## Troubleshooting + +### Composer-Probleme + +```bash +# Cache leeren +composer dump-autoload +php artisan config:clear +php artisan cache:clear + +# Paket neu installieren +composer remove acme/laravel-dhl +composer install +``` + +### Migration-Probleme + +```bash +# Migrations-Status prüfen +php artisan migrate:status + +# Rollback und neu migrieren +php artisan migrate:rollback --step=2 +php artisan migrate +``` + +### Service-Provider nicht gefunden + +```bash +# Auto-Discovery cache leeren +composer dump-autoload +php artisan package:discover +``` diff --git a/dev/dhl-modul/PLAN-OPTIMIERT.md b/dev/dhl-modul/PLAN-OPTIMIERT.md new file mode 100644 index 0000000..84a6746 --- /dev/null +++ b/dev/dhl-modul/PLAN-OPTIMIERT.md @@ -0,0 +1,195 @@ +# DHL Versandmodul - Optimierter Programmierplan (MVP) + +## Bewertung der Optimierungsvorschläge + +### ✅ Akzeptierte Optimierungen: + +1. **Vereinfachtes Datenbank-Schema**: Eine `dhl_shipments` Tabelle mit `type` Spalte für Outbound/Return +2. **Tracking vereinfacht**: Aktueller Status in Haupttabelle, separate Events-Tabelle für Phase 2 +3. **Konsolidierte Service-Klasse**: Ein `DhlApiService` statt drei separate Services +4. **Advanced Features verschoben**: Excel Export, Webhooks, Notifications als Phase 2 + +### 🔄 Kleine Anpassungen am Vorschlag: + +- Jobs bleiben getrennt (CreateShipment, Cancel, ReturnLabel) für bessere Queue-Verwaltung +- Tracking-Job für Scheduler-Integration beibehalten +- Admin-Navigation früher implementieren für bessere UX + +--- + +# Finaler Optimierter Plan - DHL Modul (MVP) + +## Phase 1: Fundament und Kernlogik (8 Schritte) + +### 1. Setup & Dependencies ✅ AKTUALISIERT + +- ~~Composer: `dhl-sdk-api-php/dhl-sdk-bcs` installieren~~ (veraltet, Probleme mit DHL-Login) +- **NEUER ANSATZ**: Eigenständiges Laravel-Paket `packages/acme-laravel-dhl` +- Direkte DHL API-Integration über HTTP-Client (Guzzle) +- Queue-System aktivieren (database driver) + +### 2. Konfiguration + +- `config/dhl.php` erstellen (API-Credentials, Absenderadressen) +- Environment-Variablen in `.env` definieren + +### 3. Datenbank & Model ✅ ABGESCHLOSSEN + +- Migration für `dhl_shipments` Tabelle: ✅ + - order_id, dhl_shipment_no, type (outbound/return) + - related_shipment_id (für Retouren), weight_kg, label_path + - status, tracking_status, last_tracked_at + - api_response_data (JSON), created_at, updated_at +- `DhlShipment` Model mit vollständigen Relationships ✅ +- `DhlTrackingEvent` Model für detaillierte Events ✅ + +### 4. Zentraler Service ✅ ABGESCHLOSSEN + +- ~~`DhlApiService` als Wrapper um DHL SDK~~ (SDK-Ansatz verworfen) +- **NEUE STRUKTUR**: + - `DhlClient` - HTTP-Client für API-Kommunikation ✅ + - `ShippingService` - Label-Erstellung und -Verwaltung ✅ + - `TrackingService` - Status-Tracking und -Updates ✅ + - `ReturnsService` - Retourenlabel-Management ✅ +- Vollständige Error-Behandlung und Validation ✅ + +### 5. Queue Jobs + +- `CreateShipmentJob` für asynchrone Label-Erstellung +- `CancelShipmentJob` für Stornierungen +- `CreateReturnLabelJob` für Retourenetiketten + +### 6. Controller Foundation + +- `DhlShipmentController` mit store/show/cancel Methoden +- Basis-Routen in `routes/web.php` + +### 7. Error Handling & Logging ✅ ABGESCHLOSSEN + +- Exception-Handling für DHL API-Fehler ✅ + - HTTP-Status spezifische Exceptions + - Retry-Mechanismus (3x mit 300ms Delay) + - Timeout-Handling (30s) +- Aussagekräftige Fehlermeldungen ✅ +- Validation aller Eingabedaten ✅ + +### 8. Basic Testing + +- Unit-Tests für `DhlApiService` +- Feature-Tests für Controller + +--- + +## Phase 2: Admin-Oberfläche (6 Schritte) + +### 9. Navigation & Layout + +- Admin-Menüpunkt "DHL Cockpit" +- Bootstrap 5 Layout für DHL-Views + +### 10. Cockpit (Zentrale Übersicht) + +- `index()` Methode im Controller +- `cockpit.blade.php` View mit DataTable +- Filter: Datum, Status, Sendungstyp + +### 11. Einzelaktionen + +- "Label herunterladen" Button +- "Sendung stornieren" Button mit Confirmation + +### 12. Batch-Operationen + +- Mehrere Sendungen auswählen +- Stapeldruck von Labels +- Massen-Stornierung + +### 13. Integration in Bestellverwaltung + +- DHL-Button in Bestelldetails +- Direkter Zugang zu Sendungsinformationen + +### 14. Status-Anzeige & Feedback + +- Alert-System für Erfolgsmeldungen/Fehler +- Real-time Status-Updates + +--- + +## Phase 3: Tracking und Retouren (5 Schritte) + +### 15. Tracking-System + +- `UpdateTrackingStatusJob` für automatische Updates +- Artisan Command `dhl:update-tracking` +- Scheduler-Integration + +### 16. Tracking-Anzeige + +- Tracking-Status im Cockpit +- Tracking-Details in Sendungsansicht +- Timeline-View für Sendungsverlauf + +### 17. Retouren-Management + +- Button "Retourenlabel erstellen" +- Retouren-Workflow im Admin +- Verknüpfung Original-Sendung ↔ Retoure + +### 18. Customer-Interface (Basic) + +- Einfache öffentliche Tracking-Seite +- Tracking-Nummer-Eingabe + +### 19. Notifications (Basic) + +- E-Mail bei Sendungserstellung +- E-Mail bei wichtigen Status-Änderungen + +--- + +## Phase 4: Finalisierung & Polish (4 Schritte) + +### 20. Performance-Optimierung + +- Eager Loading für Relationships +- Caching für häufige API-Abfragen +- Database-Indexe + +### 21. Comprehensive Testing + +- Integration-Tests für DHL SDK +- Browser-Tests für Admin-Interface +- Error-Scenario Tests + +### 22. Documentation + +- Benutzerhandbuch für Shop-Mitarbeiter +- Code-Dokumentation +- API-Dokumentation + +### 23. Production-Ready Features + +- Environment-spezifische Konfiguration +- Monitoring & Alerting +- Backup & Recovery-Konzept + +--- + +## Optionale Erweiterungen (Phase 2 des Projekts) + +### Advanced Features (später): + +- Separate `dhl_tracking_events` Tabelle für detaillierte Historie +- Excel/CSV Export von Sendungsdaten +- Webhook-Integration für automatische Updates +- SMS-Notifications +- Erweiterte Reporting-Features +- Multi-Tenant Support +- API für externe Systeme + +--- + +**Total: 23 konkrete Schritte** für ein vollständig funktionsfähiges DHL Versandmodul + +Dieser Plan fokussiert sich auf die Kernfunktionalitäten und liefert schnell ein nutzbares Ergebnis. diff --git a/dev/dhl-modul/README.md b/dev/dhl-modul/README.md new file mode 100644 index 0000000..3fcd147 --- /dev/null +++ b/dev/dhl-modul/README.md @@ -0,0 +1,120 @@ +# DHL Versandmodul - Entwicklungsdokumentation + +## Projektübersicht + +Laravel 11 DHL Versandmodul als eigenständiges Paket mit direkter DHL API-Integration. + +## Architektur-Überarbeitungen ✅ + +**August 2025**: Wechsel von SDK-Ansatz zu eigenständigem Laravel-Paket + +### Grund für Überarbeitung + +- `christoph-schaeffer/dhl-business-shipping` SDK ist veraltet +- Probleme mit DHL-Login im alten SDK +- Bessere Kontrolle durch direkte API-Integration + +### Neue Architektur + +- **Eigenständiges Laravel-Paket**: `packages/acme-laravel-dhl` +- **Direkte DHL API**: HTTP-Client statt SDK-Wrapper +- **Vereinfachtes Schema**: Eine `dhl_package_shipments` Tabelle für Outbound/Returns +- **Moderne Laravel-Patterns**: Service Provider, Facades, Models + +## Aktueller Entwicklungsstand ✅ + +### Phase 1: Paket-Grundlagen (ABGESCHLOSSEN) + +- ✅ **Code Refactoring**: Services mit besserer Lesbarkeit +- ✅ **Datenbankschema**: Vereinfacht nach optimiertem Plan +- ✅ **Error Handling**: Umfassende Exception-Behandlung +- ✅ **Models**: DhlShipment + DhlTrackingEvent, inkl. Relationen +- ✅ **Services**: ShippingService, TrackingService, ReturnsService +- ✅ **HTTP-Client**: DhlClient mit Retry-Mechanismus + +### Phase 2: Integration (ABGESCHLOSSEN) + +- ✅ **Paket-Registration**: In Hauptprojekt eingebunden +- ✅ **API-Setup**: Credentials und Konfiguration +- ✅ **Admin-Controller**: Backend-Integration +- ✅ **Queue-Jobs**: Asynchrone Verarbeitung + +### Phase 3: Admin-Interface (TEILWEISE ABGESCHLOSSEN) + +- ✅ **DHL Cockpit**: Sendungsübersicht mit serverseitigem DataTables +- ✅ **Label-Management**: Download und Druck +- [ ] **Order-Integration**: DHL-Buttons in Bestellverwaltung + +### Phase 4: Advanced Features (NÄCHSTE SCHRITTE) + +- [ ] **Tracking-Automation**: Scheduler-Integration +- [ ] **Notifications**: E-Mail-Benachrichtigungen +- [ ] **Testing**: Unit/Feature Tests +- [ ] **Documentation**: Benutzerhandbuch + +## Optimierungen und Änderungen (Letzte Updates) + +- **Performance-Boost im Cockpit**: Die Sendungsübersicht wurde auf serverseitiges DataTables umgestellt, was die Ladezeiten bei großen Datenmengen drastisch reduziert. +- **Erweiterte Druckformate**: Das Modul unterstützt jetzt die Konfiguration von `print_format` und `retoure_print_format` via `.env` oder Admin-Einstellungen. Diese werden als Query-Parameter an die DHL API übergeben, um das physische Layout der Etiketten (z.B. A4, 910-300-700) zu steuern. +- **Speicherung des Routing-Codes**: Der `routing_code` (Leitcode) von DHL wird nun aus der API-Antwort extrahiert und in der Datenbank in der Spalte `dhl_package_shipments.routing_code` gespeichert. +- **API-Antwort-Parsing verbessert**: Die Logik wurde angepasst, um die Sendungsdaten korrekt aus dem `items`-Array der API-Antwort zu extrahieren. +- **Code optimiert**: Verbesserte Error-Handling, Logging in Services und Client. +- **Fehler behoben**: Namenskonflikte (Shipment -> DhlShipment), erweiterte Status-Mappings, robuste Validierungen. +- **Optionale Queues**: Konfiguriert via 'use_queue' in `config/dhl.php`. Synchron in Entwicklung, asynchron bei hoher Last. + +## Dateistruktur + +### Aktuelle Dokumentation + +- `AKTUALISIERUNG-PAKET-ANSATZ.md` - Überarbeitungsdetails +- `PLAN-OPTIMIERT.md` - Ursprünglicher Plan (aktualisiert) +- `PAKET-INSTALLATION.md` - Setup-Anleitung +- `OPTIMIERUNGEN.md` - Details zu früheren Optimierungen + +### Paket-Struktur + +``` +packages/acme-laravel-dhl/ +├── config/dhl.php # Konfiguration +├── database/migrations/ # Datenbankstruktur +├── src/ +│ ├── Services/ # Business Logic +│ ├── Models/ # Eloquent Models +│ ├── Support/ # HTTP Client +│ └── DhlServiceProvider.php # Laravel Integration +``` + +## Technische Highlights + +### Moderne Laravel-Integration + +- **PSR-4 Autoloading**: Sauberer Namespace +- **Service Provider**: Auto-Discovery Support +- **Facades**: `DHL::createLabel()` Syntax +- **Config Publishing**: `php artisan vendor:publish` + +### Robuste API-Integration + +- **Retry-Mechanismus**: 3 Versuche mit ansteigendem Delay +- **Timeout-Handling**: 30 Sekunden pro Request +- **HTTP-Status Mapping**: Spezifische Exceptions +- **Request/Response Logging**: Vollständige Nachverfolgung +- **Flexible API-Parameter**: Unterstützt Body-Payload und Query-Parameter bei POST-Requests. + +### Optimiertes Datenbankschema + +- **Eine Haupttabelle**: `dhl_package_shipments` für Outbound + Returns +- **Zusätzliche Felder**: `routing_code` für interne DHL-Logistik +- **Efficient Indexing**: Performance-optimiert +- **JSON-Felder**: Flexible API-Daten-Speicherung +- **Self-References**: Retour-Verknüpfungen + +## Nächste Schritte + +1. **Order-Integration** abschließen (Buttons in Bestelldetails). +2. **Tracking-Automatisierung** implementieren. +3. **Umfassende Tests** für die neuen Features schreiben. + +--- + +**Status**: Admin-Cockpit implementiert und performant, Kernfunktionalität stabil. diff --git a/dev/dhl-modul/SCHRITT-3-COMPLETED.md b/dev/dhl-modul/SCHRITT-3-COMPLETED.md new file mode 100644 index 0000000..dbc99cb --- /dev/null +++ b/dev/dhl-modul/SCHRITT-3-COMPLETED.md @@ -0,0 +1,136 @@ +# Schritt 3: Datenbank & Model - ABGESCHLOSSEN ✅ + +## Durchgeführte Arbeiten + +### 3.1 Migration für dhl_shipments Tabelle erstellt ✅ +- **File**: `database/migrations/2025_08_19_155158_create_dhl_shipments_table.php` +- **Status**: Erfolgreich migriert +- **Tabelle**: `dhl_shipments` + +#### Tabellenstruktur: +```sql +- id (bigint unsigned, Primary Key) +- shopping_order_id (int unsigned, Foreign Key -> shopping_orders.id) +- related_shipment_id (bigint unsigned, Self-Reference für Retouren) +- shipment_number (varchar, DHL Sendungsnummer) +- tracking_number (varchar, DHL Tracking-Nummer) +- type (enum: 'outbound', 'return') +- weight, length, width, height (Paketdaten) +- product_code (varchar, DHL Produktcode) +- services (json, Zusätzliche Services) +- label_path, label_format, label_printed (Label-Management) +- status (enum: created, submitted, in_transit, delivered, returned, cancelled, failed) +- tracking_status, tracking_details, last_tracked_at (Tracking) +- recipient_* (vollständige Empfängeradresse) +- api_request_data, api_response_data, api_errors (API Debugging) +- shipping_cost, currency (Kostenabrechnung) +- notes, metadata (Zusätzliche Daten) +- shipped_at, delivered_at (Timestamps) +- created_at, updated_at (Standard Laravel Timestamps) +``` + +#### Indizes: +- shopping_order_id (Performance) +- shipment_number (DHL Suche) +- tracking_number (Tracking-Suche) +- [type, status] (Kombiniert für Filter) +- created_at (Chronologische Sortierung) + +#### Foreign Key Constraints: +- shopping_order_id → shopping_orders.id (CASCADE DELETE) +- related_shipment_id → dhl_shipments.id (SET NULL) + +### 3.2 DhlShipment Model erstellt ✅ +- **File**: `app/Models/DhlShipment.php` +- **Status**: Vollständig implementiert +- **Namespace**: `App\Models\DhlShipment` + +#### Model Features: +- ✅ **Vollständige Eloquent Konfiguration** (fillable, casts, table) +- ✅ **Konstanten für Status und Typen** +- ✅ **Relationships**: belongsTo ShoppingOrder, self-reference für Retouren +- ✅ **Scopes**: outbound(), returns(), active(), trackable() +- ✅ **Helper Methods**: isOutbound(), isReturn(), canBeCancelled(), hasTracking(), hasLabel() +- ✅ **Accessors**: getRecipientAddressAttribute(), getDimensionsAttribute(), getStatusLabelAttribute() +- ✅ **Boot Method**: Setzt Default-Werte aus Konfiguration + +#### Konstanten: +```php +// Typen +const TYPE_OUTBOUND = 'outbound'; +const TYPE_RETURN = 'return'; + +// Status +const STATUS_CREATED = 'created'; +const STATUS_SUBMITTED = 'submitted'; +const STATUS_IN_TRANSIT = 'in_transit'; +const STATUS_DELIVERED = 'delivered'; +const STATUS_RETURNED = 'returned'; +const STATUS_CANCELLED = 'cancelled'; +const STATUS_FAILED = 'failed'; +``` + +### 3.3 ShoppingOrder Model erweitert ✅ +- **File**: `app/Models/ShoppingOrder.php` +- **Erweitert um DHL Relationships** + +#### Neue Methods: +```php +dhlShipments() // Alle DHL Sendungen +dhlOutboundShipments() // Nur Versand-Sendungen +dhlReturnShipments() // Nur Retour-Sendungen +hasDhlShipments(): bool // Prüft ob DHL Sendungen existieren +getLatestDhlShipment() // Neueste DHL Sendung +``` + +## Optimierungen implementiert + +### MVP-Fokus erreicht ✅ +- **Eine zentrale Tabelle** statt separater Tabellen für Retouren +- **Vollständige Empfängeradresse** in der Tabelle (kein Join nötig) +- **JSON-Felder** für flexible API-Daten und Services +- **Self-Reference** für Retouren über `related_shipment_id` + +### Performance optimiert ✅ +- **Strategische Indizes** für häufige Abfragen +- **Efficient Relationships** mit korrekten Foreign Key Typen +- **Scopes** für häufige Filter-Operationen +- **Casts** für automatische Datentyp-Konvertierung + +### Developer Experience ✅ +- **Ausführliche PHPDoc** für IDE-Support +- **Helper Methods** für Business Logic +- **Konstanten** statt Magic Strings +- **Accessors** für formatierte Ausgaben + +## Bugfixes durchgeführt ✅ +- **Foreign Key Type Mismatch** behoben: `unsignedInteger` statt `unsignedBigInteger` für shopping_order_id +- **Migration Konflikt** gelöst: Tabelle manuell entfernt und korrekt migriert + +## Verifikation ✅ +```bash +# Migration erfolgreich +php artisan migrate +# ✅ 2025_08_19_155158_create_dhl_shipments_table: 78.05ms DONE + +# Model lädt korrekt +php artisan tinker --execute="use App\Models\DhlShipment; echo DhlShipment::class;" +# ✅ App\Models\DhlShipment + +# Relationships funktionieren +php artisan tinker --execute="use App\Models\ShoppingOrder; ShoppingOrder::first()->dhlShipments()->count();" +# ✅ 0 (erwartungsgemäß, da noch keine DHL Shipments existieren) +``` + +## Nächste Schritte +- **Schritt 4**: Zentraler `DhlApiService` als Wrapper um das DHL SDK +- Integration der christoph-schaeffer/dhl-business-shipping Library +- Erste API-Verbindung und Test-Calls + +## Files Created/Modified +- ✅ `database/migrations/2025_08_19_155158_create_dhl_shipments_table.php` (neu) +- ✅ `app/Models/DhlShipment.php` (neu) +- ✅ `app/Models/ShoppingOrder.php` (erweitert) + +## Technical Debt: NONE ✨ +Alle Optimierungen erfolgreich implementiert, keine bekannten Probleme. \ No newline at end of file diff --git a/dev/dhl-modul/parcel-de-shipping-v2_2.yaml b/dev/dhl-modul/parcel-de-shipping-v2_2.yaml new file mode 100644 index 0000000..1656ffe --- /dev/null +++ b/dev/dhl-modul/parcel-de-shipping-v2_2.yaml @@ -0,0 +1,2273 @@ +openapi: 3.0.1 +info: + title: Parcel DE Shipping API (Post & Parcel Germany) + description: > + Note: This is the specification of the DPDHL Group Parcel DE Shipping API for Post & Parcel Germany. This REST web service allows business customers to create shipping labels on demand. + version: 2.1.13 +servers: + - url: https://api-eu.dhl.com/parcel/de/shipping/v2 + description: Productive Server + - url: https://api-sandbox.dhl.com/parcel/de/shipping/v2 + description: Sandbox Server + +tags: + - name: General + description: Get API version info + - name: Shipments and Labels + description: Order and retrieve shipment labels + - name: Manifests + description: Manifest shipments and retrieve daily manifest lists +paths: + /: + get: + tags: + - General + summary: Return API version + description: > + Returns the current version of the API as major.minor.patch. Furthermore, it will also return more details (semantic version number, revision, environment) of the API layer. + operationId: rootGet + security: [] # No auth + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceInformation' + '401': + $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' + /labels: + get: + tags: + - Shipments and Labels + summary: Download PDF document + description: > + Public download URL for shipment labels and documents. The URL is provided in the response of the POST /orders or GET /orders resources. The document is identified via the token query parameter. There is no additional authorization, the resource URL can be shared. Please protect the URL as needed. The call returns a PDF label. + operationId: getLabel + security: [] # No auth + parameters: + - name: token + in: query + description: Identifies PDF document and requested print settings for download. + required: true + schema: + type: string + responses: + '200': + description: Returns PDF file + content: + application/pdf: + schema: + type: string + format: binary + '404': + $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' + /manifests: + get: + tags: + - Manifests + summary: Retrieve daily manifest document + description: Return the manifest document for the specific date (abbreviated ISO8601 format YYYY-MM-DD). If no date is provided, the manifest for today will be returned. The manifest PDF document will list the shipments for your EKP, separated by billing numbers. Potentially, the document is large and response time will reflect this.
Additionally, the response contains a mapping of billing numbers to sheet numbers of the manifest and a mapping of shipment numbers to sheet numbers.
The call can be repeated as often as needed. Should a date be provided which is too old or lies within the future, HTTP 400 is returned. + operationId: getManifests + parameters: + - name: billingNumber + in: query + description: Customer billingNumber number. + required: false + schema: + type: string + - name: date + in: query + schema: + type: string + - name: includeDocs + in: query + description: |- + Legacy name **labelResponseType**. Shipping labels and further shipment documents can be: + * __include__: included as base64 encoded data in the response (default) + * __URL__: provided as URL reference. + Default is include the base64 encoded labels. + schema: + type: string + enum: + - include + - URL + default: include + - name: Accept-Language + in: header + description: Control the APIs response language via locale abbreviation. English (en-US) and german (de-DE) are supported. If not specified, the default is english. + schema: + type: string + example: de-DE + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/SingleManifestResponse' + application/problem+json: + schema: + $ref: '#/components/schemas/SingleManifestResponse' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' + post: + tags: + - Manifests + summary: Mark shipments as being ready for shipping + description: |- + Shipments are normally ''closed out'' at a fixed time of the day (such as 6 pm, configured by EKP/account) for the date provided as shipDate in the create call. +
This call allows forcing the closeout for sets of shipments earlier. This will also override the original shipDate. Afterwards, the shipment cannot be changed and the shipment labels cannot be queried anymore (however they may remain cached for limited duration). + Once a shipment has been closed, then calling closeout for the same shipment will result in a warning. The same warning will also be returned if the automatic closeout happened prior to the call. It is however possible to add new shipments, they will be manifested as well and be part of the day's manifest. +
Note on billing: The manifesting step has billing implications. Some products (Parcel International partially) are billed based on the shipment data available to DHL at the end of the day. All other products (including DHL Paket Standard) are billed based on production data. For more details, please contact your account representative. + + #### Request + It's changing the status of the shipment, so parameters are provided in the body or as query parameter. + * ''profile'' attribute (request body parameter) - defines the user group profile. A user group is permitted to specific billing numbers. Shipments are only closed out if they belong to a billing number that the user group profile is entitled to use. This attribute is mandatory. Please use the standard user group profile ''STANDARD_GRUPPENPROFIL'' if no dedicated user group profile is available. + * ''billingNumber'' attribute (query parameter) - defines the billing number for which shipments shall be closed out. If a billing number is set, then only the shipments of that billing number are closed out. In that case no list of specific shipment numbers needs to be passed. + * ''shipmentNumbers'' attribute (request body parameter) - lists the specific shipping numbers of the shipments that shall be closed out. + If all shipments shall be closed, the query parameter ''all'' needs to be set to ''true''. In that case neither a billing number nor a list of shipment numbers need to be passed in the request. + + #### Response + * Closing status for each shipment + operationId: manifestsPost + parameters: + - name: Accept-Language + in: header + description: Control the APIs response language via locale abbreviation. English (en-US) and german (de-DE) are supported. If not specified, the default is english. + schema: + type: string + example: de-DE + - name: all + in: query + description: Specify if all applicable shipments shall be marked as being ready for shipping. + schema: + type: boolean + default: false + requestBody: + description: Manifest request taking multiple input elements + content: + application/json: + schema: + $ref: '#/components/schemas/ShipmentManifestingRequest' + required: true + responses: + '207': + description: Response for manifesting request taking multiple input elements + content: + application/json: + schema: + $ref: '#/components/schemas/MultipleManifestResponse' + application/problem+json: + schema: + $ref: '#/components/schemas/MultipleManifestResponse' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' + /orders: + get: + tags: + - Shipments and Labels + summary: Retrieve shipment documents - labels and customs documents + description: |- + Returns documents for existing shipment(s). The call accepts multiple shipment numbers and will provide sets of documents for those. The **format (PDF,ZPL)** and **method of delivery (URL, encoded, data)** can be selected for **all** shipments and labels in that call. You cannot chose one format and delivery method for one label and different for another label within the same call. You can also specify if you want regular labels, return labels, cod labels, or customsDoc. Any combination is possible. + + The call returns for each shipment number the status indicator and the selected labels and documents. If a label type (for example a cod label) does not exist for a shipment, it will not be returned. This is not an error. If you were sending multiple shipments, you will get an HTTP 207 response (multistatus) with detailed status for each shipment. Other standard HTTP response codes (200, 400, 401, 429, 500) are possible as well. Labels can be either provided as part of the response (base64 encoded for PDF, text for ZPL) or via URL link for view and download (PDF). Note that the format settings per query parameters apply to the shipping label. Retoure label paper type can be specified separately since a different printer may be used here. If requesting labels to be returned as URL for separate download, the URLs provided can be shared. + operationId: getOrder + parameters: + - name: shipment + in: query + description: This parameter identifies shipments. The parameter can be used multiple times in one request to get the labels and/or documents for up to 30 shipments maximum. Only documents and label for shipments that are not yet closed can be retrieved. + required: true + schema: + type: array + items: + type: string + - name: Accept-Language + in: header + description: Control the APIs response language via locale abbreviation. English (en-US) and german (de-DE) are supported. If not specified, the default is english. + schema: + type: string + example: de-DE + - name: docFormat + in: query + description: '**Defines** the **printable** document format to be used for label and manifest documents.' + schema: + type: string + enum: + - ZPL2 + - PDF + default: PDF + - name: printFormat + in: query + description: "**Defines** the print medium for the shipping label. The different option vary from standard papersizes DIN A4 and DIN A5 to specific label print formats.\_\n\nSpecific laser print formats using DIN A5 blanks are:\n\n* 910-300-600(-oz) (105 x 205mm)\n* 910-300-300(-oz) (105 x 148mm)\n\nSpecific laser print formats **not** using a DIN A5 blank:\n\n* 910-300-610 (105 x 208mm)\n* 100x70mm\n\nSpecific thermal print formats:\n\n* 910-300-600 (103 x 199mm)\n* 910-300-400 (103 x 150mm)\n* 100x70mm\n\nPlease use the different formats as follows. If you do not set the parameter the settings of DHL costumer portal account will be used as default." + schema: + type: string + enum: + - A4 + - 910-300-600 + - 910-300-610 + - 910-300-700 + - 910-300-700-oz + - 910-300-710 + - 910-300-300 + - 910-300-300-oz + - 910-300-400 + - 910-300-410 + - 100x70mm + - name: retourePrintFormat + in: query + description: "**Defines** the print medium for the return shipping label. This parameter is only usable, if you do not use **combined printing**. The different option vary from standard papersizes DIN A4 and DIN A5 to specific label print formats.\_\n\nSpecific laser print formats using DIN A5 blanks are:\n\n* 910-300-600(-oz) (105 x 205mm)\n* 910-300-300(-oz) (105 x 148mm)\n\nSpecific laser print formats **not** using a DIN A5 blank:\n\n* 910-300-610 (105 x 208mm)\n* 100x70mm\n\nSpecific thermal print formats:\n\n* 910-300-600 (103 x 199mm)\n* 910-300-400 (103 x 150mm)\n* 100x70mm\n\nPlease use the different formats as follows. If you do not set the parameter the settings of DHL costumer portal account will be used as default." + schema: + type: string + enum: + - A4 + - 910-300-600 + - 910-300-610 + - 910-300-700 + - 910-300-700-oz + - 910-300-710 + - 910-300-300 + - 910-300-300-oz + - 910-300-400 + - 910-300-410 + - 100x70mm + - name: includeDocs + in: query + description: |- + Legacy name **labelResponseType**. Shipping labels and further shipment documents can be: + * __include__: included as base64 encoded data in the response (default) + * __URL__: provided as URL reference. + Default is include the base64 encoded labels. + schema: + type: string + enum: + - include + - URL + default: include + - name: combine + in: query + description: If set, label and return label for one shipment will be printed as single PDF document with possibly multiple pages. Else, those two labels come as separate documents. The option does not affect customs documents and COD labels. + schema: + type: boolean + default: true + responses: + '200': + description: Success response for requests with a single shipment. + content: + application/json: + schema: + $ref: '#/components/schemas/LabelDataResponse' + '207': + description: Response for requests taking multiple input elements + content: + application/json: + schema: + $ref: '#/components/schemas/LabelDataResponse' + application/problem+json: + schema: + $ref: '#/components/schemas/LabelDataResponse' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' + post: + tags: + - Shipments and Labels + summary: 'Create one or more shipments and their documents. (This is the primary call of the API.)' + description: |- + This request is used to create one or more shipments and return corresponding shipment tracking numbers, labels, and documentation. Up to 30 shipments can be created in a single call. + #### Request + The selected products and corresponding billing numbers, as well as the desired services and package details are required to create a shipment. Each shipment can have a dedicated shipper address. The example request body contains sample values for most services. + #### Response + The request will return shipment tracking numbers and the applicable labels for each shipment. If multiple shipments have been included, an HTTP 207 response (multistatus) is returned and holds detailed status for each shipment. Other standard HTTP response codes (401, 500, 400, 200, 429) are possible, too. Labels can be either provided as part of the response (base64 encoded for PDF, text for ZPL) or via URL link for view and download. Note that the format settings per query parameters apply to the shipping label. It may also apply to other labels included, depending on the configuration of your account. Label paper for return shipments can be specified separately since a different printer may be used here. If requesting labels to be provided as URL for separate download, the URLs can be shared. + #### Validation + It is recommended to validate the request first prior to shipment creation by setting the `validate` query parameter to `true`. Especially, during development and test, it is recommended to perform this validation. This functionality supports both + * JSON schema validation (against this API description). During development and test, it is recommended to do this validation. JSON schema is available for local validation + * Dry run against the DHL backend + + If this succeeds, actual shipment creation will also succeed. + operationId: createOrders + parameters: + - name: Accept-Language + in: header + description: Control the APIs response language via locale abbreviation. English (en-US) and german (de-DE) are supported. If not specified, the default is english. + schema: + type: string + example: de-DE + - name: validate + in: query + description: |- + If provided and set to `true`, the input document will be: + * validated against JSON schema (/orders/ endpoint) at the API layer. In case of errors, HTTP 400 and details will be returned. + * validated against the DHL backend. + + In that case, no state changes are happening, no data is stored, shipments neither deleted nor created, no labels being returned. The call will return a status (200, 400) for each shipment element. + schema: + type: boolean + default: false + - name: mustEncode + in: query + description: Legacy name **printOnlyIfCodable**. If set to *true*, labels will only be created if an address is encodable. This is only relevant for German consignee addresses. If set to false or left out, addresses, that are not encodable will be printed even though you receive a warning. + schema: + type: boolean + default: false + - name: includeDocs + in: query + description: |- + Legacy name **labelResponseType**. Shipping labels and further shipment documents can be: + * __include__: included as base64 encoded data in the response (default) + * __URL__: provided as URL reference. + schema: + type: string + enum: + - include + - URL + default: include + - name: docFormat + in: query + description: |- + **Defines** the **printable** document format to be used for label and manifest documents. + schema: + type: string + enum: + - ZPL2 + - PDF + default: PDF + - name: printFormat + in: query + description: |- + **Defines** the print medium for the shipping label. The different option vary from standard paper sizes DIN A4 and DIN A5 to specific label print formats. + + Specific laser print formats using DIN A5 blanks are: + * 910-300-600(-oz) (105 x 205mm) + * 910-300-300(-oz) (105 x 148mm) + + Specific laser print formats **not** using a DIN A5 blank: + * 910-300-610 (105 x 208mm) + * 100x70mm + + Specific thermal print formats: + * 910-300-600 (103 x 199mm) + * 910-300-400 (103 x 150mm) + * 100x70mm + + Please use the different formats as follows. If you do not set the parameter the settings of DHL costumer portal account will be used as default. + schema: + type: string + enum: + - A4 + - 910-300-600 + - 910-300-610 + - 910-300-700 + - 910-300-700-oz + - 910-300-710 + - 910-300-300 + - 910-300-300-oz + - 910-300-400 + - 910-300-410 + - 100x70mm + - name: retourePrintFormat + in: query + description: |- + **Defines** the print medium for the return shipping label. This parameter is only usable, if you do not use **combined printing**. The different option vary from standard paper sizes DIN A4 and DIN A5 to specific label print formats. + + Specific laser print formats using DIN A5 blanks are: + * 910-300-600(-oz) (105 x 205mm) + * 910-300-300(-oz) (105 x 148mm) + + Specific laser print formats **not** using a DIN A5 blank: + * 910-300-610 (105 x 208mm) + * 100x70mm + + Specific thermal print formats: + * 910-300-600 (103 x 199mm) + * 910-300-400 (103 x 150mm) + * 100x70mm + + Please use the different formats as follows. If you do not set the parameter the settings of DHL costumer portal account will be used as default. + schema: + type: string + enum: + - A4 + - 910-300-600 + - 910-300-610 + - 910-300-700 + - 910-300-700-oz + - 910-300-710 + - 910-300-300 + - 910-300-300-oz + - 910-300-400 + - 910-300-410 + - 100x70mm + - name: combine + in: query + description: If set, label and return label for one shipment will be printed as single PDF document with possibly multiple pages. Else, those two labels come as separate documents. The option does not affect customs documents and COD labels. + schema: + type: boolean + default: true + requestBody: + description: Shipment order request. + content: + application/json: + schema: + $ref: '#/components/schemas/ShipmentOrderRequest' + examples: + DHLPaket: + $ref: '#/components/examples/DHLPaket' + DHLPaketInternational: + $ref: '#/components/examples/DHLPaketInternational' + DHLPaketInternationalWithCustoms: + $ref: '#/components/examples/DHLPaketInternationalWithCustoms' + DHLKleinpaket: + $ref: '#/components/examples/DHLKleinpaket' + WarenpostInternationalWithCustoms: + $ref: '#/components/examples/WarenpostInternationalWithCustoms' + required: true + responses: + '200': + description: Success response for requests with a single shipment. + content: + application/json: + schema: + $ref: '#/components/schemas/LabelDataResponse' + application/problem+json: + schema: + $ref: '#/components/schemas/LabelDataResponse' + '207': + description: Response for requests taking multiple input elements + content: + application/json: + schema: + $ref: '#/components/schemas/LabelDataResponse' + application/problem+json: + schema: + $ref: '#/components/schemas/LabelDataResponse' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' + delete: + tags: + - Shipments and Labels + summary: Delete one or more shipments + description: 'Delete one or more shipments created earlier. Deletion of shipments is only possible prior to them being manifested (closed out, ''Tagesabschluss''). The call will return HTTP 200 (single shipment) or 207 on success, with individual status elements for each shipment. Individual status elements are HTTP 200, 400. 400 will be returned when shipment does not exist (or was already deleted).' + operationId: ordersAccountDelete + parameters: + - name: Accept-Language + in: header + description: Control the APIs response language via locale abbreviation. English (en-US) and german (de-DE) are supported. If not specified, the default is english. + schema: + type: string + example: de-DE + - name: profile + in: query + required: true + description: 'Defines the user group profile. A user group is permitted to specific billing numbers. Shipments are only canceled if they belong to a billing number that the user group profile is entitled to use. This attribute is mandatory. Please use the standard user group profile ''STANDARD_GRUPPENPROFIL'' if no dedicated user group profile is available.' + schema: + type: string + example: 'STANDARD_GRUPPENPROFIL' + - name: shipment + required: true + in: query + description: Shipment number that shall be canceled. If multiple shipments shall be canceled, the parameter must be added multiple times. Up to 30 shipments can be canceled at once. + schema: + type: string + example: '123456789' + responses: + '200': + description: Response for requests with a single element + content: + application/json: + schema: + $ref: '#/components/schemas/LabelDataResponse' + application/problem+json: + schema: + $ref: '#/components/schemas/LabelDataResponse' + '207': + description: Response for requests taking multiple input elements + content: + application/json: + schema: + $ref: '#/components/schemas/LabelDataResponse' + application/problem+json: + schema: + $ref: '#/components/schemas/LabelDataResponse' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' +components: + schemas: + ServiceInformation: + type: object + description: Response for the API version endpoint detailing version information. + properties: + amp: + type: object + properties: + name: + type: string + description: name of api + example: pp-parcel-shipping-native + env: + type: string + description: environment + example: sandbox + version: + type: string + description: version of api + example: v2.0.4 + rev: + type: string + description: revision + example: 22 + backend: + type: object + properties: + env: + type: string + description: environment + example: sandbox + version: + type: string + description: version of backend + example: v2.1.0 + + Document: + type: object + properties: + b64: + type: string + description: The Base64 encoded byte stream + zpl2: + type: string + description: The document in zpl encoding + url: + type: string + description: URL reference to download document + example: www.dhl.de/download/myobscurelink?label.png + fileFormat: + type: string + description: format of the encoded bytes + example: PDF + enum: + - ZPL2 + - PDF + printFormat: + type: string + description: The print format used. Customs documents and cash on delivery documents can only be returned in format A4. + example: 910-300-700 + enum: + - A4-PT + - A4 + - 910-300-700 + - 910-300-700-oZ/oz + - 910-300-300 + - 910-300-300-oz + - 910-300-710 + - 910-300-600/610 + - 910-300-400/410 + - 100x70mm + description: 'Encoded document. All types of labels and documents.' + RequestStatus: + type: object + description: General status description for the attached response or response item. + required: + - statusCode + - title + properties: + title: + type: string + example: ok + statusCode: + description: The status code of the response. Usually, but not necessarliy the HTTP status code. Same as attribut "status" but deprecated. Do not use. Will be removed in the next major version. + type: integer + format: int32 + example: 200 + status: + description: The status code of the response. Usually, but not necessarliy the HTTP status code. + type: integer + format: int32 + example: 200 + instance: + type: string + description: A URI reference that identifies the specific occurrence of the problem. + detail: + maxLength: 80 + minLength: 0 + type: string + example: The Webservice call ran successfully. + LabelDataResponse: + type: object + properties: + status: + $ref: '#/components/schemas/RequestStatus' + items: + type: array + description: If the request contains a multi element array (e.g. multiple shipments), then the order of the items in the response corresponds to the order of the items in the request. For consistency, if the request contains only one item then the response contains a single element array. + example: + - shipmentNo: 340434310428091700 + routingCode: 40327653113+99000943058020 + sstatus: + title: OK + status: 200 + label: + url: https://api-dev.dhl.com/parcel/de/shipping/v1-feature-order-endpoint/labels?token=x5xzrHE7ctmqPqk33k%2BKkBwbvIfYP4elMQsBFM%2BJOdiT2bmoaXXzris%2Ftz9jBtdVFLY5cCENit0Jnd9aXuxoNEXhP9PQ8tAVdPeXD26RZ6JZqF5NCJlrihrAv1%2FAOzuDPqWJLRVaRq461BpT4bcbzChAAHVg%2FHUaQAkeIkaZ8NqfcxWEQzK1AYJWczpy6sv6 + format: PDF + items: + $ref: '#/components/schemas/ResponseItem' + ResponseItem: + required: + - sstatus + type: object + properties: + shipmentNo: + maxLength: 50 + minLength: 0 + type: string + routingCode: + maxLength: 35 + minLength: 0 + type: string + description: Routing code of the consignee address + returnRoutingCode: + maxLength: 35 + minLength: 0 + type: string + description: Routing code of the return address + returnShipmentNo: + maxLength: 50 + minLength: 0 + type: string + sstatus: + $ref: '#/components/schemas/RequestStatus' + shipmentRefNo: + maxLength: 50 + minLength: 6 + type: string + label: + $ref: '#/components/schemas/Document' + returnLabel: + $ref: '#/components/schemas/Document' + customsDoc: + $ref: '#/components/schemas/Document' + codLabel: + $ref: '#/components/schemas/Document' + validationMessages: + type: array + description: Optional validation messages attached to the shipment. + items: + $ref: '#/components/schemas/ValidationMessageItem' + description: Response for a single shipment containing status, numbers and labels + example: + - shipmentNo: 340434310428091700 + routingCode: 40327653113+99000943058020 + sstatus: + title: OK + status: 200 + label: + url: https://api-dev.dhl.com/parcel/de/shipping/v1-feature-order-endpoint/labels?token=x5xzrHE7ctmqPqk33k%2BKkBwbvIfYP4elMQsBFM%2BJOdiT2bmoaXXzris%2Ftz9jBtdVFLY5cCENit0Jnd9aXuxoNEXhP9PQ8tAVdPeXD26RZ6JZqF5NCJlrihrAv1%2FAOzuDPqWJLRVaRq461BpT4bcbzChAAHVg%2FHUaQAkeIkaZ8NqfcxWEQzK1AYJWczpy6sv6 + format: PDF + ValidationMessageItem: + type: object + properties: + property: + type: string + description: The property that is affected by the validation message. + example: dimension.weight + validationMessage: + type: string + description: The validation message describing the error. + example: The weight is too high + validationState: + type: string + description: The validation state resulting from the error. + example: Error + description: Representation of a validation message of a shipment container containing the most important information. + SingleManifestResponse: + type: object + properties: + status: + $ref: '#/components/schemas/RequestStatus' + manifestDate: + type: string + manifest: + type: array + items: + $ref: '#/components/schemas/Document' + sheetNo: + type: array + items: + $ref: '#/components/schemas/BillingNoToSheetNo' + items: + type: array + items: + $ref: '#/components/schemas/ShipmentNoToSheetNo' + BillingNoToSheetNo: + type: object + properties: + billingNumber: + type: string + sheetNo: + type: string + description: Mapping between billing number and sheet number + ShipmentNoToSheetNo: + type: object + properties: + shipmentNo: + type: string + sheetNo: + type: string + sstatus: + $ref: '#/components/schemas/RequestStatus' + description: Mapping between shipment number and sheet number + MultipleManifestResponse: + type: object + properties: + status: + $ref: '#/components/schemas/RequestStatus' + items: + type: array + items: + $ref: '#/components/schemas/ShortResponseItem' + description: Response of the POST /manifests endpoint containing an overall request status and detailed shipment status. + ShortResponseItem: + required: + - sstatus + type: object + properties: + shipmentNo: + maxLength: 50 + minLength: 0 + type: string + example: '340434310428091700' + sstatus: + $ref: '#/components/schemas/RequestStatus' + description: Response for a single shipment element not containing labels. + ShipmentManifestingRequest: + required: + - profile + type: object + properties: + profile: + maxLength: 35 + minLength: 0 + type: string + shipmentNumbers: + maxItems: 30 + minItems: 1 + type: array + description: List of shipment IDs for manifesting. + items: + type: string + billingNumber: + type: string + description: Customer billingNumber number. + description: List of shipments which shall be manifested. + BankAccount: + required: + - accountHolder + - iban + type: object + properties: + accountHolder: + maxLength: 80 + minLength: 0 + type: string + example: John D. Rockefeller + bankName: + maxLength: 80 + minLength: 0 + type: string + example: The Iron Bank, Braavos + iban: + pattern: '[A-Z]{2,2}[0-9]{2,2}[a-zA-Z0-9]{1,30}' + type: string + example: DE02100100100006820101 + bic: + pattern: '[a-zA-Z0-9]{8,11}' + type: string + example: DEUTDEFFXXX + description: Bank account data used for CoD (Cash on Delivery). + Commodity: + required: + - itemDescription + - itemValue + - itemWeight + - packagedQuantity + type: object + properties: + itemDescription: + maxLength: 256 + minLength: 1 + type: string + example: T-Shirt Boys size 164 yellow + description: A text that describes the commodity item. Only the first 50 characters of the description text is printed on the customs declaration form CN23. + countryOfOrigin: + $ref: '#/components/schemas/Country' + hsCode: + maxLength: 11 + minLength: 6 + type: string + description: Harmonized System Code aka Customs tariff number. + example: '61099090' + packagedQuantity: + type: integer + description: How many items of that type are in the package + format: int32 + example: 1 + itemValue: + $ref: '#/components/schemas/Value' + itemWeight: + $ref: '#/components/schemas/Weight' + description: |- + Commodity line item (e.g. a t-shirt) for international shipments which require individual listing of goods. Each good must contain a description of the item, the amount of the item, the net weight of one single item and the value of one single item. If you ship 5 t-shirts, you need to include the weight and value of one single shirt. The correct final weight and value will be calculated automatically. + The HS Code and the country of origin are optional. + example: + - itemDescription: T-Shirt + hscode: '61099090' + countryOfOrigin: DE + packagedQuantity: 3 + - itemDescription: Book + hscode: '49019900' + packagedQuantity: 1 + Consignee: + type: object + description: Consignee address information. Either a doorstep address (contact address) including contact information or a droppoint address. One of packstation (parcel locker), or post office (postfiliale/retail shop). + oneOf: + - $ref: '#/components/schemas/ContactAddress' + - $ref: '#/components/schemas/Locker' + - $ref: '#/components/schemas/PostOffice' + - $ref: '#/components/schemas/POBox' + ContactAddress: + required: + - addressStreet + - city + - country + - name1 + type: object + properties: + name1: + maxLength: 50 + minLength: 1 + type: string + description: Name1. Line 1 of name information + example: Blumen Krause + name2: + maxLength: 50 + minLength: 1 + type: string + description: An optional, additional line of name information + example: To the attention of Erna. + name3: + maxLength: 50 + minLength: 1 + type: string + description: An optional, additional line of name information + example: Backdrawer all the way back. + dispatchingInformation: + maxLength: 35 + minLength: 1 + type: string + description: An optional, additional line of address. It is printed on the shipping label for international shipments to the following countries only BEL, CZE, NLD. It is positioned below name3 on the label. + example: PO Box, bpack 24/7 + addressStreet: + maxLength: 50 + minLength: 1 + type: string + description: Line 1 of the street address. This is just the street name. Can also include house number. + example: Hauptstrasse + addressHouse: + maxLength: 10 + minLength: 1 + type: string + description: Line 1 of the street address. This is just the house number. Can be added to street name instead. + example: 1a + additionalAddressInformation1: + maxLength: 60 + minLength: 1 + type: string + description: Additional information that is positioned either behind or below addressStreet on international shipment labels. Where exactly it is printed on the label depends on the country. + example: 3. Etage + additionalAddressInformation2: + maxLength: 60 + minLength: 1 + type: string + description: Additional information that is positioned either behind or below addressStreet on international shipment labels. It is printed on shipping labels to the following countries only AUT, CHN, DNK, GBR, HRV, LTU, PRT, ROU, RUS. Where exactly it is printed on the label depends on the country. + example: Apartment 12 + postalCode: + maxLength: 10 + minLength: 3 + pattern: ^[0-9A-Za-z]+([ -]?[0-9A-Za-z]+)*$ + type: string + description: Mandatory for all countries but Ireland that use a postal code system. + example: '53113' + city: + maxLength: 40 + minLength: 1 + type: string + description: city + example: Berlin + state: + maxLength: 20 + minLength: 1 + type: string + description: State, province or territory. For the USA please use the official regional ISO-Codes, e.g. US-AL. + example: NRW + country: + $ref: '#/components/schemas/Country' + contactName: + maxLength: 80 + minLength: 3 + type: string + description: optional contact name. (this is not the primary name printed on label) + example: Konrad Kontaktmann + phone: + maxLength: 20 + minLength: 1 + type: string + description: Please note that, in accordance with Art. 4 No. 11 GDPR, you must obtain the recipient's consent to forward their phone number to Deutsche Post DHL Group. For shipments within Germany, the phone number cannot be transmitted. In some countries the provision of a telephone number and/or e-mail address is mandatory for a delivery to a droppoint. If your recipient has objected to the disclosure of their telephone number and/or e-mail address, the shipment can only be delivered in these countries using the service Premium. + example: +49 170 1234567 + email: + maxLength: 80 + minLength: 3 + type: string + description: "Please note that, in accordance with Art. 4 No. 11 GDPR, you must obtain the recipient's consent to forward their e-mail address to Deutsche Post DHL Group.\_For shipments within Germany, the e-mail address is used to send a DHL Parcel Notification to the recipient. The e-mail address is not mandatory for shipments within Germany. In some countries the provision of a telephone number and/or e-mail address is mandatory for a delivery to a droppoint. If your recipient has objected to the disclosure of their telephone number and/or e-mail address, the shipment can only be delivered in these countries using the service Premium." + example: mustermann@example.com + description: Combines name, address, contact information. The recommended way is to use the mandatory attribute addressStreet and submit the streetname and housenumber together – alternatively addressHouse + addressStreet can be used. For many international addresses there is no house number, please do not set a period or any other sign to indicate that the address does not have a housenumber. + CustomsDetails: + required: + - exportType + - postalCharges + - items + type: object + properties: + invoiceNo: + maxLength: 35 + minLength: 0 + type: string + description: Invoice number + exportType: + type: string + description: This contains the category of goods contained in parcel. + enum: + - OTHER + - PRESENT + - COMMERCIAL_SAMPLE + - DOCUMENT + - RETURN_OF_GOODS + - COMMERCIAL_GOODS + exportDescription: + maxLength: 80 + minLength: 0 + type: string + description: Mandatory if exporttype is 'OTHER' + example: Detailed description for OTHER goods. + shippingConditions: + type: string + description: Aka 'Terms of Trade' aka 'Frankatur'. The attribute is exclusively used for the product Europaket (V54EPAK). DDU is deprecated (use DAP instead). + enum: + - DAP + - DDP + permitNo: + maxLength: 30 + minLength: 0 + type: string + description: Permit number. Very rarely needed. Mostly relevant for higher value goods. An example use case would be an item made from crocodile leather which requires dedicated license / permit identified by that number. + attestationNo: + maxLength: 30 + minLength: 0 + type: string + description: Attest or certification identified by this number. Very rarely needed. An example use case would be a medical shipment referring to an attestation that a certain amount of medicine may be imported within e.g. the current quarter of the year. + hasElectronicExportNotification: + type: boolean + description: flag confirming whether electronic record for export was made + MRN: + type: string + maxLength: 18 + example: "abcd1234567890" + postalCharges: + allOf: + - $ref: '#/components/schemas/Value' + description: Postal charges that have been charged to the recipient. The information must match the information on the invoice. Postal charges are added to the customs value which is the basis for the calculation of import duties. Since 1.1.2021 this information is mandatory according to requirements of the Universal Postal Union. The currency of the postal charges is used throughout the customs declaration form. The currency details of the individual goods items and the currency of the postal charges must match. Otherwise no shipping label will be created. + officeOfOrigin: + maxLength: 35 + minLength: 0 + type: string + description: Deprecated (do not use anymore). Will appear on CN23. + shipperCustomsRef: + description: "Optional. The customs reference is used by customs authorities to identify economics operators an/or other persons involved. With the given reference, granted authorizations and/or relevant processes in customs clearance an/or taxation can be taken into account. Aka Zoll-Nummer or EORI-Number but dependent on destination." + type: string + maxLength: 35 + example: "DE73282932000074" + consigneeCustomsRef: + description: "Optional. The customs reference is used by customs authorities to identify economics operators an/or other persons involved. With the given reference, granted authorizations and/or relevant processes in customs clearance an/or taxation can be taken into account. Aka Zoll-Nummer or EORI-Number but dependent on destination." + type: string + maxLength: 35 + example: "GB73282932000074" + items: + maxItems: 99 + minItems: 1 + type: array + description: Commodity types in that package + example: + - itemDescription: T-Shirt + hscode: '61099090' + countryOfOrigin: DE + packagedQuantity: 3 + - itemDescription: Book + hscode: '49019900' + packagedQuantity: 1 + items: + $ref: '#/components/schemas/Commodity' + description: For international shipments, this section contains information necessary for customs about the exported goods. ExportDocument can contain one or more positions as child element. This data is also transferred as electronic declaration to customs. The custom details are mandatory depending on whether the parcel will go to a country outside the European Customs Union. For DHL Parcel International (V53WPAK) CN23 will returned as a separate document, while for Warenpost International the customs information will be printed onto the shipment label (CN22). + Dimensions: + required: + - height + - length + - uom + - width + type: object + properties: + uom: + type: string + description: Unit of metric, applies to all dimensions contained. + example: cm + enum: + - cm + - mm + height: + type: integer + format: int32 + example: 10 + length: + type: integer + format: int32 + example: 20 + width: + type: integer + format: int32 + example: 15 + description: Physical dimensions (aka 'Gurtmass') of the parcel. If you provide the dimension information, all attributes need to be provided. You cannot provide just the height, for example. If you provide length, width, and height in millimeters, they will be rounded to full cm. + Locker: + required: + - city + - lockerID + - name + - postNumber + - postalCode + type: object + properties: + name: + maxLength: 50 + minLength: 1 + type: string + description: Name + example: Paula Packstation + lockerID: + maximum: 999 + minimum: 100 + type: integer + description: Packstationnummer. Three digit number identifying the parcel locker in conjunction with city and postal code + format: int32 + example: 118 + postNumber: + pattern: ^[0-9]{6,10}$ + type: string + description: postNumber (Postnummer) is the official account number a private DHL Customer gets upon registration. + city: + maxLength: 40 + minLength: 0 + type: string + description: City where the locker is located + example: Berlin + country: + $ref: '#/components/schemas/Country' + postalCode: + maxLength: 10 + minLength: 3 + pattern: ^[0-9A-Za-z]+([ -]?[0-9A-Za-z]+)*$ + type: string + description: Only usable for German Packstation, international lockers cannot be addressed directly. If your customer wishes for international delivery to a droppoint, please use DHL Parcel International (V53WPAK) with the delivery type "Closest Droppoint". + PostOffice: + required: + - city + - name + - postalCode + - retailID + type: object + properties: + name: + maxLength: 50 + minLength: 1 + type: string + description: Name + example: Fritz Filialabholer + retailID: + maximum: 999 + minimum: 401 + type: integer + description: Id or Number of Post office / Filiale / outlet / parcel shop + format: int32 + example: 518 + postNumber: + maxLength: 10 + minLength: 3 + pattern: ^[0-9]{6,10}$ + type: string + description: postNumber (Postnummer) is the official account number a private DHL Customer gets upon registration. To address a post office or retail outlet directly, either the post number or e-mail address of the consignee is needed. + email: + maxLength: 80 + minLength: 3 + type: string + description: Email address of the consignee. To address a post office or retail outlet directly, either the post number or e-mail address of the consignee is needed. + example: mustermann@example.com + city: + maxLength: 80 + minLength: 0 + type: string + description: City where the retail location is + example: Berlin + country: + $ref: '#/components/schemas/Country' + postalCode: + maxLength: 10 + minLength: 3 + pattern: ^[0-9A-Za-z]+([ -]?[0-9A-Za-z]+)*$ + type: string + description: Only usable for German post offices or retail outlets (Paketshops), international postOffices or retail outlets cannot be addressed directly. If your customer wishes for international delivery to a droppoint, please use DHL Parcel International (V53WPAK) with the delivery type "Closest Droppoint". + POBox: + required: + - name1 + - poBoxID + - city + - postalCode + type: object + properties: + name1: + maxLength: 50 + minLength: 1 + type: string + description: Name1. Line 1 of name information + example: Joe Black + name2: + maxLength: 50 + minLength: 1 + type: string + description: An optional, additional line of name information + example: To the attention of Mr. Black. + name3: + maxLength: 50 + minLength: 1 + type: string + description: An optional, additional line of name information + example: Backdrawer all the way back. + poBoxID: + type: integer + format: int32 + description: Number of P.O. Box (Postfach) + email: + maxLength: 80 + minLength: 3 + type: string + description: Email address of the consignee + city: + maxLength: 80 + minLength: 0 + type: string + description: City of the P.O. Box (Postfach) location + example: Berlin + country: + $ref: '#/components/schemas/Country' + postalCode: + maxLength: 10 + minLength: 3 + pattern: ^[0-9A-Za-z]+([ -]?[0-9A-Za-z]+)*$ + type: string + description: Postal code of the P.O. Box (Postfach) location + description: Alternative destination - P.O. Box (Postfach) + Product: + type: string + description: |- + Determines the DHL Paket product to be used. + + * V01PAK: DHL PAKET; + * V53WPAK: DHL PAKET International; + * V54EPAK: DHL Europaket; + * V62WP: Warenpost (will be replaced by DHL Kleinpaket from 1.1.2025); + * V62KP: DHL Kleinpaket; + * V66WPI: Warenpost International + example: V01PAK + enum: + - V01PAK + - V53WPAK + - V54EPAK + - V62WP + - V62KP + - V66WPI + Shipment: + type: object + properties: + product: + $ref: '#/components/schemas/Product' + billingNumber: + pattern: \w{10}\d{2}\w{2} + type: string + description: 14 digit long number that identifies the contract the shipment is booked on. Please note that in rare cases the last to characters can be letters. Digit 11 and digit 12 must correspondent to the number of the product, e.g. 333333333301tt can only be used for the product V01PAK (DHL Paket). + example: 33333333330101 or 333333333362aa + refNo: + maxLength: 35 + minLength: 8 + type: string + description: A reference number that the user can assign for better association purposes. It appears on shipment labels. To use the reference number for tracking purposes, it should be at least 8 characters long and unique. + costCenter: + maxLength: 50 + type: string + description: Textfield that appears on the shipment label. It cannot be used to search for the shipment. + creationSoftware: + type: string + description: Is only to be indicated by DHL partners. + shipDate: + type: string + description: 'Date the shipment is transferred to DHL. The shipment date can be the current date or a date up to a few days in the future. It must not be in the past. Iso format required: yyyy-mm-dd. On the shipment date the shipment will be automatically closed at your end of day closing time.' + format: date + shipper: + description: Shipper information, including contact information and address. Alternatively, a predefined shipper reference can be used. + oneOf: + - $ref: '#/components/schemas/Shipper' + - $ref: '#/components/schemas/ShipperReference' + consignee: + $ref: '#/components/schemas/Consignee' + details: + $ref: '#/components/schemas/ShipmentDetails' + services: + $ref: '#/components/schemas/VAS' + customs: + $ref: '#/components/schemas/CustomsDetails' + description: Container for all shipments. Mixed shipment products per request are supported. Each shipment has a dedicated shipper address (or shipper reference, or both). + ShipmentDetails: + required: + - weight + type: object + properties: + dim: + $ref: '#/components/schemas/Dimensions' + weight: + $ref: '#/components/schemas/Weight' + description: Details for the shipment, such as dimensions, content + ShipmentOrderRequest: + required: + - profile + - shipments + type: object + properties: + profile: + maxLength: 35 + minLength: 0 + type: string + shipments: + maxItems: 30 + minItems: 1 + type: array + description: Shipment array having details for each shipment. + items: + $ref: '#/components/schemas/Shipment' + description: Complex request structure used to create, update, and validate shipment data. Note that most elements are part of the array of shipment items. + Shipper: + type: object + description: Shipper information, including contact information and address. + required: + - addressStreet + - city + - country + - name1 + properties: + name1: + maxLength: 50 + minLength: 1 + type: string + description: Name1. Line 1 of name information + example: Blumen Krause + name2: + maxLength: 50 + minLength: 1 + type: string + description: An optional, additional line of name information + example: To the attention of Erna. + name3: + maxLength: 50 + minLength: 1 + type: string + description: An optional, additional line of name information + example: Backdrawer all the way back. + addressStreet: + maxLength: 50 + minLength: 1 + type: string + description: Line 1 of the street address. This is just the street name. Can also include house number. + example: Hauptstrasse + addressHouse: + maxLength: 10 + minLength: 1 + type: string + description: Line 1 of the street address. This is just the house number. Can be added to street name instead. + example: 1a + postalCode: + maxLength: 10 + minLength: 3 + pattern: ^[0-9A-Za-z]+([ -]?[0-9A-Za-z]+)*$ + type: string + description: Mandatory for all countries but Ireland that use a postal code system. + example: '53113' + city: + maxLength: 40 + minLength: 1 + type: string + description: city + example: Berlin + country: + $ref: '#/components/schemas/Country' + contactName: + maxLength: 80 + minLength: 3 + type: string + description: optional contact name. (this is not the primary name printed on label) + example: Konrad Kontaktmann + email: + maxLength: 80 + minLength: 3 + type: string + description: Optional contact email address of the shipper + example: mustermann@example.com + ShipperReference: + type: object + description: Contains a reference to the Shipper data configured in GKP(Geschäftskundenportal - Business Costumer Portal). Can be used instead of a detailed shipper address. The shipper reference can be used to print a company logo which is configured in GKP onto the label. + required: + - shipperRef + properties: + shipperRef: + maxLength: 50 + minLength: 0 + type: string + description: Reference string to the shipper data configured in GKP(Geschäftskundenportal - Business Costumer Portal). + VAS: + type: object + properties: + preferredNeighbour: + maxLength: 100 + minLength: 0 + type: string + description: Preferred neighbour. Can be specified as text. + example: Please ring at Meier next door + preferredLocation: + maxLength: 100 + minLength: 0 + type: string + description: Preferred location. Can be specified as text. + example: Please leave in carport + visualCheckOfAge: + pattern: A16|A18 + type: string + description: if used it will trigger checking the age of recipient + example: A18 + enum: + - A16 + - A18 + namedPersonOnly: + type: boolean + description: Delivery can only be signed for by yourself personally. + example: true + identCheck: + $ref: '#/components/schemas/VASIdentCheck' + signedForByRecipient: + type: boolean + description: Delivery must be signed for by the recipient and not by DHL staff + example: true + endorsement: + type: string + description: Instructions and endorsement how to treat international undeliverable shipment. By default, shipments are returned if undeliverable. There are country specific rules whether the shipment is returned immediately or after a grace period. + example: RETURN + enum: + - RETURN + - ABANDON + preferredDay: + type: string + description: Preferred day of delivery in format YYYY-MM-DD. Shipper can request a preferred day of delivery. The preferred day should be between 2 and 6 working days after handover to DHL. + format: date + noNeighbourDelivery: + type: boolean + description: Delivery can only be signed for by yourself personally or by members of your household. + example: true + additionalInsurance: + $ref: '#/components/schemas/Value' + bulkyGoods: + type: boolean + description: Leaving this out is same as setting to false. Sperrgut. + example: true + cashOnDelivery: + $ref: '#/components/schemas/VASCashOnDelivery' + individualSenderRequirement: + pattern: '[a-zA-Z0-9]{2}' + type: string + description: Special instructions for delivery. 2 character code, possible values agreed in contract. + example: ZZ + premium: + type: boolean + description: 'Choice of premium vs economy parcel. Availability is country dependent and may be manipulated by DHL if choice is not available. Please review the label.' + example: true + closestDropPoint: + type: boolean + description: 'Closest Droppoint Delivery to the droppoint closest to the address of the recipient of the shipment. For this kind of delivery either the phone number and/or the e-mail address of the receiver is mandatory. For shipments using DHL Paket International it is recommended that you choose one of the three delivery types: Economy, Premium, CDP. Otherwise, the current default for the receiver country will be picked.' + example: true + parcelOutletRouting: + type: string + description: 'Undeliverable domestic shipment can be forwarded and held at retail. Notification to email (fallback: consignee email) will be used.' + example: max.mustermann@example.com + goGreenPlus: + type: boolean + description: 'GoGreen Plus enables sustainable shipping by investing in measures to reduce greenhouse gas emissions at DHL.' + example: true + dhlRetoure: + $ref: '#/components/schemas/VASDhlRetoure' + postalDeliveryDutyPaid: + type: boolean + description: 'All import duties are paid by the shipper.' + example: true + description: Value added services. Please note that services are specific to products and geographies and/or may require individual setup and billing numbers. Please test and contact your account representative in case of questions. + VASCashOnDelivery: + required: + - transferNote1 + type: object + properties: + amount: + $ref: '#/components/schemas/Value' + bankAccount: + $ref: '#/components/schemas/BankAccount' + accountReference: + maxLength: 35 + minLength: 0 + type: string + description: Reference to bank account details. Account references are maintained in customer settings in Post & DHL business customer portal under Ship -> Settings -> Cash on delivery. Please note, that the default account reference is used if the provided account reference does not exist in your customer settings! + transferNote1: + maxLength: 35 + minLength: 0 + type: string + transferNote2: + maxLength: 35 + minLength: 0 + type: string + description: Cash on delivery (Nachnahme). Currency must be Euro. Either bank account information or account reference (from customer profile) must be provided. Transfernote1 + 2 are references transmitted during bank transfer. Providing account information explicitly requires elevated privileges. + VASDhlRetoure: + required: + - billingNumber + type: object + properties: + billingNumber: + pattern: \w{10}\d{2}\w{2} + type: string + refNo: + maxLength: 50 + minLength: 6 + type: string + returnAddress: + $ref: '#/components/schemas/ContactAddress' + goGreenPlus: + type: boolean + description: 'GoGreen Plus enables sustainable shipping by investing in measures to reduce greenhouse gas emissions at DHL.' + example: true + description: Requests return label (aka 'retoure') to be provided. Also requires returnAddress and return billing number. Neither weight nor dimension need to be specified for the retoure (flat rate service). + VASIdentCheck: + required: + - firstName + - lastName + type: object + properties: + firstName: + maxLength: 35 + minLength: 1 + type: string + example: Max + lastName: + maxLength: 35 + minLength: 1 + type: string + example: Mustermann + dateOfBirth: + type: string + description: date of birth, used in conjunction with minimumAge and shipping date. Format yyyy-mm-dd is used. + format: date + minimumAge: + pattern: A16|A18 + type: string + description: Checks if recipient will have reached specified age by shipping date. + example: A18 + enum: + - A16 + - A18 + description: Check the identity of the recipient via name (firstname, lastname), date of birth or age. This uses firstName and lastName as separate attributes since for identity check an automatic split of a one-line name is not considered reliable enough. + Country: + type: string + description: A valid country code consisting of three characters according to ISO 3166-1 alpha-3. + enum: + - ABW + - AFG + - AGO + - AIA + - ALA + - ALB + - AND + - ARE + - ARG + - ARM + - ASM + - ATG + - AUS + - AUT + - AZE + - BDI + - BEL + - BEN + - BES + - BFA + - BGD + - BGR + - BHR + - BHS + - BIH + - BLM + - BLR + - BLZ + - BMU + - BOL + - BRA + - BRB + - BRN + - BTN + - BVT + - BWA + - CAF + - CAN + - CCK + - CHE + - CHL + - CHN + - CIV + - CMR + - COD + - COG + - COK + - COL + - COM + - CPV + - CRI + - CUB + - CUW + - CXR + - CYM + - CYP + - CZE + - DEU + - DJI + - DMA + - DNK + - DOM + - DZA + - ECU + - EGY + - ERI + - ESP + - EST + - ETH + - FIN + - FJI + - FLK + - FRA + - FRO + - FSM + - GAB + - GBR + - GEO + - GGY + - GHA + - GIB + - GIN + - GLP + - GMB + - GNB + - GNQ + - GRC + - GRD + - GRL + - GTM + - GUF + - GUM + - GUY + - HKG + - HMD + - HND + - HRV + - HTI + - HUN + - IDN + - IMN + - IND + - IRL + - IRN + - IRQ + - ISL + - ISR + - ITA + - JAM + - JEY + - JOR + - JPN + - KAZ + - KEN + - KGZ + - KHM + - KIR + - KNA + - KOR + - KWT + - LAO + - LBN + - LBR + - LBY + - LCA + - LIE + - LKA + - LSO + - LTU + - LUX + - LVA + - MAC + - MAF + - MAR + - MCO + - MDA + - MDG + - MDV + - MEX + - MHL + - MKD + - MLI + - MLT + - MMR + - MNE + - MNG + - MNP + - MOZ + - MRT + - MSR + - MTQ + - MUS + - MWI + - MYS + - MYT + - NAM + - NCL + - NER + - NFK + - NGA + - NIC + - NIU + - NLD + - NOR + - NPL + - NRU + - NZL + - OMN + - PAK + - PAN + - PCN + - PER + - PHL + - PLW + - PNG + - POL + - PRI + - PRK + - PRT + - PRY + - PSE + - PYF + - QAT + - REU + - ROU + - RUS + - RWA + - SAU + - SDN + - SEN + - SGP + - SHN + - SJM + - SLB + - SLE + - SLV + - SMR + - SOM + - SPM + - SRB + - SSD + - STP + - SUR + - SVK + - SVN + - SWE + - SWZ + - SXM + - SYC + - SYR + - TCA + - TCD + - TGO + - THA + - TJK + - TKL + - TKM + - TLS + - TON + - TTO + - TUN + - TUR + - TUV + - TWN + - TZA + - UGA + - UKR + - URY + - USA + - UZB + - VAT + - VCT + - VEN + - VGB + - VIR + - VNM + - VUT + - WLF + - WSM + - YEM + - ZAF + - ZMB + - ZWE + - UNKNOWN + + Value: + required: + - currency + - value + type: object + description: Currency and numeric value. + properties: + currency: + type: string + description: iso 4217 3 character currency code accepted. Recommended to use EUR where possible + enum: + - AED + - AFN + - ALL + - AMD + - ANG + - AOA + - ARS + - AUD + - AWG + - AZN + - BAM + - BBD + - BDT + - BGN + - BHD + - BIF + - BMD + - BND + - BOB + - BOV + - BRL + - BSD + - BTN + - BWP + - BYR + - BZD + - CAD + - CDF + - CHE + - CHF + - CHW + - CLF + - CLP + - CNY + - COP + - COU + - CRC + - CUC + - CUP + - CVE + - CZK + - DJF + - DKK + - DOP + - DZD + - EGP + - ERN + - ETB + - EUR + - FJD + - FKP + - GBP + - GEL + - GHS + - GIP + - GMD + - GNF + - GTQ + - GYD + - HKD + - HNL + - HRK + - HTG + - HUF + - IDR + - ILS + - INR + - IQD + - IRR + - ISK + - JMD + - JOD + - JPY + - KES + - KGS + - KHR + - KMF + - KPW + - KRW + - KWD + - KYD + - KZT + - LAK + - LBP + - LKR + - LRD + - LSL + - LTL + - LVL + - LYD + - MAD + - MDL + - MGA + - MKD + - MMK + - MNT + - MOP + - MRO + - MUR + - MVR + - MWK + - MXN + - MXV + - MYR + - MZN + - NAD + - NGN + - NIO + - NOK + - NPR + - NZD + - OMR + - PAB + - PEN + - PGK + - PHP + - PKR + - PLN + - PYG + - QAR + - RON + - RSD + - RUB + - RWF + - SAR + - SBD + - SCR + - SDG + - SEK + - SGD + - SHP + - SLL + - SOS + - SRD + - SSP + - STD + - SYP + - SZL + - THB + - TJS + - TMT + - TND + - TOP + - TRY + - TTD + - TWD + - TZS + - UAH + - UGX + - USD + - USN + - USS + - UYI + - UYU + - UZS + - VEF + - VND + - VUV + - WST + - XAF + - XAG + - XAU + - XBA + - XBB + - XBC + - XBD + - XCD + - XDR + - XFU + - XOF + - XPD + - XPF + - XPT + - XXX + - YER + - ZAR + - ZMW + - UNKNOWN + value: + type: number + description: Numeric value + maximum: 100000 + exclusiveMaximum: false + minimum: 0 + exclusiveMinimum: false + Weight: + required: + - uom + - value + type: object + properties: + uom: + type: string + description: metric unit for weight + example: g + enum: + - g + - kg + value: + maximum: 31500 + exclusiveMaximum: false + minimum: 0 + exclusiveMinimum: false + type: number + example: 500 + description: Weight of item or shipment. Both uom and value are required. + examples: + DHLPaket: + summary: DHL Paket (V01PAK) + description: Order example for DHL Paket (V01PAK) + value: + profile: STANDARD_GRUPPENPROFIL + shipments: + - product: V01PAK + billingNumber: "33333333330102" + refNo: Order No. 1234 + shipper: + name1: My Online Shop GmbH + addressStreet: Sträßchensweg 10 + postalCode: "53113" + city: Bonn + country: DEU + email: max@mustermann.de + phone: +49 123456789 + consignee: + name1: Maria Musterfrau + addressStreet: Kurt-Schumacher-Str. 20 + postalCode: "53113" + city: Bonn + country: DEU + email: maria@musterfrau.de + phone: +49 987654321 + details: + dim: + uom: mm + height: 100 + length: 200 + width: 150 + weight: + uom: g + value: 500 + DHLPaketInternational: + summary: DHL Paket International (V53WPAK) + description: Order example for DHL Paket International (V53WPAK) + value: + profile: STANDARD_GRUPPENPROFIL + shipments: + - product: V53WPAK + billingNumber: "33333333335301" + refNo: Order No. 1234 + shipper: + name1: My Online Shop GmbH + addressStreet: Sträßchensweg 10 + postalCode: "53113" + city: Bonn + country: DEU + email: max@mustermann.de + phone: +49 123456789 + consignee: + name1: Jan Vermeer + addressStreet: Museumstraat + addressHouse: "1" + additionalAddressInformation1: 2. Floor + postalCode: "1071 AA" + city: Amsterdam + country: NLD + email: jan@vermeer.com + phone: +31 888888888 + details: + dim: + uom: mm + height: 100 + length: 200 + width: 150 + weight: + uom: g + value: 500 + DHLPaketInternationalWithCustoms: + summary: DHL Paket International (V53WPAK) with customs + description: Order example for DHL Paket International (V53WPAK) with customs + value: + profile: STANDARD_GRUPPENPROFIL + shipments: + - product: V53WPAK + billingNumber: "33333333335301" + refNo: Order No. 1234 + shipper: + name1: My Online Shop GmbH + addressStreet: Sträßchensweg 10 + postalCode: "53113" + city: Bonn + country: DEU + email: max@mustermann.de + phone: +49 123456789 + consignee: + name1: Joe Black + addressStreet: 10 Downing Street + additionalAddressInformation1: 2. Floor + postalCode: "SW1A 1AA" + city: London + country: GBR + email: joe@black.uk + phone: +44 123456789 + details: + dim: + uom: mm + height: 100 + length: 200 + width: 150 + weight: + uom: g + value: 500 + customs: + exportType: COMMERCIAL_GOODS + postalCharges: + currency: EUR + value: 1 + items: + - itemDescription: Red T-Shirt + packagedQuantity: 1 + hsCode: "123456" + countryOfOrigin: FRA + itemValue: + currency: EUR + value: 10 + itemWeight: + uom: g + value: 400 + services: + endorsement: RETURN + DHLKleinpaket: + summary: DHL Kleinpaket (V62KP) + description: Order example for DHL Kleinpaket (V62KP) + value: + profile: STANDARD_GRUPPENPROFIL + shipments: + - product: V62KP + billingNumber: "33333333336201" + refNo: Order No. 1234 + shipper: + name1: My Online Shop GmbH + addressStreet: Sträßchensweg 10 + postalCode: '53113' + city: Bonn + country: DEU + email: max@mustermann.de + phone: +49 123456789 + consignee: + name1: Maria Musterfrau + addressStreet: Kurt-Schumacher-Str. 20 + postalCode: "53113" + city: Bonn + country: DEU + email: maria@musterfrau.de + phone: +49 987654321 + details: + dim: + uom: cm + height: 1 + length: 10 + width: 15 + weight: + uom: g + value: 500 + WarenpostInternationalWithCustoms: + summary: Warenpost International (V66WPI) with customs + description: Order example for Warenpost International (V66WPI) with customs + value: + profile: STANDARD_GRUPPENPROFIL + shipments: + - product: V66WPI + billingNumber: "33333333336601" + refNo: Order No. 1234 + shipper: + name1: My Online Shop GmbH + addressStreet: Sträßchensweg 10 + postalCode: "53113" + city: Bonn + country: DEU + email: max@mustermann.de + phone: "+49 123456789" + consignee: + name1: Joe Black + addressStreet: 42 Street + additionalAddressInformation1: 2. Floor + postalCode: SW1A 1AA + city: London + country: GBR + email: joe@black.uk + phone: +44 123456789 + details: + dim: + uom: cm + height: 1 + length: 10 + width: 15 + weight: + uom: g + value: 500 + customs: + exportType: PRESENT + postalCharges: + currency: EUR + value: 1 + items: + - itemDescription: Item 1 + packagedQuantity: 1 + hsCode: 123456 + countryOfOrigin: FRA + itemValue: + currency: EUR + value: 10 + itemWeight: + uom: g + value: 300 + services: + endorsement: RETURN + securitySchemes: + BasicAuth: + type: http + scheme: basic + description: Credentials provided here are username and password of the partner system. + ApiKey: + type: apiKey + description: API key to authorize requests. API key can be obtained at [DHL's development + portal](https://developer.dhl.com). + name: dhl-api-key + in: header + OAuth2: + type: oauth2 + description: See https://developer.dhl.com/api-reference/authentication-api-post-parcel-germany. For the POST call to get a token, provide all parameters in the request body. Refer to the above link for details, or to the RFC describing OAuth2 Password Grant. After having obtained the access token, use this as Bearer token in the HTTP authorization header. + flows: + password: + tokenUrl: /parcel/de/account/auth/ropc/v1/token + scopes: {} + responses: + BadRequest: + description: Bad Request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/LabelDataResponse' + Unauthorized: + description: Unauthorized + content: + application/problem+json: + schema: + $ref: '#/components/schemas/RequestStatus' + NotFound: + description: Not Found + content: + application/problem+json: + schema: + $ref: '#/components/schemas/RequestStatus' + TooManyRequests: + description: Too Many Requests + content: + application/problem+json: + schema: + $ref: '#/components/schemas/RequestStatus' + InternalServerError: + description: Internal Server Error + content: + application/problem+json: + schema: + $ref: '#/components/schemas/RequestStatus' +security: + - ApiKey: [] + - BasicAuth: [] + - OAuth2: [] \ No newline at end of file diff --git a/packages/acme-laravel-dhl/README.md b/packages/acme-laravel-dhl/README.md new file mode 100644 index 0000000..561e299 --- /dev/null +++ b/packages/acme-laravel-dhl/README.md @@ -0,0 +1,199 @@ +# DHL Laravel Package + +A comprehensive Laravel package for DHL shipping, tracking, and returns functionality with direct API integration. + +## Features + +- **Direct DHL API Integration**: No SDK dependencies, full control over API calls +- **German Address Parsing**: Automatic extraction of house numbers from German addresses +- **Queue Support**: Optional asynchronous processing for high-volume operations +- **Comprehensive Tracking**: Full tracking event history and status management +- **Return Label Generation**: Easy return shipment creation +- **Retry Mechanisms**: Built-in retry logic for API calls and file operations +- **Laravel Integration**: Native service providers, facades, and Eloquent models + +## Installation + +### 1. Add to composer.json + +```json +{ + "repositories": [ + { + "type": "path", + "url": "./packages/acme-laravel-dhl" + } + ], + "require": { + "acme/laravel-dhl": "*" + } +} +``` + +### 2. Install the package + +```bash +composer update acme/laravel-dhl +``` + +### 3. Publish configuration and run migrations + +```bash +php artisan vendor:publish --provider="Acme\Dhl\DhlServiceProvider" --tag="config" +php artisan migrate +``` + +## Configuration + +Add the following environment variables to your `.env` file: + +```env +# DHL API Configuration +DHL_BASE_URL=https://api-eu.dhl.com +DHL_API_KEY=your_api_key_here +DHL_USERNAME=your_username +DHL_PASSWORD=your_password +DHL_BILLING_NUMBER=your_billing_number + +# DHL Default Settings +DHL_PRODUCT=V01PAK +DHL_LABEL_FORMAT=PDF +DHL_PROFILE=STANDARD_GRUPPENPROFIL + +# Optional Queue Settings +DHL_USE_QUEUE=false +``` + +## Usage + +### Creating a Shipment Label + +```php +use DHL; + +$orderData = [ + 'order_id' => 12345, + 'weight_kg' => 2.5, + 'shipper' => [ + 'name' => 'Your Company', + 'street' => 'Musterstraße 123', // House number will be auto-parsed + 'postalCode' => '12345', + 'city' => 'Berlin', + 'country' => 'DE' + ], + 'consignee' => [ + 'name' => 'Customer Name', + 'street' => 'Kundenstraße 456', // House number will be auto-parsed + 'postalCode' => '54321', + 'city' => 'Munich', + 'country' => 'DE' + ] +]; + +$result = DHL::createLabel($orderData); +// Returns: ['shipmentNumber' => '123...', 'label_path' => 'dhl/labels/...'] +``` + +### German Address Parsing + +The package automatically handles German address formats by extracting house numbers from the street field: + +```php +// Input: "Musterstraße 123a" +// Parsed to: street="Musterstraße", houseNumber="123a" + +// Supported formats: +"Musterstraße 123" -> street: "Musterstraße", number: "123" +"Am Markt 7" -> street: "Am Markt", number: "7" +"Karl-Marx-Straße 156" -> street: "Karl-Marx-Straße", number: "156" +"Lindenstraße 1-3" -> street: "Lindenstraße", number: "1-3" +"Muster Str. 99" -> street: "Muster Str.", number: "99" +``` + +### Tracking Shipments + +```php +use DHL; + +// Get current tracking status +$status = DHL::track('1234567890123'); + +// Returns detailed tracking information including events +``` + +### Creating Return Labels + +```php +use DHL; + +$returnData = [ + 'original_shipment_id' => 1, + 'return_reason' => 'Customer return', + // ... address data +]; + +$return = DHL::createReturn($returnData); +``` + +### Using Services Directly + +```php +use Acme\Dhl\Services\ShippingService; +use Acme\Dhl\Services\TrackingService; + +// Dependency injection in controllers +public function createLabel(ShippingService $shipping, Request $request) +{ + $result = $shipping->createLabel($request->validated()); + return response()->json($result); +} +``` + +## Database Schema + +The package creates the following tables: + +- `dhl_shipments` - Main shipment records (outbound and returns) +- `dhl_tracking_events` - Tracking event history + +## Queue Configuration + +For high-volume applications, enable queue processing: + +```env +DHL_USE_QUEUE=true +``` + +When enabled, label creation and tracking updates will be processed asynchronously. + +## Error Handling + +The package includes comprehensive error handling: + +- `DhlApiException` - General API errors +- `DhlAuthenticationException` - Authentication failures +- `DhlValidationException` - Data validation errors + +## Testing + +Run the included tests: + +```bash +vendor/bin/phpunit packages/acme-laravel-dhl/tests/ +``` + +## Address Parsing Tests + +The package includes comprehensive tests for German address parsing: + +```bash +vendor/bin/phpunit packages/acme-laravel-dhl/tests/Unit/AddressParserTest.php +``` + +## Logging + +All API calls, address parsing, and errors are logged using Laravel's logging system. Check your logs for debugging information. + +## License + +MIT License diff --git a/packages/acme-laravel-dhl/composer.json b/packages/acme-laravel-dhl/composer.json new file mode 100644 index 0000000..bfd6430 --- /dev/null +++ b/packages/acme-laravel-dhl/composer.json @@ -0,0 +1,30 @@ +{ + "name": "acme/laravel-dhl", + "description": "DHL (Post & Paket DE) Shipping, Returns & Tracking for Laravel.", + "type": "library", + "license": "MIT", + "require": { + "php": "^8.1", + "illuminate/support": "^10.0|^11.0", + "illuminate/http": "^10.0|^11.0", + "illuminate/database": "^10.0|^11.0", + "illuminate/queue": "^10.0|^11.0", + "illuminate/events": "^10.0|^11.0", + "guzzlehttp/guzzle": "^7.0" + }, + "autoload": { + "psr-4": { + "Acme\\Dhl\\": "src/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Acme\\Dhl\\DhlServiceProvider" + ], + "aliases": { + "DHL": "Acme\\Dhl\\Facades\\DHL" + } + } + } +} \ No newline at end of file diff --git a/packages/acme-laravel-dhl/config/dhl.php b/packages/acme-laravel-dhl/config/dhl.php new file mode 100644 index 0000000..1937a15 --- /dev/null +++ b/packages/acme-laravel-dhl/config/dhl.php @@ -0,0 +1,13 @@ + env('DHL_BASE_URL', 'https://api-eu.dhl.com'), + 'api_key' => env('DHL_API_KEY'), + 'username' => env('DHL_USERNAME'), + 'password' => env('DHL_PASSWORD'), + 'billing_number' => env('DHL_BILLING_NUMBER'), + 'default_product' => env('DHL_PRODUCT', 'V01PAK'), + 'label_format' => env('DHL_LABEL_FORMAT', 'PDF'), + 'profile' => env('DHL_PROFILE', 'STANDARD_GRUPPENPROFIL'), + 'webhook' => ['enabled' => env('DHL_WEBHOOK_ENABLED', false), 'secret' => env('DHL_WEBHOOK_SECRET'), 'route' => env('DHL_WEBHOOK_ROUTE', 'dhl/webhooks/tracking')], + 'use_queue' => env('DHL_USE_QUEUE', false), // Set to true to enable queue jobs for async processing +]; diff --git a/packages/acme-laravel-dhl/database/migrations/2025_01_01_000000_create_dhl_shipments_table.php b/packages/acme-laravel-dhl/database/migrations/2025_01_01_000000_create_dhl_shipments_table.php new file mode 100644 index 0000000..d185aca --- /dev/null +++ b/packages/acme-laravel-dhl/database/migrations/2025_01_01_000000_create_dhl_shipments_table.php @@ -0,0 +1,53 @@ +id(); + + // Basic shipment info + $table->unsignedBigInteger('order_id')->nullable()->index(); + $table->string('dhl_shipment_no')->nullable()->index(); + $table->enum('type', ['outbound', 'return'])->default('outbound'); + $table->unsignedBigInteger('related_shipment_id')->nullable()->index(); // For returns + + // Product and billing + $table->string('product_code')->default('V01PAK'); + $table->string('billing_number')->nullable(); + $table->decimal('weight_kg', 8, 3)->nullable(); + + // Label information + $table->string('label_format')->default('PDF'); + $table->string('label_path')->nullable(); + + // Status tracking (simplified) + $table->string('status')->default('created'); // created, in_transit, delivered, canceled, etc. + $table->string('tracking_status')->nullable(); // Last DHL status text + $table->timestamp('last_tracked_at')->nullable(); + + // API response data + $table->json('api_response_data')->nullable(); + + $table->timestamps(); + + // Foreign key constraints + $table->foreign('related_shipment_id')->references('id')->on('dhl_package_shipments')->onDelete('set null'); + + // Indexes for performance + $table->index(['order_id', 'type']); + $table->index(['status', 'created_at']); + }); + } + + public function down(): void + { + Schema::dropIfExists('dhl_package_shipments'); + } +}; diff --git a/packages/acme-laravel-dhl/database/migrations/2025_01_01_000000_create_shipments_table.php b/packages/acme-laravel-dhl/database/migrations/2025_01_01_000000_create_shipments_table.php new file mode 100644 index 0000000..f266ba8 --- /dev/null +++ b/packages/acme-laravel-dhl/database/migrations/2025_01_01_000000_create_shipments_table.php @@ -0,0 +1,29 @@ +id(); + $t->unsignedBigInteger('order_id')->nullable(); + $t->string('carrier')->default('dhl'); + $t->string('dhl_shipment_no')->nullable()->index(); + $t->string('product_code')->nullable(); + $t->string('billing_number')->nullable(); + $t->decimal('weight_kg', 8, 3)->nullable(); + $t->string('status')->default('created'); + $t->string('label_format')->nullable(); + $t->string('label_path')->nullable(); + $t->json('meta')->nullable(); + $t->timestamps(); + }); + } + public function down(): void + { + Schema::dropIfExists('shipments'); + } +}; diff --git a/packages/acme-laravel-dhl/database/migrations/2025_01_01_000100_create_shipment_labels_table.php b/packages/acme-laravel-dhl/database/migrations/2025_01_01_000100_create_shipment_labels_table.php new file mode 100644 index 0000000..7f6270a --- /dev/null +++ b/packages/acme-laravel-dhl/database/migrations/2025_01_01_000100_create_shipment_labels_table.php @@ -0,0 +1,22 @@ +id(); + $t->foreignId('shipment_id')->constrained('shipments')->cascadeOnDelete(); + $t->string('format')->default('PDF'); + $t->string('path'); + $t->timestamps(); + }); + } + public function down(): void + { + Schema::dropIfExists('shipment_labels'); + } +}; diff --git a/packages/acme-laravel-dhl/database/migrations/2025_01_01_000200_create_dhl_tracking_events_table.php b/packages/acme-laravel-dhl/database/migrations/2025_01_01_000200_create_dhl_tracking_events_table.php new file mode 100644 index 0000000..2a0370a --- /dev/null +++ b/packages/acme-laravel-dhl/database/migrations/2025_01_01_000200_create_dhl_tracking_events_table.php @@ -0,0 +1,30 @@ +id(); + $table->unsignedBigInteger('shipment_id')->index(); + $table->string('status_code')->nullable(); + $table->string('status_text')->nullable(); + $table->string('location')->nullable(); + $table->timestamp('event_time')->nullable(); + $table->json('raw')->nullable(); // Full event data from API + $table->timestamps(); + + $table->foreign('shipment_id')->references('id')->on('dhl_package_shipments')->onDelete('cascade'); + $table->index(['shipment_id', 'event_time']); + }); + } + + public function down(): void + { + Schema::dropIfExists('dhl_package_tracking_events'); + } +}; \ No newline at end of file diff --git a/packages/acme-laravel-dhl/database/migrations/2025_01_01_000200_create_tracking_events_table.php b/packages/acme-laravel-dhl/database/migrations/2025_01_01_000200_create_tracking_events_table.php new file mode 100644 index 0000000..a736351 --- /dev/null +++ b/packages/acme-laravel-dhl/database/migrations/2025_01_01_000200_create_tracking_events_table.php @@ -0,0 +1,24 @@ +id(); + $t->foreignId('shipment_id')->constrained('shipments')->cascadeOnDelete(); + $t->string('status_code')->nullable(); + $t->string('status_text')->nullable(); + $t->string('location')->nullable(); + $t->timestamp('event_time')->nullable(); + $t->json('raw')->nullable(); + }); + } + public function down(): void + { + Schema::dropIfExists('tracking_events'); + } +}; diff --git a/packages/acme-laravel-dhl/database/migrations/2025_01_01_000300_create_return_labels_table.php b/packages/acme-laravel-dhl/database/migrations/2025_01_01_000300_create_return_labels_table.php new file mode 100644 index 0000000..057a233 --- /dev/null +++ b/packages/acme-laravel-dhl/database/migrations/2025_01_01_000300_create_return_labels_table.php @@ -0,0 +1,22 @@ +id(); + $t->unsignedBigInteger('order_id')->nullable()->index(); + $t->string('dhl_return_no')->nullable()->index(); + $t->string('label_path')->nullable(); + $t->timestamps(); + }); + } + public function down(): void + { + Schema::dropIfExists('return_labels'); + } +}; diff --git a/packages/acme-laravel-dhl/routes/api.php b/packages/acme-laravel-dhl/routes/api.php new file mode 100644 index 0000000..c26d9b4 --- /dev/null +++ b/packages/acme-laravel-dhl/routes/api.php @@ -0,0 +1,6 @@ +name('dhl.webhook.tracking'); diff --git a/packages/acme-laravel-dhl/src/DhlManager.php b/packages/acme-laravel-dhl/src/DhlManager.php new file mode 100644 index 0000000..05563dd --- /dev/null +++ b/packages/acme-laravel-dhl/src/DhlManager.php @@ -0,0 +1,40 @@ +shipping->createLabel($orderData); + } + /** + * @param string $shipmentNumber + * @return bool + */ + public function cancelLabel(string $shipmentNumber): bool + { + return $this->shipping->cancelLabel($shipmentNumber); + } + /** + * @param string $trackingNumber + * @return array + */ + public function track(string $trackingNumber): array + { + return $this->tracking->fetchStatus($trackingNumber); + } + /** + * @param array $returnData + * @return array + */ + public function createReturn(array $returnData): array + { + return $this->returns->createReturn($returnData); + } +} diff --git a/packages/acme-laravel-dhl/src/DhlServiceProvider.php b/packages/acme-laravel-dhl/src/DhlServiceProvider.php new file mode 100644 index 0000000..cee76d9 --- /dev/null +++ b/packages/acme-laravel-dhl/src/DhlServiceProvider.php @@ -0,0 +1,55 @@ +mergeConfigFrom(__DIR__ . '/../config/dhl.php', 'dhl'); + + // Bind DhlClient as a singleton + $this->app->singleton(Support\DhlClient::class, function ($app) { + return new Support\DhlClient( + config('dhl.base_url'), + config('dhl.api_key'), + config('dhl.username'), + config('dhl.password') + ); + }); + + // Bind services as singletons + $this->app->singleton(Services\ShippingService::class, function ($app) { + return new Services\ShippingService($app->make(Support\DhlClient::class)); + }); + + $this->app->singleton(Services\TrackingService::class, function ($app) { + return new Services\TrackingService($app->make(Support\DhlClient::class)); + }); + + $this->app->singleton(Services\ReturnsService::class, function ($app) { + return new Services\ReturnsService($app->make(Support\DhlClient::class)); + }); + + // Bind the main manager class + $this->app->singleton(DhlManager::class, function ($app) { + return new DhlManager( + $app->make(Support\DhlClient::class), + $app->make(Services\ShippingService::class), + $app->make(Services\TrackingService::class), + $app->make(Services\ReturnsService::class) + ); + }); + } + public function boot(): void + { + $this->publishes([__DIR__ . '/../config/dhl.php' => config_path('dhl.php')], 'dhl-config'); + $this->publishes([__DIR__ . '/../database/migrations/' => database_path('migrations')], 'dhl-migrations'); + $this->loadMigrationsFrom(__DIR__ . '/../database/migrations'); + if (config('dhl.webhook.enabled')) { + $this->loadRoutesFrom(__DIR__ . '/../routes/api.php'); + } + } +} diff --git a/packages/acme-laravel-dhl/src/Exceptions/DhlApiException.php b/packages/acme-laravel-dhl/src/Exceptions/DhlApiException.php new file mode 100644 index 0000000..ff78271 --- /dev/null +++ b/packages/acme-laravel-dhl/src/Exceptions/DhlApiException.php @@ -0,0 +1,8 @@ +header('X-DHL-Secret') !== $secret) { + abort(401, 'Invalid webhook secret'); + } + $payload = $r->all(); + Log::info('DHL webhook received', ['payload' => $payload]); + $events = data_get($payload, 'events', []); + foreach ($events as $e) { + $tracking = $e['trackingNumber'] ?? null; + if (!$tracking) continue; + $s = DhlShipment::firstOrCreate(['dhl_shipment_no' => $tracking], ['carrier' => 'dhl', 'status' => 'unknown',]); + DhlTrackingEvent::create(['shipment_id' => $s->id, 'status_code' => $e['statusCode'] ?? null, 'status_text' => $e['status'] ?? null, 'location' => data_get($e, 'location'), 'event_time' => $e['timestamp'] ?? now(), 'raw' => $e,]); + $statusCode = $e['statusCode'] ?? ''; + $newStatus = match (strtolower($statusCode)) { + 'delivered' => 'delivered', + 'returned' => 'returned', + 'exception', 'failed' => 'failed', + 'in_transit', 'transit' => 'in_transit', + default => $s->status + }; + if ($newStatus !== $s->status) { + $s->status = $newStatus; + $s->save(); + Log::info('Webhook updated shipment status', ['tracking' => $tracking, 'newStatus' => $newStatus]); + } + } + return response()->json(['ok' => true]); + } +} diff --git a/packages/acme-laravel-dhl/src/Jobs/CreateReturnLabelJob.php b/packages/acme-laravel-dhl/src/Jobs/CreateReturnLabelJob.php new file mode 100644 index 0000000..d611146 --- /dev/null +++ b/packages/acme-laravel-dhl/src/Jobs/CreateReturnLabelJob.php @@ -0,0 +1,29 @@ +createReturn($this->returnData); + Log::info('Queued return label creation successful'); + } catch (\Exception $e) { + Log::error('Queued return label creation failed', ['error' => $e->getMessage()]); + $this->fail($e); + } + } +} diff --git a/packages/acme-laravel-dhl/src/Jobs/CreateShipmentJob.php b/packages/acme-laravel-dhl/src/Jobs/CreateShipmentJob.php new file mode 100644 index 0000000..43027a8 --- /dev/null +++ b/packages/acme-laravel-dhl/src/Jobs/CreateShipmentJob.php @@ -0,0 +1,29 @@ +createLabel($this->orderData); + Log::info('Queued shipment creation successful'); + } catch (\Exception $e) { + Log::error('Queued shipment creation failed', ['error' => $e->getMessage()]); + $this->fail($e); + } + } +} diff --git a/packages/acme-laravel-dhl/src/Jobs/SyncTrackingJob.php b/packages/acme-laravel-dhl/src/Jobs/SyncTrackingJob.php new file mode 100644 index 0000000..cc0f2b1 --- /dev/null +++ b/packages/acme-laravel-dhl/src/Jobs/SyncTrackingJob.php @@ -0,0 +1,29 @@ +trackingNumbers as $trackingNumber) { + try { + $t->fetchStatus($trackingNumber); + Log::info('Synced tracking', ['trackingNumber' => $trackingNumber]); + } catch (\Exception $e) { + Log::error('Tracking sync failed', ['trackingNumber' => $trackingNumber, 'error' => $e->getMessage()]); + $this->fail($e); + } + } + } +} diff --git a/packages/acme-laravel-dhl/src/Models/DhlShipment.php b/packages/acme-laravel-dhl/src/Models/DhlShipment.php new file mode 100644 index 0000000..89b4063 --- /dev/null +++ b/packages/acme-laravel-dhl/src/Models/DhlShipment.php @@ -0,0 +1,149 @@ + 'array', + 'last_tracked_at' => 'datetime', + 'weight_kg' => 'decimal:3' + ]; + + public const STATUS_MAP = [ + 'pre-transit' => 'created', + 'transit' => 'in_transit', + 'out_for_delivery' => 'out_for_delivery', + 'delivered' => 'delivered', + 'exception' => 'exception', + 'returned' => 'returned', + 'failed' => 'failed', + 'unknown' => 'unknown' + ]; + + /** + * Get the tracking events for this shipment + */ + public function trackingEvents(): HasMany + { + return $this->hasMany(DhlTrackingEvent::class, 'shipment_id'); + } + + /** + * Get the related shopping order for this shipment + */ + public function shoppingOrder(): BelongsTo + { + return $this->belongsTo(ShoppingOrder::class, 'order_id'); + } + + /** + * Get the related shipment (for returns) + */ + public function relatedShipment(): BelongsTo + { + return $this->belongsTo(self::class, 'related_shipment_id'); + } + + /** + * Get returns for this shipment (if this is an outbound shipment) + */ + public function returns(): HasMany + { + return $this->hasMany(self::class, 'related_shipment_id'); + } + + /** + * Scope for outbound shipments + */ + public function scopeOutbound($query) + { + return $query->where('type', 'outbound'); + } + + /** + * Scope for return shipments + */ + public function scopeReturns($query) + { + return $query->where('type', 'return'); + } + + /** + * Scope for shipments by order + */ + public function scopeForOrder($query, int $orderId) + { + return $query->where('order_id', $orderId); + } + + /** + * Check if this is a return shipment + */ + public function isReturn(): bool + { + return $this->type === 'return'; + } + + /** + * Check if this is an outbound shipment + */ + public function isOutbound(): bool + { + return $this->type === 'outbound'; + } + + /** + * Get the latest tracking status + */ + public function getLatestStatus(): ?string + { + return $this->trackingEvents() + ->orderBy('event_time', 'desc') + ->first() + ?->status_text; + } + + /** + * Check if shipment can be canceled + */ + public function canCancel(): bool + { + return in_array($this->status, ['created', 'pending']); + } + + /** + * Check if shipment is delivered + */ + public function isDelivered(): bool + { + return $this->status === 'delivered'; + } +} diff --git a/packages/acme-laravel-dhl/src/Models/DhlTrackingEvent.php b/packages/acme-laravel-dhl/src/Models/DhlTrackingEvent.php new file mode 100644 index 0000000..a0045cc --- /dev/null +++ b/packages/acme-laravel-dhl/src/Models/DhlTrackingEvent.php @@ -0,0 +1,52 @@ + 'array', + 'event_time' => 'datetime' + ]; + + /** + * Get the shipment this event belongs to + */ + public function shipment(): BelongsTo + { + return $this->belongsTo(DhlShipment::class, 'shipment_id'); + } + + /** + * Scope for events ordered by time + */ + public function scopeOrderedByTime($query, string $direction = 'desc') + { + return $query->orderBy('event_time', $direction); + } + + /** + * Scope for recent events + */ + public function scopeRecent($query, int $days = 30) + { + return $query->where('event_time', '>=', now()->subDays($days)); + } +} diff --git a/packages/acme-laravel-dhl/src/Models/ReturnLabel.php b/packages/acme-laravel-dhl/src/Models/ReturnLabel.php new file mode 100644 index 0000000..9bf910c --- /dev/null +++ b/packages/acme-laravel-dhl/src/Models/ReturnLabel.php @@ -0,0 +1,10 @@ + 'array',]; + public function labels() + { + return $this->hasMany(ShipmentLabel::class); + } + public function events() + { + return $this->hasMany(TrackingEvent::class); + } +} diff --git a/packages/acme-laravel-dhl/src/Models/ShipmentLabel.php b/packages/acme-laravel-dhl/src/Models/ShipmentLabel.php new file mode 100644 index 0000000..86f4e24 --- /dev/null +++ b/packages/acme-laravel-dhl/src/Models/ShipmentLabel.php @@ -0,0 +1,14 @@ +belongsTo(Shipment::class); + } +} diff --git a/packages/acme-laravel-dhl/src/Models/TrackingEvent.php b/packages/acme-laravel-dhl/src/Models/TrackingEvent.php new file mode 100644 index 0000000..925f3f0 --- /dev/null +++ b/packages/acme-laravel-dhl/src/Models/TrackingEvent.php @@ -0,0 +1,16 @@ + 'array', 'event_time' => 'datetime',]; + public function shipment() + { + return $this->belongsTo(Shipment::class); + } +} diff --git a/packages/acme-laravel-dhl/src/Services/ReturnsService.php b/packages/acme-laravel-dhl/src/Services/ReturnsService.php new file mode 100644 index 0000000..71e38ec --- /dev/null +++ b/packages/acme-laravel-dhl/src/Services/ReturnsService.php @@ -0,0 +1,180 @@ +validateReturnData($returnData); + if (config('dhl.use_queue')) { + CreateReturnLabelJob::dispatch($validatedData); + return ['queued' => true]; + } + + $payload = $this->buildReturnPayload($returnData); + $response = $this->client->request('post', '/parcel/de/returns/v1/labels', $payload); + + $returnNumber = $this->extractReturnNumber($response); + $labelBase64 = $this->extractLabelData($response); + + $labelPath = $this->saveLabelFile($returnNumber, $labelBase64, $payload['labelFormat']); + $returnShipment = $this->createReturnRecord($returnData, $returnNumber, $labelPath, $response); + + Log::info('Created return label', ['returnNumber' => $returnNumber]); + + return [ + 'returnNumber' => $returnNumber, + 'label_path' => $labelPath, + 'returnShipment' => $returnShipment, + 'raw' => $response + ]; + } + + /** + * Get return shipment by return number + * + * @param string $returnNumber DHL return number + * @return DhlShipment|null Return shipment model or null if not found + */ + public function getReturnShipment(string $returnNumber): ?DhlShipment + { + return DhlShipment::where('dhl_shipment_no', $returnNumber) + ->where('type', 'return') + ->first(); + } + + /** + * Get all return shipments for an order + * + * @param int $orderId Order ID + * @return \Illuminate\Database\Eloquent\Collection Return shipments collection + */ + public function getOrderReturns(int $orderId): \Illuminate\Database\Eloquent\Collection + { + return DhlShipment::where('order_id', $orderId) + ->where('type', 'return') + ->get(); + } + + /** + * Get returns for a specific outbound shipment + * + * @param int $shipmentId Original outbound shipment ID + * @return \Illuminate\Database\Eloquent\Collection Related return shipments + */ + public function getShipmentReturns(int $shipmentId): \Illuminate\Database\Eloquent\Collection + { + return DhlShipment::where('related_shipment_id', $shipmentId) + ->where('type', 'return') + ->get(); + } + + /** + * Validate required return data + */ + private function validateReturnData(array $data): array + { + $validator = Validator::make($data, [ + 'original_shipment_id' => 'nullable|integer|exists:dhl_shipments,id', + 'weight_kg' => 'nullable|numeric|min:0.1', + 'shipper' => 'required|array', + 'shipper.name' => 'required|string|max:50', + // Add similar rules as in ShippingService + 'consignee' => 'required|array', + 'consignee.name' => 'required|string|max:50', + // ... more fields + ]); + if ($validator->fails()) { + throw new InvalidArgumentException($validator->errors()->first()); + } + if (empty(config('dhl.billing_number'))) { + throw new InvalidArgumentException('DHL billing number must be configured'); + } + return $validator->validated(); + } + + /** + * Build DHL return API payload + */ + private function buildReturnPayload(array $returnData): array + { + return [ + 'labelFormat' => $returnData['label_format'] ?? 'PDF', + 'shipper' => $returnData['shipper'], + 'consignee' => $returnData['consignee'], + 'billingNumber' => config('dhl.billing_number') + ]; + } + + /** + * Extract return number from API response + */ + private function extractReturnNumber(array $response): ?string + { + return data_get($response, 'returnShipmentNo') + ?? data_get($response, 'shipments.0.returnShipmentNo'); + } + + /** + * Extract base64 label data from API response + */ + private function extractLabelData(array $response): ?string + { + return data_get($response, 'label'); + } + + /** + * Save return label file to storage + */ + private function saveLabelFile(?string $returnNumber, ?string $labelBase64, string $format): ?string + { + if (!$labelBase64 || !$returnNumber) { + return null; + } + + $path = 'dhl/returns/' . $returnNumber . '.' . strtolower($format); + Storage::disk('local')->put($path, base64_decode($labelBase64)); + + return $path; + } + + /** + * Create return shipment database record + */ + private function createReturnRecord(array $returnData, ?string $returnNumber, ?string $labelPath, array $response): DhlShipment + { + return DhlShipment::create([ + 'order_id' => $returnData['order_id'] ?? null, + 'dhl_shipment_no' => $returnNumber, + 'type' => 'return', + 'related_shipment_id' => $returnData['original_shipment_id'] ?? null, + 'label_format' => $returnData['label_format'] ?? 'PDF', + 'label_path' => $labelPath, + 'status' => 'created', + 'api_response_data' => $response + ]); + } +} diff --git a/packages/acme-laravel-dhl/src/Services/ShippingService.php b/packages/acme-laravel-dhl/src/Services/ShippingService.php new file mode 100644 index 0000000..5ffa6e1 --- /dev/null +++ b/packages/acme-laravel-dhl/src/Services/ShippingService.php @@ -0,0 +1,480 @@ +validateOrderData($orderData); + if (config('dhl.use_queue')) { + CreateShipmentJob::dispatch($validatedData); + return ['queued' => true]; + } + + return DB::transaction(function () use ($validatedData) { + $payload = $this->buildShipmentPayload($validatedData); + + // Debug logging: Log the exact payload being sent to DHL API + Log::info('[DHL API] Sending payload to DHL', [ + 'endpoint' => '/parcel/de/shipping/v2/orders', + 'payload' => $payload, + 'payload_json' => json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) + ]); + + try { + // Build query parameters for print format + $query = array_filter([ + 'printFormat' => $validatedData['print_format'] ?? null, + 'retourePrintFormat' => $validatedData['retoure_print_format'] ?? null, + ]); + + $response = $this->client->request('post', '/parcel/de/shipping/v2/orders', $payload, $query); + + Log::info('[DHL API] Response received', [ + 'response' => $response + ]); + } catch (Exception $e) { + Log::error('[DHL API] Request failed', [ + 'error' => $e->getMessage(), + 'payload' => $payload + ]); + throw $e; + } + + $shipmentNumber = $this->extractShipmentNumber($response); + $labelBase64 = $this->extractLabelData($response); + + $shipment = $this->createShipmentRecord($validatedData, $payload, $response, $shipmentNumber); + $labelPath = $this->saveLabelFile($shipment, $labelBase64, $payload['shipments'][0]['print']['format']); + + Log::info('Created shipment label', ['shipmentNumber' => $shipmentNumber]); + + return [ + 'shipmentNumber' => $shipmentNumber, + 'label_path' => $labelPath, + 'shipment' => $shipment, + 'raw' => $response + ]; + }); + } + + /** + * Cancel an existing DHL shipment + * + * @param string $shipmentNumber DHL shipment number + * @return bool Success status + * @throws Exception When cancellation fails + */ + public function cancelLabel(string $shipmentNumber): bool + { + if (empty($shipmentNumber)) { + throw new InvalidArgumentException('Shipment number is required'); + } + $shipment = DhlShipment::where('dhl_shipment_no', $shipmentNumber)->first(); + if (!$shipment || !$shipment->canCancel()) { + throw new InvalidArgumentException('Shipment cannot be canceled'); + } + $this->client->request('delete', "/parcel/de/shipping/v2/orders/{$shipmentNumber}"); + $shipment->update(['status' => 'canceled']); + Log::info('Canceled shipment', ['shipmentNumber' => $shipmentNumber]); + return true; + } + + /** + * Validate required order data according to DHL API v2 specification + */ + private function validateOrderData(array $data): array + { + // Pre-process German addresses before validation + $data = $this->preprocessAddresses($data); + + $validator = Validator::make($data, [ + 'order_id' => 'nullable|integer', + 'weight_kg' => 'required|numeric|min:0.1|max:31.5', // DHL weight limit + 'product_code' => 'nullable|string|in:V01PAK,V53PAK,V62WP,V07PAK', + 'label_format' => 'nullable|string|in:PDF,ZPL', + 'print_format' => 'nullable|string', // Values like A4, 910-300-700 etc. + 'retoure_print_format' => 'nullable|string', + + // Shipper validation (sender) + 'shipper' => 'required|array', + 'shipper.name' => 'required|string|max:50', + 'shipper.name2' => 'nullable|string|max:50', + 'shipper.street' => 'required|string|max:50', + 'shipper.houseNumber' => 'required|string|max:10', + 'shipper.postalCode' => 'required|string|max:10', + 'shipper.city' => 'required|string|max:50', + 'shipper.country' => 'required|string|size:2', // ISO 3166-1 alpha-2 + 'shipper.email' => 'nullable|email|max:100', + 'shipper.phone' => 'nullable|string|max:20', + + // Consignee validation (recipient) + 'consignee' => 'required|array', + 'consignee.name' => 'required|string|max:50', + 'consignee.name2' => 'nullable|string|max:50', + 'consignee.street' => 'required|string|max:50', + 'consignee.houseNumber' => 'required|string|max:10', + 'consignee.postalCode' => 'required|string|max:10', + 'consignee.city' => 'required|string|max:50', + 'consignee.country' => 'required|string|size:2', + 'consignee.email' => 'nullable|email|max:100', + 'consignee.phone' => 'nullable|string|max:20', + + // Optional dimensions + 'dimensions' => 'nullable|array', + 'dimensions.length' => 'nullable|numeric|min:1|max:120', + 'dimensions.width' => 'nullable|numeric|min:1|max:60', + 'dimensions.height' => 'nullable|numeric|min:1|max:60', + + // Optional services and reference + 'services' => 'nullable|array', + 'reference' => 'nullable|string|max:35', // DHL reference field limit + ]); + + if ($validator->fails()) { + throw new InvalidArgumentException($validator->errors()->first()); + } + + return $validator->validated(); + } + + /** + * Preprocess addresses to extract house numbers from street field + */ + private function preprocessAddresses(array $data): array + { + // Process shipper address + if (isset($data['shipper'])) { + $data['shipper'] = $this->parseAddressFields($data['shipper']); + } + + // Process consignee address + if (isset($data['consignee'])) { + $data['consignee'] = $this->parseAddressFields($data['consignee']); + } + + return $data; + } + + /** + * Parse German address to extract street and house number + */ + private function parseAddressFields(array $addressData): array + { + // If houseNumber is already provided, use it + if (!empty($addressData['houseNumber'])) { + return $addressData; + } + + // If no houseNumber provided, try to parse from street + if (empty($addressData['street'])) { + return $addressData; + } + + $street = trim($addressData['street']); + $parsed = $this->parseGermanAddress($street); + + // Only update if we successfully parsed both parts + if ($parsed['street'] && $parsed['houseNumber']) { + $addressData['street'] = $parsed['street']; + $addressData['houseNumber'] = $parsed['houseNumber']; + + Log::info('Parsed German address', [ + 'original' => $street, + 'parsed_street' => $parsed['street'], + 'parsed_houseNumber' => $parsed['houseNumber'] + ]); + } elseif (!$parsed['houseNumber']) { + // If we can't parse house number, use a default + $addressData['houseNumber'] = '1'; + + Log::warning('Could not parse house number from address, using default', [ + 'street' => $street + ]); + } + + return $addressData; + } + + /** + * Parse German address string to extract street name and house number + * Handles formats like: "Musterstraße 123", "Muster Str. 123a", "Am Markt 1-3" + */ + private function parseGermanAddress(string $address): array + { + $address = trim($address); + + // Pattern to match German addresses + // Captures everything before the last word that contains numbers + $patterns = [ + // "Musterstraße 123a" -> street: "Musterstraße", number: "123a" + '/^(.+?)\s+([0-9]+[a-zA-Z]?)\s*$/', + // "Am Markt 1-3" -> street: "Am Markt", number: "1-3" + '/^(.+?)\s+([0-9]+[-\/][0-9]+[a-zA-Z]?)\s*$/', + // "Muster Str. 123" -> street: "Muster Str.", number: "123" + '/^(.+?)\s+([0-9]+)\s*$/', + ]; + + foreach ($patterns as $pattern) { + if (preg_match($pattern, $address, $matches)) { + return [ + 'street' => trim($matches[1]), + 'houseNumber' => trim($matches[2]) + ]; + } + } + + // If no pattern matches, return original street with empty house number + return [ + 'street' => $address, + 'houseNumber' => null + ]; + } + + /** + * Build DHL API v2 payload from order data + * + * Structure follows official DHL API v2 createOrders specification: + * https://developer.dhl.com/api-reference/parcel-de-shipping-post-parcel-germany-v2 + */ + private function buildShipmentPayload(array $orderData): array + { + $productCode = $orderData['product_code'] ?? config('dhl.default_product', 'V01PAK'); + $billingNumber = $this->getBillingNumberForProduct($productCode); + + $payload = [ + 'profile' => config('dhl.profile', 'STANDARD_GRUPPENPROFIL'), + 'shipments' => [[ + 'product' => $productCode, + 'billingNumber' => $billingNumber, + + // Shipper information (sender) - separate street and house number as per official spec + 'shipper' => array_filter([ + 'name1' => $orderData['shipper']['name'] ?? '', + 'name2' => !empty($orderData['shipper']['name2']) ? $orderData['shipper']['name2'] : null, + 'addressStreet' => $orderData['shipper']['street'] ?? '', + 'addressHouse' => $orderData['shipper']['houseNumber'] ?? null, + 'postalCode' => $orderData['shipper']['postalCode'] ?? '', + 'city' => $orderData['shipper']['city'] ?? '', + 'country' => $this->convertCountryCode($orderData['shipper']['country'] ?? 'DE'), + 'email' => !empty($orderData['shipper']['email']) ? $orderData['shipper']['email'] : null, + 'phone' => !empty($orderData['shipper']['phone']) ? $orderData['shipper']['phone'] : null, + ], function ($value) { + return $value !== null; + }), + + // Consignee information (recipient) - separate street and house number as per official spec + 'consignee' => array_filter([ + 'name1' => $orderData['consignee']['name'] ?? '', + 'name2' => !empty($orderData['consignee']['name2']) ? $orderData['consignee']['name2'] : null, + 'addressStreet' => $orderData['consignee']['street'] ?? '', + 'addressHouse' => $orderData['consignee']['houseNumber'] ?? null, + 'postalCode' => $orderData['consignee']['postalCode'] ?? '', + 'city' => $orderData['consignee']['city'] ?? '', + 'country' => $this->convertCountryCode($orderData['consignee']['country'] ?? 'DE'), + 'email' => !empty($orderData['consignee']['email']) ? $orderData['consignee']['email'] : null, + 'phone' => !empty($orderData['consignee']['phone']) ? $orderData['consignee']['phone'] : null, + ], function ($value) { + return $value !== null; + }), + + 'details' => [ + 'weight' => [ + 'value' => ($orderData['weight_kg'] ?? 1.0) * 1000, // Convert kg to grams + 'uom' => 'g' + ] + ], + + 'print' => [ + 'format' => $orderData['label_format'] ?? config('dhl.label_format', 'PDF') + ] + ]] + ]; + + // Add dimensions if provided (convert cm to mm) + if (!empty($orderData['dimensions'])) { + $payload['shipments'][0]['details']['dim'] = [ + 'uom' => 'mm', + 'length' => ($orderData['dimensions']['length'] ?? 30) * 10, // cm to mm + 'width' => ($orderData['dimensions']['width'] ?? 25) * 10, // cm to mm + 'height' => ($orderData['dimensions']['height'] ?? 10) * 10, // cm to mm + ]; + } + + // Add custom reference if provided + if (!empty($orderData['reference'])) { + $payload['shipments'][0]['refNo'] = $orderData['reference']; + } + + return $payload; + } + + /** + * Convert 2-letter country code to 3-letter country code for DHL API + */ + private function convertCountryCode(string $countryCode): string + { + $countryMap = [ + 'DE' => 'DEU', + 'AT' => 'AUT', + 'CH' => 'CHE', + 'US' => 'USA', + 'GB' => 'GBR', + 'FR' => 'FRA', + 'IT' => 'ITA', + 'ES' => 'ESP', + 'NL' => 'NLD', + 'BE' => 'BEL', + 'PL' => 'POL', + 'CZ' => 'CZE', + 'DK' => 'DNK', + 'SE' => 'SWE', + 'NO' => 'NOR', + ]; + + return $countryMap[strtoupper($countryCode)] ?? 'DEU'; + } + + /** + * Get the correct billing number for the given product code + */ + private function getBillingNumberForProduct(string $productCode): string + { + // Try to get account number from config by product code + $accountNumber = config("dhl.account_numbers.{$productCode}"); + + if ($accountNumber) { + return $accountNumber; + } + + // Try to get from admin settings via Setting model + try { + $settingKey = 'dhl_account_' . strtolower($productCode); + $accountNumber = \App\Models\Setting::getContentBySlug($settingKey); + if ($accountNumber) { + return $accountNumber; + } + } catch (\Exception $e) { + Log::warning('Could not load DHL account number from settings', [ + 'product_code' => $productCode, + 'setting_key' => $settingKey, + 'error' => $e->getMessage() + ]); + } + + // Fallback to default billing number + $defaultBillingNumber = config('dhl.billing_number') ?: config('dhl.account_numbers.default'); + + Log::warning('Using default billing number for product code', [ + 'product_code' => $productCode, + 'billing_number' => $defaultBillingNumber + ]); + + return $defaultBillingNumber; + } + + /** + * Extract shipment number from API response + */ + private function extractShipmentNumber(array $response): ?string + { + return data_get($response, 'items.0.shipmentNo') + ?? data_get($response, 'shipments.0.shipmentNo') + ?? data_get($response, 'shipmentNo') + ?? null; + } + + /** + * Extract base64 label data from API response + */ + private function extractLabelData(array $response): ?string + { + return data_get($response, 'items.0.label.b64') + ?? data_get($response, 'shipments.0.label.b64') + ?? data_get($response, 'shipments.0.label') + ?? data_get($response, 'label'); + } + + /** + * Extract routing code from API response + */ + private function extractRoutingCode(array $response): ?string + { + return data_get($response, 'items.0.routingCode') + ?? data_get($response, 'shipments.0.routingCode') + ?? null; + } + + /** + * Create shipment database record + */ + private function createShipmentRecord(array $orderData, array $payload, array $response, ?string $shipmentNumber): DhlShipment + { + return DhlShipment::create([ + 'order_id' => $orderData['order_id'] ?? null, + 'dhl_shipment_no' => $shipmentNumber, + 'routing_code' => $this->extractRoutingCode($response), + 'type' => 'outbound', + 'product_code' => $payload['shipments'][0]['product'], + 'billing_number' => $payload['shipments'][0]['billingNumber'], + 'weight_kg' => $payload['shipments'][0]['details']['weight']['value'] / 1000, + 'status' => 'created', + 'label_format' => $payload['shipments'][0]['print']['format'], + 'label_path' => null, + 'api_response_data' => $response + ]); + } + + /** + * Save label file to storage and create label record + */ + private function saveLabelFile(DhlShipment $shipment, ?string $labelBase64, string $format): ?string + { + if (!$labelBase64) { + Log::warning('No label data received for shipment', ['shipmentId' => $shipment->id]); + return null; + } + $path = 'dhl/labels/' . $shipment->dhl_shipment_no . '.' . strtolower($format); + $success = false; + for ($attempt = 1; $attempt <= 3; $attempt++) { + try { + Storage::disk('local')->put($path, base64_decode($labelBase64)); + $success = true; + break; + } catch (\Exception $e) { + Log::warning('Storage put failed, retrying', ['attempt' => $attempt, 'error' => $e->getMessage()]); + usleep(1000000); // 1 second in microseconds + } + } + if (!$success) { + throw new \Exception('Failed to save label after 3 attempts'); + } + $shipment->update(['label_path' => $path]); + return $path; + } +} diff --git a/packages/acme-laravel-dhl/src/Services/TrackingService.php b/packages/acme-laravel-dhl/src/Services/TrackingService.php new file mode 100644 index 0000000..df13789 --- /dev/null +++ b/packages/acme-laravel-dhl/src/Services/TrackingService.php @@ -0,0 +1,180 @@ + true]; + } + + $response = $this->client->request('get', '/post-tracking/api/shipments', [], [ + 'trackingNumber' => $trackingNumber + ]); + Log::info('Fetched tracking status', ['trackingNumber' => $trackingNumber]); + + $events = data_get($response, 'shipments.0.events', []); + $shipment = $this->findOrCreateShipment($trackingNumber); + + $this->updateTrackingEvents($shipment, $events); + + return $response; + } + + /** + * Update tracking status for multiple shipments + * + * @param array $trackingNumbers Array of tracking numbers to update + * @return array Results for each tracking number + */ + public function updateMultipleStatus(array $trackingNumbers): array + { + $results = []; + + foreach ($trackingNumbers as $trackingNumber) { + try { + $results[$trackingNumber] = $this->fetchStatus($trackingNumber); + } catch (Exception $e) { + Log::error('Tracking update failed', ['trackingNumber' => $trackingNumber, 'error' => $e->getMessage()]); + $results[$trackingNumber] = ['error' => $e->getMessage()]; + } + } + + return $results; + } + + /** + * Get latest tracking status for a shipment + * + * @param string $trackingNumber DHL tracking number + * @return array|null Latest tracking event or null if not found + */ + public function getLatestStatus(string $trackingNumber): ?array + { + $shipment = DhlShipment::where('dhl_shipment_no', $trackingNumber)->first(); + + if (!$shipment) { + return null; + } + + $latestEvent = $shipment->trackingEvents() + ->orderBy('event_time', 'desc') + ->first(); + + return $latestEvent ? $latestEvent->toArray() : null; + } + + /** + * Find existing shipment or create new one + */ + private function findOrCreateShipment(string $trackingNumber): DhlShipment + { + return DB::transaction(function () use ($trackingNumber) { + return DhlShipment::firstOrCreate( + ['dhl_shipment_no' => $trackingNumber], + [ + 'type' => 'outbound', + 'status' => 'unknown' + ] + ); + }); + } + + /** + * Update tracking events for a shipment + */ + private function updateTrackingEvents(DhlShipment $shipment, array $events): void + { + foreach ($events as $eventData) { + $this->createOrUpdateTrackingEvent($shipment, $eventData); + } + + // Update shipment status based on latest event + $this->updateShipmentStatus($shipment, $events); + } + + /** + * Create or update a tracking event + */ + private function createOrUpdateTrackingEvent(DhlShipment $shipment, array $eventData): void + { + DhlTrackingEvent::updateOrCreate( + [ + 'shipment_id' => $shipment->id, + 'status_code' => $eventData['statusCode'] ?? null, + 'event_time' => $eventData['timestamp'] ?? null + ], + [ + 'status_text' => $eventData['status'] ?? null, + 'location' => $this->extractLocation($eventData), + 'raw' => $eventData + ] + ); + } + + /** + * Extract location from event data + */ + private function extractLocation(array $eventData): ?string + { + return data_get($eventData, 'location.address.addressLocality') + ?? data_get($eventData, 'location') + ?? null; + } + + /** + * Update shipment status based on latest tracking event + */ + private function updateShipmentStatus(DhlShipment $shipment, array $events): void + { + if (empty($events)) { + return; + } + + // Sort events by timestamp to get the latest + usort($events, function ($a, $b) { + return strtotime($b['timestamp'] ?? '0') <=> strtotime($a['timestamp'] ?? '0'); + }); + + $latestEvent = $events[0]; + $statusCode = $latestEvent['statusCode'] ?? null; + + // Map DHL status codes to our internal status + $status = DhlShipment::STATUS_MAP[$statusCode] ?? 'unknown'; + + $shipment->update([ + 'status' => $status, + 'tracking_status' => $latestEvent['status'] ?? null, + 'last_tracked_at' => now() + ]); + Log::info('Updated shipment status', ['shipmentId' => $shipment->id, 'newStatus' => $status]); + } +} diff --git a/packages/acme-laravel-dhl/src/Support/DhlClient.php b/packages/acme-laravel-dhl/src/Support/DhlClient.php new file mode 100644 index 0000000..e92b49a --- /dev/null +++ b/packages/acme-laravel-dhl/src/Support/DhlClient.php @@ -0,0 +1,198 @@ +baseUrl) + ->withHeaders($this->buildHeaders()) + ->timeout(30) + ->retry(3, 300, function ($exception, $attempt) { + if ($exception instanceof RequestException && $exception->response->status() === 429) { + $delay = min(1000000 * $attempt, 10000000); // Max 10 seconds + usleep($delay); // Microseconds + return true; + } + return $exception instanceof ConnectionException || + ($exception instanceof RequestException && in_array($exception->response->status(), [500, 502, 503, 504])); + }, false); + + // Add authentication if provided + if ($this->username && $this->password) { + $request = $request->withBasicAuth($this->username, $this->password); + } + + // Make the request + $response = match (strtolower($method)) { + 'get' => $request->get($uri, $query), + 'post' => $request->post($uri . '?' . http_build_query($query), $payload), + 'put' => $request->put($uri, $payload), + 'delete' => $request->delete($uri), + default => throw new Exception("Unsupported HTTP method: {$method}") + }; + + // Handle response + if ($response->failed()) { + // Log additional debug info for 400 errors + if ($response->status() === 400) { + Log::error('[DHL API] HTTP 400 Bad Request Details', [ + 'method' => $method, + 'uri' => $uri, + 'request_payload' => $payload, + 'response_body' => $response->body(), + 'response_json' => $response->json(), + 'status_code' => $response->status(), + 'headers' => $response->headers() + ]); + } + + $this->handleErrorResponse($response, $method, $uri); + } + + return $response->json() ?? []; + } catch (RequestException $e) { + Log::error('DHL API request failed', ['error' => $e->getMessage(), 'method' => $method, 'uri' => $uri]); + throw new DhlApiException("DHL API request failed: {$e->getMessage()}", $e->getCode(), $e); + } catch (Exception $e) { + Log::error('DHL API error', ['error' => $e->getMessage()]); + // Re-throw our own exceptions + if ($e instanceof DhlApiException) { + throw $e; + } + throw new DhlApiException("DHL API error: {$e->getMessage()}", $e->getCode(), $e); + } + } + + /** + * Build HTTP headers for DHL API requests + */ + private function buildHeaders(): array + { + $headers = [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + 'User-Agent' => 'acme-laravel-dhl/1.0' + ]; + + if ($this->apiKey) { + $headers['dhl-api-key'] = $this->apiKey; + } + + return $headers; + } + + /** + * Handle error responses from DHL API + */ + private function handleErrorResponse($response, string $method, string $uri): void + { + $status = $response->status(); + $body = $response->json(); + + $errorMessage = $this->extractErrorMessage($body) ?? "HTTP {$status} error"; + + match (true) { + $status === 401 => throw new DhlAuthenticationException("DHL API authentication failed: {$errorMessage}"), + $status === 403 => throw new DhlAuthenticationException("DHL API access forbidden: {$errorMessage}"), + $status === 404 => throw new DhlApiException("DHL API endpoint not found: {$method} {$uri}"), + $status === 422 => throw new DhlValidationException("DHL API validation error: {$errorMessage}"), + $status === 429 => throw new DhlApiException("DHL API rate limit exceeded. Please try again later."), + $status >= 500 => throw new DhlApiException("DHL API server error: {$errorMessage}"), + default => throw new DhlApiException("DHL API error ({$status}): {$errorMessage}") + }; + } + + /** + * Extract error message from DHL API response + */ + private function extractErrorMessage(?array $body): ?string + { + if (!$body) { + return null; + } + + // Try different possible error message fields + return $body['message'] + ?? $body['error'] + ?? $body['detail'] + ?? data_get($body, 'errors.0.message') + ?? data_get($body, 'error.message') + ?? null; + } + + /** + * Test connection to DHL API + * + * @return bool True if connection successful + */ + public function testConnection(): bool + { + try { + // Check basic connectivity and authentication + // Use the simplest possible endpoint to minimize permission issues + $response = Http::baseUrl($this->baseUrl) + ->withHeaders($this->buildHeaders()) + ->withBasicAuth($this->username, $this->password) + ->timeout(10) + ->get('/'); + + // If we get any response (even 404), the connection and auth are working + if ($response->status() === 401) { + Log::error('DHL API authentication failed: Invalid username/password'); + return false; + } + + if ($response->status() === 403 && str_contains($response->body(), 'api-key')) { + Log::error('DHL API authentication failed: Invalid API key'); + return false; + } + + // Any other response code (including 404, 403 for endpoint access) means connection works + Log::info('DHL API connection test successful', [ + 'status' => $response->status(), + 'has_api_key' => !empty($this->apiKey), + 'has_credentials' => !empty($this->username) && !empty($this->password) + ]); + + return true; + } catch (Exception $e) { + Log::error('DHL API connection test failed', [ + 'error' => $e->getMessage(), + 'base_url' => $this->baseUrl + ]); + return false; + } + } +} diff --git a/packages/acme-laravel-dhl/tests/Unit/AddressParserTest.php b/packages/acme-laravel-dhl/tests/Unit/AddressParserTest.php new file mode 100644 index 0000000..f621a6c --- /dev/null +++ b/packages/acme-laravel-dhl/tests/Unit/AddressParserTest.php @@ -0,0 +1,114 @@ +createMock(\Acme\Dhl\Support\DhlClient::class); + $this->shippingService = new ShippingService($mockClient); + + // Make private method accessible for testing + $reflection = new ReflectionClass($this->shippingService); + $this->parseMethod = $reflection->getMethod('parseGermanAddress'); + $this->parseMethod->setAccessible(true); + } + + /** + * Test standard German address formats + */ + public function testStandardGermanAddresses(): void + { + $testCases = [ + // Basic format: Street + Number + 'Musterstraße 123' => ['street' => 'Musterstraße', 'houseNumber' => '123'], + 'Hauptstraße 1' => ['street' => 'Hauptstraße', 'houseNumber' => '1'], + + // With letter suffix + 'Bahnhofstraße 45a' => ['street' => 'Bahnhofstraße', 'houseNumber' => '45a'], + 'Kirchgasse 12B' => ['street' => 'Kirchgasse', 'houseNumber' => '12B'], + + // Multi-word street names + 'Am Markt 7' => ['street' => 'Am Markt', 'houseNumber' => '7'], + 'An der Kirche 23' => ['street' => 'An der Kirche', 'houseNumber' => '23'], + 'Karl-Marx-Straße 156' => ['street' => 'Karl-Marx-Straße', 'houseNumber' => '156'], + + // Range numbers + 'Lindenstraße 1-3' => ['street' => 'Lindenstraße', 'houseNumber' => '1-3'], + 'Gartenweg 12/14' => ['street' => 'Gartenweg', 'houseNumber' => '12/14'], + + // With abbreviations + 'Muster Str. 99' => ['street' => 'Muster Str.', 'houseNumber' => '99'], + 'Berliner Pl. 5' => ['street' => 'Berliner Pl.', 'houseNumber' => '5'], + ]; + + foreach ($testCases as $input => $expected) { + $result = $this->parseMethod->invoke($this->shippingService, $input); + + $this->assertEquals($expected['street'], $result['street'], "Failed parsing street for: {$input}"); + $this->assertEquals($expected['houseNumber'], $result['houseNumber'], "Failed parsing house number for: {$input}"); + } + } + + /** + * Test edge cases and problematic addresses + */ + public function testEdgeCases(): void + { + $edgeCases = [ + // No house number - should return null for houseNumber + 'Musterstraße' => ['street' => 'Musterstraße', 'houseNumber' => null], + 'Am Markt' => ['street' => 'Am Markt', 'houseNumber' => null], + + // Empty string + '' => ['street' => '', 'houseNumber' => null], + + // Only number + '123' => ['street' => '123', 'houseNumber' => null], + + // Multiple spaces + 'Muster Straße 123' => ['street' => 'Muster Straße', 'houseNumber' => '123'], + ]; + + foreach ($edgeCases as $input => $expected) { + $result = $this->parseMethod->invoke($this->shippingService, $input); + + $this->assertEquals($expected['street'], $result['street'], "Failed parsing street for edge case: {$input}"); + $this->assertEquals($expected['houseNumber'], $result['houseNumber'], "Failed parsing house number for edge case: {$input}"); + } + } + + /** + * Test international addresses (should not be parsed) + */ + public function testInternationalAddresses(): void + { + $internationalCases = [ + // English-style addresses + '123 Main Street' => ['street' => '123 Main Street', 'houseNumber' => null], + '456 Oak Avenue' => ['street' => '456 Oak Avenue', 'houseNumber' => null], + ]; + + foreach ($internationalCases as $input => $expected) { + $result = $this->parseMethod->invoke($this->shippingService, $input); + + $this->assertEquals($expected['street'], $result['street'], "International address should not be parsed: {$input}"); + $this->assertEquals($expected['houseNumber'], $result['houseNumber'], "International address should have null house number: {$input}"); + } + } +} \ No newline at end of file diff --git a/packages/laravel-collective-spatie-html-parser/.editorconfig b/packages/laravel-collective-spatie-html-parser/.editorconfig new file mode 100644 index 0000000..6f313c6 --- /dev/null +++ b/packages/laravel-collective-spatie-html-parser/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.yml] +indent_size = 2 diff --git a/packages/laravel-collective-spatie-html-parser/.gitignore b/packages/laravel-collective-spatie-html-parser/.gitignore new file mode 100644 index 0000000..61ead86 --- /dev/null +++ b/packages/laravel-collective-spatie-html-parser/.gitignore @@ -0,0 +1 @@ +/vendor diff --git a/packages/laravel-collective-spatie-html-parser/CONTRIBUTING.md b/packages/laravel-collective-spatie-html-parser/CONTRIBUTING.md new file mode 100644 index 0000000..7b648ed --- /dev/null +++ b/packages/laravel-collective-spatie-html-parser/CONTRIBUTING.md @@ -0,0 +1,23 @@ +# Contributing to laravel-collective-spatie-html-parser + +Thank you for considering contributing to this project! Your contributions help maintain and improve this Laravel compatibility layer, ensuring a smooth transition between the obsolete laravel-collective library to the maintained laravel-spatie-html. This library also helps to that old proyects to be updated to the new versions of Laravel. + +## How to Contribute + +### 1. Submitting a Pull Request (PR) +- Always create your PR against the `develop` branch. +- Use a **descriptive name** and provide a **clear description** of what the PR does (bug fix, new feature, improvement, etc.). +- Once reviewed and approved, I will merge it into `master` and handle the deployment. + +### 2. Reporting Issues +If you encounter a bug but are unable to fix it yourself, you can still help by reporting it: +- Open a new **Issue** in the repository. +- Provide a **detailed description** of the issue. +- Include **steps to reproduce the problem** so others can verify and fix it. + +## Code Style Guidelines +- Follow Laravel’s coding standards and best practices. +- Keep the code clean and well-documented. +- Ensure that new features or fixes do not break existing functionality. + +Thank you for your support and contributions! diff --git a/packages/laravel-collective-spatie-html-parser/LICENCE.md b/packages/laravel-collective-spatie-html-parser/LICENCE.md new file mode 100644 index 0000000..bc49dc0 --- /dev/null +++ b/packages/laravel-collective-spatie-html-parser/LICENCE.md @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright © 2024 Christian Albán + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/laravel-collective-spatie-html-parser/README.md b/packages/laravel-collective-spatie-html-parser/README.md new file mode 100644 index 0000000..3dc438f --- /dev/null +++ b/packages/laravel-collective-spatie-html-parser/README.md @@ -0,0 +1,66 @@ +# Laravel Collective to Spatie Laravel HTML Adapter + +This package serves as an adapter to help projects that depend on the obsolete `laravel-collective/html` library. It provides an interface that uses the same syntax as the `Form` class of `laravel-collective/html` to create HTML elements. Under the hood, it utilizes the `spatie/laravel-html` library, which is actively maintained, to generate the HTML elements. This allows projects to update to newer Laravel versions without changing the HTML creation syntax. + +## Features +- Zero configuration needed, works out of the box. +- Uses the same syntax as `laravel-collective/html`. +- Leverages the actively maintained `spatie/laravel-html` library. + +## Available Methods +The following methods can be used and are located in the `src/FormAdapter` directory: + +- `checkbox($name, $value = 1, $checked = null, $options = [])` +- `open(array $options = [])` +- `label($name, $value = null, $options = [], $escape_html = true)` +- `text($name, $value = null, $options = [])` +- `password($name, $options = [])` +- `select($name, $list = [], $selected = null, array $selectAttributes = [], array $optionsAttributes = [], array $optgroupsAttributes = [])` +- `radio($name, $value = null, $checked = null, $options = [])` +- `submit($value = null, $options = [])` +- `close()` +- `input($type, $name, $value = null, $options = [])` +- `search($name, $value = null, $options = [])` +- `model($model, array $options = [])` +- `hidden($name, $value = null, $options = [])` +- `email($name, $value = null, $options = [])` +- `tel($name, $value = null, $options = [])` +- `number($name, $value = null, $options = [])` +- `date($name, $value = null, $options = [])` +- `datetime($name, $value = null, $options = [])` +- `datetimeLocal($name, $value = null, $options = [])` +- `time($name, $value = null, $options = [])` +- `url($name, $value = null, $options = [])` +- `file($name, $options = [])` +- `textarea($name, $value = null, $options = [])` +- `reset($value, $attributes = [])` +- `image($url, $name = null, $attributes = [])` +- `color($name, $value = null, $options = [])` +- `button($value = null, $options = [])` + +## Installation + +To install the package, use composer: + +```sh +composer require alban/laravel-collective-spatie-html-parser +``` + +## Usage + +The methods listed above can be used in the same way as you would use the Form class from laravel-collective. Here is an example in a Blade template: + +```php +{{-- Using the FormAdapter class in a Blade template --}} +{!! Form::text('relationship', $item->client->agent_relationship, ['required', 'class' => 'form-control input-sm']) !!} +``` + +For more examples, please refer to the source code in the `src/FormAdapter.php` class file. + +## License + +This package is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). + +## Contributing + +You are welcome to contribute to this project. Please refer to the [contributing guidelines](CONTRIBUTING.md) for more information. diff --git a/packages/laravel-collective-spatie-html-parser/composer.json b/packages/laravel-collective-spatie-html-parser/composer.json new file mode 100644 index 0000000..6556a3d --- /dev/null +++ b/packages/laravel-collective-spatie-html-parser/composer.json @@ -0,0 +1,39 @@ +{ + "name": "alban/laravel-collective-spatie-html-parser", + "description": "Adapter class that allows use spatie/laravel-html for old projects that were using the abanondated laravelcollective/html package, and allow to update to new versions of laravel", + "type": "library", + "require": { + "php": "^8.3 | ^8.2 | ^8.0 | ^7.4", + "spatie/laravel-html": "^3.12", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0" + }, + "license": "MIT", + "autoload": { + "psr-4": { + "Alban\\LaravelCollectiveSpatieHtmlParser\\": "src/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Alban\\LaravelCollectiveSpatieHtmlParser\\ServiceProvider" + ], + "aliases": { + "Form": "Alban\\LaravelCollectiveSpatieHtmlParser\\FormFacade" + } + } + }, + "authors": [ + { + "name": "Christian Albán", + "email": "christianalbanctdb1d@gmail.com" + } + ], + + "minimum-stability": "stable", + "config": { + "allow-plugins": { + "kylekatarnls/update-helper": true + } + } +} diff --git a/packages/laravel-collective-spatie-html-parser/composer.lock b/packages/laravel-collective-spatie-html-parser/composer.lock new file mode 100644 index 0000000..dff9e95 --- /dev/null +++ b/packages/laravel-collective-spatie-html-parser/composer.lock @@ -0,0 +1,3375 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + + "content-hash": "23e543978c15a0bc9f94b485c4b6ebd8", + + "packages": [ + { + "name": "carbonphp/carbon-doctrine-types", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d", + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "conflict": { + "doctrine/dbal": "<4.0.0 || >=5.0.0" + }, + "require-dev": { + "doctrine/dbal": "^4.0.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2024-02-09T16:56:22+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.0.10", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^11.0", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.5", + "vimeo/psalm": "^4.25 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.10" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2024-02-18T20:23:39+00:00" + }, + { + "name": "fruitcake/php-cors", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "symfony/http-foundation": "^4.4|^5.4|^6|^7" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Fruitcake\\Cors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, + { + "name": "Barryvdh", + "email": "barryvdh@gmail.com" + } + ], + "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", + "homepage": "https://github.com/fruitcake/php-cors", + "keywords": [ + "cors", + "laravel", + "symfony" + ], + "support": { + "issues": "https://github.com/fruitcake/php-cors/issues", + "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2023-10-12T05:21:21+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.9.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.9.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2024-07-24T11:22:20+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2024-10-17T10:06:22+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.7.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2024-07-18T11:15:46+00:00" + }, + { + "name": "guzzlehttp/uri-template", + "version": "v1.0.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/uri-template.git", + "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/ecea8feef63bd4fef1f037ecb288386999ecc11c", + "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "uri-template/tests": "1.0.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\UriTemplate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + } + ], + "description": "A polyfill class for uri_template of PHP", + "keywords": [ + "guzzlehttp", + "uri-template" + ], + "support": { + "issues": "https://github.com/guzzle/uri-template/issues", + "source": "https://github.com/guzzle/uri-template/tree/v1.0.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template", + "type": "tidelift" + } + ], + "time": "2023-12-03T19:50:20+00:00" + }, + { + "name": "illuminate/collections", + "version": "v11.35.1", + "source": { + "type": "git", + "url": "https://github.com/illuminate/collections.git", + "reference": "b8be9c0fedfc5be1524b290fed640d4b7d42c813" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/collections/zipball/b8be9c0fedfc5be1524b290fed640d4b7d42c813", + "reference": "b8be9c0fedfc5be1524b290fed640d4b7d42c813", + + "shasum": "" + }, + "require": { + "illuminate/conditionable": "^11.0", + "illuminate/contracts": "^11.0", + "illuminate/macroable": "^11.0", + "php": "^8.2" + }, + "suggest": { + "symfony/var-dumper": "Required to use the dump method (^7.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "11.x-dev" + } + }, + "autoload": { + "files": [ + "functions.php", + "helpers.php" + ], + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Collections package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + + "time": "2024-12-10T14:54:28+00:00" + }, + { + "name": "illuminate/conditionable", + "version": "v11.35.1", + "source": { + "type": "git", + "url": "https://github.com/illuminate/conditionable.git", + "reference": "911df1bda950a3b799cf80671764e34eede131c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/conditionable/zipball/911df1bda950a3b799cf80671764e34eede131c6", + "reference": "911df1bda950a3b799cf80671764e34eede131c6", + + "shasum": "" + }, + "require": { + "php": "^8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "11.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Conditionable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + + "time": "2024-11-21T16:28:56+00:00" + }, + { + "name": "illuminate/contracts", + "version": "v11.35.1", + "source": { + "type": "git", + "url": "https://github.com/illuminate/contracts.git", + "reference": "184317f701ba20ca265e36808ed54b75b115972d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/184317f701ba20ca265e36808ed54b75b115972d", + "reference": "184317f701ba20ca265e36808ed54b75b115972d", + + "shasum": "" + }, + "require": { + "php": "^8.2", + "psr/container": "^1.1.1|^2.0.1", + "psr/simple-cache": "^1.0|^2.0|^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "11.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Contracts\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Contracts package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + + "time": "2024-11-25T15:33:38+00:00" + }, + { + "name": "illuminate/filesystem", + "version": "v11.35.1", + "source": { + "type": "git", + "url": "https://github.com/illuminate/filesystem.git", + "reference": "ec19853eaa9e25bbf3e10cf880a3cd0b6a4b75c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/filesystem/zipball/ec19853eaa9e25bbf3e10cf880a3cd0b6a4b75c4", + "reference": "ec19853eaa9e25bbf3e10cf880a3cd0b6a4b75c4", + + "shasum": "" + }, + "require": { + "illuminate/collections": "^11.0", + "illuminate/contracts": "^11.0", + "illuminate/macroable": "^11.0", + "illuminate/support": "^11.0", + "php": "^8.2", + + "symfony/finder": "^7.0.3" + + }, + "suggest": { + "ext-fileinfo": "Required to use the Filesystem class.", + "ext-ftp": "Required to use the Flysystem FTP driver.", + "ext-hash": "Required to use the Filesystem class.", + "illuminate/http": "Required for handling uploaded files (^7.0).", + "league/flysystem": "Required to use the Flysystem local driver (^3.25.1).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.0).", + "symfony/mime": "Required to enable support for guessing extensions (^7.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "11.x-dev" + } + }, + "autoload": { + "files": [ + "functions.php" + ], + "psr-4": { + "Illuminate\\Filesystem\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Filesystem package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + + "time": "2024-11-26T14:46:56+00:00" + }, + { + "name": "illuminate/http", + "version": "v11.35.1", + "source": { + "type": "git", + "url": "https://github.com/illuminate/http.git", + "reference": "d19f4b1e3aeb9f8a857c7dc7a26f630b6b7a2785" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/http/zipball/d19f4b1e3aeb9f8a857c7dc7a26f630b6b7a2785", + "reference": "d19f4b1e3aeb9f8a857c7dc7a26f630b6b7a2785", + + "shasum": "" + }, + "require": { + "ext-filter": "*", + "fruitcake/php-cors": "^1.3", + + "guzzlehttp/guzzle": "^7.8.2", + + "guzzlehttp/uri-template": "^1.0", + "illuminate/collections": "^11.0", + "illuminate/macroable": "^11.0", + "illuminate/session": "^11.0", + "illuminate/support": "^11.0", + "php": "^8.2", + + "symfony/http-foundation": "^7.0.3", + "symfony/http-kernel": "^7.0.3", + "symfony/mime": "^7.0.3", + "symfony/polyfill-php83": "^1.31" + + }, + "suggest": { + "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image()." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "11.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Http\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Http package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + + "time": "2024-12-11T23:22:44+00:00" + }, + { + "name": "illuminate/macroable", + "version": "v11.35.1", + + "source": { + "type": "git", + "url": "https://github.com/illuminate/macroable.git", + "reference": "e1cb9e51b9ed5d3c9bc1ab431d0a52fe42a990ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/e1cb9e51b9ed5d3c9bc1ab431d0a52fe42a990ed", + "reference": "e1cb9e51b9ed5d3c9bc1ab431d0a52fe42a990ed", + "shasum": "" + }, + "require": { + "php": "^8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "11.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Macroable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2024-06-28T20:10:30+00:00" + }, + { + "name": "illuminate/session", + + "version": "v11.35.1", + "source": { + "type": "git", + "url": "https://github.com/illuminate/session.git", + "reference": "49e98e03b4d6c4f089eb9679bad45b78d9d197d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/session/zipball/49e98e03b4d6c4f089eb9679bad45b78d9d197d5", + "reference": "49e98e03b4d6c4f089eb9679bad45b78d9d197d5", + + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-session": "*", + "illuminate/collections": "^11.0", + "illuminate/contracts": "^11.0", + "illuminate/filesystem": "^11.0", + "illuminate/support": "^11.0", + "php": "^8.2", + + "symfony/finder": "^7.0.3", + "symfony/http-foundation": "^7.0.3" + + }, + "suggest": { + "illuminate/console": "Required to use the session:table command (^11.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "11.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Session\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Session package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + + "time": "2024-12-03T16:20:26+00:00" + }, + { + "name": "illuminate/support", + "version": "v11.35.1", + "source": { + "type": "git", + "url": "https://github.com/illuminate/support.git", + "reference": "bc363403d9ed3214754a0501e33c05d2afcfd474" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/support/zipball/bc363403d9ed3214754a0501e33c05d2afcfd474", + "reference": "bc363403d9ed3214754a0501e33c05d2afcfd474", + + "shasum": "" + }, + "require": { + "doctrine/inflector": "^2.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-mbstring": "*", + "illuminate/collections": "^11.0", + "illuminate/conditionable": "^11.0", + "illuminate/contracts": "^11.0", + "illuminate/macroable": "^11.0", + + "nesbot/carbon": "^2.72.2|^3.4", + "php": "^8.2", + "voku/portable-ascii": "^2.0.2" + + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "replace": { + "spatie/once": "*" + }, + "suggest": { + + "illuminate/filesystem": "Required to use the Composer class (^11.0).", + + "laravel/serializable-closure": "Required to use the once function (^1.3).", + "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.0.2).", + "league/uri": "Required to use the Uri class (^7.5.1).", + "ramsey/uuid": "Required to use Str::uuid() (^4.7).", + + "symfony/process": "Required to use the Composer class (^7.0).", + "symfony/uid": "Required to use Str::ulid() (^7.0).", + "symfony/var-dumper": "Required to use the dd function (^7.0).", + "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.6.1)." + + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "11.x-dev" + } + }, + "autoload": { + "files": [ + "functions.php", + "helpers.php" + ], + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Support package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + + "time": "2024-12-11T23:23:18+00:00" + }, + { + "name": "nesbot/carbon", + "version": "3.8.2", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "e1268cdbc486d97ce23fef2c666dc3c6b6de9947" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/e1268cdbc486d97ce23fef2c666dc3c6b6de9947", + "reference": "e1268cdbc486d97ce23fef2c666dc3c6b6de9947", + + "shasum": "" + }, + "require": { + "carbonphp/carbon-doctrine-types": "<100.0", + "ext-json": "*", + "php": "^8.1", + "psr/clock": "^1.0", + "symfony/clock": "^6.3 || ^7.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation": "^4.4.18 || ^5.2.1|| ^6.0 || ^7.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^3.6.3 || ^4.0", + "doctrine/orm": "^2.15.2 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.57.2", + "kylekatarnls/multi-tester": "^2.5.3", + "ondrejmirtes/better-reflection": "^6.25.0.4", + "phpmd/phpmd": "^2.15.0", + "phpstan/extension-installer": "^1.3.1", + "phpstan/phpstan": "^1.11.2", + "phpunit/phpunit": "^10.5.20", + "squizlabs/php_codesniffer": "^3.9.0" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev", + "dev-2.x": "2.x-dev" + }, + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/Carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2024-11-07T17:46:48+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + + + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "spatie/laravel-html", + "version": "3.11.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-html.git", + "reference": "167e5b8243103072155b562e5cc396c90a3c1055" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-html/zipball/167e5b8243103072155b562e5cc396c90a3c1055", + "reference": "167e5b8243103072155b562e5cc396c90a3c1055", + "shasum": "" + }, + "require": { + "illuminate/http": "^10.0|^11.0", + "illuminate/support": "^10.0|^11.0", + "php": "^8.2" + }, + "require-dev": { + "mockery/mockery": "^1.3", + "orchestra/testbench": "^8.0|^9.0", + "pestphp/pest": "^2.34" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\Html\\HtmlServiceProvider" + ], + "aliases": { + "Html": "Spatie\\Html\\Facades\\Html" + } + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\Html\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian De Deyne", + "email": "sebastian@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + }, + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "A fluent html builder", + "homepage": "https://github.com/spatie/laravel-html", + "keywords": [ + "html", + "spatie" + ], + "support": { + "source": "https://github.com/spatie/laravel-html/tree/3.11.1" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + } + ], + "time": "2024-10-18T14:37:21+00:00" + }, + { + "name": "symfony/clock", + + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/clock.git", + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/clock": "^1.0", + "symfony/polyfill-php83": "^1.28" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/now.php" + ], + "psr-4": { + "Symfony\\Component\\Clock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Decouples applications from the system clock", + "homepage": "https://symfony.com", + "keywords": [ + "clock", + "psr20", + "time" + ], + "support": { + + "source": "https://github.com/symfony/clock/tree/v7.2.0" + + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + + "time": "2024-09-25T14:21:43+00:00" + + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/error-handler", + + "version": "v7.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "6150b89186573046167796fa5f3f76601d5145f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/6150b89186573046167796fa5f3f76601d5145f8", + "reference": "6150b89186573046167796fa5f3f76601d5145f8", + + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^6.4|^7.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + + "source": "https://github.com/symfony/error-handler/tree/v7.2.1" + + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + + "time": "2024-12-07T08:50:44+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/910c5db85a5356d0fea57680defec4e99eb9c8c1", + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1", + + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + + "source": "https://github.com/symfony/event-dispatcher/tree/v7.2.0" + + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + + "time": "2024-09-25T14:21:43+00:00" + + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f", + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/finder", + + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "6de263e5868b9a137602dd1e33e4d48bfae99c49" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/6de263e5868b9a137602dd1e33e4d48bfae99c49", + "reference": "6de263e5868b9a137602dd1e33e4d48bfae99c49", + + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + + "source": "https://github.com/symfony/finder/tree/v7.2.0" + + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + + "time": "2024-10-23T06:56:12+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "e88a66c3997859532bc2ddd6dd8f35aba2711744" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e88a66c3997859532bc2ddd6dd8f35aba2711744", + "reference": "e88a66c3997859532bc2ddd6dd8f35aba2711744", + + "shasum": "" + }, + "require": { + "php": ">=8.2", + + "symfony/deprecation-contracts": "^2.5|^3.0", + + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" + }, + "conflict": { + "doctrine/dbal": "<3.6", + + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + + }, + "require-dev": { + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + + "symfony/cache": "^6.4.12|^7.1.5", + + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + + "source": "https://github.com/symfony/http-foundation/tree/v7.2.0" + + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + + "time": "2024-11-13T18:58:46+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v7.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "d8ae58eecae44c8e66833e76cc50a4ad3c002d97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/d8ae58eecae44c8e66833e76cc50a4ad3c002d97", + "reference": "d8ae58eecae44c8e66833e76cc50a4ad3c002d97", + + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/browser-kit": "<6.4", + "symfony/cache": "<6.4", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<6.4", + "symfony/form": "<6.4", + "symfony/http-client": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<6.4", + "symfony/messenger": "<6.4", + "symfony/translation": "<6.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<6.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.4", + + "twig/twig": "<3.12" + + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^6.4|^7.0", + "symfony/clock": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/css-selector": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dom-crawler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^7.1", + "symfony/routing": "^6.4|^7.0", + "symfony/serializer": "^7.1", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0", + "symfony/var-exporter": "^6.4|^7.0", + + "twig/twig": "^3.12" + + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + + "source": "https://github.com/symfony/http-kernel/tree/v7.2.1" + + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + + "time": "2024-12-11T12:09:10+00:00" + }, + { + "name": "symfony/mime", + "version": "v7.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "7f9617fcf15cb61be30f8b252695ed5e2bfac283" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/7f9617fcf15cb61be30f8b252695ed5e2bfac283", + "reference": "7f9617fcf15cb61be30f8b252695ed5e2bfac283", + + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<6.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + + "source": "https://github.com/symfony/mime/tree/v7.2.1" + + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + + "time": "2024-12-07T08:50:44+00:00" + + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php83", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/translation", + + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "dc89e16b44048ceecc879054e5b7f38326ab6cc5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/dc89e16b44048ceecc879054e5b7f38326ab6cc5", + "reference": "dc89e16b44048ceecc879054e5b7f38326ab6cc5", + + "shasum": "" + }, + "require": { + "php": ">=8.2", + + "symfony/deprecation-contracts": "^2.5|^3", + + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<6.4", + "symfony/yaml": "<6.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.18|^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + + "source": "https://github.com/symfony/translation/tree/v7.2.0" + + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + + "time": "2024-11-12T20:47:56+00:00" + + }, + { + "name": "symfony/translation-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c", + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/var-dumper", + + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "c6a22929407dec8765d6e2b6ff85b800b245879c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c6a22929407dec8765d6e2b6ff85b800b245879c", + "reference": "c6a22929407dec8765d6e2b6ff85b800b245879c", + + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0", + "twig/twig": "^3.12" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + + "source": "https://github.com/symfony/var-dumper/tree/v7.2.0" + + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + + "time": "2024-11-08T15:48:14+00:00" + + }, + { + "name": "voku/portable-ascii", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "https://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2024-11-21T01:49:47+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^8.2" + }, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/packages/laravel-collective-spatie-html-parser/src/FormAdapter.php b/packages/laravel-collective-spatie-html-parser/src/FormAdapter.php new file mode 100644 index 0000000..c094ab6 --- /dev/null +++ b/packages/laravel-collective-spatie-html-parser/src/FormAdapter.php @@ -0,0 +1,218 @@ +mergeOptions($element, $options); + } + + public function open(array $options = []) + { + $method = array_key_exists('method', $options) ? $options['method'] : 'POST'; + $route = array_key_exists('route', $options) ? $options['route'] : ''; + $files = array_key_exists('files', $options) ? $options['files'] : false; + + unset($options['method'], $options['route'], $options['files']); + + $form = \Html::getFacadeRoot(); + + if (is_array($route) && count($route)) { + $action = array_shift($route); + $form = $form->form($method, route($action, $route)); + } elseif ($route != null && $route != [] && $route != '') { + $form = $form->form($method, route($route)); + } else { + $form = $form->form($method); + } + + if ($files) { + $form = $form->acceptsFiles(); + } + + return $this->mergeOptions($form, $options)->open(); + } + + public function label($name, $value = null, $options = [], $escape_html = true) + { + $element = \Html::label($value, $name); + + return $this->mergeOptions($element, $options); + } + + public function text($name, $value = null, $options = []) + { + $element = \Html::text($name, $value); + + return $this->mergeOptions($element, $options); + } + + public function password($name, $options = []) + { + $element = \Html::password($name); + + return $this->mergeOptions($element, $options); + } + + public function select( + $name, + $list = [], + $selected = null, + array $selectAttributes = [], + array $optionsAttributes = [], + array $optgroupsAttributes = [] + ) { + if (isset($selectAttributes['multiple']) || in_array('multiple', $selectAttributes)) { + $element = \Html::select($name, $list, $selected)->multiple(); + } else { + $element = \Html::select($name, $list, $selected); + } + // $element = \Html::select($name, $list, $selected); + + return $this->mergeOptions($element, $selectAttributes); + } + + + public function radio($name, $value = null, $checked = null, $options = []) + { + $element = \Html::radio($name, $checked, $value); + + return $this->mergeOptions($element, $options); + } + + public function submit($value = null, $options = []) + { + $element = \Html::submit($value); + + return $this->mergeOptions($element, $options); + } + + public function close() + { + \Html::endModel(); + + return \Html::form()->close(); + } + + public function input($type, $name, $value = null, $options = []) + { + $element = \Html::input($type, $name, $value); + + return $this->mergeOptions($element, $options); + } + + public function search($name, $value = null, $options = []) + { + return $this->input('search', $name, $value, $options); + } + + public function model($model, array $options = []) + { + \Html::model($model); + + return $this->open($options); + } + + public function hidden($name, $value = null, $options = []) + { + $element = \Html::hidden($name, $value); + + return $this->mergeOptions($element, $options); + } + + public function email($name, $value = null, $options = []) + { + $element = \Html::email($name, $value); + + return $this->mergeOptions($element, $options); + } + + public function tel($name, $value = null, $options = []) + { + $element = \Html::tel($name, $value); + + return $this->mergeOptions($element, $options); + } + + public function number($name, $value = null, $options = []) + { + return $this->input('number', $name, $value, $options); + } + + public function date($name, $value = null, $options = []) + { + $element = \Html::date($name, $value); + + return $this->mergeOptions($element, $options); + } + + public function datetime($name, $value = null, $options = []) + { + return $this->input('datetime', $name, $value, $options); + } + + public function datetimeLocal($name, $value = null, $options = []) + { + return $this->input('datetime-local', $name, $value, $options); + } + + public function time($name, $value = null, $options = []) + { + $element = \Html::time($name, $value); + + return $this->mergeOptions($element, $options); + } + + public function url($name, $value = null, $options = []) + { + return $this->input('url', $name, $value, $options); + } + + public function file($name, $options = []) + { + $element = \Html::file($name); + + return $this->mergeOptions($element, $options); + } + + public function textarea($name, $value = null, $options = []) + { + $element = \Html::textarea($name, $value); + + return $this->mergeOptions($element, $options); + } + + public function reset($value, $attributes = []) + { + $element = \Html::reset($value); + + return $this->mergeOptions($element, $attributes); + } + + public function image($url, $name = null, $attributes = []) + { + $element = \Html::img($url, $name); + + return $this->mergeOptions($element, $attributes); + } + + public function color($name, $value = null, $options = []) + { + return $this->input('color', $name, $value, $options); + } + + public function button($value = null, $options = []) + { + $element = \Html::button($value); + + return $this->mergeOptions($element, $options); + } +} diff --git a/packages/laravel-collective-spatie-html-parser/src/FormFacade.php b/packages/laravel-collective-spatie-html-parser/src/FormFacade.php new file mode 100644 index 0000000..1ebe332 --- /dev/null +++ b/packages/laravel-collective-spatie-html-parser/src/FormFacade.php @@ -0,0 +1,22 @@ +registerFormAdpater(); + $this->registerHtmlAdapter(); + + $this->app->alias('form', FormAdapter::class); + $this->app->alias('html', HtmlAdapter::class); + } + + /** + * Register the HTML adapter instance. + * + * @return void + */ + protected function registerFormAdpater() + { + $this->app->singleton('form', function () { + return new FormAdapter(); + }); + } + + /** + * Register the HTML adapter instance. + * + * @return void + */ + protected function registerHtmlAdapter() + { + $this->app->singleton('html', function () { + return new HtmlAdapter(); + }); + } +} diff --git a/packages/laravel-collective-spatie-html-parser/src/Traits/AttributesUtils.php b/packages/laravel-collective-spatie-html-parser/src/Traits/AttributesUtils.php new file mode 100644 index 0000000..320f3f7 --- /dev/null +++ b/packages/laravel-collective-spatie-html-parser/src/Traits/AttributesUtils.php @@ -0,0 +1,35 @@ +addClass($options['class']); + unset($options['class']); + } + + if (isset($options['placeholder'])) { + $newElement = $newElement->placeholder($options['placeholder']); + unset($options['placeholder']); + } + + foreach ($options as $key => $value) { + if (!$value) { + continue; + } + + if (is_numeric($key)) { + $newElement = $newElement->attribute($value, ''); + continue; + } + + $newElement = $newElement->attribute($key, $value); + } + return $newElement; + } +} diff --git a/resources/lang/de/navigation.php b/resources/lang/de/navigation.php index d6cdd0c..c8ba298 100644 --- a/resources/lang/de/navigation.php +++ b/resources/lang/de/navigation.php @@ -68,4 +68,6 @@ return array ( 'shop' => 'Shop', 'to_shop' => 'Zum Shop', 'marketingplan' => 'Marketingplan', + 'dhl_cockpit' => 'DHL Cockpit', + 'revenue' => 'Umsatz', ); diff --git a/resources/lang/de/shop.php b/resources/lang/de/shop.php index 6269442..a4968ef 100644 --- a/resources/lang/de/shop.php +++ b/resources/lang/de/shop.php @@ -8,6 +8,7 @@ return array ( 'Your Shop' => 'Dein Shop', 'Your Shop Name' => 'Gib deinem Shop einen Namen', 'accept_and_next' => 'akzeptieren und weiter', + 'accept_and_change' => 'akzeptieren und ändern', 'active_since' => 'Aktiv seit', 'available' => 'erreichbar', 'check' => 'prüfen', @@ -16,11 +17,12 @@ return array ( 'name' => 'Name', 'not_available' => 'nicht erreichbar', 'not_available_copy' => 'Bei neu angelegten Shops dauert es einige Minuten, bis die Domain zu erreichen ist. Bitte schaue in einigen Minuten noch einmal nach.', - 'open_copy_1' => 'Wähle das Präfix Deines Shopnamens - z. B. vorname(.mivita.care) - Du musst nicht "mivita.care" eingeben. Wähle einen kurzen und prägnanten Namen, damit Du diese URL auch auf kleinen Werbemitteln unterbringen kannst. Achte auch darauf, dass Du keine Markenrechte Dritter verletzt. Dafür kann MIVITA keine Haftung übernehmen. Daher versucht Phantasiebegriffe zu vermeiden, es sei denn Du hast sie als Marke angemeldet.', - 'open_copy_2' => 'Deine Internetadresse kann später mit „www.“ und ohne aufgerufen werden.', - 'open_note_1' => 'Aus Deinem Shop-Namen wird die Internet-Adresse (Domain) erstellt, mit der Dein Shop aufgerufen werden kann. Wähle Deinen Shop-Namen sorgfältig aus. Dieser ist nur durch die IT änderbar und wir müssten eine Aufwandsentschädigung von 59,50 € erheben.', + 'open_copy_1' => 'Wähle das Präfix Deines Shopnamens - z. B. vorname(.mivita.care).
Du musst nicht "mivita.care" eingeben.

Achte darauf, einen kurzen und prägnanten Namen zu wählen, damit Deine Internetadresse auch auf kleinen Werbemitteln gut Platz findet. Stelle außerdem sicher, dass Du keine Markenrechte Dritter verletzt – dafür kann MIVITA keine Haftung übernehmen. Vermeide daher möglichst Fantasiebegriffe, es sei denn, Du hast diese bereits als Marke angemeldet.', + 'open_copy_2' => 'Deine Internetadresse wird ohne „www.“ aufgerufen: https://deinshopname.mivita.care', + 'open_note_1' => 'Du kannst den Namen Deines Shops jederzeit ändern. Der alte Shopname verfällt jedoch sofort und unwiderruflich. Wenn Du die alte Adresse bereits verteilt hast (z. B. auf Flyern, Visitenkarten oder in sozialen Medien), ist diese danach nicht mehr erreichbar.', 'open_note_hl' => 'WICHTIGER HINWEIS:', - 'open_your_shop' => 'Eröffne Deinen eigenen mivita-Shop.', + 'open_your_shop' => 'Eröffne jetzt Deinen eigenen mivita-Shop!', + 'edit_your_shop' => 'Dein Shop-Name anpassen', 'order_customer' => 'Bestellung Kunde', 'orders_customers' => 'Bestellungen Kunden', 'preview_shop_internet_address' => 'Vorschau Shop-Internet Adresse', @@ -50,6 +52,6 @@ return array ( 'your_city' => 'Dein Ort', 'your_phone_number' => 'Deine Festnetz-Nummer', 'your_mobile_number' => 'Deine Mobil-Nummer', - 'error_subdomain_exists' => 'Fehler: Subdomain existierts bereits, bitte einen neues Namen wählen' - + 'error_subdomain_exists' => 'Fehler: Subdomain existierts bereits, bitte einen neues Namen wählen', + 'your_current_shop_name' => 'Dein aktueller Shop-Name lautet:' ); diff --git a/resources/views/admin/dhl/cockpit.blade.php b/resources/views/admin/dhl/cockpit.blade.php new file mode 100644 index 0000000..718c3e9 --- /dev/null +++ b/resources/views/admin/dhl/cockpit.blade.php @@ -0,0 +1,578 @@ +@extends('layouts.layout-2') + +@section('content') + +

+ + DHL Cockpit +

+ + +
+
+
+
+
+
+
{{ number_format($stats['total_shipments']) }}
+ Sendungen gesamt +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
{{ number_format($stats['pending_shipments']) }}
+ In Bearbeitung +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
{{ number_format($stats['shipped_today']) }}
+ Heute versandt +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
{{ number_format($stats['returns_count']) }}
+ Retouren +
+
+ +
+
+
+
+
+
+ + +
+
+
+ + Filter & Suche +
+
+
+
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+
+ + +
+ +
+ + + Zurücksetzen + +
+
+
+
+
+ + +
+
+
+ + Schnellaktionen +
+
+
+
+
+
+
+ +
Neue Sendung erstellen
+

+ Erstellen Sie eine neue DHL-Sendung basierend auf einer bestehenden Bestellung. +

+ +
+
+
+ +
+
+
+ +
Sendung anzeigen
+

+ Suchen und anzeigen einer bestimmten Sendung nach ID oder Tracking-Nummer. +

+ +
+
+
+ +
+
+
+ +
API Login Test
+

+ Überprüfen Sie, ob die in der .env-Datei hinterlegten DHL-Zugangsdaten korrekt sind. +

+ +
+
+
+
+
+
+
+ + +
+
+
+ + Sendungsübersicht +
+ + +
+
+ + + + + + + + + + + + + + + + + + {{-- Data will be loaded by DataTables --}} + +
+ + #TypBestellungKundeSendungsnummerStatusTracking-StatusGewichtErstelltAktionen
+
+ + {{-- Pagination will be handled by DataTables --}} +
+ + +@endsection + +@section('scripts') + +@endsection \ No newline at end of file diff --git a/resources/views/admin/dhl/create.blade.php b/resources/views/admin/dhl/create.blade.php new file mode 100644 index 0000000..87baa81 --- /dev/null +++ b/resources/views/admin/dhl/create.blade.php @@ -0,0 +1,428 @@ +@extends('layouts.layout-2') + +@section('content') + +
+

+ + DHL Sendung erstellen +

+ + Zurück zum Cockpit + +
+ + +
+
+
+ + Bestellinformationen +
+
+
+
+
+ + + + + + + + + + + + + + + + + +
Bestellungs-ID:#{{ $order->id }}
Kunde: + {{ $order->user->first_name ?? '' }} {{ $order->user->last_name ?? '' }} +
+ {{ $order->user->email ?? '' }} +
Bestelldatum:{{ $order->created_at->format('d.m.Y H:i') }}
Bestellwert:{{ number_format($order->order_total, 2) }} €
+
+
+ + + + + + + + + + + + + +
Lieferadresse: + {{ $order->shipping_first_name ?? $order->user->first_name }} {{ $order->shipping_last_name ?? $order->user->last_name }}
+ {{ $order->shipping_street ?? $order->user->street }} {{ $order->shipping_house_number ?? $order->user->house_number }}
+ {{ $order->shipping_postcode ?? $order->user->postcode }} {{ $order->shipping_city ?? $order->user->city }}
+ {{ $order->shippingCountry->name ?? 'Deutschland' }} +
Status: + + {{ $order->status }} + +
Artikel:{{ $order->items->count() }} Artikel
+
+
+
+
+ + +
+
+
+ + Artikel in der Bestellung +
+
+
+
+ + + + + + + + + + + + @php $totalWeight = 0; @endphp + @foreach($order->items as $item) + @php + $itemWeight = ($item->product->weight ?? 0) * $item->qty; + $totalWeight += $itemWeight; + @endphp + + + + + + + + @endforeach + + + + + + + +
ArtikelMengeEinzelpreisGesamtpreisGewicht (kg)
+ {{ $item->product->name ?? 'Unbekanntes Produkt' }} + @if($item->product->number) +
+ Art.-Nr: {{ $item->product->number }} + @endif +
{{ $item->qty }}{{ number_format($item->price, 2) }} €{{ number_format($item->price * $item->qty, 2) }} €{{ number_format($itemWeight, 3) }}
Geschätztes Gesamtgewicht:{{ number_format($totalWeight, 3) }} kg
+
+
+
+ + +
+
+
+ + Sendung konfigurieren +
+
+
+
+ @csrf + + +
+
+
+ + + + Mindestens 0.1 kg, maximal 31.5 kg (DHL Paket Limit) + +
+ +
+ + + + Standardmäßig DHL Paket für nationale Sendungen + +
+ +
+ + + + Hohe Priorität für dringende Sendungen + +
+
+ +
+
+ + +
+ + + + Startet automatische Sendungsverfolgung nach Erstellung + +
+
+ +
+
+
+ + Versandhinweise +
+
    +
  • Die Sendung wird asynchron erstellt
  • +
  • Sie erhalten eine Benachrichtigung nach Fertigstellung
  • +
  • Das Versandlabel wird automatisch generiert
  • +
  • Bei aktiviertem Auto-Tracking wird die Sendung überwacht
  • + @if($totalWeight > 10) +
  • + + Hohes Gewicht - prüfen Sie die Verpackung +
  • + @endif +
+
+
+
+
+ +
+ +
+
+
+ + * Pflichtfelder | Geschätzte Verarbeitungszeit: 2-5 Minuten + +
+
+ + +
+
+
+
+
+
+ + + + +@endsection + +@section('scripts') + +@endsection \ No newline at end of file diff --git a/resources/views/admin/dhl/modal_create_shipment.blade.php b/resources/views/admin/dhl/modal_create_shipment.blade.php new file mode 100644 index 0000000..afd7677 --- /dev/null +++ b/resources/views/admin/dhl/modal_create_shipment.blade.php @@ -0,0 +1,498 @@ +{{-- + DHL Shipment Creation Modal + + This view is optimized to work with data prepared by DhlModalService. + The service handles address parsing, validation, and data preparation. +--}} + + + + \ No newline at end of file diff --git a/resources/views/admin/dhl/show.blade.php b/resources/views/admin/dhl/show.blade.php new file mode 100644 index 0000000..53ca9c2 --- /dev/null +++ b/resources/views/admin/dhl/show.blade.php @@ -0,0 +1,600 @@ +@extends('layouts.layout-2') + +@section('content') + +
+

+ + DHL Sendung #{{ $shipment->id }} + @if($shipment->type == 'return') + + Retoure + + @endif +

+
+ + Zurück zum Cockpit + + @if($shipment->shoppingOrder) + + Bestellung anzeigen + + @endif +
+
+ + +
+
+
+
+
+
+
Status
+ @switch($shipment->status) + @case('pending') +
+ Wartend +
+ @break + @case('created') +
+ Erstellt +
+ @break + @case('shipped') +
+ Versendet +
+ @break + @case('delivered') +
+ Zugestellt +
+ @break + @case('cancelled') +
+ Storniert +
+ @break + @case('failed') +
+ Fehler +
+ @break + @default +
+ {{ $shipment->status }} +
+ @endswitch +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
Sendungsnummer
+
+ @if($shipment->shipment_number) + {{ $shipment->shipment_number }} + @else + Nicht verfügbar + @endif +
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
Tracking-Nummer
+
+ @if($shipment->tracking_number) + {{ $shipment->tracking_number }} +
+ + Verfolgen + + @else + Nicht verfügbar + @endif +
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
Gewicht
+
+ {{ number_format($shipment->weight, 2) }} kg +
+
+
+ +
+
+
+
+
+
+ + +@if($shipment->status != 'cancelled') +
+
+
+ @if($shipment->label_path) + + Label herunterladen + + @endif + + @if($shipment->canCancel()) + + @endif + + @if($shipment->type == 'outbound' && !$shipment->relatedShipment) + + @endif + + @if($shipment->tracking_number) + + @endif +
+
+
+@endif + + +
+ +
+ +
+
+
+ + Sendungsinformationen +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
ID:#{{ $shipment->id }}
Typ: + @if($shipment->type == 'outbound') + + Ausgehend + + @else + + Retoure + + @if($shipment->relatedShipment) +
+ + Bezieht sich auf Sendung + + #{{ $shipment->relatedShipment->id }} + + + @endif + @endif +
Produktcode:{{ $shipment->product_code }}
Label-Format:{{ strtoupper($shipment->label_format) }}
Label gedruckt: + @if($shipment->label_printed) + Ja + @else + Nein + @endif +
+
+
+ + + + + + + + + + @if($shipment->last_tracked_at) + + + + + @endif + @if($shipment->length || $shipment->width || $shipment->height) + + + + + @endif +
Erstellt:{{ $shipment->created_at->format('d.m.Y H:i:s') }}
Letzte Änderung:{{ $shipment->updated_at->format('d.m.Y H:i:s') }}
Letztes Tracking:{{ $shipment->last_tracked_at->format('d.m.Y H:i:s') }}
Abmessungen: + {{ $shipment->length }}×{{ $shipment->width }}×{{ $shipment->height }} cm +
+
+
+
+
+ + + @if($shipment->shoppingOrder) +
+
+
+ + Bestellinformationen +
+
+
+
+
+ + + + + + + + + + + + + +
Bestellungs-ID: + + #{{ $shipment->shoppingOrder->id }} + +
Kunde: + @if($shipment->shoppingOrder->shopping_user) + {{ $shipment->shoppingOrder->shopping_user->billing_firstname }} {{ $shipment->shoppingOrder->shopping_user->billing_lastname }} +
+ {{ $shipment->shoppingOrder->shopping_user->billing_email }} + @else + Unbekannt + @endif +
Bestelldatum:{{ $shipment->shoppingOrder->created_at->format('d.m.Y H:i') }}
+
+
+ + + + + + + + + + + + + +
Bestellwert:{{ number_format($shipment->shoppingOrder->total, 2) }} €
Status: + + {{ $shipment->shoppingOrder->status }} + +
Artikel:{{ $shipment->shoppingOrder->shopping_order_items->count() ?? 0 }} Artikel
+
+
+
+
+ @endif + + + @if($shipment->tracking_status || $shipment->tracking_number) +
+
+
+ + Tracking-Informationen +
+
+
+ @if($shipment->tracking_status) +
+
+ + Aktueller Status: {{ $shipment->tracking_status }} +
+ @if($shipment->last_tracked_at) + + Zuletzt aktualisiert: {{ $shipment->last_tracked_at->format('d.m.Y H:i') }} + + @endif +
+ @endif + + @if($shipment->tracking_number) +
+

Verfolgen Sie diese Sendung direkt bei DHL:

+ + + Bei DHL verfolgen + + + + Lokales Tracking + +
+ @endif +
+
+ @endif +
+ + +
+ + @if($shipment->type == 'outbound' && $shipment->relatedShipments && $shipment->relatedShipments->count() > 0) +
+
+
+ + Verknüpfte Retouren +
+
+
+ @foreach($shipment->relatedShipments as $relatedShipment) +
+
+ + Retoure #{{ $relatedShipment->id }} + +
+ {{ $relatedShipment->created_at->format('d.m.Y H:i') }} +
+ + {{ $relatedShipment->status }} + +
+ @endforeach +
+
+ @endif + + + @if($shipment->services) +
+
+
+ + Zusatzleistungen +
+
+
+ @foreach($shipment->services as $service => $enabled) + @if($enabled) +
{{ $service }}
+ @endif + @endforeach +
+
+ @endif + + + @if($shipment->api_response_data && auth()->user()->isSuperAdmin()) +
+
+
+ + API Response (Debug) +
+
+
+
{{ json_encode($shipment->api_response_data, JSON_PRETTY_PRINT) }}
+
+
+ @endif +
+
+ +@endsection + +@section('scripts') + +@endsection \ No newline at end of file diff --git a/resources/views/admin/settings/index.blade.php b/resources/views/admin/settings/index.blade.php index 4bffc87..aadd12c 100755 --- a/resources/views/admin/settings/index.blade.php +++ b/resources/views/admin/settings/index.blade.php @@ -107,6 +107,220 @@ + + +
+
+

DHL Versandeinstellungen

+ + @if(Session::has('alert-save-dhl')) + + @endif + + +
+
+
Standard-Einstellungen
+
+
+ + {{ Form::text('settings[dhl_base_url][val]', \App\Models\Setting::getContentBySlug('dhl_base_url') ?: 'https://api-eu.dhl.com', array('class'=>'form-control')) }} + {{ Form::hidden('settings[dhl_base_url][type]', 'text') }} +
+
+ + {{ Form::text('settings[dhl_api_key][val]', \App\Models\Setting::getContentBySlug('dhl_api_key'), array('class'=>'form-control')) }} + {{ Form::hidden('settings[dhl_api_key][type]', 'text') }} +
+
+ + {{ Form::text('settings[dhl_username][val]', \App\Models\Setting::getContentBySlug('dhl_username'), array('class'=>'form-control')) }} + {{ Form::hidden('settings[dhl_username][type]', 'text') }} +
+
+ + {{ Form::password('settings[dhl_password][val]', array('class'=>'form-control', 'value' => \App\Models\Setting::getContentBySlug('dhl_password'))) }} + {{ Form::hidden('settings[dhl_password][type]', 'text') }} +
+
+ + {{ Form::text('settings[dhl_billing_number][val]', \App\Models\Setting::getContentBySlug('dhl_billing_number'), array('class'=>'form-control')) }} + {{ Form::hidden('settings[dhl_billing_number][type]', 'text') }} +
+
+ + {{ Form::select('settings[dhl_product][val]', [ + 'V01PAK' => 'V01PAK - DHL Paket National', + 'V53PAK' => 'V53PAK - DHL Paket International', + 'V62WP' => 'V62WP - Warenpost National' + ], \App\Models\Setting::getContentBySlug('dhl_product') ?: 'V01PAK', array('class'=>'form-control custom-select')) }} + {{ Form::hidden('settings[dhl_product][type]', 'text') }} +
+
+ + {{ Form::select('settings[dhl_label_format][val]', [ + 'PDF' => 'PDF', + 'ZPL' => 'ZPL' + ], \App\Models\Setting::getContentBySlug('dhl_label_format') ?: 'PDF', array('class'=>'form-control custom-select')) }} + {{ Form::hidden('settings[dhl_label_format][type]', 'text') }} +
+
+ + {{ Form::hidden('settings[dhl_use_queue][type]', 'bool') }} + + Aktiviert: Versandlabel werden im Hintergrund erstellt (erfordert Queue Worker)
+ Deaktiviert: Versandlabel werden sofort erstellt (synchron) +
+
+
+ +
+ + +
+
+
Account Nummern für verschiedene Produkte
+
+
+ + {{ Form::text('settings[dhl_account_v01pak][val]', \App\Models\Setting::getContentBySlug('dhl_account_v01pak'), array('class'=>'form-control')) }} + {{ Form::hidden('settings[dhl_account_v01pak][type]', 'text') }} +
+
+ + {{ Form::text('settings[dhl_account_v07pak][val]', \App\Models\Setting::getContentBySlug('dhl_account_v07pak'), array('class'=>'form-control')) }} + {{ Form::hidden('settings[dhl_account_v07pak][type]', 'text') }} +
+
+ + {{ Form::text('settings[dhl_account_v53pak][val]', \App\Models\Setting::getContentBySlug('dhl_account_v53pak'), array('class'=>'form-control')) }} + {{ Form::hidden('settings[dhl_account_v53pak][type]', 'text') }} +
+
+ + {{ Form::text('settings[dhl_account_v62wp][val]', \App\Models\Setting::getContentBySlug('dhl_account_v62wp'), array('class'=>'form-control')) }} + {{ Form::hidden('settings[dhl_account_v62wp][type]', 'text') }} +
+
+ +
+ + +
+
+
Absenderadresse
+
+
+ + {{ Form::text('settings[dhl_sender_company][val]', \App\Models\Setting::getContentBySlug('dhl_sender_company') ?: 'mivita care gmbh', array('class'=>'form-control')) }} + {{ Form::hidden('settings[dhl_sender_company][type]', 'text') }} +
+
+ + {{ Form::text('settings[dhl_sender_name][val]', \App\Models\Setting::getContentBySlug('dhl_sender_name'), array('class'=>'form-control')) }} + {{ Form::hidden('settings[dhl_sender_name][type]', 'text') }} +
+
+ + {{ Form::text('settings[dhl_sender_street][val]', \App\Models\Setting::getContentBySlug('dhl_sender_street') ?: 'Leinfeld', array('class'=>'form-control')) }} + {{ Form::hidden('settings[dhl_sender_street][type]', 'text') }} +
+
+ + {{ Form::text('settings[dhl_sender_house_number][val]', \App\Models\Setting::getContentBySlug('dhl_sender_house_number') ?: '2', array('class'=>'form-control')) }} + {{ Form::hidden('settings[dhl_sender_house_number][type]', 'text') }} +
+
+ + {{ Form::text('settings[dhl_sender_postal_code][val]', \App\Models\Setting::getContentBySlug('dhl_sender_postal_code') ?: '87755', array('class'=>'form-control')) }} + {{ Form::hidden('settings[dhl_sender_postal_code][type]', 'text') }} +
+
+ + {{ Form::text('settings[dhl_sender_city][val]', \App\Models\Setting::getContentBySlug('dhl_sender_city') ?: 'Kirchhaslach', array('class'=>'form-control')) }} + {{ Form::hidden('settings[dhl_sender_city][type]', 'text') }} +
+
+ + {{ Form::select('settings[dhl_sender_country][val]', [ + 'DE' => 'Deutschland', + 'AT' => 'Österreich', + 'CH' => 'Schweiz' + ], \App\Models\Setting::getContentBySlug('dhl_sender_country') ?: 'DE', array('class'=>'form-control custom-select')) }} + {{ Form::hidden('settings[dhl_sender_country][type]', 'text') }} +
+
+ + {{ Form::email('settings[dhl_sender_email][val]', \App\Models\Setting::getContentBySlug('dhl_sender_email') ?: 'versand@mivita.care', array('class'=>'form-control')) }} + {{ Form::hidden('settings[dhl_sender_email][type]', 'text') }} +
+
+ + {{ Form::text('settings[dhl_sender_phone][val]', \App\Models\Setting::getContentBySlug('dhl_sender_phone') ?: '+49 123 456789', array('class'=>'form-control')) }} + {{ Form::hidden('settings[dhl_sender_phone][type]', 'text') }} +
+
+ + + +
+
+
Papierformat
+
+
+ + {{ Form::select('settings[dhl_print_format][val]', [ + 'A4' => 'Common Label Laserdruck A4 Normalpapier', + '910-300-600' => 'Common Label Thermodruck 103 x 199 mm (910-300-600)', + '910-300-610' => 'Common Label Thermodruck 103 x 199 mm (910-300-610)', + '910-300-700' => 'Common Label Laserdruck 105 x 205 mm (DIN A5 Normalpapier, 910-300-700)', + '910-300-700-oz' => 'Common Label Laserdruck 105 x 205 mm (DIN A5 Normalpapier, 910-300-700) ohne Zusatzetiketten', + '910-300-710' => 'Common Label Laserdruck 105 x 208 mm (910-300-710)', + '910-300-300' => 'Common Label Laserdruck 105 x 148 mm (DIN A5 Normalpapier, 910-300-300)', + '910-300-300-oz' => 'Common Label Laserdruck 105 x 148 mm (DIN A5 Normalpapier, 910-300-300) ohne Zusatzetiketten', + '910-300-400' => 'Common Label Thermodruck 103 x 150 mm (910-300-400)', + '910-300-410' => 'Common Label Thermodruck 103 x 150 mm (910-300-410)', + '100x70mm' => '100x70mm', + ], \App\Models\Setting::getContentBySlug('dhl_print_format') ?: 'A4', array('class'=>'form-control custom-select')) }} + {{ Form::hidden('settings[dhl_print_format][type]', 'text') }} +
+
+ + {{ Form::select('settings[dhl_retoure_print_format][val]', [ + 'A4' => 'Common Label Laserdruck A4 Normalpapier', + '910-300-600' => 'Common Label Thermodruck 103 x 199 mm (910-300-600)', + '910-300-610' => 'Common Label Thermodruck 103 x 199 mm (910-300-610)', + '910-300-700' => 'Common Label Laserdruck 105 x 205 mm (DIN A5 Normalpapier, 910-300-700)', + '910-300-700-oz' => 'Common Label Laserdruck 105 x 205 mm (DIN A5 Normalpapier, 910-300-700) ohne Zusatzetiketten', + '910-300-710' => 'Common Label Laserdruck 105 x 208 mm (910-300-710)', + '910-300-300' => 'Common Label Laserdruck 105 x 148 mm (DIN A5 Normalpapier, 910-300-300)', + '910-300-300-oz' => 'Common Label Laserdruck 105 x 148 mm (DIN A5 Normalpapier, 910-300-300) ohne Zusatzetiketten', + '910-300-400' => 'Common Label Thermodruck 103 x 150 mm (910-300-400)', + '910-300-410' => 'Common Label Thermodruck 103 x 150 mm (910-300-410)', + '100x70mm' => '100x70mm', + ], \App\Models\Setting::getContentBySlug('dhl_retoure_print_format') ?: 'A4', array('class'=>'form-control custom-select')) }} + {{ Form::hidden('settings[dhl_retoure_print_format][type]', 'text') }} +
+
+ + + + + + + + + + +
+
{!! Form::close() !!} diff --git a/resources/views/auth/change.blade.php b/resources/views/auth/change.blade.php index 0d90697..cd8abcf 100644 --- a/resources/views/auth/change.blade.php +++ b/resources/views/auth/change.blade.php @@ -40,8 +40,8 @@
- - + +
{{__('portal.back_to_shop')}} diff --git a/resources/views/layouts/includes/layout-sidenav.blade.php b/resources/views/layouts/includes/layout-sidenav.blade.php index a17851b..703020d 100755 --- a/resources/views/layouts/includes/layout-sidenav.blade.php +++ b/resources/views/layouts/includes/layout-sidenav.blade.php @@ -212,6 +212,11 @@ +
  • +
    {{ __('navigation.dhl_cockpit') }}
    +
  • + +
  • @@ -302,6 +307,7 @@
  • +
  • diff --git a/resources/views/status/not_found.blade.php b/resources/views/status/not_found.blade.php index 9ecaa47..6085c79 100755 --- a/resources/views/status/not_found.blade.php +++ b/resources/views/status/not_found.blade.php @@ -35,7 +35,7 @@ {{ __('go to login') }} {{ __('create new password') }}
    - {{ __('back to the homepage') }} + {{ __('back to the homepage') }}
  • diff --git a/resources/views/status/status_error.blade.php b/resources/views/status/status_error.blade.php index c4397a5..0f010b2 100755 --- a/resources/views/status/status_error.blade.php +++ b/resources/views/status/status_error.blade.php @@ -32,7 +32,7 @@

    {{ __('Page not available') }}

    - {{ __('back to the homepage') }} + {{ __('back to the homepage') }} diff --git a/resources/views/status/status_register.blade.php b/resources/views/status/status_register.blade.php index 8010456..781e75f 100755 --- a/resources/views/status/status_register.blade.php +++ b/resources/views/status/status_register.blade.php @@ -33,7 +33,7 @@

    {{ __('Thank you for your registration!') }}

    {{ __('We have sent you an e-mail with a link to activate your data.') }}

    - {{ __('back to the homepage') }} + {{ __('back to the homepage') }} diff --git a/resources/views/status/status_verify.blade.php b/resources/views/status/status_verify.blade.php index 59891e3..58568e1 100755 --- a/resources/views/status/status_verify.blade.php +++ b/resources/views/status/status_verify.blade.php @@ -33,7 +33,7 @@

    {{ __('Now assign a password.') }}

    {{ __('create new password') }}


    - {{ __('back to the homepage') }} + {{ __('back to the homepage') }} diff --git a/resources/views/user/components/user_shop_edit.blade.php b/resources/views/user/components/user_shop_edit.blade.php index 46a637b..1ffdfe5 100644 --- a/resources/views/user/components/user_shop_edit.blade.php +++ b/resources/views/user/components/user_shop_edit.blade.php @@ -70,6 +70,17 @@
  • +
    {{ __('Status') }}
    +
    + @if($user->shop->getSubdomainAvailable()) + {{ __('shop.available') }} + @else + {{ __('shop.not_available') }} + @endif +
    +
  • + + {{--
  • {{ __('Status') }}
    @if($user->shop->getSubdomainStatus()) @@ -94,17 +105,13 @@ @endif
    +
  • --}} +
  • +
    {{ __('shop.active_since') }}
    +
  • - @if(!$user->shop->getSubdomainStatus() || !$user->shop->getSubdomainAvailable() || !$user->shop->getSubdomainSslSinActive()) -
  • -

    {{ __('shop.not_available_copy') }} - - - -

    - -
  • - @endif
  • {{ __('shop.active_since') }}
    diff --git a/resources/views/user/components/user_shop_edit_name.blade.php b/resources/views/user/components/user_shop_edit_name.blade.php new file mode 100644 index 0000000..77f86a2 --- /dev/null +++ b/resources/views/user/components/user_shop_edit_name.blade.php @@ -0,0 +1,174 @@ +
    +

    {{ __('shop.edit_your_shop') }}

    + + {!! Form::open(['action' => route('user_shop_register_form'), 'class' => 'form-horizontal' , 'id'=>'data-shop-form-validations']) !!} + {{ Form::hidden('user_shop_id', $user_shop_id) }} + + @php + $shop_name_btn_color = 'btn-secondary'; + $shop_name_fa = ''; + $shop_name_form_control = ''; + @endphp + @if(Session::has('shop-name-error')) + @if(Session::get('shop-name-error') == 'check') + @php + $shop_name_btn_color = 'btn-success'; + $shop_name_fa = 'fa-check'; + $shop_name_form_control = ' is-valid'; + @endphp + + @endif + @if(Session::get('shop-name-error') == 'error') + @php + $shop_name_btn_color = 'btn-danger'; + $shop_name_fa = 'fa-times'; + $shop_name_form_control = ' is-invalid'; + @endphp + @endif + @endif + + +

    + {!! __('shop.open_copy_1') !!} +

    + {!! __('shop.open_copy_2') !!} +

    + {{ __('shop.open_note_hl') }}
    + {!! __('shop.open_note_1') !!} +


    + {{ __('shop.your_current_shop_name') }} {{ $user_shop_domain }} +

    +
    +
    + {{ Form::text('user_shop_name', old('user_shop_name'), array('placeholder'=>__('shop.your_shop_name'), 'class'=>'form-control'.$shop_name_form_control.($errors->has('user_shop_name') ? ' is-invalid' : ''), 'id'=>'user_shop_name', 'tabindex' => 2)) }} + + + +
    + @if ($errors->has('user_shop_name')) + + {{ $errors->first('user_shop_name') }} + + @endif +
    +
    + {{ Form::text('preview_user_shop_name', '', array('placeholder'=>__('shop.preview_shop_internet_address'), 'class'=>'form-control', 'id'=>'preview_user_shop_name', 'readonly')) }} +
    +
    + + @if ($errors->has('user_shop_active')) + + {{ $errors->first('user_shop_active') }} + + @endif +
    +
    +
    +   + +
    + + {!! Form::close() !!} + +
    + + diff --git a/resources/views/user/components/user_shop_register.blade.php b/resources/views/user/components/user_shop_register.blade.php index febf78c..d40f40a 100644 --- a/resources/views/user/components/user_shop_register.blade.php +++ b/resources/views/user/components/user_shop_register.blade.php @@ -27,9 +27,9 @@

    - {{ __('shop.open_copy_1') }} + {!! __('shop.open_copy_1') !!}

    - {{ __('shop.open_copy_2') }} + {!! __('shop.open_copy_2') !!}

    {{ __('shop.open_note_hl') }}
    {{ __('shop.open_note_1') }} diff --git a/resources/views/user/shop_edit_name.blade.php b/resources/views/user/shop_edit_name.blade.php new file mode 100644 index 0000000..b777f9e --- /dev/null +++ b/resources/views/user/shop_edit_name.blade.php @@ -0,0 +1,23 @@ +@extends('layouts.layout-2') + +@section('content') + + {{-- @if ($errors->any()) +

    +
    +
    +
      + @foreach ($errors->all() as $error) +
    • {{ $error }}
    • + @endforeach +
    +
    +
    +
    + @endif +--}} +

    + {{ __('navigation.my_shop') }} +

    + @include('user.components.user_shop_edit_name') +@endsection diff --git a/resources/views/web/layouts/includes/footer.blade.php b/resources/views/web/layouts/includes/footer.blade.php index 9321b11..585961b 100644 --- a/resources/views/web/layouts/includes/footer.blade.php +++ b/resources/views/web/layouts/includes/footer.blade.php @@ -68,9 +68,10 @@ href="{{url('/geschaeftsmodell/karrierechancen')}}">{{ __('website.career_opportunities') }}
  • {{ __('website.contact') }}
  • -
  • {{ __('website.partner') }}
  • -
  • +
  • {{ __('website.register') }}
  • diff --git a/resources/views/web/user/layouts/includes/footer.blade.php b/resources/views/web/user/layouts/includes/footer.blade.php index adbbaee..7de8b4c 100644 --- a/resources/views/web/user/layouts/includes/footer.blade.php +++ b/resources/views/web/user/layouts/includes/footer.blade.php @@ -101,7 +101,7 @@
  • {{ __('website.partner') }}
  • @if($user_shop) -
  • {{ __('website.register') }}
  • @endif diff --git a/routes/domains/crm.php b/routes/domains/crm.php index d481fbf..cdfce56 100644 --- a/routes/domains/crm.php +++ b/routes/domains/crm.php @@ -11,8 +11,8 @@ /* ROUTING FOR CRM my.mivita / CMS*/ Route::domain(config('app.pre_url_crm') . config('app.domain') . config('app.tld_care'))->group(function () { - Route::get('/cron/jobs/action/{action}/{key}', 'CronController@action')->name('cron_jobs_action'); - Route::get('/cron/jobs/run/{key}', 'CronController@runCron')->name('cron_jobs_run'); + // Route::get('/cron/jobs/action/{action}/{key}', 'CronController@action')->name('cron_jobs_action'); + // Route::get('/cron/jobs/run/{key}', 'CronController@runCron')->name('cron_jobs_run'); Auth::routes(); @@ -38,8 +38,6 @@ Route::domain(config('app.pre_url_crm') . config('app.domain') . config('app.tld Route::get('/user/update_email_confirm/{token}', 'UserUpdateEmailController@activateMail')->name('user_update_email_confirm'); Route::post('/user/check/mail', 'HomeController@checkMail')->name('user_check_mail'); - Route::get('/register/verify/{confirmationCode}', 'HomeController@verify')->name('register_verify'); - Route::get('/status/register', 'HomeController@statusRegister')->name('status_register'); Route::get('/status/verify', 'HomeController@statusVerify')->name('status_verify'); Route::get('/status/error', 'HomeController@statusError')->name('status_error'); @@ -65,24 +63,14 @@ Route::domain(config('app.pre_url_crm') . config('app.domain') . config('app.tld Route::get('/wizard/delete/file/{id}/{relation}', 'WizardController@delete')->name('wizard_delete_file'); - Route::get('/storage/file/{id}/{disk}', function ($id = null, $disk = null) { - // Prüfe ob der Disk existiert und nur 'public' erlaubt ist - if($disk != 'public' || !config("filesystems.disks.{$disk}")){ - abort(404); - } - $file = \App\Models\File::findOrFail($id); - $path = Storage::disk($disk)->path($file->dir . $file->filename); - if (file_exists($path)) { - return Response::file($path); - } - })->name('storage_file'); - - Route::get('/storage/file/{id}/{from}/{do?}', 'FileController@show')->name('storage_file'); + // storage_file Route wurde nach common.php verschoben für domain-übergreifende Nutzung }); Route::group(['middleware' => ['auth:user']], function () { Route::get('storage/{type?}/{file?}', function ($type = null, $file = null) { + $path = ""; + $filename = ""; if ($type == 'xls') { $path = storage_path("app/export/"); $filename = $file . '.xls'; @@ -124,6 +112,7 @@ Route::domain(config('app.pre_url_crm') . config('app.domain') . config('app.tld Route::post('/user/shop/store', 'UserShopController@store')->name('user_shop_store'); Route::post('/user/shop/register/form', 'UserShopController@userShopRegisterForm')->name('user_shop_register_form'); Route::post('/user/shop/name/check', 'UserShopController@checkUserShopName')->name('user_shop_name_check'); + Route::get('/user/shop/name/edit', 'UserShopController@editName')->name('user_shop_name_edit'); Route::get('/user/shop/translate', 'UserShopController@translate')->name('user_shop_translate'); Route::post('/user/shop/translate/store', 'UserShopController@translateStore')->name('user_shop_tanslate_store'); Route::post('/user/shop/upload/image', 'UserShopController@uploadImage')->name('user_shop_upload_image'); @@ -278,8 +267,21 @@ Route::domain(config('app.pre_url_crm') . config('app.domain') . config('app.tld //products images Route::post('/admin/product/category/image/upload', 'CategoryController@imageUpload')->name('admin_product_category_image_upload'); Route::get('/admin/product/category/image/delete{image_id}/{category_id}', 'CategoryController@imageDelete')->name('admin_product_category_image_delete'); - Route::get('/admin/product/category/image/attribute/{image_id}/{attr}/{val}', 'CategoryController@imageAttribute')->name('admin_product_category_image_attribute'); + // DHL Shipping Routes + Route::prefix('admin/dhl')->group(function () { + Route::get('/', 'DhlShipmentController@index')->name('admin.dhl.cockpit'); + Route::post('/datatable', 'DhlShipmentController@datatable')->name('admin.dhl.datatable'); + Route::get('/shipment/{shipment}', 'DhlShipmentController@show')->name('admin.dhl.show'); + Route::post('/shipment', 'DhlShipmentController@store')->name('admin.dhl.store'); + Route::delete('/shipment/{shipment}/cancel', 'DhlShipmentController@cancel')->name('admin.dhl.cancel'); + Route::post('/shipment/{shipment}/return-label', 'DhlShipmentController@createReturnLabel')->name('admin.dhl.create-return'); + Route::post('/shipment/{shipment}/update-tracking', 'DhlShipmentController@updateTracking')->name('admin.dhl.update-tracking'); + Route::get('/shipment/{shipment}/download-label', 'DhlShipmentController@downloadLabel')->name('admin.dhl.download-label'); + Route::post('/batch-action', 'DhlShipmentController@batchAction')->name('admin.dhl.batch-action'); + Route::post('/test-login', 'DhlShipmentController@testLogin')->name('admin.dhl.test_login'); + Route::get('/public/track', 'DhlShipmentController@track')->name('public.tracking'); + }); //products attributes Route::get('/admin/product/attributes', 'AttributeController@index')->name('admin_product_attributes'); @@ -438,7 +440,6 @@ Route::domain(config('app.pre_url_crm') . config('app.domain') . config('app.tld Route::get('/admin/settings', 'SettingController@index')->name('admin_settings'); Route::post('/admin/setting/store', 'SettingController@store')->name('admin_setting_store'); - }); //login pages for sysadmin diff --git a/routes/domains/main.php b/routes/domains/main.php index 3a70d2a..d9de714 100644 --- a/routes/domains/main.php +++ b/routes/domains/main.php @@ -17,10 +17,15 @@ Route::get('/agb', 'HomeController@legalAGB')->name('agb'); Route::get('/kontakt', 'Web\ContactController@create')->name('contact_create'); Route::post('/kontakt', 'Web\ContactController@store')->name('contact_store'); -Route::get('/registrierung', 'Web\RegisterController@index')->name('register_user'); -Route::get('/reg/{member_id?}', 'Web\RegisterController@member')->name('register_user_member'); -Route::post('/registrierung', 'Web\RegisterController@register')->name('register_user'); -Route::get('/registrierung/finish', 'Web\RegisterController@finish')->name('register_user_finish'); +// Public DHL Tracking +Route::get('/tracking', 'DhlShipmentController@track')->name('public.tracking'); +Route::post('/tracking', 'DhlShipmentController@track')->name('public.tracking.check'); + +Route::get('/registrierung', 'Web\RegisterController@index')->name('main.register_user'); +Route::get('/reg/{member_id?}', 'Web\RegisterController@member')->name('main.register_user_member'); +Route::post('/registrierung', 'Web\RegisterController@register')->name('main.register_user_post'); +Route::get('/registrierung/finish', 'Web\RegisterController@finish')->name('main.register_user_finish'); Route::get('/', 'Web\SiteController@index')->name('/'); Route::get('/{site}/{subsite?}/{product_slug?}', 'Web\SiteController@site')->name('base.site'); + diff --git a/routes/domains/portal.php b/routes/domains/portal.php index 8a173cb..ec1f9a5 100644 --- a/routes/domains/portal.php +++ b/routes/domains/portal.php @@ -6,11 +6,12 @@ |-------------------------------------------------------------------------- | */ -use App\Http\Controllers\Portal\AboController; -use App\Http\Controllers\Portal\Auth\LoginController; -use App\Http\Controllers\Portal\CustomerController; +use App\Http\Controllers\FileController; use App\Http\Controllers\Portal\InController; +use App\Http\Controllers\Portal\AboController; use App\Http\Controllers\Portal\OrderController; +use App\Http\Controllers\Portal\CustomerController; +use App\Http\Controllers\Portal\Auth\LoginController; @@ -37,6 +38,7 @@ Route::domain(config('app.pre_url_portal') . config('app.domain') . config('app. // Geschützte Kunden-Routen Route::middleware('auth:customers')->group(function () { + Route::get('portal/dashboard', [InController::class, 'dashboard'])->name('portal.dashboard'); //Route::get('portal/go-to-shop', [InController::class, 'goToShop'])->name('portal.go_to_shop'); Route::get('portal/my-data/edit', [CustomerController::class, 'myDataEdit'])->name('portal.my_data.edit'); diff --git a/routes/shared/common.php b/routes/shared/common.php index 5fce8bf..584a5e8 100644 --- a/routes/shared/common.php +++ b/routes/shared/common.php @@ -16,37 +16,37 @@ if ($context->type === 'checkout') { // Für Checkout-Domain: Umleitung zur Shop-Domain Route::get('/datenschutz', function () { $domainService = app(\App\Services\DomainService::class); - $shopUrl = $domainService->buildUrl('main-shop', '/datenschutz'); + $shopUrl = $domainService->buildUrl('shop', '/datenschutz'); return redirect()->away($shopUrl); })->name('legal.data-protected'); Route::get('/impressum', function () { $domainService = app(\App\Services\DomainService::class); - $shopUrl = $domainService->buildUrl('main-shop', '/impressum'); + $shopUrl = $domainService->buildUrl('shop', '/impressum'); return redirect()->away($shopUrl); })->name('legal.imprint'); Route::get('/agb', function () { $domainService = app(\App\Services\DomainService::class); - $shopUrl = $domainService->buildUrl('main-shop', '/agb'); + $shopUrl = $domainService->buildUrl('shop', '/agb'); return redirect()->away($shopUrl); })->name('legal.agb'); Route::get('/kontakt', function () { $domainService = app(\App\Services\DomainService::class); - $shopUrl = $domainService->buildUrl('main-shop', '/kontakt'); + $shopUrl = $domainService->buildUrl('shop', '/kontakt'); return redirect()->away($shopUrl); })->name('contact.create'); Route::get('/zahlungsarten', function () { $domainService = app(\App\Services\DomainService::class); - $shopUrl = $domainService->buildUrl('main-shop', '/zahlungsarten'); + $shopUrl = $domainService->buildUrl('shop', '/zahlungsarten'); return redirect()->away($shopUrl); })->name('zahlungsarten'); Route::get('/versandkosten', function () { $domainService = app(\App\Services\DomainService::class); - $shopUrl = $domainService->buildUrl('main-shop', '/versandkosten'); + $shopUrl = $domainService->buildUrl('shop', '/versandkosten'); return redirect()->away($shopUrl); })->name('versandkosten'); } else { @@ -59,37 +59,10 @@ if ($context->type === 'checkout') { Route::get('/zahlungsarten', 'HomeController@zahlungsarten')->name('zahlungsarten'); Route::get('/versandkosten', 'HomeController@versandkosten')->name('versandkosten'); } -//debug subdomain -Route::get('/debug-subdomain', function () { - $domainContext = app(\App\Domain\DomainContext::class); - $domainService = app(\App\Services\DomainService::class); - - return response()->json([ - 'host' => request()->getHost(), - 'headers' => [ - 'X-Forwarded-Host' => request()->header('X-Forwarded-Host'), - 'X-Subdomain' => request()->header('X-Subdomain'), - 'Host' => request()->header('Host'), - ], - 'url' => request()->url(), - 'full_url' => request()->fullUrl(), - 'server' => [ - 'HTTP_HOST' => $_SERVER['HTTP_HOST'] ?? 'not set', - 'SERVER_NAME' => $_SERVER['SERVER_NAME'] ?? 'not set', - ], - 'domain_context' => [ - 'type' => $domainContext->type, - 'host' => $domainContext->host, - 'subdomain' => $domainContext->subdomain, - 'cloudflare_subdomain' => $domainContext->cloudflareSubdomain, - 'effective_subdomain' => $domainContext->getEffectiveSubdomain(), - 'is_cloudflare' => $domainContext->isCloudflareSubdomain(), - 'is_user_shop' => $domainContext->isUserShop(), - 'user_shop_slug' => $domainContext->getUserShopSlug(), - ], - 'parsed_domain' => $domainService->parseDomain(request()->getHost(), request()->header('X-Subdomain')) - ]); -}); + +// File storage route für alle Domains (Rechnungen, Downloads, etc.) +// Auth-Prüfung erfolgt im FileController selbst je nach Kontext +Route::get('/storage/file/{id}/{from}/{do?}', 'FileController@show')->name('storage_file'); // Sprachwechsler Route::post('/change_website_lang', 'Web\SiteController@changeLang')->name('language.change'); @@ -199,3 +172,6 @@ if ($context->type !== 'checkout') { } +Route::get('/register/verify/{confirmationCode}', 'HomeController@verify')->name('register_verify'); + +